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 }