1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xml.internal.dtm.ref; 23 24 import com.sun.org.apache.xalan.internal.utils.ObjectFactory; 25 import com.sun.org.apache.xerces.internal.parsers.SAXParser; 26 import com.sun.org.apache.xml.internal.res.XMLErrorResources; 27 import com.sun.org.apache.xml.internal.res.XMLMessages; 28 import java.io.IOException; 29 import java.lang.reflect.Constructor; 30 import java.lang.reflect.Method; 31 import org.xml.sax.InputSource; 32 import org.xml.sax.SAXException; 33 import org.xml.sax.XMLReader; 34 35 36 /** <p>IncrementalSAXSource_Xerces takes advantage of the fact that Xerces1 37 * incremental mode is already a coroutine of sorts, and just wraps our 38 * IncrementalSAXSource API around it.</p> 39 * 40 * <p>Usage example: See main().</p> 41 * 42 * <p>Status: Passes simple main() unit-test. NEEDS JAVADOC.</p> 43 * */ 44 public class IncrementalSAXSource_Xerces 45 implements IncrementalSAXSource 46 { 47 // 48 // Reflection. To allow this to compile with both Xerces1 and Xerces2, which 49 // require very different methods and objects, we need to avoid static 50 // references to those APIs. So until Xerces2 is pervasive and we're willing 51 // to make it a prerequisite, we will rely upon relection. 52 // 53 Method fParseSomeSetup=null; // Xerces1 method 54 Method fParseSome=null; // Xerces1 method 55 Object fPullParserConfig=null; // Xerces2 pull control object 56 Method fConfigSetInput=null; // Xerces2 method 57 Method fConfigParse=null; // Xerces2 method 58 Method fSetInputSource=null; // Xerces2 pull control method 59 Constructor<?> fConfigInputSourceCtor=null; // Xerces2 initialization method 60 Method fConfigSetByteStream=null; // Xerces2 initialization method 61 Method fConfigSetCharStream=null; // Xerces2 initialization method 62 Method fConfigSetEncoding=null; // Xerces2 initialization method 63 Method fReset=null; // Both Xerces1 and Xerces2, but diff. signatures 64 65 // 66 // Data 67 // 68 SAXParser fIncrementalParser; 69 private boolean fParseInProgress=false; 70 71 // 72 // Constructors 73 // 74 75 /** Create a IncrementalSAXSource_Xerces, and create a SAXParser 76 * to go with it. Xerces2 incremental parsing is only supported if 77 * this constructor is used, due to limitations in the Xerces2 API (as of 78 * Beta 3). If you don't like that restriction, tell the Xerces folks that 79 * there should be a simpler way to request incremental SAX parsing. 80 * */ 81 public IncrementalSAXSource_Xerces() 82 throws NoSuchMethodException 83 { 84 try 85 { 86 // This should be cleaned up and the use of reflection 87 // removed - see JDK-8129880 88 89 // Xerces-2 incremental parsing support (as of Beta 3) 90 // ContentHandlers still get set on fIncrementalParser (to get 91 // conversion from XNI events to SAX events), but 92 // _control_ for incremental parsing must be exercised via the config. 93 // 94 // At this time there's no way to read the existing config, only 95 // to assert a new one... and only when creating a brand-new parser. 96 // 97 // Reflection is used to allow us to continue to compile against 98 // Xerces1. If/when we can abandon the older versions of the parser, 99 // this will simplify significantly. 100 101 // If we can't get the magic constructor, no need to look further. 102 Class<?> xniConfigClass=ObjectFactory.findProviderClass( 103 "com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration", 104 true); 105 Class<?>[] args1={xniConfigClass}; 106 Constructor<?> ctor=SAXParser.class.getConstructor(args1); 107 108 // Build the parser configuration object. StandardParserConfiguration 109 // happens to implement XMLPullParserConfiguration, which is the API 110 // we're going to want to use. 111 Class<?> xniStdConfigClass=ObjectFactory.findProviderClass( 112 "com.sun.org.apache.xerces.internal.parsers.StandardParserConfiguration", 113 true); 114 fPullParserConfig=xniStdConfigClass.getConstructor().newInstance(); 115 Object[] args2={fPullParserConfig}; 116 fIncrementalParser = (SAXParser)ctor.newInstance(args2); 117 118 // Preload all the needed the configuration methods... I want to know they're 119 // all here before we commit to trying to use them, just in case the 120 // API changes again. 121 Class<?> fXniInputSourceClass=ObjectFactory.findProviderClass( 122 "com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource", 123 true); 124 Class<?>[] args3={fXniInputSourceClass}; 125 fConfigSetInput=xniStdConfigClass.getMethod("setInputSource",args3); 126 127 Class<?>[] args4={String.class,String.class,String.class}; 128 fConfigInputSourceCtor=fXniInputSourceClass.getConstructor(args4); 129 Class<?>[] args5={java.io.InputStream.class}; 130 fConfigSetByteStream=fXniInputSourceClass.getMethod("setByteStream",args5); 131 Class<?>[] args6={java.io.Reader.class}; 132 fConfigSetCharStream=fXniInputSourceClass.getMethod("setCharacterStream",args6); 133 Class<?>[] args7={String.class}; 134 fConfigSetEncoding=fXniInputSourceClass.getMethod("setEncoding",args7); 135 136 Class<?>[] argsb={Boolean.TYPE}; 137 fConfigParse=xniStdConfigClass.getMethod("parse",argsb); 138 Class<?>[] noargs=new Class<?>[0]; 139 fReset=fIncrementalParser.getClass().getMethod("reset",noargs); 140 } 141 catch(Exception e) 142 { 143 // Fallback if this fails (implemented in createIncrementalSAXSource) is 144 // to attempt Xerces-1 incremental setup. Can't do tail-call in 145 // constructor, so create new, copy Xerces-1 initialization, 146 // then throw it away... Ugh. 147 IncrementalSAXSource_Xerces dummy=new IncrementalSAXSource_Xerces(new SAXParser()); 148 this.fParseSomeSetup=dummy.fParseSomeSetup; 149 this.fParseSome=dummy.fParseSome; 150 this.fIncrementalParser=dummy.fIncrementalParser; 151 } 152 } 153 154 /** Create a IncrementalSAXSource_Xerces wrapped around 155 * an existing SAXParser. Currently this works only for recent 156 * releases of Xerces-1. Xerces-2 incremental is currently possible 157 * only if we are allowed to create the parser instance, due to 158 * limitations in the API exposed by Xerces-2 Beta 3; see the 159 * no-args constructor for that code. 160 * 161 * @exception if the SAXParser class doesn't support the Xerces 162 * incremental parse operations. In that case, caller should 163 * fall back upon the IncrementalSAXSource_Filter approach. 164 * */ 165 public IncrementalSAXSource_Xerces(SAXParser parser) 166 throws NoSuchMethodException 167 { 168 // Reflection is used to allow us to compile against 169 // Xerces2. If/when we can abandon the older versions of the parser, 170 // this constructor will simply have to fail until/unless the 171 // Xerces2 incremental support is made available on previously 172 // constructed SAXParser instances. 173 fIncrementalParser=parser; 174 Class<?> me=parser.getClass(); 175 Class<?>[] parms={InputSource.class}; 176 fParseSomeSetup=me.getMethod("parseSomeSetup",parms); 177 parms=new Class<?>[0]; 178 fParseSome=me.getMethod("parseSome",parms); 179 // Fallback if this fails (implemented in createIncrementalSAXSource) is 180 // to use IncrementalSAXSource_Filter rather than Xerces-specific code. 181 } 182 183 // 184 // Factories 185 // 186 static public IncrementalSAXSource createIncrementalSAXSource() 187 { 188 try 189 { 190 return new IncrementalSAXSource_Xerces(); 191 } 192 catch(NoSuchMethodException e) 193 { 194 // Xerces version mismatch; neither Xerces1 nor Xerces2 succeeded. 195 // Fall back on filtering solution. 196 IncrementalSAXSource_Filter iss=new IncrementalSAXSource_Filter(); 197 iss.setXMLReader(new SAXParser()); 198 return iss; 199 } 200 } 201 202 static public IncrementalSAXSource 203 createIncrementalSAXSource(SAXParser parser) { 204 try 205 { 206 return new IncrementalSAXSource_Xerces(parser); 207 } 208 catch(NoSuchMethodException e) 209 { 210 // Xerces version mismatch; neither Xerces1 nor Xerces2 succeeded. 211 // Fall back on filtering solution. 212 IncrementalSAXSource_Filter iss=new IncrementalSAXSource_Filter(); 213 iss.setXMLReader(parser); 214 return iss; 215 } 216 } 217 218 // 219 // Public methods 220 // 221 222 // Register handler directly with the incremental parser 223 public void setContentHandler(org.xml.sax.ContentHandler handler) 224 { 225 // Typecast required in Xerces2; SAXParser doesn't inheret XMLReader 226 // %OPT% Cast at asignment? 227 ((XMLReader)fIncrementalParser).setContentHandler(handler); 228 } 229 230 // Register handler directly with the incremental parser 231 public void setLexicalHandler(org.xml.sax.ext.LexicalHandler handler) 232 { 233 // Not supported by all SAX2 parsers but should work in Xerces: 234 try 235 { 236 // Typecast required in Xerces2; SAXParser doesn't inheret XMLReader 237 // %OPT% Cast at asignment? 238 ((XMLReader)fIncrementalParser).setProperty("http://xml.org/sax/properties/lexical-handler", 239 handler); 240 } 241 catch(org.xml.sax.SAXNotRecognizedException e) 242 { 243 // Nothing we can do about it 244 } 245 catch(org.xml.sax.SAXNotSupportedException e) 246 { 247 // Nothing we can do about it 248 } 249 } 250 251 // Register handler directly with the incremental parser 252 public void setDTDHandler(org.xml.sax.DTDHandler handler) 253 { 254 // Typecast required in Xerces2; SAXParser doesn't inheret XMLReader 255 // %OPT% Cast at asignment? 256 ((XMLReader)fIncrementalParser).setDTDHandler(handler); 257 } 258 259 //================================================================ 260 /** startParse() is a simple API which tells the IncrementalSAXSource 261 * to begin reading a document. 262 * 263 * @throws SAXException is parse thread is already in progress 264 * or parsing can not be started. 265 * */ 266 public void startParse(InputSource source) throws SAXException 267 { 268 if (fIncrementalParser==null) 269 throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_STARTPARSE_NEEDS_SAXPARSER, null)); //"startParse needs a non-null SAXParser."); 270 if (fParseInProgress) 271 throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_STARTPARSE_WHILE_PARSING, null)); //"startParse may not be called while parsing."); 272 273 boolean ok=false; 274 275 try 276 { 277 ok = parseSomeSetup(source); 278 } 279 catch(Exception ex) 280 { 281 throw new SAXException(ex); 282 } 283 284 if(!ok) 285 throw new SAXException(XMLMessages.createXMLMessage(XMLErrorResources.ER_COULD_NOT_INIT_PARSER, null)); //"could not initialize parser with"); 286 } 287 288 289 /** deliverMoreNodes() is a simple API which tells the coroutine 290 * parser that we need more nodes. This is intended to be called 291 * from one of our partner routines, and serves to encapsulate the 292 * details of how incremental parsing has been achieved. 293 * 294 * @param parsemore If true, tells the incremental parser to generate 295 * another chunk of output. If false, tells the parser that we're 296 * satisfied and it can terminate parsing of this document. 297 * @return Boolean.TRUE if the CoroutineParser believes more data may be available 298 * for further parsing. Boolean.FALSE if parsing ran to completion. 299 * Exception if the parser objected for some reason. 300 * */ 301 public Object deliverMoreNodes (boolean parsemore) 302 { 303 if(!parsemore) 304 { 305 fParseInProgress=false; 306 return Boolean.FALSE; 307 } 308 309 Object arg; 310 try { 311 boolean keepgoing = parseSome(); 312 arg = keepgoing ? Boolean.TRUE : Boolean.FALSE; 313 } catch (SAXException ex) { 314 arg = ex; 315 } catch (IOException ex) { 316 arg = ex; 317 } catch (Exception ex) { 318 arg = new SAXException(ex); 319 } 320 return arg; 321 } 322 323 // Private methods -- conveniences to hide the reflection details 324 private boolean parseSomeSetup(InputSource source) 325 throws SAXException, IOException, IllegalAccessException, 326 java.lang.reflect.InvocationTargetException, 327 java.lang.InstantiationException 328 { 329 if(fConfigSetInput!=null) 330 { 331 // Obtain input from SAX inputSource object, construct XNI version of 332 // that object. Logic adapted from Xerces2. 333 Object[] parms1={source.getPublicId(),source.getSystemId(),null}; 334 Object xmlsource=fConfigInputSourceCtor.newInstance(parms1); 335 Object[] parmsa={source.getByteStream()}; 336 fConfigSetByteStream.invoke(xmlsource,parmsa); 337 parmsa[0]=source.getCharacterStream(); 338 fConfigSetCharStream.invoke(xmlsource,parmsa); 339 parmsa[0]=source.getEncoding(); 340 fConfigSetEncoding.invoke(xmlsource,parmsa); 341 342 // Bugzilla5272 patch suggested by Sandy Gao. 343 // Has to be reflection to run with Xerces2 344 // after compilation against Xerces1. or vice 345 // versa, due to return type mismatches. 346 Object[] noparms=new Object[0]; 347 fReset.invoke(fIncrementalParser,noparms); 348 349 parmsa[0]=xmlsource; 350 fConfigSetInput.invoke(fPullParserConfig,parmsa); 351 352 // %REVIEW% Do first pull. Should we instead just return true? 353 return parseSome(); 354 } 355 else 356 { 357 Object[] parm={source}; 358 Object ret=fParseSomeSetup.invoke(fIncrementalParser,parm); 359 return ((Boolean)ret).booleanValue(); 360 } 361 } 362 // Would null work??? 363 private static final Object[] noparms=new Object[0]; 364 private static final Object[] parmsfalse={Boolean.FALSE}; 365 private boolean parseSome() 366 throws SAXException, IOException, IllegalAccessException, 367 java.lang.reflect.InvocationTargetException 368 { 369 // Take next parsing step, return false iff parsing complete: 370 if(fConfigSetInput!=null) 371 { 372 Object ret=(Boolean)(fConfigParse.invoke(fPullParserConfig,parmsfalse)); 373 return ((Boolean)ret).booleanValue(); 374 } 375 else 376 { 377 Object ret=fParseSome.invoke(fIncrementalParser,noparms); 378 return ((Boolean)ret).booleanValue(); 379 } 380 } 381 382 383 //================================================================ 384 /** Simple unit test. Attempt coroutine parsing of document indicated 385 * by first argument (as a URI), report progress. 386 */ 387 @Deprecated 388 public static void _main(String args[]) 389 { 390 System.out.println("Starting..."); 391 392 CoroutineManager co = new CoroutineManager(); 393 int appCoroutineID = co.co_joinCoroutineSet(-1); 394 if (appCoroutineID == -1) 395 { 396 System.out.println("ERROR: Couldn't allocate coroutine number.\n"); 397 return; 398 } 399 IncrementalSAXSource parser= 400 createIncrementalSAXSource(); 401 402 // Use a serializer as our sample output 403 com.sun.org.apache.xml.internal.serialize.XMLSerializer trace; 404 trace=new com.sun.org.apache.xml.internal.serialize.XMLSerializer(System.out,null); 405 parser.setContentHandler(trace); 406 parser.setLexicalHandler(trace); 407 408 // Tell coroutine to begin parsing, run while parsing is in progress 409 410 for(int arg=0;arg<args.length;++arg) 411 { 412 try 413 { 414 InputSource source = new InputSource(args[arg]); 415 Object result=null; 416 boolean more=true; 417 parser.startParse(source); 418 for(result = parser.deliverMoreNodes(more); 419 result==Boolean.TRUE; 420 result = parser.deliverMoreNodes(more)) 421 { 422 System.out.println("\nSome parsing successful, trying more.\n"); 423 424 // Special test: Terminate parsing early. 425 if(arg+1<args.length && "!".equals(args[arg+1])) 426 { 427 ++arg; 428 more=false; 429 } 430 431 } 432 433 if (result instanceof Boolean && ((Boolean)result)==Boolean.FALSE) 434 { 435 System.out.println("\nParser ended (EOF or on request).\n"); 436 } 437 else if (result == null) { 438 System.out.println("\nUNEXPECTED: Parser says shut down prematurely.\n"); 439 } 440 else if (result instanceof Exception) { 441 throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException((Exception)result); 442 // System.out.println("\nParser threw exception:"); 443 // ((Exception)result).printStackTrace(); 444 } 445 446 } 447 448 catch(SAXException e) 449 { 450 e.printStackTrace(); 451 } 452 } 453 454 } 455 456 457 } // class IncrementalSAXSource_Xerces