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