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.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.api.dsmlv2.request.BatchRequestDsml; 031import org.apache.directory.api.dsmlv2.request.Dsmlv2Grammar; 032import org.apache.directory.api.i18n.I18n; 033import org.apache.directory.api.ldap.model.message.Request; 034import org.apache.directory.api.util.Strings; 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 plain DSMLv2 Request input document or the one inside a SOAP envelop. 043 * 044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 045 */ 046public class Dsmlv2Parser 047{ 048 /** The associated DSMLv2 container */ 049 private Dsmlv2Container container; 050 051 /** 052 * flag to indicate if the batch request should maintain a list of all the 053 * operation request objects present in the DSML document. Default is true 054 */ 055 private boolean storeMsgInBatchReq = true; 056 057 /** The thread safe DSMLv2 Grammar */ 058 private Dsmlv2Grammar grammar; 059 060 061 /** 062 * Creates a new instance of Dsmlv2Parser. 063 * 064 * @throws XmlPullParserException if an error occurs during the initialization of the parser 065 */ 066 public Dsmlv2Parser() throws XmlPullParserException 067 { 068 this( true ); 069 } 070 071 072 /** 073 * Creates a new instance of Dsmlv2Parser. 074 * 075 * @param storeMsgInBatchReq flag to set if the parsed requests should b stored 076 * @throws XmlPullParserException if an error occurs during the initialization of the parser 077 */ 078 public Dsmlv2Parser( boolean storeMsgInBatchReq ) throws XmlPullParserException 079 { 080 this.storeMsgInBatchReq = storeMsgInBatchReq; 081 082 this.grammar = new Dsmlv2Grammar(); 083 this.container = new Dsmlv2Container( grammar.getLdapCodecService() ); 084 085 this.container.setGrammar( grammar ); 086 087 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 088 factory.setNamespaceAware( true ); 089 XmlPullParser xpp = factory.newPullParser(); 090 091 container.setParser( xpp ); 092 } 093 094 095 /** 096 * Creates a new instance of Dsmlv2Parser. 097 * 098 * @throws XmlPullParserException if an error occurs during the initialization of the parser 099 */ 100 public Dsmlv2Parser( Dsmlv2Grammar grammar ) throws XmlPullParserException 101 { 102 this.container = new Dsmlv2Container( grammar.getLdapCodecService() ); 103 this.container.setGrammar( grammar ); 104 this.grammar = grammar; 105 106 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 107 factory.setNamespaceAware( true ); 108 XmlPullParser xpp = factory.newPullParser(); 109 110 container.setParser( xpp ); 111 } 112 113 114 /** 115 * Sets the input file the parser is going to parse 116 * 117 * @param fileName the name of the file 118 * @throws FileNotFoundException if the file does not exist 119 * @throws XmlPullParserException if an error occurs in the parser 120 */ 121 public void setInputFile( String fileName ) throws FileNotFoundException, XmlPullParserException 122 { 123 Reader reader = new FileReader( fileName ); 124 container.getParser().setInput( reader ); 125 } 126 127 128 /** 129 * Sets the input stream the parser is going to process 130 * 131 * @param inputStream contains a raw byte input stream of possibly unknown encoding (when inputEncoding is null) 132 * @param inputEncoding if not null it MUST be used as encoding for inputStream 133 * @throws XmlPullParserException if an error occurs in the parser 134 */ 135 public void setInput( InputStream inputStream, String inputEncoding ) throws XmlPullParserException 136 { 137 container.getParser().setInput( inputStream, inputEncoding ); 138 } 139 140 141 /** 142 * Sets the input string the parser is going to parse 143 * 144 * @param str the string the parser is going to parse 145 * @throws XmlPullParserException if an error occurs in the parser 146 */ 147 public void setInput( String str ) throws XmlPullParserException 148 { 149 container.getParser().setInput( new StringReader( str ) ); 150 } 151 152 153 /** 154 * Launches the parsing on the input 155 * This method will parse the whole DSML document, without considering the flag {@link #storeMsgInBatchReq} 156 * @throws XmlPullParserException when an unrecoverable error occurs 157 * @throws IOException when an IO execption occurs 158 */ 159 public void parse() throws XmlPullParserException, IOException 160 { 161 grammar.executeAction( container ); 162 } 163 164 165 /** 166 * Launches the parsing of the Batch Request only 167 * 168 * @throws XmlPullParserException if an error occurs in the parser 169 */ 170 public void parseBatchRequest() throws XmlPullParserException 171 { 172 XmlPullParser xpp = container.getParser(); 173 174 int eventType = xpp.getEventType(); 175 176 do 177 { 178 switch ( eventType ) 179 { 180 case XmlPullParser.START_DOCUMENT: 181 container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE ); 182 break; 183 184 case XmlPullParser.END_DOCUMENT: 185 container.setState( Dsmlv2StatesEnum.GRAMMAR_END ); 186 break; 187 188 case XmlPullParser.START_TAG: 189 processTag( container, Tag.START ); 190 break; 191 192 case XmlPullParser.END_TAG: 193 processTag( container, Tag.END ); 194 break; 195 } 196 197 try 198 { 199 eventType = xpp.next(); 200 } 201 catch ( IOException e ) 202 { 203 throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null ); 204 } 205 } 206 while ( container.getState() != Dsmlv2StatesEnum.BATCHREQUEST_START_TAG ); 207 208 BatchRequestDsml br = container.getBatchRequest(); 209 210 if ( br != null ) 211 { 212 br.setStoreReq( storeMsgInBatchReq ); 213 } 214 } 215 216 217 /** 218 * Processes the task required in the grammar to the given tag type 219 * 220 * @param container the DSML container 221 * @param tagType the tag type 222 * @throws XmlPullParserException when an error occurs during the parsing 223 */ 224 private static void processTag( Dsmlv2Container container, int tagType ) throws XmlPullParserException 225 { 226 XmlPullParser xpp = container.getParser(); 227 228 String tagName = Strings.toLowerCase( xpp.getName() ); 229 230 GrammarTransition transition = container.getTransition( container.getState(), new Tag( tagName, tagType ) ); 231 232 if ( transition != null ) 233 { 234 container.setState( transition.getNextState() ); 235 236 if ( transition.hasAction() ) 237 { 238 transition.getAction().action( container ); 239 } 240 } 241 else 242 { 243 throw new XmlPullParserException( I18n.err( I18n.ERR_03036, new Tag( tagName, tagType ) ), xpp, null ); 244 } 245 } 246 247 248 /** 249 * Gets the Batch Request or null if the it has not been parsed yet 250 * 251 * @return the Batch Request or null if the it has not been parsed yet 252 */ 253 public BatchRequestDsml getBatchRequest() 254 { 255 return container.getBatchRequest(); 256 } 257 258 259 /** 260 * Gets the next Request or null if there's no more request 261 * @return the next Request or null if there's no more request 262 * @throws XmlPullParserException when an error occurs during the parsing 263 */ 264 public DsmlDecorator<? extends Request> getNextRequest() throws XmlPullParserException 265 { 266 if ( container.getBatchRequest() == null ) 267 { 268 parseBatchRequest(); 269 } 270 271 XmlPullParser xpp = container.getParser(); 272 273 int eventType = xpp.getEventType(); 274 do 275 { 276 while ( eventType == XmlPullParser.TEXT ) 277 { 278 try 279 { 280 xpp.next(); 281 } 282 catch ( IOException e ) 283 { 284 throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null ); 285 } 286 eventType = xpp.getEventType(); 287 } 288 289 switch ( eventType ) 290 { 291 case XmlPullParser.START_DOCUMENT: 292 container.setState( Dsmlv2StatesEnum.INIT_GRAMMAR_STATE ); 293 break; 294 295 case XmlPullParser.END_DOCUMENT: 296 container.setState( Dsmlv2StatesEnum.GRAMMAR_END ); 297 return null; 298 299 case XmlPullParser.START_TAG: 300 processTag( container, Tag.START ); 301 break; 302 303 case XmlPullParser.END_TAG: 304 processTag( container, Tag.END ); 305 break; 306 } 307 308 try 309 { 310 eventType = xpp.next(); 311 } 312 catch ( IOException e ) 313 { 314 throw new XmlPullParserException( I18n.err( I18n.ERR_03037, e.getLocalizedMessage() ), xpp, null ); 315 } 316 } 317 while ( container.getState() != Dsmlv2StatesEnum.BATCHREQUEST_LOOP ); 318 319 return container.getBatchRequest().getCurrentRequest(); 320 } 321 322 323 /** 324 * Parses all the requests 325 * 326 * @throws XmlPullParserException when an error occurs during the parsing 327 */ 328 public void parseAllRequests() throws XmlPullParserException 329 { 330 while ( getNextRequest() != null ) 331 { 332 continue; 333 } 334 } 335}