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.response.BatchResponseDsml; 033import org.apache.directory.api.dsmlv2.response.Dsmlv2ResponseGrammar; 034import org.apache.directory.api.i18n.I18n; 035import org.apache.directory.api.ldap.codec.api.LdapApiService; 036import org.apache.directory.api.ldap.model.message.Response; 037import org.apache.directory.api.util.Strings; 038import org.xmlpull.v1.XmlPullParser; 039import org.xmlpull.v1.XmlPullParserException; 040import org.xmlpull.v1.XmlPullParserFactory; 041 042 043/** 044 * This class represents the DSMLv2 Parser. 045 * It can be used to parse a DSMLv2 Response input. 046 * 047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 048 */ 049public class Dsmlv2ResponseParser 050{ 051 /** The associated DSMLv2 container */ 052 private Dsmlv2Container container; 053 054 055 /** 056 * Creates a new instance of Dsmlv2ResponseParser. 057 * 058 * @param codec The Ldap Service to use 059 * @throws XmlPullParserException if an error occurs while the initialization of the parser 060 */ 061 public Dsmlv2ResponseParser( LdapApiService codec ) throws XmlPullParserException 062 { 063 this.container = new Dsmlv2Container( codec ); 064 065 this.container.setGrammar( Dsmlv2ResponseGrammar.getInstance() ); 066 067 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 068 factory.setNamespaceAware( true ); 069 XmlPullParser xpp = factory.newPullParser(); 070 071 container.setParser( xpp ); 072 } 073 074 075 /** 076 * Sets the input string the parser is going to parse 077 * 078 * @param str the string the parser is going to parse 079 * @throws XmlPullParserException if an error occurs in the parser 080 */ 081 public void setInput( String str ) throws XmlPullParserException 082 { 083 container.getParser().setInput( new StringReader( str ) ); 084 } 085 086 087 /** 088 * Sets the input file the parser is going to parse. Default charset is used. 089 * 090 * @param fileName the name of the file 091 * @throws IOException if the file does not exist 092 * @throws XmlPullParserException if an error occurs in the parser 093 */ 094 public void setInputFile( String fileName ) throws IOException, XmlPullParserException 095 { 096 try ( Reader reader = new InputStreamReader( Files.newInputStream( Paths.get( fileName ) ), 097 Charset.defaultCharset() ) ) 098 { 099 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_03019_IO_EXCEPTION_OCCURED, 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_MISSING_TAG, 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_03019_IO_EXCEPTION_OCCURED, 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_03019_IO_EXCEPTION_OCCURED, 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}