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