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.parsers;
21  
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.nio.charset.Charset;
28  import java.nio.file.Files;
29  import java.nio.file.Paths;
30  import java.text.ParseException;
31  import java.util.ArrayList;
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.Map;
35  
36  import org.apache.commons.lang.exception.ExceptionUtils;
37  import org.apache.directory.api.i18n.I18n;
38  import org.apache.directory.api.ldap.model.schema.AttributeType;
39  import org.apache.directory.api.ldap.model.schema.MutableAttributeType;
40  import org.apache.directory.api.ldap.model.schema.ObjectClass;
41  import org.apache.directory.api.ldap.model.schema.SchemaObject;
42  import org.apache.directory.api.ldap.model.schema.syntaxCheckers.OpenLdapObjectIdentifierMacro;
43  
44  import antlr.RecognitionException;
45  import antlr.TokenStreamException;
46  
47  
48  /**
49   * A reusable wrapper for antlr generated OpenLDAP schema parsers.
50   *
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   */
53  public class OpenLdapSchemaParser extends AbstractSchemaParser<SchemaObject>
54  {
55  
56      /** The list of parsed schema descriptions */
57      private List<Object> schemaDescriptions;
58  
59      /** The list of attribute type, initialized by splitParsedSchemaDescriptions() */
60      private List<MutableAttributeType> attributeTypes;
61  
62      /** The list of object classes, initialized by splitParsedSchemaDescriptions()*/
63      private List<ObjectClass> objectClasses;
64  
65      /** The map of object identifier macros, initialized by splitParsedSchemaDescriptions()*/
66      private Map<String, OpenLdapObjectIdentifierMacro> objectIdentifierMacros;
67  
68      /** Flag whether object identifier macros should be resolved. */
69      private boolean isResolveObjectIdentifierMacros;
70  
71  
72      /**
73       * Creates a reusable instance of an OpenLdapSchemaParser.
74       *
75       * @throws IOException if the pipe cannot be formed
76       */
77      public OpenLdapSchemaParser() throws IOException
78      {
79          super( null, null, null, null );
80          isResolveObjectIdentifierMacros = true;
81          super.setQuirksMode( true );
82      }
83  
84  
85      @Override
86      protected SchemaObject doParse() throws RecognitionException, TokenStreamException
87      {
88          throw new UnsupportedOperationException( "OpenLdapSchemaParser is not a normal schema parser" );
89      }
90  
91  
92      /**
93       * Reset the parser
94       */
95      public void clear()
96      {
97      }
98  
99  
100     /**
101      * Gets the attribute types.
102      * 
103      * @return the attribute types
104      */
105     public List<MutableAttributeType> getAttributeTypes()
106     {
107         return attributeTypes;
108     }
109 
110 
111     /**
112      * Gets the object class types.
113      * 
114      * @return the object class types
115      */
116     public List<ObjectClass> getObjectClassTypes()
117     {
118         return objectClasses;
119     }
120 
121 
122     /**
123      * Gets the object identifier macros.
124      * 
125      * @return the object identifier macros
126      */
127     public Map<String, OpenLdapObjectIdentifierMacro> getObjectIdentifierMacros()
128     {
129         return objectIdentifierMacros;
130     }
131 
132 
133     /**
134      * Splits parsed schema descriptions and resolved
135      * object identifier macros.
136      * 
137      * @throws ParseException the parse exception
138      */
139     private void afterParse() throws ParseException
140     {
141         objectClasses = new ArrayList<>();
142         attributeTypes = new ArrayList<>();
143         objectIdentifierMacros = new HashMap<>();
144 
145         // split parsed schema descriptions
146         for ( Object obj : schemaDescriptions )
147         {
148             if ( obj instanceof OpenLdapObjectIdentifierMacro )
149             {
150                 OpenLdapObjectIdentifierMacro oid = ( OpenLdapObjectIdentifierMacro ) obj;
151                 objectIdentifierMacros.put( oid.getName(), oid );
152             }
153             else if ( obj instanceof AttributeType )
154             {
155                 MutableAttributeType attributeType = ( MutableAttributeType ) obj;
156 
157                 attributeTypes.add( attributeType );
158             }
159             else if ( obj instanceof ObjectClass )
160             {
161                 ObjectClass objectClass = ( ObjectClass ) obj;
162 
163                 objectClasses.add( objectClass );
164             }
165         }
166 
167         if ( isResolveObjectIdentifierMacros() )
168         {
169             // resolve object identifier macros
170             for ( OpenLdapObjectIdentifierMacro oid : objectIdentifierMacros.values() )
171             {
172                 resolveObjectIdentifierMacro( oid );
173             }
174 
175             // apply object identifier macros to object classes
176             for ( ObjectClass objectClass : objectClasses )
177             {
178                 objectClass.setOid( getResolveOid( objectClass.getOid() ) );
179             }
180 
181             // apply object identifier macros to attribute types
182             for ( MutableAttributeType attributeType : attributeTypes )
183             {
184                 attributeType.setOid( getResolveOid( attributeType.getOid() ) );
185                 attributeType.setSyntaxOid( getResolveOid( attributeType.getSyntaxOid() ) );
186             }
187 
188         }
189     }
190 
191 
192     private String getResolveOid( String oid )
193     {
194         if ( oid != null && oid.indexOf( ':' ) != -1 )
195         {
196             // resolve OID
197             String[] nameAndSuffix = oid.split( ":" );
198             if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) )
199             {
200                 OpenLdapObjectIdentifierMacro macro = objectIdentifierMacros.get( nameAndSuffix[0] );
201                 return macro.getResolvedOid() + "." + nameAndSuffix[1];
202             }
203         }
204         return oid;
205     }
206 
207 
208     private void resolveObjectIdentifierMacro( OpenLdapObjectIdentifierMacro macro ) throws ParseException
209     {
210         String rawOidOrNameSuffix = macro.getRawOidOrNameSuffix();
211 
212         if ( macro.isResolved() )
213         {
214             // finished
215             return;
216         }
217         else if ( rawOidOrNameSuffix.indexOf( ':' ) != -1 )
218         {
219             // resolve OID
220             String[] nameAndSuffix = rawOidOrNameSuffix.split( ":" );
221             if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) )
222             {
223                 OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( nameAndSuffix[0] );
224                 resolveObjectIdentifierMacro( parentMacro );
225                 macro.setResolvedOid( parentMacro.getResolvedOid() + "." + nameAndSuffix[1] );
226             }
227             else
228             {
229                 throw new ParseException( I18n.err( I18n.ERR_04257, nameAndSuffix[0] ), 0 );
230             }
231 
232         }
233         else
234         {
235             // no :suffix,
236             if ( objectIdentifierMacros.containsKey( rawOidOrNameSuffix ) )
237             {
238                 OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( rawOidOrNameSuffix );
239                 resolveObjectIdentifierMacro( parentMacro );
240                 macro.setResolvedOid( parentMacro.getResolvedOid() );
241             }
242             else
243             {
244                 macro.setResolvedOid( rawOidOrNameSuffix );
245             }
246         }
247     }
248 
249 
250     /**
251      * Parses an OpenLDAP schemaObject element/object.
252      *
253      * @param schemaObject the String image of a complete schema object
254      * @return the schema object
255      * @throws ParseException If the schemaObject can't be parsed
256      */
257     @Override
258     public SchemaObject parse( String schemaObject ) throws ParseException
259     {
260         if ( schemaObject == null || ( schemaObject.trim().length() == 0 ) )
261         {
262             throw new ParseException( I18n.err( I18n.ERR_04258 ), 0 );
263         }
264 
265         // reset and initialize the parser / lexer pair
266         reset( schemaObject );
267         invokeParser( schemaObject );
268 
269         if ( !schemaDescriptions.isEmpty() )
270         {
271             for ( Object obj : schemaDescriptions )
272             {
273                 if ( obj instanceof SchemaObject )
274                 {
275                     return ( SchemaObject ) obj;
276                 }
277             }
278         }
279         return null;
280     }
281 
282 
283     private void invokeParser( String subject ) throws ParseException
284     {
285         try
286         {
287             monitor.startedParse( "starting parse on:\n" + subject );
288             schemaDescriptions = parser.openLdapSchema();
289             afterParse();
290             monitor.finishedParse( "Done parsing!" );
291         }
292         catch ( RecognitionException re )
293         {
294             String msg = "Parser failure on:\n\t" + subject;
295             msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( re );
296             throw new ParseException( msg, re.getColumn() );
297         }
298         catch ( TokenStreamException tse )
299         {
300             String msg = "Parser failure on:\n\t" + subject;
301             msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( tse );
302             throw new ParseException( msg, 0 );
303         }
304     }
305 
306 
307     /**
308      * Parses a stream of OpenLDAP schemaObject elements/objects. Default charset is used.
309      *
310      * @param schemaIn a stream of schema objects
311      * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream
312      * @throws ParseException If the schemaObject can't be parsed
313      */
314     public void parse( InputStream schemaIn ) throws IOException, ParseException
315     {
316         InputStreamReader in = new InputStreamReader( schemaIn, Charset.defaultCharset() );
317         lexer.prepareNextInput( in );
318         parser.resetState();
319 
320         invokeParser( "schema input stream ==> " + schemaIn.toString() );
321     }
322 
323 
324     /**
325      * Parses a file of OpenLDAP schemaObject elements/objects. Default charset is used.
326      *
327      * @param schemaFile a file of schema objects
328      * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream
329      * @throws ParseException If the schemaObject can't be parsed
330      */
331     public void parse( File schemaFile ) throws IOException, ParseException
332     {
333         InputStreamReader in = new InputStreamReader(
334             Files.newInputStream( Paths.get( schemaFile.getPath() ) ), Charset.defaultCharset() );
335         lexer.prepareNextInput( in );
336         parser.resetState();
337 
338         invokeParser( "schema file ==> " + schemaFile.getAbsolutePath() );
339     }
340 
341 
342     /**
343      * Checks if object identifier macros should be resolved.
344      * 
345      * @return true, object identifier macros should be resolved.
346      */
347     public boolean isResolveObjectIdentifierMacros()
348     {
349         return isResolveObjectIdentifierMacros;
350     }
351 
352 
353     /**
354      * Sets if object identifier macros should be resolved.
355      * 
356      * @param resolveObjectIdentifierMacros true if object identifier macros should be resolved
357      */
358     public void setResolveObjectIdentifierMacros( boolean resolveObjectIdentifierMacros )
359     {
360         this.isResolveObjectIdentifierMacros = resolveObjectIdentifierMacros;
361     }
362 
363 }