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.StringReader; 024import java.text.ParseException; 025import java.util.List; 026 027import org.apache.directory.api.i18n.I18n; 028import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants; 029import org.apache.directory.api.ldap.model.schema.SchemaObject; 030import org.apache.directory.api.util.Strings; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import antlr.RecognitionException; 035import antlr.TokenStreamException; 036import antlr.TokenStreamRecognitionException; 037 038 039/** 040 * Base class of all schema parsers. 041 * 042 * @param <T> The type of SchemaObject 043 * 044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 045 */ 046public abstract class AbstractSchemaParser<T extends SchemaObject> 047{ 048 /** The LoggerFactory used by this class */ 049 protected static final Logger LOG = LoggerFactory.getLogger( AbstractSchemaParser.class ); 050 051 /** the monitor to use for this parser */ 052 protected ParserMonitor monitor = new ParserMonitorAdapter(); 053 054 /** the antlr generated parser being wrapped */ 055 protected ReusableAntlrSchemaParser parser; 056 057 /** the antlr generated lexer being wrapped */ 058 protected ReusableAntlrSchemaLexer lexer; 059 060 /** the schema object sub-type */ 061 private Class<T> schemaObjectType; 062 063 /** error code used when schema descritpion is null */ 064 private I18n errorCodeOnNull; 065 066 /** error code used on parse error when position is known */ 067 private I18n errorCodeOnParseExceptionWithPosition; 068 069 /** error code used on parse error when position is unknown */ 070 private I18n errorCodeOnParseException; 071 072 073 /** 074 * Instantiates a new abstract schema parser. 075 * 076 * @param schemaObjectType The Schema object type 077 * @param errorCodeOnNull error code used when schema element is null 078 * @param errorCodeOnParseExceptionWithPosition error code used on parse error when position is known 079 * @param errorCodeOnParseException error code used on parse error when position is unknown 080 */ 081 protected AbstractSchemaParser( Class<T> schemaObjectType, I18n errorCodeOnNull, 082 I18n errorCodeOnParseExceptionWithPosition, 083 I18n errorCodeOnParseException ) 084 { 085 this.schemaObjectType = schemaObjectType; 086 this.errorCodeOnNull = errorCodeOnNull; 087 this.errorCodeOnParseExceptionWithPosition = errorCodeOnParseExceptionWithPosition; 088 this.errorCodeOnParseException = errorCodeOnParseException; 089 lexer = new ReusableAntlrSchemaLexer( new StringReader( "" ) ); 090 parser = new ReusableAntlrSchemaParser( lexer ); 091 } 092 093 094 /** 095 * Initializes the plumbing by creating a pipe and coupling the parser/lexer 096 * pair with it. param spec the specification to be parsed 097 * 098 * @param spec the spec 099 */ 100 protected void reset( String spec ) 101 { 102 StringReader in = new StringReader( spec ); 103 lexer.prepareNextInput( in ); 104 parser.resetState(); 105 } 106 107 108 /** 109 * Sets the parser monitor. 110 * 111 * @param parserMonitor the new parser monitor 112 */ 113 public void setParserMonitor( ParserMonitor parserMonitor ) 114 { 115 this.monitor = parserMonitor; 116 parser.setParserMonitor( parserMonitor ); 117 } 118 119 120 /** 121 * Sets the quirks mode. 122 * 123 * If enabled the parser accepts non-numeric OIDs and some 124 * special characters in descriptions. 125 * 126 * @param enabled the new quirks mode 127 */ 128 public void setQuirksMode( boolean enabled ) 129 { 130 parser.setQuirksMode( enabled ); 131 } 132 133 134 /** 135 * Checks if quirks mode is enabled. 136 * 137 * @return true, if is quirks mode is enabled 138 */ 139 public boolean isQuirksMode() 140 { 141 return parser.isQuirksMode(); 142 } 143 144 145 /** 146 * Parse a SchemaObject description and returns back an instance of SchemaObject. 147 * 148 * @param schemaDescription The SchemaObject description 149 * @return A SchemaObject instance 150 * @throws ParseException If the parsing failed 151 */ 152 public synchronized T parse( String schemaDescription ) throws ParseException 153 { 154 LOG.debug( "Parsing a {} : {}", schemaObjectType.getClass().getSimpleName(), schemaDescription ); 155 156 if ( schemaDescription == null ) 157 { 158 LOG.error( I18n.err( errorCodeOnNull ) ); 159 throw new ParseException( "Null", 0 ); 160 } 161 162 // reset and initialize the parser / lexer pair 163 reset( schemaDescription ); 164 165 try 166 { 167 T schemaObject = doParse(); 168 schemaObject.setSpecification( schemaDescription ); 169 170 // Update the schemaName 171 updateSchemaName( schemaObject ); 172 173 return schemaObject; 174 } 175 catch ( RecognitionException re ) 176 { 177 throw wrapRecognitionException( schemaDescription, re ); 178 } 179 catch ( TokenStreamRecognitionException tsre ) 180 { 181 if ( tsre.recog != null ) 182 { 183 throw wrapRecognitionException( schemaDescription, tsre.recog ); 184 } 185 else 186 { 187 throw wrapTokenStreamException( schemaDescription, tsre ); 188 } 189 } 190 catch ( TokenStreamException tse ) 191 { 192 throw wrapTokenStreamException( schemaDescription, tse ); 193 } 194 } 195 196 197 private ParseException wrapRecognitionException( String schemaDescription, RecognitionException re ) 198 { 199 String msg = I18n.err( errorCodeOnParseExceptionWithPosition, schemaDescription, re.getMessage(), 200 re.getColumn() ); 201 LOG.error( msg ); 202 ParseException parseException = new ParseException( msg, re.getColumn() ); 203 parseException.initCause( re ); 204 return parseException; 205 } 206 207 208 private ParseException wrapTokenStreamException( String schemaDescription, TokenStreamException tse ) 209 { 210 String msg = I18n.err( errorCodeOnParseException, schemaDescription, tse.getMessage() ); 211 LOG.error( msg ); 212 ParseException parseException = new ParseException( msg, 0 ); 213 parseException.initCause( tse ); 214 return parseException; 215 } 216 217 218 /** 219 * Parse a SchemaObject description and returns back an instance of SchemaObject. 220 * 221 * @return A SchemaObject instance 222 * @throws RecognitionException the native antlr exception 223 * @throws TokenStreamException the native antlr exception 224 */ 225 protected abstract T doParse() throws RecognitionException, TokenStreamException; 226 227 228 /** 229 * Update the schemaName for the given SchemaObject, accordingly to the X-SCHEMA parameter. If 230 * not present, default to 'other' 231 * 232 * @param schemaObject the schema object where the name should be updated 233 */ 234 private void updateSchemaName( SchemaObject schemaObject ) 235 { 236 // Update the Schema if we have the X-SCHEMA extension 237 List<String> schemaExtension = schemaObject.getExtension( MetaSchemaConstants.X_SCHEMA_AT ); 238 239 if ( schemaExtension != null ) 240 { 241 String schemaName = schemaExtension.get( 0 ); 242 243 if ( Strings.isEmpty( schemaName ) ) 244 { 245 schemaObject.setSchemaName( MetaSchemaConstants.SCHEMA_OTHER ); 246 } 247 else 248 { 249 schemaObject.setSchemaName( schemaName ); 250 } 251 } 252 else 253 { 254 schemaObject.setSchemaName( MetaSchemaConstants.SCHEMA_OTHER ); 255 } 256 } 257}