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 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 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<MutableAttributeType> 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<MutableAttributeType>(); 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 MutableAttributeType attributeType = ( MutableAttributeType ) 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 ( MutableAttributeType 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 return; 206 } 207 else if ( rawOidOrNameSuffix.indexOf( ':' ) != -1 ) 208 { 209 // resolve OID 210 String[] nameAndSuffix = rawOidOrNameSuffix.split( ":" ); 211 if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) ) 212 { 213 OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( nameAndSuffix[0] ); 214 resolveObjectIdentifierMacro( parentMacro ); 215 macro.setResolvedOid( parentMacro.getResolvedOid() + "." + nameAndSuffix[1] ); 216 } 217 else 218 { 219 throw new ParseException( I18n.err( I18n.ERR_04257, nameAndSuffix[0] ), 0 ); 220 } 221 222 } 223 else 224 { 225 // no :suffix, 226 if ( objectIdentifierMacros.containsKey( rawOidOrNameSuffix ) ) 227 { 228 OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( rawOidOrNameSuffix ); 229 resolveObjectIdentifierMacro( parentMacro ); 230 macro.setResolvedOid( parentMacro.getResolvedOid() ); 231 } 232 else 233 { 234 macro.setResolvedOid( rawOidOrNameSuffix ); 235 } 236 } 237 } 238 239 240 /** 241 * Parses an OpenLDAP schemaObject element/object. 242 * 243 * @param schemaObject the String image of a complete schema object 244 * @return the schema object 245 * @throws ParseException If the schemaObject can't be parsed 246 */ 247 public SchemaObject parse( String schemaObject ) throws ParseException 248 { 249 if ( schemaObject == null || schemaObject.trim().equals( "" ) ) 250 { 251 throw new ParseException( I18n.err( I18n.ERR_04258 ), 0 ); 252 } 253 254 reset( schemaObject ); // reset and initialize the parser / lexer pair 255 invokeParser( schemaObject ); 256 257 if ( !schemaDescriptions.isEmpty() ) 258 { 259 for ( Object obj : schemaDescriptions ) 260 { 261 if ( obj instanceof SchemaObject ) 262 { 263 return ( SchemaObject ) obj; 264 } 265 } 266 } 267 return null; 268 } 269 270 271 private void invokeParser( String subject ) throws ParseException 272 { 273 try 274 { 275 monitor.startedParse( "starting parse on:\n" + subject ); 276 schemaDescriptions = parser.openLdapSchema(); 277 afterParse(); 278 monitor.finishedParse( "Done parsing!" ); 279 } 280 catch ( RecognitionException e ) 281 { 282 String msg = "Parser failure on:\n\t" + subject; 283 msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( e ); 284 throw new ParseException( msg, e.getColumn() ); 285 } 286 catch ( TokenStreamException e2 ) 287 { 288 String msg = "Parser failure on:\n\t" + subject; 289 msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( e2 ); 290 throw new ParseException( msg, 0 ); 291 } 292 } 293 294 295 /** 296 * Parses a stream of OpenLDAP schemaObject elements/objects. 297 * 298 * @param schemaIn a stream of schema objects 299 * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream 300 * @throws ParseException If the schemaObject can't be parsed 301 */ 302 public void parse( InputStream schemaIn ) throws IOException, ParseException 303 { 304 InputStreamReader in = new InputStreamReader( schemaIn ); 305 lexer.prepareNextInput( in ); 306 parser.resetState(); 307 308 invokeParser( "schema input stream ==> " + schemaIn.toString() ); 309 } 310 311 312 /** 313 * Parses a file of OpenLDAP schemaObject elements/objects. 314 * 315 * @param schemaFile a file of schema objects 316 * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream 317 * @throws ParseException If the schemaObject can't be parsed 318 */ 319 public void parse( File schemaFile ) throws IOException, ParseException 320 { 321 FileReader in = new FileReader( schemaFile ); 322 lexer.prepareNextInput( in ); 323 parser.resetState(); 324 325 invokeParser( "schema file ==> " + schemaFile.getAbsolutePath() ); 326 } 327 328 329 /** 330 * Checks if object identifier macros should be resolved. 331 * 332 * @return true, object identifier macros should be resolved. 333 */ 334 public boolean isResolveObjectIdentifierMacros() 335 { 336 return isResolveObjectIdentifierMacros; 337 } 338 339 340 /** 341 * Sets if object identifier macros should be resolved. 342 * 343 * @param resolveObjectIdentifierMacros true if object identifier macros should be resolved 344 */ 345 public void setResolveObjectIdentifierMacros( boolean resolveObjectIdentifierMacros ) 346 { 347 this.isResolveObjectIdentifierMacros = resolveObjectIdentifierMacros; 348 } 349 350}