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