View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.api.ldap.model.schema.parsers;
21  
22  
23  import java.io.StringReader;
24  import java.text.ParseException;
25  import java.util.List;
26  
27  import org.apache.directory.api.i18n.I18n;
28  import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
29  import org.apache.directory.api.ldap.model.schema.SchemaObject;
30  import org.apache.directory.api.util.Strings;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import antlr.RecognitionException;
35  import antlr.TokenStreamException;
36  import antlr.TokenStreamRecognitionException;
37  
38  
39  /**
40   * Base class of all schema parsers.
41   * 
42   * @param <T> The type of SchemaObject
43   *
44   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
45   */
46  public abstract class AbstractSchemaParser<T extends SchemaObject>
47  {
48      /** The LoggerFactory used by this class */
49      protected static final Logger LOG = LoggerFactory.getLogger( AbstractSchemaParser.class );
50  
51      /** the monitor to use for this parser */
52      protected ParserMonitor monitor = new ParserMonitorAdapter();
53  
54      /** the antlr generated parser being wrapped */
55      protected ReusableAntlrSchemaParser parser;
56  
57      /** the antlr generated lexer being wrapped */
58      protected ReusableAntlrSchemaLexer lexer;
59  
60      /** the schema object sub-type */
61      private Class<T> schemaObjectType;
62  
63      /** error code used when schema descritpion is null */
64      private I18n errorCodeOnNull;
65  
66      /** error code used on parse error when position is known */
67      private I18n errorCodeOnParseExceptionWithPosition;
68  
69      /** error code used on parse error when position is unknown */
70      private I18n errorCodeOnParseException;
71  
72  
73      /**
74       * Instantiates a new abstract schema parser.
75       * 
76       * @param schemaObjectType The Schema object type
77       * @param errorCodeOnNull error code used when schema element is null
78       * @param errorCodeOnParseExceptionWithPosition error code used on parse error when position is known
79       * @param errorCodeOnParseException error code used on parse error when position is unknown
80       */
81      protected AbstractSchemaParser( Class<T> schemaObjectType, I18n errorCodeOnNull,
82          I18n errorCodeOnParseExceptionWithPosition,
83          I18n errorCodeOnParseException )
84      {
85          this.schemaObjectType = schemaObjectType;
86          this.errorCodeOnNull = errorCodeOnNull;
87          this.errorCodeOnParseExceptionWithPosition = errorCodeOnParseExceptionWithPosition;
88          this.errorCodeOnParseException = errorCodeOnParseException;
89          lexer = new ReusableAntlrSchemaLexer( new StringReader( "" ) );
90          parser = new ReusableAntlrSchemaParser( lexer );
91      }
92  
93  
94      /**
95       * Initializes the plumbing by creating a pipe and coupling the parser/lexer
96       * pair with it. param spec the specification to be parsed
97       *
98       * @param spec the spec
99       */
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 }