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.IOException; 025import java.io.InputStream; 026import java.io.InputStreamReader; 027import java.nio.charset.Charset; 028import java.nio.file.Files; 029import java.nio.file.Paths; 030import java.text.ParseException; 031import java.util.ArrayList; 032import java.util.HashMap; 033import java.util.List; 034import java.util.Map; 035 036import org.apache.commons.lang.exception.ExceptionUtils; 037import org.apache.directory.api.i18n.I18n; 038import org.apache.directory.api.ldap.model.schema.AttributeType; 039import org.apache.directory.api.ldap.model.schema.MutableAttributeType; 040import org.apache.directory.api.ldap.model.schema.ObjectClass; 041import org.apache.directory.api.ldap.model.schema.SchemaObject; 042import org.apache.directory.api.ldap.model.schema.syntaxCheckers.OpenLdapObjectIdentifierMacro; 043 044import antlr.RecognitionException; 045import antlr.TokenStreamException; 046 047 048/** 049 * A reusable wrapper for antlr generated OpenLDAP schema parsers. 050 * 051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 052 */ 053public class OpenLdapSchemaParser extends AbstractSchemaParser<SchemaObject> 054{ 055 056 /** The list of parsed schema descriptions */ 057 private List<Object> schemaDescriptions; 058 059 /** The list of attribute type, initialized by splitParsedSchemaDescriptions() */ 060 private List<MutableAttributeType> attributeTypes; 061 062 /** The list of object classes, initialized by splitParsedSchemaDescriptions()*/ 063 private List<ObjectClass> objectClasses; 064 065 /** The map of object identifier macros, initialized by splitParsedSchemaDescriptions()*/ 066 private Map<String, OpenLdapObjectIdentifierMacro> objectIdentifierMacros; 067 068 /** Flag whether object identifier macros should be resolved. */ 069 private boolean isResolveObjectIdentifierMacros; 070 071 072 /** 073 * Creates a reusable instance of an OpenLdapSchemaParser. 074 * 075 * @throws IOException if the pipe cannot be formed 076 */ 077 public OpenLdapSchemaParser() throws IOException 078 { 079 super( null, null, null, null ); 080 isResolveObjectIdentifierMacros = true; 081 super.setQuirksMode( true ); 082 } 083 084 085 @Override 086 protected SchemaObject doParse() throws RecognitionException, TokenStreamException 087 { 088 throw new UnsupportedOperationException( "OpenLdapSchemaParser is not a normal schema parser" ); 089 } 090 091 092 /** 093 * Reset the parser 094 */ 095 public void clear() 096 { 097 } 098 099 100 /** 101 * Gets the attribute types. 102 * 103 * @return the attribute types 104 */ 105 public List<MutableAttributeType> getAttributeTypes() 106 { 107 return attributeTypes; 108 } 109 110 111 /** 112 * Gets the object class types. 113 * 114 * @return the object class types 115 */ 116 public List<ObjectClass> getObjectClassTypes() 117 { 118 return objectClasses; 119 } 120 121 122 /** 123 * Gets the object identifier macros. 124 * 125 * @return the object identifier macros 126 */ 127 public Map<String, OpenLdapObjectIdentifierMacro> getObjectIdentifierMacros() 128 { 129 return objectIdentifierMacros; 130 } 131 132 133 /** 134 * Splits parsed schema descriptions and resolved 135 * object identifier macros. 136 * 137 * @throws ParseException the parse exception 138 */ 139 private void afterParse() throws ParseException 140 { 141 objectClasses = new ArrayList<>(); 142 attributeTypes = new ArrayList<>(); 143 objectIdentifierMacros = new HashMap<>(); 144 145 // split parsed schema descriptions 146 for ( Object obj : schemaDescriptions ) 147 { 148 if ( obj instanceof OpenLdapObjectIdentifierMacro ) 149 { 150 OpenLdapObjectIdentifierMacro oid = ( OpenLdapObjectIdentifierMacro ) obj; 151 objectIdentifierMacros.put( oid.getName(), oid ); 152 } 153 else if ( obj instanceof AttributeType ) 154 { 155 MutableAttributeType attributeType = ( MutableAttributeType ) obj; 156 157 attributeTypes.add( attributeType ); 158 } 159 else if ( obj instanceof ObjectClass ) 160 { 161 ObjectClass objectClass = ( ObjectClass ) obj; 162 163 objectClasses.add( objectClass ); 164 } 165 } 166 167 if ( isResolveObjectIdentifierMacros() ) 168 { 169 // resolve object identifier macros 170 for ( OpenLdapObjectIdentifierMacro oid : objectIdentifierMacros.values() ) 171 { 172 resolveObjectIdentifierMacro( oid ); 173 } 174 175 // apply object identifier macros to object classes 176 for ( ObjectClass objectClass : objectClasses ) 177 { 178 objectClass.setOid( getResolveOid( objectClass.getOid() ) ); 179 } 180 181 // apply object identifier macros to attribute types 182 for ( MutableAttributeType attributeType : attributeTypes ) 183 { 184 attributeType.setOid( getResolveOid( attributeType.getOid() ) ); 185 attributeType.setSyntaxOid( getResolveOid( attributeType.getSyntaxOid() ) ); 186 } 187 188 } 189 } 190 191 192 private String getResolveOid( String oid ) 193 { 194 if ( oid != null && oid.indexOf( ':' ) != -1 ) 195 { 196 // resolve OID 197 String[] nameAndSuffix = oid.split( ":" ); 198 if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) ) 199 { 200 OpenLdapObjectIdentifierMacro macro = objectIdentifierMacros.get( nameAndSuffix[0] ); 201 return macro.getResolvedOid() + "." + nameAndSuffix[1]; 202 } 203 } 204 return oid; 205 } 206 207 208 private void resolveObjectIdentifierMacro( OpenLdapObjectIdentifierMacro macro ) throws ParseException 209 { 210 String rawOidOrNameSuffix = macro.getRawOidOrNameSuffix(); 211 212 if ( macro.isResolved() ) 213 { 214 // finished 215 return; 216 } 217 else if ( rawOidOrNameSuffix.indexOf( ':' ) != -1 ) 218 { 219 // resolve OID 220 String[] nameAndSuffix = rawOidOrNameSuffix.split( ":" ); 221 if ( objectIdentifierMacros.containsKey( nameAndSuffix[0] ) ) 222 { 223 OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( nameAndSuffix[0] ); 224 resolveObjectIdentifierMacro( parentMacro ); 225 macro.setResolvedOid( parentMacro.getResolvedOid() + "." + nameAndSuffix[1] ); 226 } 227 else 228 { 229 throw new ParseException( I18n.err( I18n.ERR_04257, nameAndSuffix[0] ), 0 ); 230 } 231 232 } 233 else 234 { 235 // no :suffix, 236 if ( objectIdentifierMacros.containsKey( rawOidOrNameSuffix ) ) 237 { 238 OpenLdapObjectIdentifierMacro parentMacro = objectIdentifierMacros.get( rawOidOrNameSuffix ); 239 resolveObjectIdentifierMacro( parentMacro ); 240 macro.setResolvedOid( parentMacro.getResolvedOid() ); 241 } 242 else 243 { 244 macro.setResolvedOid( rawOidOrNameSuffix ); 245 } 246 } 247 } 248 249 250 /** 251 * Parses an OpenLDAP schemaObject element/object. 252 * 253 * @param schemaObject the String image of a complete schema object 254 * @return the schema object 255 * @throws ParseException If the schemaObject can't be parsed 256 */ 257 @Override 258 public SchemaObject parse( String schemaObject ) throws ParseException 259 { 260 if ( schemaObject == null || ( schemaObject.trim().length() == 0 ) ) 261 { 262 throw new ParseException( I18n.err( I18n.ERR_04258 ), 0 ); 263 } 264 265 // reset and initialize the parser / lexer pair 266 reset( schemaObject ); 267 invokeParser( schemaObject ); 268 269 if ( !schemaDescriptions.isEmpty() ) 270 { 271 for ( Object obj : schemaDescriptions ) 272 { 273 if ( obj instanceof SchemaObject ) 274 { 275 return ( SchemaObject ) obj; 276 } 277 } 278 } 279 return null; 280 } 281 282 283 private void invokeParser( String subject ) throws ParseException 284 { 285 try 286 { 287 monitor.startedParse( "starting parse on:\n" + subject ); 288 schemaDescriptions = parser.openLdapSchema(); 289 afterParse(); 290 monitor.finishedParse( "Done parsing!" ); 291 } 292 catch ( RecognitionException re ) 293 { 294 String msg = "Parser failure on:\n\t" + subject; 295 msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( re ); 296 throw new ParseException( msg, re.getColumn() ); 297 } 298 catch ( TokenStreamException tse ) 299 { 300 String msg = "Parser failure on:\n\t" + subject; 301 msg += "\nAntlr exception trace:\n" + ExceptionUtils.getFullStackTrace( tse ); 302 throw new ParseException( msg, 0 ); 303 } 304 } 305 306 307 /** 308 * Parses a stream of OpenLDAP schemaObject elements/objects. Default charset is used. 309 * 310 * @param schemaIn a stream of schema objects 311 * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream 312 * @throws ParseException If the schemaObject can't be parsed 313 */ 314 public void parse( InputStream schemaIn ) throws IOException, ParseException 315 { 316 InputStreamReader in = new InputStreamReader( schemaIn, Charset.defaultCharset() ); 317 lexer.prepareNextInput( in ); 318 parser.resetState(); 319 320 invokeParser( "schema input stream ==> " + schemaIn.toString() ); 321 } 322 323 324 /** 325 * Parses a file of OpenLDAP schemaObject elements/objects. Default charset is used. 326 * 327 * @param schemaFile a file of schema objects 328 * @throws IOException If the schemaObject can't be transformed to a byteArrayInputStream 329 * @throws ParseException If the schemaObject can't be parsed 330 */ 331 public void parse( File schemaFile ) throws IOException, ParseException 332 { 333 InputStreamReader in = new InputStreamReader( 334 Files.newInputStream( Paths.get( schemaFile.getPath() ) ), Charset.defaultCharset() ); 335 lexer.prepareNextInput( in ); 336 parser.resetState(); 337 338 invokeParser( "schema file ==> " + schemaFile.getAbsolutePath() ); 339 } 340 341 342 /** 343 * Checks if object identifier macros should be resolved. 344 * 345 * @return true, object identifier macros should be resolved. 346 */ 347 public boolean isResolveObjectIdentifierMacros() 348 { 349 return isResolveObjectIdentifierMacros; 350 } 351 352 353 /** 354 * Sets if object identifier macros should be resolved. 355 * 356 * @param resolveObjectIdentifierMacros true if object identifier macros should be resolved 357 */ 358 public void setResolveObjectIdentifierMacros( boolean resolveObjectIdentifierMacros ) 359 { 360 this.isResolveObjectIdentifierMacros = resolveObjectIdentifierMacros; 361 } 362 363}