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.IOException; 024import java.io.InputStream; 025import java.io.InputStreamReader; 026import java.io.Reader; 027import java.io.StringReader; 028import java.nio.charset.Charset; 029import java.nio.file.Files; 030import java.nio.file.Paths; 031 032import org.apache.directory.api.dsmlv2.request.BatchRequestDsml; 033import org.apache.directory.api.dsmlv2.request.Dsmlv2Grammar; 034import org.apache.directory.api.i18n.I18n; 035import org.apache.directory.api.ldap.model.message.Request; 036import org.apache.directory.api.util.Strings; 037import org.xmlpull.v1.XmlPullParser; 038import org.xmlpull.v1.XmlPullParserException; 039import org.xmlpull.v1.XmlPullParserFactory; 040 041 042/** 043 * This class represents the DSMLv2 Parser. 044 * It can be used to parse a plain DSMLv2 Request input document or the one inside a SOAP envelop. 045 * 046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 047 */ 048public class Dsmlv2Parser 049{ 050 /** The associated DSMLv2 container */ 051 private Dsmlv2Container container; 052 053 /** 054 * flag to indicate if the batch request should maintain a list of all the 055 * operation request objects present in the DSML document. Default is true 056 */ 057 private boolean storeMsgInBatchReq = true; 058 059 /** The thread safe DSMLv2 Grammar */ 060 private Dsmlv2Grammar grammar; 061 062 063 /** 064 * Creates a new instance of Dsmlv2Parser. 065 * 066 * @throws XmlPullParserException if an error occurs during the initialization of the parser 067 */ 068 public Dsmlv2Parser() throws XmlPullParserException 069 { 070 this( true ); 071 } 072 073 074 /** 075 * Creates a new instance of Dsmlv2Parser. 076 * 077 * @param storeMsgInBatchReq flag to set if the parsed requests should b stored 078 * @throws XmlPullParserException if an error occurs during the initialization of the parser 079 */ 080 public Dsmlv2Parser( boolean storeMsgInBatchReq ) throws XmlPullParserException 081 { 082 this.storeMsgInBatchReq = storeMsgInBatchReq; 083 084 this.grammar = new Dsmlv2Grammar(); 085 this.container = new Dsmlv2Container( grammar.getLdapCodecService() ); 086 087 this.container.setGrammar( grammar ); 088 089 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 090 factory.setNamespaceAware( true ); 091 XmlPullParser xpp = factory.newPullParser(); 092 093 container.setParser( xpp ); 094 } 095 096 097 /** 098 * Creates a new instance of Dsmlv2Parser. 099 * 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}