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     */
020    
021    package org.apache.directory.shared.dsmlv2;
022    
023    
024    import java.io.FileNotFoundException;
025    import java.io.FileReader;
026    import java.io.IOException;
027    import java.io.InputStream;
028    import java.io.Reader;
029    import java.io.StringReader;
030    
031    import org.apache.directory.shared.dsmlv2.reponse.BatchResponse;
032    import org.apache.directory.shared.dsmlv2.reponse.Dsmlv2ResponseGrammar;
033    import org.apache.directory.shared.i18n.I18n;
034    import org.apache.directory.shared.ldap.codec.LdapResponseCodec;
035    import org.xmlpull.v1.XmlPullParser;
036    import org.xmlpull.v1.XmlPullParserException;
037    import 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     * @version $Rev$, $Date$
046     */
047    public 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
057         *      if an error occurs while the initialization of the parser
058         */
059        public Dsmlv2ResponseParser() throws XmlPullParserException
060        {
061            this.container = new Dsmlv2Container();
062    
063            this.container.setGrammar( Dsmlv2ResponseGrammar.getInstance() );
064    
065            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
066            factory.setNamespaceAware( true );
067            XmlPullParser xpp = factory.newPullParser();
068    
069            container.setParser( xpp );
070        }
071    
072    
073        /**
074         * Sets the input string the parser is going to parse
075         *
076         * @param str
077         *      the string the parser is going to parse
078         * @throws XmlPullParserException
079         *      if an error occurs in the parser
080         */
081        public void setInput( String str ) throws FileNotFoundException, XmlPullParserException
082        {
083            container.getParser().setInput( new StringReader( str ) );
084        }
085    
086    
087        /**
088         * Sets the input file the parser is going to parse
089         *
090         * @param fileName
091         *      the name of the file
092         * @throws FileNotFoundException
093         *      if the file does not exist
094         * @throws XmlPullParserException
095         *      if an error occurs in the parser
096         */
097        public void setInputFile( String fileName ) throws FileNotFoundException, XmlPullParserException
098        {
099            Reader reader = new FileReader( fileName );
100            container.getParser().setInput( reader );
101        }
102    
103    
104        /**
105         * Sets the input stream the parser is going to process
106         *
107         * @param inputStream
108         *      contains a raw byte input stream of possibly unknown encoding (when inputEncoding is null)
109         * @param inputEncoding
110         *      if not null it MUST be used as encoding for inputStream
111         * @throws XmlPullParserException
112         *      if an error occurs in the parser
113         */
114        public void setInput( InputStream inputStream, String inputEncoding ) throws XmlPullParserException
115        {
116            container.getParser().setInput( inputStream, inputEncoding );
117        }
118    
119    
120        /**
121         * Launches the parsing on the input
122         * 
123         * @throws XmlPullParserException 
124         *      when an unrecoverable error occurs
125         * @throws IOException
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.END_STATE );
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,
171                        null );
172                }
173            }
174            while ( container.getState() != Dsmlv2StatesEnum.BATCH_RESPONSE_LOOP );
175        }
176    
177    
178        /**
179         * Processes the task required in the grammar to the given tag type
180         *
181         * @param container
182         *      the DSML container
183         * @param tagType
184         *      the tag type
185         * @throws XmlPullParserException 
186         *      when an error occurs during the parsing
187         */
188        private void processTag( Dsmlv2Container container, int tagType ) throws XmlPullParserException
189        {
190            XmlPullParser xpp = container.getParser();
191    
192            String tagName = xpp.getName().toLowerCase();
193    
194            GrammarTransition transition = container.getTransition( container.getState(), new Tag( tagName, tagType ) );
195    
196            if ( transition != null )
197            {
198                container.setState( transition.getNextState() );
199    
200                if ( transition.hasAction() )
201                {
202                    transition.getAction().action( container );
203                }
204            }
205            else
206            {
207                throw new XmlPullParserException( I18n.err( I18n.ERR_03036, new Tag( tagName, tagType ) ), xpp, null );
208            }
209        }
210    
211    
212        /**
213         * Gets the Batch Response or null if the it has not been parsed yet
214         *
215         * @return 
216         *      the Batch Response or null if the it has not been parsed yet
217         */
218        public BatchResponse getBatchResponse()
219        {
220            return container.getBatchResponse();
221        }
222    
223    
224        /**
225         * Returns the next Request or null if there's no more request
226         * @return
227         *      the next Request or null if there's no more request
228         * @throws XmlPullParserException 
229         *      when an error occurs during the parsing
230         */
231        public LdapResponseCodec getNextResponse() throws XmlPullParserException
232        {
233            if ( container.getBatchResponse() == null )
234            {
235                parseBatchResponse();
236            }
237    
238            XmlPullParser xpp = container.getParser();
239    
240            int eventType = xpp.getEventType();
241            do
242            {
243                while ( eventType == XmlPullParser.TEXT )
244                {
245                    try
246                    {
247                        xpp.next();
248                    }
249                    catch ( IOException e )
250                    {
251                        throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp,
252                            null );
253                    }
254                    eventType = xpp.getEventType();
255                }
256    
257                if ( eventType == XmlPullParser.START_DOCUMENT )
258                {
259                    container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE );
260                }
261                else if ( eventType == XmlPullParser.END_DOCUMENT )
262                {
263                    container.setState( Dsmlv2StatesEnum.END_STATE );
264                    return null;
265                }
266                else if ( eventType == XmlPullParser.START_TAG )
267                {
268                    processTag( container, Tag.START );
269                }
270                else if ( eventType == XmlPullParser.END_TAG )
271                {
272                    processTag( container, Tag.END );
273                }
274                try
275                {
276                    eventType = xpp.next();
277                }
278                catch ( IOException e )
279                {
280                    throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp,
281                        null );
282                }
283            }
284            while ( container.getState() != Dsmlv2StatesEnum.BATCH_RESPONSE_LOOP );
285    
286            return container.getBatchResponse().getCurrentResponse();
287        }
288    
289    
290        /**
291         * Parses all the responses
292         *
293         * @throws XmlPullParserException
294         *      when an error occurs during the parsing
295         */
296        public void parseAllResponses() throws XmlPullParserException
297        {
298            while ( getNextResponse() != null )
299            {
300                continue;
301            }
302        }
303    }