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.shared.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.shared.i18n.I18n; 036import org.apache.directory.shared.ldap.model.schema.AttributeType; 037import org.apache.directory.shared.ldap.model.schema.ObjectClass; 038import org.apache.directory.shared.ldap.model.schema.SchemaObject; 039import org.apache.directory.shared.ldap.model.schema.syntaxCheckers.OpenLdapObjectIdentifierMacro; 040 041import antlr.RecognitionException; 042import 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 */ 050public class OpenLdapSchemaParser extends AbstractSchemaParser 051{ 052 053 /** The list of parsed schema descriptions */ 054 private List<Object> schemaDescriptions; 055 056 /** The list of attribute type, initialized by splitParsedSchemaDescriptions() */ 057 private List<AttributeType> attributeTypes; 058 059 /** The list of object classes, initialized by splitParsedSchemaDescriptions()*/ 060 private List<ObjectClass> objectClasses; 061 062 /** The map of object identifier macros, initialized by splitParsedSchemaDescriptions()*/ 063 private Map<String, OpenLdapObjectIdentifierMacro> objectIdentifierMacros; 064 065 /** Flag whether object identifier macros should be resolved. */ 066 private boolean isResolveObjectIdentifierMacros; 067 068 069 /** 070 * Creates a reusable instance of an OpenLdapSchemaParser. 071 * 072 * @throws IOException if the pipe cannot be formed 073 */ 074 public OpenLdapSchemaParser() throws IOException 075 { 076 isResolveObjectIdentifierMacros = true; 077 super.setQuirksMode( true ); 078 } 079 080 081 /** 082 * Reset the parser 083 */ 084 public void clear() 085 { 086 } 087 088 089 /** 090 * Gets the attribute types. 091 * 092 * @return the attribute types 093 */ 094 public List<AttributeType> getAttributeTypes() 095 { 096 return attributeTypes; 097 } 098 099 100 /** 101 * Gets the object class types. 102 * 103 * @return the object class types 104 */ 105 public List<ObjectClass> getObjectClassTypes() 106 { 107 return objectClasses; 108 } 109 110 111 /** 112 * Gets the object identifier macros. 113 * 114 * @return the object identifier macros 115 */ 116 public Map<String, OpenLdapObjectIdentifierMacro> getObjectIdentifierMacros() 117 { 118 return objectIdentifierMacros; 119 } 120 121 122 /** 123 * Splits parsed schema descriptions and resolved 124 * object identifier macros. 125 * 126 * @throws ParseException the parse exception 127 */ 128 private void afterParse() throws ParseException 129 { 130 objectClasses = new ArrayList<ObjectClass>(); 131 attributeTypes = new ArrayList<AttributeType>(); 132 objectIdentifierMacros = new HashMap<String, OpenLdapObjectIdentifierMacro>(); 133 134 // split parsed schema descriptions 135 for ( Object obj : schemaDescriptions ) 136 { 137 if ( obj instanceof OpenLdapObjectIdentifierMacro ) 138 { 139 OpenLdapObjectIdentifierMacro oid = (OpenLdapObjectIdentifierMacro) obj; 140 objectIdentifierMacros.put( oid.getName(), oid ); 141 } 142 else if ( obj instanceof AttributeType ) 143 { 144 AttributeType attributeType = ( AttributeType ) obj; 145 146 attributeTypes.add( attributeType ); 147 } 148 else if ( obj instanceof ObjectClass ) 149 { 150 ObjectClass objectClass = ( ObjectClass ) obj; 151 152 objectClasses.add( objectClass ); 153 } 154 } 155 156 if ( isResolveObjectIdentifierMacros() ) 157 { 158 // resolve object identifier macros 159 for ( OpenLdapObjectIdentifierMacro oid : objectIdentifierMacros.values() ) 160 { 161 resolveObjectIdentifierMacro( oid ); 162 } 163 164 // apply object identifier macros to object classes 165 for ( ObjectClass objectClass : objectClasses ) 166 { 167 objectClass.setOid( getResolveOid( objectClass.getOid() ) ); 168 } 169 170 // apply object identifier macros to attribute types 171 for ( AttributeType attributeType : attributeTypes ) 172 { 173 attributeType.setOid( getResolveOid( attributeType.getOid() ) ); 174 attributeType.setSyntaxOid( getResolveOid( attributeType.getSyntaxOid() ) ); 175 } 176 177 } 178 } 179 180 181 private String getResolveOid( String oid ) 182 { 183 if ( oid != null && oid.indexOf( ':' ) != -1 ) 184 { 185 // resolve OID 186 String[] nameAndSuffix = oid.split( ":" ); 187 if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) ) 188 { 189 OpenLdapObjectIdentifierMacro macro = objectIdentifierMacros.get( nameAndSuffix[0] ); 190 return macro.getResolvedOid() + "." + nameAndSuffix[1]; 191 } 192 } 193 return oid; 194 } 195 196 197 private void resolveObjectIdentifierMacro( OpenLdapObjectIdentifierMacro macro ) throws ParseException 198 { 199 String rawOidOrNameSuffix = macro.getRawOidOrNameSuffix(); 200 201 if ( macro.isResolved() ) 202 { 203 // finished 204 return; 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 * @return the schema object 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 resolveObjectIdentifierMacros true if object identifier macros should be resolved 343 */ 344 public void setResolveObjectIdentifierMacros( boolean resolveObjectIdentifierMacros ) 345 { 346 this.isResolveObjectIdentifierMacros = resolveObjectIdentifierMacros; 347 } 348 349}