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.schema.converter;
21  
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.PipedInputStream;
28  import java.io.PipedOutputStream;
29  import java.nio.file.Files;
30  import java.nio.file.Paths;
31  import java.text.ParseException;
32  import java.util.List;
33  
34  import org.apache.commons.lang.exception.ExceptionUtils;
35  import org.apache.directory.api.i18n.I18n;
36  import org.apache.directory.api.util.Strings;
37  
38  import antlr.RecognitionException;
39  import antlr.TokenStreamException;
40  
41  
42  /**
43   * A reusable wrapper for antlr generated schema parsers.
44   *
45   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
46   */
47  public class SchemaParser
48  {
49      /** The antlr generated parser */
50      private antlrSchemaConverterParser parser = null;
51  
52      /** A pipe into the parser */
53      private PipedOutputStream parserIn = null;
54  
55      /** A temporary buffer storing the read schema bytes */
56      private byte[] buf = new byte[128];
57  
58      /** The inputStream mapped over the schema file to parse */
59      private InputStream schemaIn;
60  
61      /** The thread used to read the schema */
62      private Thread producerThread;
63  
64  
65      /**
66       * Creates a reusable instance of an SchemaParser.
67       *
68       * @throws java.io.IOException if the pipe cannot be formed
69       */
70      public SchemaParser() throws IOException
71      {
72          init();
73      }
74  
75  
76      /**
77       * Initializes a parser and its plumbing.
78       *
79       * @throws java.io.IOException if a pipe cannot be formed.
80       */
81      public synchronized void init() throws IOException
82      {
83          parserIn = new PipedOutputStream();
84          PipedInputStream in = new PipedInputStream();
85          parserIn.connect( in );
86          antlrSchemaConverterLexer lexer = new antlrSchemaConverterLexer( in );
87          parser = new antlrSchemaConverterParser( lexer );
88      }
89  
90  
91      /**
92       * Clear the parser.
93       */
94      public synchronized void clear()
95      {
96          parser.clear();
97      }
98  
99  
100     /**
101      * Thread safe method parses an OpenLDAP schemaObject element/object.
102      *
103      * @param schemaObject the String image of a complete schema object
104      * @return The list of parsed schema elements
105      * @throws java.io.IOException If the schema file can't be processed
106      * @throws java.text.ParseException If we weren't able to parse the schema
107      */
108     public synchronized List<SchemaElement> parse( String schemaObject ) throws IOException, ParseException
109     {
110         if ( ( schemaObject == null ) || ( schemaObject.trim().equals( Strings.EMPTY_STRING ) ) )
111         {
112             throw new ParseException( I18n.err( I18n.ERR_06001_EMPTY_OR_NULL_SCHEMA_OBJECT ), 0 );
113         }
114 
115         schemaIn = new ByteArrayInputStream( Strings.getBytesUtf8( schemaObject ) );
116 
117         if ( producerThread == null )
118         {
119             producerThread = new Thread( new DataProducer() );
120         }
121 
122         producerThread.start();
123         
124         return invokeParser( schemaObject );
125     }
126 
127 
128     /**
129      * Invoke the parser
130      * 
131      * @param schemaName The schema to be parsed
132      * @return A list of schema elements
133      * @throws java.io.IOException If the schema file can't be processed
134      * @throws java.text.ParseException If we weren't able to parse the schema
135      */
136     private List<SchemaElement> invokeParser( String schemaName ) throws IOException, ParseException
137     {
138         try
139         {
140             parser.parseSchema();
141 
142             return parser.getSchemaElements();
143         }
144         catch ( RecognitionException re )
145         {
146             String msg = I18n.err( I18n.ERR_06002_PARSER_FAILURE, schemaName, ExceptionUtils.getFullStackTrace( re ) );
147             init();
148             throw new ParseException( msg, re.getColumn() );
149         }
150         catch ( TokenStreamException tse )
151         {
152             String msg = I18n.err( I18n.ERR_06002_PARSER_FAILURE, schemaName, ExceptionUtils.getFullStackTrace( tse ) );
153             init();
154             throw new ParseException( msg, 0 );
155         }
156     }
157 
158 
159     /**
160      * Thread safe method parses a stream of OpenLDAP schemaObject elements/objects.
161      *
162      * @param schemaIn a stream of schema objects
163      * @return A list of schema elements
164      * @throws java.io.IOException If the schema file can't be processed
165      * @throws java.text.ParseException If we weren't able to parse the schema
166      */
167     public synchronized List<SchemaElement> parse( InputStream schemaIn ) throws IOException, ParseException
168     {
169         this.schemaIn = schemaIn;
170 
171         if ( producerThread == null )
172         {
173             producerThread = new Thread( new DataProducer() );
174         }
175 
176         producerThread.start();
177 
178         return invokeParser( "schema input stream ==> " + schemaIn.toString() );
179     }
180 
181 
182     /**
183      * Thread safe method parses a file of OpenLDAP schemaObject elements/objects.
184      *
185      * @param schemaFile a file of schema objects
186      * @throws java.io.IOException If the schema file can't be processed
187      * @throws java.text.ParseException If we weren't able to parse the schema
188      */
189     public synchronized void parse( File schemaFile ) throws IOException, ParseException
190     {
191         schemaIn = Files.newInputStream( Paths.get( schemaFile.getPath() ) );
192 
193         if ( producerThread == null )
194         {
195             producerThread = new Thread( new DataProducer() );
196         }
197 
198         producerThread.start();
199         invokeParser( "schema file ==> " + schemaFile.getAbsolutePath() );
200     }
201     
202 
203     /**
204      * The thread which read the schema files and fill the
205      * temporary buffer used by the lexical analyzer.
206      */
207     private class DataProducer implements Runnable
208     {
209         /**
210          * {@inheritDoc}
211          */
212         @Override
213         public void run()
214         {
215             int count = -1;
216 
217             try
218             {
219                 while ( ( count = schemaIn.read( buf ) ) != -1 )
220                 {
221                     parserIn.write( buf, 0, count );
222                     parserIn.flush();
223                 }
224 
225                 // using an input termination token END - need extra space to return
226                 parserIn.write( Strings.getBytesUtf8( "END " ) );
227             }
228             catch ( IOException e )
229             {
230                 e.printStackTrace();
231             }
232         }
233     }
234 }