View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  package org.apache.directory.api.dsmlv2;
21  
22  
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.io.Reader;
27  import java.io.StringReader;
28  import java.nio.charset.Charset;
29  import java.nio.file.Files;
30  import java.nio.file.Paths;
31  
32  import org.apache.directory.api.dsmlv2.response.BatchResponseDsml;
33  import org.apache.directory.api.dsmlv2.response.Dsmlv2ResponseGrammar;
34  import org.apache.directory.api.i18n.I18n;
35  import org.apache.directory.api.ldap.codec.api.LdapApiService;
36  import org.apache.directory.api.ldap.model.message.Response;
37  import org.apache.directory.api.util.Strings;
38  import org.xmlpull.v1.XmlPullParser;
39  import org.xmlpull.v1.XmlPullParserException;
40  import org.xmlpull.v1.XmlPullParserFactory;
41  
42  
43  /**
44   * This class represents the DSMLv2 Parser.
45   * It can be used to parse a DSMLv2 Response input.
46   *
47   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
48   */
49  public class Dsmlv2ResponseParser
50  {
51      /** The associated DSMLv2 container */
52      private Dsmlv2Container container;
53  
54  
55      /**
56       * Creates a new instance of Dsmlv2ResponseParser.
57       *
58       * @param codec The Ldap Service to use
59       * @throws XmlPullParserException if an error occurs while the initialization of the parser
60       */
61      public Dsmlv2ResponseParser( LdapApiService codec ) throws XmlPullParserException
62      {
63          this.container = new Dsmlv2Container( codec );
64  
65          this.container.setGrammar( Dsmlv2ResponseGrammar.getInstance() );
66  
67          XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
68          factory.setNamespaceAware( true );
69          XmlPullParser xpp = factory.newPullParser();
70  
71          container.setParser( xpp );
72      }
73  
74  
75      /**
76       * Sets the input string the parser is going to parse
77       *
78       * @param str the string the parser is going to parse
79       * @throws XmlPullParserException if an error occurs in the parser
80       */
81      public void setInput( String str ) throws XmlPullParserException
82      {
83          container.getParser().setInput( new StringReader( str ) );
84      }
85  
86  
87      /**
88       * Sets the input file the parser is going to parse. Default charset is used.
89       *
90       * @param fileName the name of the file
91       * @throws FileNotFoundException if the file does not exist
92       * @throws XmlPullParserException if an error occurs in the parser
93       */
94      public void setInputFile( String fileName ) throws IOException, XmlPullParserException
95      {
96          try ( Reader reader = new InputStreamReader( Files.newInputStream( Paths.get( fileName ) ), 
97              Charset.defaultCharset() ) )
98          {
99              container.getParser().setInput( reader );
100         }
101     }
102 
103 
104     /**
105      * Sets the input stream the parser is going to process
106      *
107      * @param inputStream contains a raw byte input stream of possibly unknown encoding (when inputEncoding is null)
108      * @param inputEncoding if not null it MUST be used as encoding for inputStream
109      * @throws XmlPullParserException if an error occurs in the parser
110      */
111     public void setInput( InputStream inputStream, String inputEncoding ) throws XmlPullParserException
112     {
113         container.getParser().setInput( inputStream, inputEncoding );
114     }
115 
116 
117     /**
118      * Launches the parsing on the input
119      * 
120      * @throws XmlPullParserException when an unrecoverable error occurs
121      * @throws IOException when an IO exception occurs
122      */
123     public void parse() throws XmlPullParserException, IOException
124     {
125         Dsmlv2ResponseGrammar grammar = Dsmlv2ResponseGrammar.getInstance();
126 
127         grammar.executeAction( container );
128     }
129 
130 
131     /**
132      * Launches the parsing of the Batch Response only
133      *
134      * @throws XmlPullParserException if an error occurs in the parser
135      */
136     public void parseBatchResponse() throws XmlPullParserException
137     {
138         XmlPullParser xpp = container.getParser();
139 
140         int eventType = xpp.getEventType();
141         
142         do
143         {
144             switch ( eventType )
145             {
146                 case XmlPullParser.START_DOCUMENT :
147                     container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE );
148                     break;
149 
150                 case XmlPullParser.END_DOCUMENT :
151                     container.setState( Dsmlv2StatesEnum.GRAMMAR_END );
152                     break;
153 
154                 case XmlPullParser.START_TAG :
155                     processTag( container, Tag.START );
156                     break;
157 
158                 case XmlPullParser.END_TAG :
159                     processTag( container, Tag.END );
160                     break;
161 
162                 default:
163                     break;
164             }
165             
166             try
167             {
168                 eventType = xpp.next();
169             }
170             catch ( IOException ioe )
171             {
172                 throw new XmlPullParserException( I18n.err( I18n.ERR_03037, ioe.getLocalizedMessage() ), xpp, ioe );
173             }
174         }
175         while ( container.getState() != Dsmlv2StatesEnum.BATCH_RESPONSE_LOOP );
176     }
177 
178 
179     /**
180      * Processes the task required in the grammar to the given tag type
181      *
182      * @param container the DSML container
183      * @param tagType the tag type
184      * @throws XmlPullParserException when an error occurs during the parsing
185      */
186     private static void processTag( Dsmlv2Container container, int tagType ) throws XmlPullParserException
187     {
188         XmlPullParser xpp = container.getParser();
189 
190         String tagName = Strings.lowerCase( xpp.getName() );
191 
192         GrammarTransition transition = container.getTransition( container.getState(), new Tag( tagName, tagType ) );
193 
194         if ( transition != null )
195         {
196             container.setState( transition.getNextState() );
197 
198             if ( transition.hasAction() )
199             {
200                 transition.getAction().action( container );
201             }
202         }
203         else
204         {
205             throw new XmlPullParserException( I18n.err( I18n.ERR_03036, new Tag( tagName, tagType ) ), xpp, null );
206         }
207     }
208 
209 
210     /**
211      * Gets the Batch Response or null if the it has not been parsed yet
212      *
213      * @return the Batch Response or null if the it has not been parsed yet
214      */
215     public BatchResponseDsml getBatchResponse()
216     {
217         return container.getBatchResponse();
218     }
219 
220 
221     /**
222      * Returns the next Request or null if there's no more request
223      * @return the next Request or null if there's no more request
224      * @throws XmlPullParserException when an error occurs during the parsing
225      */
226     public DsmlDecorator<? extends Response> getNextResponse() throws XmlPullParserException
227     {
228         if ( container.getBatchResponse() == null )
229         {
230             parseBatchResponse();
231         }
232 
233         XmlPullParser xpp = container.getParser();
234 
235         int eventType = xpp.getEventType();
236         do
237         {
238             while ( eventType == XmlPullParser.TEXT )
239             {
240                 try
241                 {
242                     xpp.next();
243                 }
244                 catch ( IOException ioe )
245                 {
246                     throw new XmlPullParserException( I18n.err( I18n.ERR_03037, ioe.getLocalizedMessage() ), xpp, ioe );
247                 }
248                 eventType = xpp.getEventType();
249             }
250 
251             switch ( eventType )
252             {
253                 case XmlPullParser.START_DOCUMENT :
254                     container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE );
255                     break;
256 
257                 case XmlPullParser.END_DOCUMENT :
258                     container.setState( Dsmlv2StatesEnum.GRAMMAR_END );
259                     return null;
260 
261                 case XmlPullParser.START_TAG :
262                     processTag( container, Tag.START );
263                     break;
264 
265                 case XmlPullParser.END_TAG :
266                     processTag( container, Tag.END );
267                     break;
268 
269                 default:
270                     break;
271             }
272             
273             try
274             {
275                 eventType = xpp.next();
276             }
277             catch ( IOException ioe )
278             {
279                 throw new XmlPullParserException( I18n.err( I18n.ERR_03037, ioe.getLocalizedMessage() ), xpp, ioe );
280             }
281         }
282         while ( container.getState() != Dsmlv2StatesEnum.BATCH_RESPONSE_LOOP );
283 
284         return container.getBatchResponse().getCurrentResponse();
285     }
286 
287 
288     /**
289      * Parses all the responses
290      *
291      * @throws XmlPullParserException 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 }