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