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