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