View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.api.ldap.model.schema;
21  
22  
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Map.Entry;
30  import java.util.TreeMap;
31  
32  import org.apache.directory.api.util.Strings;
33  
34  
35  /**
36   * Various utility methods for sorting schema objects.
37   * 
38   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
39   */
40  public final class SchemaObjectSorter
41  {
42      private SchemaObjectSorter()
43      {
44      }
45  
46  
47      /**
48       * Gets an hierarchical ordered {@link Iterable} of the given {@link AttributeType}s. 
49       * In other words parent {@link AttributeType}s are returned before child {@link AttributeType}s.
50       * @param attributeTypes list of attribute types to order
51       * @return the hierarchical ordered attribute types
52       */
53      public static Iterable<AttributeType> hierarchicalOrdered( List<AttributeType> attributeTypes )
54      {
55          return new SchemaObjectIterable<>( attributeTypes, new ReferenceCallback<AttributeType>()
56          {
57              @Override
58              public Collection<String> getSuperiorOids( AttributeType at )
59              {
60                  return Collections.singleton( at.getSuperiorOid() );
61              }
62          } );
63      }
64  
65  
66      /**
67       * Gets an hierarchical ordered {@link Iterable} of the given {@link ObjectClass}es. 
68       * In other words parent {@link ObjectClass}es are returned before child {@link ObjectClass}es.
69       * @param objectClasses list of object classes to order
70       * @return the hierarchical ordered object classes
71       */
72      public static Iterable<ObjectClass> sortObjectClasses( List<ObjectClass> objectClasses )
73      {
74          return new SchemaObjectIterable<>( objectClasses, new ReferenceCallback<ObjectClass>()
75          {
76              @Override
77              public Collection<String> getSuperiorOids( ObjectClass oc )
78              {
79                  return oc.getSuperiorOids();
80              }
81          } );
82      }
83  
84      private interface ReferenceCallback<T extends SchemaObject>
85      {
86  
87          Collection<String> getSuperiorOids( T schemaObject );
88  
89      }
90  
91      private static final class SchemaObjectIterable<T extends SchemaObject> implements Iterable<T>
92      {
93  
94          private final List<T> schemaObjects;
95          private final ReferenceCallback<T> callback;
96  
97  
98          private SchemaObjectIterable( List<T> schemaObjects, ReferenceCallback<T> callback )
99          {
100             this.schemaObjects = schemaObjects;
101             this.callback = callback;
102         }
103 
104 
105         @Override
106         public Iterator<T> iterator()
107         {
108             return new SchemaObjectIterator<>( schemaObjects, callback );
109         }
110 
111     }
112 
113     private static final class SchemaObjectIterator<T extends SchemaObject> implements Iterator<T>
114     {
115         private final List<T> schemaObjects;
116         private final ReferenceCallback<T> callback;
117 
118         private final Map<String, String> oid2numericOid;
119         private final Map<String, T> numericOid2schemaObject;
120 
121         private int loopCount;
122         private Iterator<Entry<String, T>> schemaObjectIterator;
123 
124 
125         private SchemaObjectIterator( List<T> schemaObjects, ReferenceCallback<T> callback )
126         {
127             this.schemaObjects = schemaObjects;
128             this.callback = callback;
129 
130             this.oid2numericOid = new HashMap<>();
131             this.numericOid2schemaObject = new TreeMap<>();
132             this.loopCount = 0;
133 
134             for ( T schemaObject : schemaObjects )
135             {
136                 String oid = Strings.toLowerCaseAscii( schemaObject.getOid() );
137                 oid2numericOid.put( oid, oid );
138                 
139                 for ( String name : schemaObject.getNames() )
140                 {
141                     oid2numericOid.put( Strings.toLowerCaseAscii( name ), oid );
142                 }
143                 
144                 numericOid2schemaObject.put( oid, schemaObject );
145             }
146         }
147 
148 
149         @Override
150         public boolean hasNext()
151         {
152             return !numericOid2schemaObject.isEmpty();
153         }
154 
155 
156         @Override
157         public T next()
158         {
159             while ( !maxLoopCountReached() )
160             {
161                 Iterator<Entry<String, T>> iterator = getIterator();
162 
163                 while ( iterator.hasNext() )
164                 {
165                     Entry<String, T> entry = iterator.next();
166                     T schemaObject = entry.getValue();
167 
168                     Collection<String> superiorOids = callback.getSuperiorOids( schemaObject );
169 
170                     // schema object has no superior
171                     if ( superiorOids == null )
172                     {
173                         iterator.remove();
174                         return schemaObject;
175                     }
176 
177                     boolean allSuperiorsProcessed = true;
178 
179                     for ( String superiorOid : superiorOids )
180                     {
181                         if ( superiorOid == null )
182                         {
183                             continue;
184                         }
185 
186                         String superiorNumeridOid = oid2numericOid.get( Strings.toLowerCaseAscii( superiorOid ) );
187 
188                         // AT's superior is not within the processed AT list
189                         if ( superiorNumeridOid == null )
190                         {
191                             continue;
192                         }
193 
194                         T superiorSchemaObject = numericOid2schemaObject.get( Strings.toLowerCaseAscii( superiorNumeridOid ) );
195 
196                         // AT's superior was already removed
197                         if ( superiorSchemaObject == null )
198                         {
199                             continue;
200                         }
201 
202                         allSuperiorsProcessed = false;
203                         break;
204                     }
205 
206                     if ( allSuperiorsProcessed )
207                     {
208                         iterator.remove();
209                         return schemaObject;
210                     }
211                 }
212             }
213             throw new IllegalStateException( "Loop detected: " + numericOid2schemaObject.values() );
214         }
215 
216 
217         private Iterator<Entry<String, T>> getIterator()
218         {
219             if ( schemaObjectIterator != null && schemaObjectIterator.hasNext() )
220             {
221                 return schemaObjectIterator;
222             }
223 
224             if ( !maxLoopCountReached() )
225             {
226                 schemaObjectIterator = numericOid2schemaObject.entrySet().iterator();
227                 loopCount++;
228                 return schemaObjectIterator;
229             }
230 
231             throw new IllegalStateException( "Loop detected: " + numericOid2schemaObject.values() );
232         }
233 
234 
235         private boolean maxLoopCountReached()
236         {
237             return loopCount > schemaObjects.size();
238         }
239 
240 
241         @Override
242         public void remove()
243         {
244             throw new UnsupportedOperationException();
245         }
246 
247     }
248 
249 }