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