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