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   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43   */
44  public abstract class AbstractSchemaParser<T extends SchemaObject>
45  {
46      /** The LoggerFactory used by this class */
47      protected static final Logger LOG = LoggerFactory.getLogger( AbstractSchemaParser.class );
48  
49      /** the monitor to use for this parser */
50      protected ParserMonitor monitor = new ParserMonitorAdapter();
51  
52      /** the antlr generated parser being wrapped */
53      protected ReusableAntlrSchemaParser parser;
54  
55      /** the antlr generated lexer being wrapped */
56      protected ReusableAntlrSchemaLexer lexer;
57  
58      /** the schema object sub-type */
59      private Class<T> schemaObjectType;
60  
61      /** error code used when schema descritpion is null */
62      private I18n errorCodeOnNull;
63  
64      /** error code used on parse error when position is known */
65      private I18n errorCodeOnParseExceptionWithPosition;
66  
67      /** error code used on parse error when position is unknown */
68      private I18n errorCodeOnParseException;
69  
70  
71      /**
72       * Instantiates a new abstract schema parser.
73       * @param errorCodeOnNull error code used when schema element is null
74       * @param errorCodeOnParseExceptionWithPosition error code used on parse error when position is known
75       * @param errorCodeOnParseException error code used on parse error when position is unknown
76       */
77      protected AbstractSchemaParser( Class<T> schemaObjectType, I18n errorCodeOnNull, I18n errorCodeOnParseExceptionWithPosition,
78          I18n errorCodeOnParseException )
79      {
80          this.schemaObjectType = schemaObjectType;
81          this.errorCodeOnNull = errorCodeOnNull;
82          this.errorCodeOnParseExceptionWithPosition = errorCodeOnParseExceptionWithPosition;
83          this.errorCodeOnParseException = errorCodeOnParseException;
84          lexer = new ReusableAntlrSchemaLexer( new StringReader( "" ) );
85          parser = new ReusableAntlrSchemaParser( lexer );
86      }
87  
88  
89      /**
90       * Initializes the plumbing by creating a pipe and coupling the parser/lexer
91       * pair with it. param spec the specification to be parsed
92       *
93       * @param spec the spec
94       */
95      protected void reset( String spec )
96      {
97          StringReader in = new StringReader( spec );
98          lexer.prepareNextInput( in );
99          parser.resetState();
100     }
101 
102 
103     /**
104      * Sets the parser monitor.
105      * 
106      * @param parserMonitor the new parser monitor
107      */
108     public void setParserMonitor( ParserMonitor parserMonitor )
109     {
110         this.monitor = parserMonitor;
111         parser.setParserMonitor( parserMonitor );
112     }
113 
114 
115     /**
116      * Sets the quirks mode. 
117      * 
118      * If enabled the parser accepts non-numeric OIDs and some 
119      * special characters in descriptions.
120      * 
121      * @param enabled the new quirks mode
122      */
123     public void setQuirksMode( boolean enabled )
124     {
125         parser.setQuirksMode( enabled );
126     }
127 
128 
129     /**
130      * Checks if quirks mode is enabled.
131      * 
132      * @return true, if is quirks mode is enabled
133      */
134     public boolean isQuirksMode()
135     {
136         return parser.isQuirksMode();
137     }
138 
139 
140     /**
141      * Parse a SchemaObject description and returns back an instance of SchemaObject.
142      * 
143      * @param schemaDescription The SchemaObject description
144      * @return A SchemaObject instance
145      * @throws ParseException If the parsing failed
146      */
147     public synchronized T parse( String schemaDescription ) throws ParseException
148     {
149         LOG.debug( "Parsing a {} : {}", schemaObjectType.getClass().getSimpleName(), schemaDescription );
150 
151         if ( schemaDescription == null )
152         {
153             LOG.error( I18n.err( errorCodeOnNull ) );
154             throw new ParseException( "Null", 0 );
155         }
156 
157         reset( schemaDescription ); // reset and initialize the parser / lexer pair
158 
159         try
160         {
161             T schemaObject = doParse();
162             schemaObject.setSpecification( schemaDescription );
163 
164             // Update the schemaName
165             updateSchemaName( schemaObject );
166 
167             return schemaObject;
168         }
169         catch ( RecognitionException re )
170         {
171             ParseException parseException = wrapRecognitionException( schemaDescription, re );
172             throw parseException;
173         }
174         catch ( TokenStreamRecognitionException tsre )
175         {
176             if ( tsre.recog != null )
177             {
178                 ParseException parseException = wrapRecognitionException( schemaDescription, tsre.recog );
179                 throw parseException;
180             }
181             else
182             {
183                 ParseException parseException = wrapTokenStreamException( schemaDescription, tsre );
184                 throw parseException;
185             }
186         }
187         catch ( TokenStreamException tse )
188         {
189             ParseException parseException = wrapTokenStreamException( schemaDescription, tse );
190             throw parseException;
191         }
192     }
193 
194 
195     private ParseException wrapRecognitionException( String schemaDescription, RecognitionException re )
196     {
197         String msg = I18n.err( errorCodeOnParseExceptionWithPosition, schemaDescription, re.getMessage(),
198             re.getColumn() );
199         LOG.error( msg );
200         ParseException parseException = new ParseException( msg, re.getColumn() );
201         parseException.initCause( re );
202         return parseException;
203     }
204 
205 
206     private ParseException wrapTokenStreamException( String schemaDescription, TokenStreamException tse )
207     {
208         String msg = I18n.err( errorCodeOnParseException, schemaDescription, tse.getMessage() );
209         LOG.error( msg );
210         ParseException parseException = new ParseException( msg, 0 );
211         parseException.initCause( tse );
212         return parseException;
213     }
214 
215 
216     /**
217      * Parse a SchemaObject description and returns back an instance of SchemaObject.
218      * 
219      * @return A SchemaObject instance
220      * @throws RecognitionException the native antlr exception
221      * @throws TokenStreamException the native antlr exception
222      */
223     protected abstract T doParse() throws RecognitionException, TokenStreamException;
224 
225 
226     /**
227      * Update the schemaName for the given SchemaObject, accordingly to the X-SCHEMA parameter. If
228      * not present, default to 'other'
229      *
230      * @param schemaObject the schema object where the name should be updated
231      */
232     private void updateSchemaName( SchemaObject schemaObject )
233     {
234         // Update the Schema if we have the X-SCHEMA extension
235         List<String> schemaExtension = schemaObject.getExtension( MetaSchemaConstants.X_SCHEMA_AT );
236 
237         if ( schemaExtension != null )
238         {
239             String schemaName = schemaExtension.get( 0 );
240 
241             if ( Strings.isEmpty( schemaName ) )
242             {
243                 schemaObject.setSchemaName( MetaSchemaConstants.SCHEMA_OTHER );
244             }
245             else
246             {
247                 schemaObject.setSchemaName( schemaName );
248             }
249         }
250         else
251         {
252             schemaObject.setSchemaName( MetaSchemaConstants.SCHEMA_OTHER );
253         }
254     }
255 }