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            
157            try
158            {
159                eventType = xpp.next();
160            }
161            catch ( IOException e )
162            {
163                throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null );
164            }
165        }
166        while ( container.getState() != Dsmlv2StatesEnum.BATCH_RESPONSE_LOOP );
167    }
168
169
170    /**
171     * Processes the task required in the grammar to the given tag type
172     *
173     * @param container the DSML container
174     * @param tagType the tag type
175     * @throws XmlPullParserException when an error occurs during the parsing
176     */
177    private static void processTag( Dsmlv2Container container, int tagType ) throws XmlPullParserException
178    {
179        XmlPullParser xpp = container.getParser();
180
181        String tagName = Strings.toLowerCase( xpp.getName() );
182
183        GrammarTransition transition = container.getTransition( container.getState(), new Tag( tagName, tagType ) );
184
185        if ( transition != null )
186        {
187            container.setState( transition.getNextState() );
188
189            if ( transition.hasAction() )
190            {
191                transition.getAction().action( container );
192            }
193        }
194        else
195        {
196            throw new XmlPullParserException( I18n.err( I18n.ERR_03036, new Tag( tagName, tagType ) ), xpp, null );
197        }
198    }
199
200
201    /**
202     * Gets the Batch Response or null if the it has not been parsed yet
203     *
204     * @return the Batch Response or null if the it has not been parsed yet
205     */
206    public BatchResponseDsml getBatchResponse()
207    {
208        return container.getBatchResponse();
209    }
210
211
212    /**
213     * Returns the next Request or null if there's no more request
214     * @return the next Request or null if there's no more request
215     * @throws XmlPullParserException when an error occurs during the parsing
216     */
217    public DsmlDecorator<? extends Response> getNextResponse() throws XmlPullParserException
218    {
219        if ( container.getBatchResponse() == null )
220        {
221            parseBatchResponse();
222        }
223
224        XmlPullParser xpp = container.getParser();
225
226        int eventType = xpp.getEventType();
227        do
228        {
229            while ( eventType == XmlPullParser.TEXT )
230            {
231                try
232                {
233                    xpp.next();
234                }
235                catch ( IOException e )
236                {
237                    throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null );
238                }
239                eventType = xpp.getEventType();
240            }
241
242            switch ( eventType )
243            {
244                case XmlPullParser.START_DOCUMENT :
245                    container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE );
246                    break;
247
248                case XmlPullParser.END_DOCUMENT :
249                    container.setState( Dsmlv2StatesEnum.GRAMMAR_END );
250                    return null;
251
252                case XmlPullParser.START_TAG :
253                    processTag( container, Tag.START );
254                    break;
255
256                case XmlPullParser.END_TAG :
257                    processTag( container, Tag.END );
258                    break;
259            }
260            
261            try
262            {
263                eventType = xpp.next();
264            }
265            catch ( IOException e )
266            {
267                throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null );
268            }
269        }
270        while ( container.getState() != Dsmlv2StatesEnum.BATCH_RESPONSE_LOOP );
271
272        return container.getBatchResponse().getCurrentResponse();
273    }
274
275
276    /**
277     * Parses all the responses
278     *
279     * @throws XmlPullParserException when an error occurs during the parsing
280     */
281    public void parseAllResponses() throws XmlPullParserException
282    {
283        while ( getNextResponse() != null )
284        {
285            continue;
286        }
287    }
288}