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.dsmlv2;
021
022
023import java.io.FileNotFoundException;
024import java.io.FileReader;
025import java.io.IOException;
026import java.io.InputStream;
027import java.io.Reader;
028import java.io.StringReader;
029
030import org.apache.directory.shared.dsmlv2.request.BatchRequestDsml;
031import org.apache.directory.shared.dsmlv2.request.Dsmlv2Grammar;
032import org.apache.directory.shared.i18n.I18n;
033import org.apache.directory.shared.ldap.model.message.Request;
034import org.xmlpull.v1.XmlPullParser;
035import org.xmlpull.v1.XmlPullParserException;
036import org.xmlpull.v1.XmlPullParserFactory;
037
038
039/**
040 * This class represents the DSMLv2 Parser.
041 * It can be used to parse a plain DSMLv2 Request input document or the one inside a SOAP envelop.
042 *
043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044 */
045public class Dsmlv2Parser
046{
047    /** The associated DSMLv2 container */
048    private Dsmlv2Container container;
049
050    /**
051     * flag to indicate if the batch request should maintain a list of all the 
052     * operation request objects present in the DSML document. Default is true
053     */
054    private boolean storeMsgInBatchReq = true;
055
056    
057    /** The thread safe DSMLv2 Grammar */
058    private Dsmlv2Grammar grammar;
059
060
061    /**
062     * Creates a new instance of Dsmlv2Parser.
063     *
064     * @throws XmlPullParserException
065     *      if an error occurs while the initialization of the parser
066     */
067    public Dsmlv2Parser() throws XmlPullParserException
068    {
069        this( true );
070    }
071    
072    /**
073     * 
074     * Creates a new instance of Dsmlv2Parser.
075     *
076     * @param storeMsgInBatchReq flag to set if the parsed requests should b stored
077     * @throws XmlPullParserException
078     */
079    public Dsmlv2Parser( boolean storeMsgInBatchReq ) throws XmlPullParserException
080    {
081        this.storeMsgInBatchReq = storeMsgInBatchReq;
082        
083        this.grammar = new Dsmlv2Grammar();
084        this.container = new Dsmlv2Container( grammar.getLdapCodecService() );
085
086        this.container.setGrammar( grammar );
087
088        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
089        factory.setNamespaceAware( true );
090        XmlPullParser xpp = factory.newPullParser();
091
092        container.setParser( xpp );
093    }
094
095
096    /**
097     * Creates a new instance of Dsmlv2Parser.
098     *
099     * @throws XmlPullParserException
100     *      if an error occurs while the initialization of the parser
101     */
102    public Dsmlv2Parser( Dsmlv2Grammar grammar ) throws XmlPullParserException
103    {
104        this.container = new Dsmlv2Container( grammar.getLdapCodecService() );
105        this.container.setGrammar( grammar );
106        this.grammar = grammar;
107        
108        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
109        factory.setNamespaceAware( true );
110        XmlPullParser xpp = factory.newPullParser();
111
112        container.setParser( xpp );
113    }
114
115
116    /**
117     * Sets the input file the parser is going to parse
118     *
119     * @param fileName
120     *      the name of the file
121     * @throws FileNotFoundException
122     *      if the file does not exist
123     * @throws XmlPullParserException
124     *      if an error occurs in the parser
125     */
126    public void setInputFile( String fileName ) throws FileNotFoundException, XmlPullParserException
127    {
128        Reader reader = new FileReader( fileName );
129        container.getParser().setInput( reader );
130    }
131
132
133    /**
134     * Sets the input stream the parser is going to process
135     *
136     * @param inputStream
137     *      contains a raw byte input stream of possibly unknown encoding (when inputEncoding is null)
138     * @param inputEncoding
139     *      if not null it MUST be used as encoding for inputStream
140     * @throws XmlPullParserException
141     *      if an error occurs in the parser
142     */
143    public void setInput( InputStream inputStream, String inputEncoding ) throws XmlPullParserException
144    {
145        container.getParser().setInput( inputStream, inputEncoding );
146    }
147
148
149    /**
150     * Sets the input string the parser is going to parse
151     *
152     * @param str
153     *      the string the parser is going to parse
154     * @throws XmlPullParserException
155     *      if an error occurs in the parser
156     */
157    public void setInput( String str ) throws XmlPullParserException
158    {
159        container.getParser().setInput( new StringReader( str ) );
160    }
161
162
163    /**
164     * Launches the parsing on the input
165     * This method will parse the whole DSML document, without considering the flag {@link #storeMsgInBatchReq} 
166     * @throws XmlPullParserException 
167     *      when an unrecoverable error occurs
168     * @throws IOException
169     *      when an IO execption occurs
170     */
171    public void parse() throws XmlPullParserException, IOException
172    {
173        grammar.executeAction( container );
174    }
175
176
177    /**
178     * Launches the parsing of the Batch Request only
179     *
180     * @throws XmlPullParserException
181     *      if an error occurs in the parser
182     */
183    public void parseBatchRequest() throws XmlPullParserException
184    {
185        XmlPullParser xpp = container.getParser();
186
187        int eventType = xpp.getEventType();
188        do
189        {
190            if ( eventType == XmlPullParser.START_DOCUMENT )
191            {
192                container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE );
193            }
194            else if ( eventType == XmlPullParser.END_DOCUMENT )
195            {
196                container.setState( Dsmlv2StatesEnum.GRAMMAR_END );
197            }
198            else if ( eventType == XmlPullParser.START_TAG )
199            {
200                processTag( container, Tag.START );
201            }
202            else if ( eventType == XmlPullParser.END_TAG )
203            {
204                processTag( container, Tag.END );
205            }
206            try
207            {
208                eventType = xpp.next();
209            }
210            catch ( IOException e )
211            {
212                throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null );
213            }
214        }
215        while ( container.getState() != Dsmlv2StatesEnum.BATCHREQUEST_START_TAG );
216        
217        BatchRequestDsml br = container.getBatchRequest();
218        
219        if ( br != null )
220        {
221            br.setStoreReq( storeMsgInBatchReq );
222        }
223    }
224
225
226    /**
227     * Processes the task required in the grammar to the given tag type
228     *
229     * @param container
230     *      the DSML container
231     * @param tagType
232     *      the tag type
233     * @throws XmlPullParserException 
234     *      when an error occurs during the parsing
235     */
236    private static void processTag( Dsmlv2Container container, int tagType ) throws XmlPullParserException
237    {
238        XmlPullParser xpp = container.getParser();
239
240        String tagName = xpp.getName().toLowerCase();
241
242        GrammarTransition transition = container.getTransition( container.getState(), new Tag( tagName, tagType ) );
243
244        if ( transition != null )
245        {
246            container.setState( transition.getNextState() );
247
248            if ( transition.hasAction() )
249            {
250                transition.getAction().action( container );
251            }
252        }
253        else
254        {
255            throw new XmlPullParserException( I18n.err( I18n.ERR_03036, new Tag( tagName, tagType ) ), xpp, null );
256        }
257    }
258
259
260    /**
261     * Gets the Batch Request or null if the it has not been parsed yet
262     *
263     * @return 
264     *      the Batch Request or null if the it has not been parsed yet
265     */
266    public BatchRequestDsml getBatchRequest()
267    {
268        return container.getBatchRequest();
269    }
270
271
272    /**
273     * Gets the next Request or null if there's no more request
274     * @return
275     *      the next Request or null if there's no more request
276     * @throws XmlPullParserException      
277     *      when an error occurs during the parsing
278     */
279    public DsmlDecorator<? extends Request> getNextRequest() throws XmlPullParserException
280    {
281        if ( container.getBatchRequest() == null )
282        {
283            parseBatchRequest();
284        }
285
286        XmlPullParser xpp = container.getParser();
287
288        int eventType = xpp.getEventType();
289        do
290        {
291            while ( eventType == XmlPullParser.TEXT )
292            {
293                try
294                {
295                    xpp.next();
296                }
297                catch ( IOException e )
298                {
299                    throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null );
300                }
301                eventType = xpp.getEventType();
302            }
303
304            if ( eventType == XmlPullParser.START_DOCUMENT )
305            {
306                container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE );
307            }
308            else if ( eventType == XmlPullParser.END_DOCUMENT )
309            {
310                container.setState( Dsmlv2StatesEnum.GRAMMAR_END );
311                return null;
312            }
313            else if ( eventType == XmlPullParser.START_TAG )
314            {
315                processTag( container, Tag.START );
316            }
317            else if ( eventType == XmlPullParser.END_TAG )
318            {
319                processTag( container, Tag.END );
320            }
321            try
322            {
323                eventType = xpp.next();
324            }
325            catch ( IOException e )
326            {
327                throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null );
328            }
329        }
330        while ( container.getState() != Dsmlv2StatesEnum.BATCHREQUEST_LOOP );
331
332        return container.getBatchRequest().getCurrentRequest();
333    }
334
335
336    /**
337     * Parses all the requests
338     *
339     * @throws XmlPullParserException
340     *      when an error occurs during the parsing
341     */
342    public void parseAllRequests() throws XmlPullParserException
343    {
344        while ( getNextRequest() != null )
345        {
346            continue;
347        }
348    }
349}