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.converter.schema;
021
022
023import java.io.ByteArrayInputStream;
024import java.io.File;
025import java.io.FileInputStream;
026import java.io.IOException;
027import java.io.InputStream;
028import java.io.PipedInputStream;
029import java.io.PipedOutputStream;
030import java.text.ParseException;
031import java.util.List;
032
033import org.apache.commons.lang.exception.ExceptionUtils;
034import org.apache.directory.api.i18n.I18n;
035
036import antlr.RecognitionException;
037import antlr.TokenStreamException;
038
039
040/**
041 * A reusable wrapper for antlr generated schema parsers.
042 *
043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044 */
045public class SchemaParser
046{
047    /** The antlr generated parser */
048    private antlrSchemaConverterParser parser = null;
049
050    /** A pipe into the parser */
051    private PipedOutputStream parserIn = null;
052
053    /** A temporary buffer storing the read schema bytes */
054    private byte[] buf = new byte[128];
055
056    /** The inputStream mapped over the schema file to parse */
057    private InputStream schemaIn;
058
059    /** The thread used to read the schema */
060    private Thread producerThread;
061
062
063    /**
064     * Creates a reusable instance of an SchemaParser.
065     *
066     * @throws java.io.IOException if the pipe cannot be formed
067     */
068    public SchemaParser() throws IOException
069    {
070        init();
071    }
072
073
074    /**
075     * Initializes a parser and its plumbing.
076     *
077     * @throws java.io.IOException if a pipe cannot be formed.
078     */
079    public synchronized void init() throws IOException
080    {
081        parserIn = new PipedOutputStream();
082        PipedInputStream in = new PipedInputStream();
083        parserIn.connect( in );
084        antlrSchemaConverterLexer lexer = new antlrSchemaConverterLexer( in );
085        parser = new antlrSchemaConverterParser( lexer );
086    }
087
088
089    /**
090     * Clear the parser.
091     */
092    public synchronized void clear()
093    {
094        parser.clear();
095    }
096
097
098    /**
099     * Thread safe method parses an OpenLDAP schemaObject element/object.
100     *
101     * @param schemaObject the String image of a complete schema object
102     * @return The list of parsed schema elements
103     * @throws java.io.IOException If the schema file can't be processed
104     * @throws java.text.ParseException If we weren't able to parse the schema
105     */
106    public synchronized List<SchemaElement> parse( String schemaObject ) throws IOException, ParseException
107    {
108        if ( ( schemaObject == null ) || ( schemaObject.trim().equals( "" ) ) )
109        {
110            throw new ParseException( I18n.err( I18n.ERR_06001_EMPTY_OR_NULL_SCHEMA_OBJECT ), 0 );
111        }
112
113        schemaIn = new ByteArrayInputStream( schemaObject.getBytes() );
114
115        if ( producerThread == null )
116        {
117            producerThread = new Thread( new DataProducer() );
118        }
119
120        producerThread.start();
121        return invokeParser( schemaObject );
122    }
123
124
125    /**
126     * Invoke the parser
127     * @param schemaName The schema to be parsed
128     * @return A list of schema elements
129     *
130     * @throws java.io.IOException If the schema file can't be processed
131     * @throws java.text.ParseException If we weren't able to parse the schema
132     */
133    private List<SchemaElement> invokeParser( String schemaName ) throws IOException, ParseException
134    {
135        try
136        {
137            parser.parseSchema();
138
139            return parser.getSchemaElements();
140        }
141        catch ( RecognitionException re )
142        {
143            String msg = I18n.err( I18n.ERR_06002_PARSER_FAILURE, schemaName, ExceptionUtils.getFullStackTrace( re ) );
144            init();
145            throw new ParseException( msg, re.getColumn() );
146        }
147        catch ( TokenStreamException tse )
148        {
149            String msg = I18n.err( I18n.ERR_06002_PARSER_FAILURE, schemaName, ExceptionUtils.getFullStackTrace( tse ) );
150            init();
151            throw new ParseException( msg, 0 );
152        }
153    }
154
155
156    /**
157     * Thread safe method parses a stream of OpenLDAP schemaObject elements/objects.
158     *
159     * @param schemaIn a stream of schema objects
160     * @return A list of schema elements
161     * @throws java.io.IOException If the schema file can't be processed
162     * @throws java.text.ParseException If we weren't able to parse the schema
163     */
164    public synchronized List<SchemaElement> parse( InputStream schemaIn ) throws IOException, ParseException
165    {
166        this.schemaIn = schemaIn;
167
168        if ( producerThread == null )
169        {
170            producerThread = new Thread( new DataProducer() );
171        }
172
173        producerThread.start();
174
175        return invokeParser( "schema input stream ==> " + schemaIn.toString() );
176    }
177
178
179    /**
180     * Thread safe method parses a file of OpenLDAP schemaObject elements/objects.
181     *
182     * @param schemaFile a file of schema objects
183     * @throws java.io.IOException If the schema file can't be processed
184     * @throws java.text.ParseException If we weren't able to parse the schema
185     */
186    public synchronized void parse( File schemaFile ) throws IOException, ParseException
187    {
188        this.schemaIn = new FileInputStream( schemaFile );
189
190        if ( producerThread == null )
191        {
192            producerThread = new Thread( new DataProducer() );
193        }
194
195        producerThread.start();
196        invokeParser( "schema file ==> " + schemaFile.getAbsolutePath() );
197    }
198
199    /**
200     * The thread which read the schema files and fill the
201     * temporary buffer used by the lexical analyzer.
202     */
203    private class DataProducer implements Runnable
204    {
205        public void run()
206        {
207            int count = -1;
208
209            try
210            {
211                while ( ( count = schemaIn.read( buf ) ) != -1 )
212                {
213                    parserIn.write( buf, 0, count );
214                    parserIn.flush();
215                }
216
217                // using an input termination token END - need extra space to return
218                parserIn.write( "END ".getBytes() );
219            }
220            catch ( IOException e )
221            {
222                e.printStackTrace();
223            }
224        }
225    }
226}