001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.schema.parsers;
021    
022    
023    import java.io.File;
024    import java.io.FileReader;
025    import java.io.IOException;
026    import java.io.InputStream;
027    import java.io.InputStreamReader;
028    import java.text.ParseException;
029    import java.util.ArrayList;
030    import java.util.HashMap;
031    import java.util.List;
032    import java.util.Map;
033    
034    import org.apache.directory.shared.ldap.schema.syntaxes.OpenLdapObjectIdentifierMacro;
035    import org.apache.directory.shared.ldap.util.ExceptionUtils;
036    
037    import antlr.RecognitionException;
038    import antlr.TokenStreamException;
039    
040    
041    /**
042     * A reusable wrapper for antlr generated OpenLDAP schema parsers.
043     *
044     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045     * @version $Rev: 494164 $
046     */
047    public class OpenLdapSchemaParser extends AbstractSchemaParser
048    {
049    
050        /** The list of parsed schema descriptions */
051        private List<Object> schemaDescriptions;
052    
053        /** The list of attribute type literals, initialized by splitParsedSchemaDescriptions() */
054        private List<AttributeTypeLiteral> attributeTypeLiterals;
055    
056        /** The list of object class literals, initialized by splitParsedSchemaDescriptions()*/
057        private List<ObjectClassLiteral> objectClassLiterals;
058    
059        /** The map of object identifier macros, initialized by splitParsedSchemaDescriptions()*/
060        private Map<String, OpenLdapObjectIdentifierMacro> objectIdentifierMacros;
061    
062        /** Flag whether object identifier macros should be resolved. */
063        private boolean isResolveObjectIdentifierMacros;
064    
065    
066        /**
067         * Creates a reusable instance of an OpenLdapSchemaParser.
068         *
069         * @throws IOException if the pipe cannot be formed
070         */
071        public OpenLdapSchemaParser() throws IOException
072        {
073            isResolveObjectIdentifierMacros = true;
074            super.setQuirksMode( true );
075        }
076    
077    
078        /**
079         * Reset the parser 
080         */
081        public void clear()
082        {
083        }
084    
085    
086        /**
087         * Gets the attribute types.
088         * 
089         * @return the attribute types
090         */
091        public List<AttributeTypeLiteral> getAttributeTypes()
092        {
093            return attributeTypeLiterals;
094        }
095    
096    
097        /**
098         * Gets the object class types.
099         * 
100         * @return the object class types
101         */
102        public List<ObjectClassLiteral> getObjectClassTypes()
103        {
104            return objectClassLiterals;
105        }
106    
107    
108        /**
109         * Gets the object identifier macros.
110         * 
111         * @return the object identifier macros
112         */
113        public Map<String, OpenLdapObjectIdentifierMacro> getObjectIdentifierMacros()
114        {
115            return objectIdentifierMacros;
116        }
117    
118    
119        /**
120         * Splits parsed schema descriptions and resolved
121         * object identifier macros.
122         * 
123         * @throws ParseException the parse exception
124         */
125        private void afterParse() throws ParseException
126        {
127            objectClassLiterals = new ArrayList<ObjectClassLiteral>();
128            attributeTypeLiterals = new ArrayList<AttributeTypeLiteral>();
129            objectIdentifierMacros = new HashMap<String, OpenLdapObjectIdentifierMacro>();
130    
131            // split parsed schema descriptions
132            for ( Object obj : schemaDescriptions )
133            {
134                if ( obj instanceof OpenLdapObjectIdentifierMacro )
135                {
136                    OpenLdapObjectIdentifierMacro oid = ( OpenLdapObjectIdentifierMacro ) obj;
137                    objectIdentifierMacros.put( oid.getName(), oid );
138                }
139                else if ( obj instanceof AttributeTypeDescription )
140                {
141                    AttributeTypeDescription atd = ( AttributeTypeDescription ) obj;
142                    AttributeTypeLiteral literal = new AttributeTypeLiteral( atd.getNumericOid() );
143                    literal.setNames( atd.getNames().toArray( new String[atd.getNames().size()] ) );
144                    literal.setDescription( atd.getDescription() );
145                    literal.setSuperior( atd.getSuperType() );
146                    literal.setEquality( atd.getEqualityMatchingRule() );
147                    literal.setOrdering( atd.getOrderingMatchingRule() );
148                    literal.setSubstr( atd.getSubstringsMatchingRule() );
149                    literal.setSyntax( atd.getSyntax() );
150                    literal.setLength( atd.getSyntaxLength() );
151                    literal.setObsolete( atd.isObsolete() );
152                    literal.setCollective( atd.isCollective() );
153                    literal.setSingleValue( atd.isSingleValued() );
154                    literal.setNoUserModification( !atd.isUserModifiable() );
155                    literal.setUsage( atd.getUsage() );
156                    attributeTypeLiterals.add( literal );
157                }
158                else if ( obj instanceof ObjectClassDescription )
159                {
160                    ObjectClassDescription ocd = ( ObjectClassDescription ) obj;
161                    ObjectClassLiteral literal = new ObjectClassLiteral( ocd.getNumericOid() );
162                    literal.setNames( ocd.getNames().toArray( new String[ocd.getNames().size()] ) );
163                    literal.setDescription( ocd.getDescription() );
164                    literal.setSuperiors( ocd.getSuperiorObjectClasses().toArray(
165                        new String[ocd.getSuperiorObjectClasses().size()] ) );
166                    literal.setMay( ocd.getMayAttributeTypes().toArray( new String[ocd.getMayAttributeTypes().size()] ) );
167                    literal.setMust( ocd.getMustAttributeTypes().toArray( new String[ocd.getMustAttributeTypes().size()] ) );
168                    literal.setClassType( ocd.getKind() );
169                    literal.setObsolete( ocd.isObsolete() );
170                    objectClassLiterals.add( literal );
171                }
172            }
173    
174            if ( isResolveObjectIdentifierMacros() )
175            {
176                // resolve object identifier macros
177                for ( OpenLdapObjectIdentifierMacro oid : objectIdentifierMacros.values() )
178                {
179                    resolveObjectIdentifierMacro( oid );
180                }
181    
182                // apply object identifier macros to object classes and attribute types
183                for ( ObjectClassLiteral ocl : objectClassLiterals )
184                {
185                    ocl.setOid( getResolveOid( ocl.getOid() ) );
186                }
187                for ( AttributeTypeLiteral atl : attributeTypeLiterals )
188                {
189                    atl.setOid( getResolveOid( atl.getOid() ) );
190                    atl.setSyntax( getResolveOid( atl.getSyntax() ) );
191                }
192            }
193    
194        }
195    
196    
197        private String getResolveOid( String oid )
198        {
199            if ( oid != null && oid.indexOf( ':' ) != -1 )
200            {
201                // resolve OID
202                String[] nameAndSuffix = oid.split( ":" );
203                if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) )
204                {
205                    OpenLdapObjectIdentifierMacro macro = objectIdentifierMacros.get( nameAndSuffix[0] );
206                    return macro.getResolvedOid() + "." + nameAndSuffix[1];
207                }
208            }
209            return oid;
210        }
211    
212    
213        private void resolveObjectIdentifierMacro( OpenLdapObjectIdentifierMacro macro ) throws ParseException
214        {
215            String rawOidOrNameSuffix = macro.getRawOidOrNameSuffix();
216    
217            if ( macro.isResolved() )
218            {
219                // finished
220            }
221            else if ( rawOidOrNameSuffix.indexOf( ':' ) != -1 )
222            {
223                // resolve OID
224                String[] nameAndSuffix = rawOidOrNameSuffix.split( ":" );
225                if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) )
226                {
227                    OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( nameAndSuffix[0] );
228                    resolveObjectIdentifierMacro( parentMacro );
229                    macro.setResolvedOid( parentMacro.getResolvedOid() + "." + nameAndSuffix[1] );
230                }
231                else
232                {
233                    throw new ParseException( "No object identifier macro with name " + nameAndSuffix[0], 0 );
234                }
235    
236            }
237            else
238            {
239                // no :suffix, 
240                if ( objectIdentifierMacros.containsKey( rawOidOrNameSuffix ) )
241                {
242                    OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( rawOidOrNameSuffix );
243                    resolveObjectIdentifierMacro( parentMacro );
244                    macro.setResolvedOid( parentMacro.getResolvedOid() );
245                }
246                else
247                {
248                    macro.setResolvedOid( rawOidOrNameSuffix );
249                }
250            }
251        }
252    
253    
254        /**
255         * Parses an OpenLDAP schemaObject element/object.
256         *
257         * @param schemaObject the String image of a complete schema object
258         * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream
259         * @throws ParseException If the schemaObject can't be parsed
260         */
261        public AbstractSchemaDescription parse( String schemaObject ) throws ParseException
262        {
263            if ( schemaObject == null || schemaObject.trim().equals( "" ) )
264            {
265                throw new ParseException( "The schemaObject is either null or is " + "the empty String!", 0 );
266            }
267    
268            reset( schemaObject ); // reset and initialize the parser / lexer pair
269            invokeParser( schemaObject );
270    
271            if ( !schemaDescriptions.isEmpty() )
272            {
273                for ( Object obj : schemaDescriptions )
274                {
275                    if ( obj instanceof AbstractSchemaDescription )
276                    {
277                        return ( AbstractSchemaDescription ) obj;
278                    }
279                }
280            }
281            return null;
282        }
283    
284    
285        private void invokeParser( String subject ) throws ParseException
286        {
287            try
288            {
289                monitor.startedParse( "starting parse on:\n" + subject );
290                schemaDescriptions = parser.openLdapSchema();
291                afterParse();
292                monitor.finishedParse( "Done parsing!" );
293            }
294            catch ( RecognitionException e )
295            {
296                String msg = "Parser failure on:\n\t" + subject;
297                msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( e );
298                throw new ParseException( msg, e.getColumn() );
299            }
300            catch ( TokenStreamException e2 )
301            {
302                String msg = "Parser failure on:\n\t" + subject;
303                msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( e2 );
304                throw new ParseException( msg, 0 );
305            }
306        }
307    
308    
309        /**
310         * Parses a stream of OpenLDAP schemaObject elements/objects.
311         *
312         * @param schemaIn a stream of schema objects
313         * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream
314         * @throws ParseException If the schemaObject can't be parsed
315         */
316        public void parse( InputStream schemaIn ) throws IOException, ParseException
317        {
318            InputStreamReader in = new InputStreamReader( schemaIn );
319            lexer.prepareNextInput( in );
320            parser.resetState();
321    
322            invokeParser( "schema input stream ==> " + schemaIn.toString() );
323        }
324    
325    
326        /**
327         * Parses a file of OpenLDAP schemaObject elements/objects.
328         *
329         * @param schemaFile a file of schema objects
330         * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream
331         * @throws ParseException If the schemaObject can't be parsed
332         */
333        public void parse( File schemaFile ) throws IOException, ParseException
334        {
335            FileReader in = new FileReader( schemaFile );
336            lexer.prepareNextInput( in );
337            parser.resetState();
338    
339            invokeParser( "schema file ==> " + schemaFile.getAbsolutePath() );
340        }
341    
342    
343        /**
344         * Checks if object identifier macros should be resolved.
345         * 
346         * @return true, object identifier macros should be resolved.
347         */
348        public boolean isResolveObjectIdentifierMacros()
349        {
350            return isResolveObjectIdentifierMacros;
351        }
352    
353    
354        /**
355         * Sets if object identifier macros should be resolved.
356         * 
357         * @param isResolveObjectIdentifierMacros true if object identifier macros should be resolved
358         */
359        public void setResolveObjectIdentifierMacros( boolean isResolveObjectIdentifierMacros )
360        {
361            this.isResolveObjectIdentifierMacros = isResolveObjectIdentifierMacros;
362        }
363    
364    }