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.request.BatchRequestDsml;
33  import org.apache.directory.api.dsmlv2.request.Dsmlv2Grammar;
34  import org.apache.directory.api.i18n.I18n;
35  import org.apache.directory.api.ldap.model.message.Request;
36  import org.apache.directory.api.util.Strings;
37  import org.xmlpull.v1.XmlPullParser;
38  import org.xmlpull.v1.XmlPullParserException;
39  import org.xmlpull.v1.XmlPullParserFactory;
40  
41  
42  /**
43   * This class represents the DSMLv2 Parser.
44   * It can be used to parse a plain DSMLv2 Request input document or the one inside a SOAP envelop.
45   *
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   */
48  public class Dsmlv2Parser
49  {
50      /** The associated DSMLv2 container */
51      private Dsmlv2Container container;
52  
53      /**
54       * flag to indicate if the batch request should maintain a list of all the
55       * operation request objects present in the DSML document. Default is true
56       */
57      private boolean storeMsgInBatchReq = true;
58  
59      /** The thread safe DSMLv2 Grammar */
60      private Dsmlv2Grammar grammar;
61  
62  
63      /**
64       * Creates a new instance of Dsmlv2Parser.
65       *
66       * @throws XmlPullParserException if an error occurs during the initialization of the parser
67       */
68      public Dsmlv2Parser() throws XmlPullParserException
69      {
70          this( true );
71      }
72  
73  
74      /**
75       * Creates a new instance of Dsmlv2Parser.
76       *
77       * @param storeMsgInBatchReq flag to set if the parsed requests should b stored
78       * @throws XmlPullParserException if an error occurs during the initialization of the parser
79       */
80      public Dsmlv2Parser( boolean storeMsgInBatchReq ) throws XmlPullParserException
81      {
82          this.storeMsgInBatchReq = storeMsgInBatchReq;
83  
84          this.grammar = new Dsmlv2Grammar();
85          this.container = new Dsmlv2Container( grammar.getLdapCodecService() );
86  
87          this.container.setGrammar( grammar );
88  
89          XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
90          factory.setNamespaceAware( true );
91          XmlPullParser xpp = factory.newPullParser();
92  
93          container.setParser( xpp );
94      }
95  
96  
97      /**
98       * Creates a new instance of Dsmlv2Parser.
99       *
100      * @param grammar The grammar in use
101      * @throws XmlPullParserException if an error occurs during the initialization of the parser
102      */
103     public Dsmlv2Parser( Dsmlv2Grammar grammar ) throws XmlPullParserException
104     {
105         this.container = new Dsmlv2Container( grammar.getLdapCodecService() );
106         this.container.setGrammar( grammar );
107         this.grammar = grammar;
108 
109         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
110         factory.setNamespaceAware( true );
111         XmlPullParser xpp = factory.newPullParser();
112 
113         container.setParser( xpp );
114     }
115 
116 
117     /**
118      * Sets the input file the parser is going to parse. Default charset is used.
119      *
120      * @param fileName the name of the file
121      * @throws FileNotFoundException if the file does not exist
122      * @throws XmlPullParserException if an error occurs in the parser
123      */
124     public void setInputFile( String fileName ) throws IOException, XmlPullParserException
125     {
126         try ( Reader reader = new InputStreamReader( Files.newInputStream( Paths.get( ( fileName ) ) ), 
127             Charset.defaultCharset() ) )
128         {
129             container.getParser().setInput( reader );
130         }
131     }
132 
133 
134     /**
135      * Sets the input stream the parser is going to process
136      *
137      * @param inputStream contains a raw byte input stream of possibly unknown encoding (when inputEncoding is null)
138      * @param inputEncoding if not null it MUST be used as encoding for inputStream
139      * @throws XmlPullParserException if an error occurs in the parser
140      */
141     public void setInput( InputStream inputStream, String inputEncoding ) throws XmlPullParserException
142     {
143         container.getParser().setInput( inputStream, inputEncoding );
144     }
145 
146 
147     /**
148      * Sets the input string the parser is going to parse
149      *
150      * @param str the string the parser is going to parse
151      * @throws XmlPullParserException if an error occurs in the parser
152      */
153     public void setInput( String str ) throws XmlPullParserException
154     {
155         container.getParser().setInput( new StringReader( str ) );
156     }
157 
158 
159     /**
160      * Launches the parsing on the input
161      * This method will parse the whole DSML document, without considering the flag storeMsgInBatchReq
162      * @throws XmlPullParserException when an unrecoverable error occurs
163      * @throws IOException when an IO execption occurs
164      */
165     public void parse() throws XmlPullParserException, IOException
166     {
167         grammar.executeAction( container );
168     }
169 
170 
171     /**
172      * Launches the parsing of the Batch Request only
173      *
174      * @throws XmlPullParserException if an error occurs in the parser
175      */
176     public void parseBatchRequest() throws XmlPullParserException
177     {
178         XmlPullParser xpp = container.getParser();
179 
180         int eventType = xpp.getEventType();
181 
182         do
183         {
184             switch ( eventType )
185             {
186                 case XmlPullParser.START_DOCUMENT:
187                     container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE );
188                     break;
189 
190                 case XmlPullParser.END_DOCUMENT:
191                     container.setState( Dsmlv2StatesEnum.GRAMMAR_END );
192                     break;
193 
194                 case XmlPullParser.START_TAG:
195                     processTag( container, Tag.START );
196                     break;
197 
198                 case XmlPullParser.END_TAG:
199                     processTag( container, Tag.END );
200                     break;
201 
202                 default:
203                     break;
204             }
205 
206             try
207             {
208                 eventType = xpp.next();
209             }
210             catch ( IOException ioe )
211             {
212                 throw new XmlPullParserException( I18n.err( I18n.ERR_03037, ioe.getLocalizedMessage() ), xpp, ioe );
213             }
214         }
215         while ( container.getState() != Dsmlv2StatesEnum.BATCHREQUEST_START_TAG );
216 
217         BatchRequestDsml br = container.getBatchRequest();
218 
219         if ( br != null )
220         {
221             br.setStoreReq( storeMsgInBatchReq );
222         }
223     }
224 
225 
226     /**
227      * Processes the task required in the grammar to the given tag type
228      *
229      * @param container the DSML container
230      * @param tagType the tag type
231      * @throws XmlPullParserException when an error occurs during the parsing
232      */
233     private static void processTag( Dsmlv2Container container, int tagType ) throws XmlPullParserException
234     {
235         XmlPullParser xpp = container.getParser();
236 
237         String tagName = Strings.lowerCase( xpp.getName() );
238 
239         GrammarTransition transition = container.getTransition( container.getState(), new Tag( tagName, tagType ) );
240 
241         if ( transition != null )
242         {
243             container.setState( transition.getNextState() );
244 
245             if ( transition.hasAction() )
246             {
247                 transition.getAction().action( container );
248             }
249         }
250         else
251         {
252             throw new XmlPullParserException( I18n.err( I18n.ERR_03036, new Tag( tagName, tagType ) ), xpp, null );
253         }
254     }
255 
256 
257     /**
258      * Gets the Batch Request or null if the it has not been parsed yet
259      *
260      * @return the Batch Request or null if the it has not been parsed yet
261      */
262     public BatchRequestDsml getBatchRequest()
263     {
264         return container.getBatchRequest();
265     }
266 
267 
268     /**
269      * Gets the next Request or null if there's no more request
270      * @return the next Request or null if there's no more request
271      * @throws XmlPullParserException when an error occurs during the parsing
272      */
273     public DsmlDecorator<? extends Request> getNextRequest() throws XmlPullParserException
274     {
275         if ( container.getBatchRequest() == null )
276         {
277             parseBatchRequest();
278         }
279 
280         XmlPullParser xpp = container.getParser();
281 
282         int eventType = xpp.getEventType();
283         do
284         {
285             while ( eventType == XmlPullParser.TEXT )
286             {
287                 try
288                 {
289                     xpp.next();
290                 }
291                 catch ( IOException ioe )
292                 {
293                     throw new XmlPullParserException( I18n.err( I18n.ERR_03037, ioe.getLocalizedMessage() ), xpp, ioe );
294                 }
295                 eventType = xpp.getEventType();
296             }
297 
298             switch ( eventType )
299             {
300                 case XmlPullParser.START_DOCUMENT:
301                     container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE );
302                     break;
303 
304                 case XmlPullParser.END_DOCUMENT:
305                     container.setState( Dsmlv2StatesEnum.GRAMMAR_END );
306                     return null;
307 
308                 case XmlPullParser.START_TAG:
309                     processTag( container, Tag.START );
310                     break;
311 
312                 case XmlPullParser.END_TAG:
313                     processTag( container, Tag.END );
314                     break;
315 
316                 default:
317                     break;
318             }
319 
320             try
321             {
322                 eventType = xpp.next();
323             }
324             catch ( IOException ioe )
325             {
326                 throw new XmlPullParserException( I18n.err( I18n.ERR_03037, ioe.getLocalizedMessage() ), xpp, ioe );
327             }
328         }
329         while ( container.getState() != Dsmlv2StatesEnum.BATCHREQUEST_LOOP );
330 
331         return container.getBatchRequest().getCurrentRequest();
332     }
333 
334 
335     /**
336      * Parses all the requests
337      *
338      * @throws XmlPullParserException when an error occurs during the parsing
339      */
340     public void parseAllRequests() throws XmlPullParserException
341     {
342         while ( getNextRequest() != null )
343         {
344             continue;
345         }
346     }
347 }