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