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