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 */ 020package org.apache.directory.api.ldap.model.schema.parsers; 021 022 023import java.io.File; 024import java.io.FileReader; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.InputStreamReader; 028import java.text.ParseException; 029import java.util.ArrayList; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033 034import org.apache.commons.lang.exception.ExceptionUtils; 035import org.apache.directory.api.i18n.I18n; 036import org.apache.directory.api.ldap.model.schema.AttributeType; 037import org.apache.directory.api.ldap.model.schema.MutableAttributeType; 038import org.apache.directory.api.ldap.model.schema.ObjectClass; 039import org.apache.directory.api.ldap.model.schema.SchemaObject; 040import org.apache.directory.api.ldap.model.schema.syntaxCheckers.OpenLdapObjectIdentifierMacro; 041 042import antlr.RecognitionException; 043import antlr.TokenStreamException; 044 045 046/** 047 * A reusable wrapper for antlr generated OpenLDAP schema parsers. 048 * 049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 050 */ 051public class OpenLdapSchemaParser extends AbstractSchemaParser<SchemaObject> 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<MutableAttributeType> 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 super( null, null, null, null ); 078 isResolveObjectIdentifierMacros = true; 079 super.setQuirksMode( true ); 080 } 081 082 083 @Override 084 protected SchemaObject doParse() throws RecognitionException, TokenStreamException 085 { 086 throw new UnsupportedOperationException( "OpenLdapSchemaParser is not a normal schema parser" ); 087 } 088 089 090 /** 091 * Reset the parser 092 */ 093 public void clear() 094 { 095 } 096 097 098 /** 099 * 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}