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.i18n.I18n;
035    import org.apache.directory.shared.ldap.schema.AttributeType;
036    import org.apache.directory.shared.ldap.schema.ObjectClass;
037    import org.apache.directory.shared.ldap.schema.SchemaObject;
038    import org.apache.directory.shared.ldap.schema.syntaxCheckers.OpenLdapObjectIdentifierMacro;
039    import org.apache.directory.shared.ldap.util.ExceptionUtils;
040    
041    import antlr.RecognitionException;
042    import antlr.TokenStreamException;
043    
044    
045    /**
046     * A reusable wrapper for antlr generated OpenLDAP schema parsers.
047     *
048     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
049     * @version $Rev: 494164 $
050     */
051    public class OpenLdapSchemaParser extends AbstractSchemaParser
052    {
053    
054        /** The list of parsed schema descriptions */
055        private List<Object> schemaDescriptions;
056    
057        /** The list of attribute type, initialized by splitParsedSchemaDescriptions() */
058        private List<AttributeType> attributeTypes;
059    
060        /** The list of object classes, initialized by splitParsedSchemaDescriptions()*/
061        private List<ObjectClass> objectClasses;
062    
063        /** The map of object identifier macros, initialized by splitParsedSchemaDescriptions()*/
064        private Map<String, OpenLdapObjectIdentifierMacro> objectIdentifierMacros;
065    
066        /** Flag whether object identifier macros should be resolved. */
067        private boolean isResolveObjectIdentifierMacros;
068    
069    
070        /**
071         * Creates a reusable instance of an OpenLdapSchemaParser.
072         *
073         * @throws IOException if the pipe cannot be formed
074         */
075        public OpenLdapSchemaParser() throws IOException
076        {
077            isResolveObjectIdentifierMacros = true;
078            super.setQuirksMode( true );
079        }
080    
081    
082        /**
083         * Reset the parser 
084         */
085        public void clear()
086        {
087        }
088    
089    
090        /**
091         * Gets the attribute types.
092         * 
093         * @return the attribute types
094         */
095        public List<AttributeType> getAttributeTypes()
096        {
097            return attributeTypes;
098        }
099    
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<AttributeType>();
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                    AttributeType attributeType = ( AttributeType ) 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 ( AttributeType 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            }
206            else if ( rawOidOrNameSuffix.indexOf( ':' ) != -1 )
207            {
208                // resolve OID
209                String[] nameAndSuffix = rawOidOrNameSuffix.split( ":" );
210                if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) )
211                {
212                    OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( nameAndSuffix[0] );
213                    resolveObjectIdentifierMacro( parentMacro );
214                    macro.setResolvedOid( parentMacro.getResolvedOid() + "." + nameAndSuffix[1] );
215                }
216                else
217                {
218                    throw new ParseException( I18n.err( I18n.ERR_04257, nameAndSuffix[0] ), 0 );
219                }
220    
221            }
222            else
223            {
224                // no :suffix, 
225                if ( objectIdentifierMacros.containsKey( rawOidOrNameSuffix ) )
226                {
227                    OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( rawOidOrNameSuffix );
228                    resolveObjectIdentifierMacro( parentMacro );
229                    macro.setResolvedOid( parentMacro.getResolvedOid() );
230                }
231                else
232                {
233                    macro.setResolvedOid( rawOidOrNameSuffix );
234                }
235            }
236        }
237    
238    
239        /**
240         * Parses an OpenLDAP schemaObject element/object.
241         *
242         * @param schemaObject the String image of a complete schema object
243         * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream
244         * @throws ParseException If the schemaObject can't be parsed
245         */
246        public SchemaObject parse( String schemaObject ) throws ParseException
247        {
248            if ( schemaObject == null || schemaObject.trim().equals( "" ) )
249            {
250                throw new ParseException( I18n.err( I18n.ERR_04258 ), 0 );
251            }
252    
253            reset( schemaObject ); // reset and initialize the parser / lexer pair
254            invokeParser( schemaObject );
255    
256            if ( !schemaDescriptions.isEmpty() )
257            {
258                for ( Object obj : schemaDescriptions )
259                {
260                    if ( obj instanceof SchemaObject )
261                    {
262                        return ( SchemaObject ) obj;
263                    }
264                }
265            }
266            return null;
267        }
268    
269    
270        private void invokeParser( String subject ) throws ParseException
271        {
272            try
273            {
274                monitor.startedParse( "starting parse on:\n" + subject );
275                schemaDescriptions = parser.openLdapSchema();
276                afterParse();
277                monitor.finishedParse( "Done parsing!" );
278            }
279            catch ( RecognitionException e )
280            {
281                String msg = "Parser failure on:\n\t" + subject;
282                msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( e );
283                throw new ParseException( msg, e.getColumn() );
284            }
285            catch ( TokenStreamException e2 )
286            {
287                String msg = "Parser failure on:\n\t" + subject;
288                msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( e2 );
289                throw new ParseException( msg, 0 );
290            }
291        }
292    
293    
294        /**
295         * Parses a stream of OpenLDAP schemaObject elements/objects.
296         *
297         * @param schemaIn a stream of schema objects
298         * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream
299         * @throws ParseException If the schemaObject can't be parsed
300         */
301        public void parse( InputStream schemaIn ) throws IOException, ParseException
302        {
303            InputStreamReader in = new InputStreamReader( schemaIn );
304            lexer.prepareNextInput( in );
305            parser.resetState();
306    
307            invokeParser( "schema input stream ==> " + schemaIn.toString() );
308        }
309    
310    
311        /**
312         * Parses a file of OpenLDAP schemaObject elements/objects.
313         *
314         * @param schemaFile a file of schema objects
315         * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream
316         * @throws ParseException If the schemaObject can't be parsed
317         */
318        public void parse( File schemaFile ) throws IOException, ParseException
319        {
320            FileReader in = new FileReader( schemaFile );
321            lexer.prepareNextInput( in );
322            parser.resetState();
323    
324            invokeParser( "schema file ==> " + schemaFile.getAbsolutePath() );
325        }
326    
327    
328        /**
329         * Checks if object identifier macros should be resolved.
330         * 
331         * @return true, object identifier macros should be resolved.
332         */
333        public boolean isResolveObjectIdentifierMacros()
334        {
335            return isResolveObjectIdentifierMacros;
336        }
337    
338    
339        /**
340         * Sets if object identifier macros should be resolved.
341         * 
342         * @param isResolveObjectIdentifierMacros true if object identifier macros should be resolved
343         */
344        public void setResolveObjectIdentifierMacros( boolean isResolveObjectIdentifierMacros )
345        {
346            this.isResolveObjectIdentifierMacros = isResolveObjectIdentifierMacros;
347        }
348    
349    }