1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xml.dtm;
  58. import java.io.IOException;
  59. import java.io.File;
  60. import java.io.FileInputStream;
  61. import java.io.InputStream;
  62. import java.io.InputStreamReader;
  63. import java.io.BufferedReader;
  64. import java.util.Properties;
  65. import java.util.Enumeration;
  66. import org.apache.xml.utils.PrefixResolver;
  67. import org.apache.xml.utils.XMLString;
  68. import org.apache.xml.utils.XMLStringFactory;
  69. import org.apache.xalan.res.XSLMessages;
  70. import org.apache.xalan.res.XSLTErrorResources;
  71. /**
  72. * A DTMManager instance can be used to create DTM and
  73. * DTMIterator objects, and manage the DTM objects in the system.
  74. *
  75. * <p>The system property that determines which Factory implementation
  76. * to create is named "org.apache.xml.utils.DTMFactory". This
  77. * property names a concrete subclass of the DTMFactory abstract
  78. * class. If the property is not defined, a platform default is be used.</p>
  79. *
  80. * <p>An instance of this class <emph>must</emph> be safe to use across
  81. * thread instances. It is expected that a client will create a single instance
  82. * of a DTMManager to use across multiple threads. This will allow sharing
  83. * of DTMs across multiple processes.</p>
  84. *
  85. * <p>Note: this class is incomplete right now. It will be pretty much
  86. * modeled after javax.xml.transform.TransformerFactory in terms of its
  87. * factory support.</p>
  88. *
  89. * <p>State: In progress!!</p>
  90. */
  91. public abstract class DTMManager
  92. {
  93. /** The default property name to load the manager. */
  94. private static final String defaultPropName =
  95. "org.apache.xml.dtm.DTMManager";
  96. /**
  97. * Factory for creating XMLString objects.
  98. * %TBD% Make this set by the caller.
  99. */
  100. protected XMLStringFactory m_xsf = null;
  101. /**
  102. * Default constructor is protected on purpose.
  103. */
  104. protected DTMManager(){}
  105. /**
  106. * Get the XMLStringFactory used for the DTMs.
  107. *
  108. *
  109. * @return a valid XMLStringFactory object, or null if it hasn't been set yet.
  110. */
  111. public XMLStringFactory getXMLStringFactory()
  112. {
  113. return m_xsf;
  114. }
  115. /**
  116. * Set the XMLStringFactory used for the DTMs.
  117. *
  118. *
  119. * @param xsf a valid XMLStringFactory object, should not be null.
  120. */
  121. public void setXMLStringFactory(XMLStringFactory xsf)
  122. {
  123. m_xsf = xsf;
  124. }
  125. /**
  126. * Obtain a new instance of a <code>DTMManager</code>.
  127. * This static method creates a new factory instance
  128. * This method uses the following ordered lookup procedure to determine
  129. * the <code>DTMManager</code> implementation class to
  130. * load:
  131. * <ul>
  132. * <li>
  133. * Use the <code>org.apache.xml.dtm.DTMManager</code> system
  134. * property.
  135. * </li>
  136. * <li>
  137. * Use the JAVA_HOME(the parent directory where jdk is
  138. * installed)/lib/jaxp.properties for a property file that contains the
  139. * name of the implementation class keyed on the same value as the
  140. * system property defined above.
  141. * </li>
  142. * <li>
  143. * Use the Services API (as detailed in the JAR specification), if
  144. * available, to determine the classname. The Services API will look
  145. * for a classname in the file
  146. * <code>META-INF/services/javax.xml.parsers.DTMManager</code>
  147. * in jars available to the runtime.
  148. * </li>
  149. * <li>
  150. * Use the default <code>DTMManager</code> classname, which is
  151. * <code>org.apache.xml.dtm.ref.DTMManagerDefault</code>.
  152. * </li>
  153. * </ul>
  154. *
  155. * Once an application has obtained a reference to a <code>
  156. * DTMManager</code> it can use the factory to configure
  157. * and obtain parser instances.
  158. *
  159. * @return new DTMManager instance, never null.
  160. *
  161. * @throws DTMConfigurationException
  162. * if the implementation is not available or cannot be instantiated.
  163. */
  164. public static DTMManager newInstance(XMLStringFactory xsf)
  165. throws DTMConfigurationException
  166. {
  167. String classname = findFactory(defaultPropName,
  168. "org.apache.xml.dtm.ref.DTMManagerDefault");
  169. if (classname == null)
  170. {
  171. throw new DTMConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_DEFAULT_IMPL, null)); //"No default implementation found");
  172. }
  173. DTMManager factoryImpl;
  174. try
  175. {
  176. Class clazz = Class.forName(classname);
  177. factoryImpl = (DTMManager) clazz.newInstance();
  178. }
  179. catch (ClassNotFoundException cnfe)
  180. {
  181. throw new DTMConfigurationException(cnfe);
  182. }
  183. catch (IllegalAccessException iae)
  184. {
  185. throw new DTMConfigurationException(iae);
  186. }
  187. catch (InstantiationException ie)
  188. {
  189. throw new DTMConfigurationException(ie);
  190. }
  191. factoryImpl.setXMLStringFactory(xsf);
  192. return factoryImpl;
  193. }
  194. /**
  195. * Get an instance of a DTM, loaded with the content from the
  196. * specified source. If the unique flag is true, a new instance will
  197. * always be returned. Otherwise it is up to the DTMManager to return a
  198. * new instance or an instance that it already created and may be being used
  199. * by someone else.
  200. *
  201. * (More parameters may eventually need to be added for error handling
  202. * and entity resolution, and to better control selection of implementations.)
  203. *
  204. * @param source the specification of the source object, which may be null,
  205. * in which case it is assumed that node construction will take
  206. * by some other means.
  207. * @param unique true if the returned DTM must be unique, probably because it
  208. * is going to be mutated.
  209. * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
  210. * be null.
  211. * @param incremental true if the DTM should be built incrementally, if
  212. * possible.
  213. * @param doIndexing true if the caller considers it worth it to use
  214. * indexing schemes.
  215. *
  216. * @return a non-null DTM reference.
  217. */
  218. public abstract DTM getDTM(javax.xml.transform.Source source,
  219. boolean unique, DTMWSFilter whiteSpaceFilter,
  220. boolean incremental, boolean doIndexing);
  221. /**
  222. * Get the instance of DTM that "owns" a node handle.
  223. *
  224. * @param nodeHandle the nodeHandle.
  225. *
  226. * @return a non-null DTM reference.
  227. */
  228. public abstract DTM getDTM(int nodeHandle);
  229. /**
  230. * Given a W3C DOM node, try and return a DTM handle.
  231. * Note: calling this may be non-optimal.
  232. *
  233. * @param node Non-null reference to a DOM node.
  234. *
  235. * @return a valid DTM handle.
  236. */
  237. public abstract int getDTMHandleFromNode(org.w3c.dom.Node node);
  238. /**
  239. * Creates a DTM representing an empty <code>DocumentFragment</code> object.
  240. * @return a non-null DTM reference.
  241. */
  242. public abstract DTM createDocumentFragment();
  243. /**
  244. * Release a DTM either to a lru pool, or completely remove reference.
  245. * DTMs without system IDs are always hard deleted.
  246. * State: experimental.
  247. *
  248. * @param dtm The DTM to be released.
  249. * @param shouldHardDelete True if the DTM should be removed no matter what.
  250. * @return true if the DTM was removed, false if it was put back in a lru pool.
  251. */
  252. public abstract boolean release(DTM dtm, boolean shouldHardDelete);
  253. /**
  254. * Create a new <code>DTMIterator</code> based on an XPath
  255. * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
  256. * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
  257. *
  258. * @param xpathCompiler ??? Somehow we need to pass in a subpart of the
  259. * expression. I hate to do this with strings, since the larger expression
  260. * has already been parsed.
  261. *
  262. * @param pos The position in the expression.
  263. * @return The newly created <code>DTMIterator</code>.
  264. */
  265. public abstract DTMIterator createDTMIterator(Object xpathCompiler,
  266. int pos);
  267. /**
  268. * Create a new <code>DTMIterator</code> based on an XPath
  269. * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
  270. * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
  271. *
  272. * @param xpathString Must be a valid string expressing a
  273. * <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a> or
  274. * a <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
  275. *
  276. * @param presolver An object that can resolve prefixes to namespace URLs.
  277. *
  278. * @return The newly created <code>DTMIterator</code>.
  279. */
  280. public abstract DTMIterator createDTMIterator(String xpathString,
  281. PrefixResolver presolver);
  282. /**
  283. * Create a new <code>DTMIterator</code> based only on a whatToShow
  284. * and a DTMFilter. The traversal semantics are defined as the
  285. * descendant access.
  286. * <p>
  287. * Note that DTMIterators may not be an exact match to DOM
  288. * NodeIterators. They are initialized and used in much the same way
  289. * as a NodeIterator, but their response to document mutation is not
  290. * currently defined.
  291. *
  292. * @param whatToShow This flag specifies which node types may appear in
  293. * the logical view of the tree presented by the iterator. See the
  294. * description of <code>NodeFilter</code> for the set of possible
  295. * <code>SHOW_</code> values.These flags can be combined using
  296. * <code>OR</code>.
  297. * @param filter The <code>NodeFilter</code> to be used with this
  298. * <code>DTMFilter</code>, or <code>null</code> to indicate no filter.
  299. * @param entityReferenceExpansion The value of this flag determines
  300. * whether entity reference nodes are expanded.
  301. *
  302. * @return The newly created <code>DTMIterator</code>.
  303. */
  304. public abstract DTMIterator createDTMIterator(int whatToShow,
  305. DTMFilter filter, boolean entityReferenceExpansion);
  306. /**
  307. * Create a new <code>DTMIterator</code> that holds exactly one node.
  308. *
  309. * @param node The node handle that the DTMIterator will iterate to.
  310. *
  311. * @return The newly created <code>DTMIterator</code>.
  312. */
  313. public abstract DTMIterator createDTMIterator(int node);
  314. /* Flag indicating whether an incremental transform is desired */
  315. public static boolean m_incremental = false;
  316. /**
  317. * Set a flag indicating whether an incremental transform is desired
  318. * @param incremental boolean to use to set m_incremental.
  319. *
  320. */
  321. public synchronized static boolean getIncremental()
  322. {
  323. return m_incremental;
  324. }
  325. /**
  326. * Set a flag indicating whether an incremental transform is desired
  327. * @param incremental boolean to use to set m_incremental.
  328. *
  329. */
  330. public synchronized static void setIncremental(boolean incremental)
  331. {
  332. m_incremental = incremental;
  333. }
  334. // -------------------- private methods --------------------
  335. /**
  336. * Avoid reading all the files when the findFactory
  337. * method is called the second time (cache the result of
  338. * finding the default impl).
  339. */
  340. private static String foundFactory = null;
  341. /**
  342. * Temp debug code - this will be removed after we test everything
  343. */
  344. private static boolean debug;
  345. static
  346. {
  347. try
  348. {
  349. debug = System.getProperty("dtm.debug") != null;
  350. }
  351. catch (SecurityException ex){}
  352. }
  353. /**
  354. * Private implementation method - will find the implementation
  355. * class in the specified order.
  356. *
  357. * @param factoryId Name of the factory interface.
  358. * @param xmlProperties Name of the properties file based on JAVA/lib.
  359. * @param defaultFactory Default implementation, if nothing else is found.
  360. *
  361. * @return The factory class name.
  362. */
  363. private static String findFactory(String factoryId, String defaultFactory)
  364. {
  365. // Use the system property first
  366. try
  367. {
  368. String systemProp = null;
  369. try
  370. {
  371. systemProp = System.getProperty(factoryId);
  372. }
  373. catch (SecurityException se){}
  374. if (systemProp != null)
  375. {
  376. if (debug)
  377. {
  378. System.err.println("DTM: found system property" + systemProp);
  379. }
  380. return systemProp;
  381. }
  382. }
  383. catch (SecurityException se){}
  384. if (foundFactory != null)
  385. {
  386. return foundFactory;
  387. }
  388. // try to read from $java.home/lib/jaxp.properties
  389. try
  390. {
  391. String javah = System.getProperty("java.home");
  392. String configFile = javah + File.separator + "lib" + File.separator
  393. + "jaxp.properties";
  394. File f = new File(configFile);
  395. if (f.exists())
  396. {
  397. Properties props = new Properties();
  398. props.load(new FileInputStream(f));
  399. foundFactory = props.getProperty(factoryId);
  400. if (debug)
  401. {
  402. System.err.println("DTM: found java.home property " + foundFactory);
  403. }
  404. if (foundFactory != null)
  405. {
  406. return foundFactory;
  407. }
  408. }
  409. }
  410. catch (Exception ex)
  411. {
  412. if (debug)
  413. {
  414. ex.printStackTrace();
  415. }
  416. }
  417. String serviceId = "META-INF/services/" + factoryId;
  418. // try to find services in CLASSPATH
  419. try
  420. {
  421. ClassLoader cl = DTMManager.class.getClassLoader();
  422. InputStream is = null;
  423. if (cl == null)
  424. {
  425. is = ClassLoader.getSystemResourceAsStream(serviceId);
  426. }
  427. else
  428. {
  429. is = cl.getResourceAsStream(serviceId);
  430. }
  431. if (is != null)
  432. {
  433. if (debug)
  434. {
  435. System.err.println("DTM: found " + serviceId);
  436. }
  437. BufferedReader rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  438. foundFactory = rd.readLine();
  439. rd.close();
  440. if (debug)
  441. {
  442. System.err.println("DTM: loaded from services: " + foundFactory);
  443. }
  444. if ((foundFactory != null) &&!"".equals(foundFactory))
  445. {
  446. return foundFactory;
  447. }
  448. }
  449. }
  450. catch (Exception ex)
  451. {
  452. if (debug)
  453. {
  454. ex.printStackTrace();
  455. }
  456. }
  457. return defaultFactory;
  458. }
  459. /** This value, set at compile time, controls how many bits of the
  460. * DTM node identifier numbers are used to identify a node within a
  461. * document, and thus sets the maximum number of nodes per
  462. * document. The remaining bits are used to identify the DTM
  463. * document which contains this node.
  464. *
  465. * If you change IDENT_DTM_NODE_BITS, be sure to rebuild _ALL_ the
  466. * files which use it... including the IDKey testcases.
  467. *
  468. * (FuncGenerateKey currently uses the node identifier directly and
  469. * thus is affected when this changes. The IDKEY results will still be
  470. * _correct_ (presuming no other breakage), but simple equality
  471. * comparison against the previous "golden" files will probably
  472. * complain.)
  473. * */
  474. public static final int IDENT_DTM_NODE_BITS = 16;
  475. /** When this bitmask is ANDed with a DTM node handle number, the result
  476. * is the low bits of the node's index number within that DTM. To obtain
  477. * the high bits, add the DTM ID portion's offset as assigned in the DTM
  478. * Manager.
  479. */
  480. public static final int IDENT_NODE_DEFAULT = (1<<IDENT_DTM_NODE_BITS)-1;
  481. /** When this bitmask is ANDed with a DTM node handle number, the result
  482. * is the DTM's document identity number.
  483. */
  484. public static final int IDENT_DTM_DEFAULT = ~IDENT_NODE_DEFAULT;
  485. /** This is the maximum number of DTMs available. The highest DTM is
  486. * one less than this.
  487. */
  488. public static final int IDENT_MAX_DTMS = (IDENT_DTM_DEFAULT >>> IDENT_DTM_NODE_BITS) + 1;
  489. /**
  490. * %TBD% Doc
  491. *
  492. * NEEDSDOC @param dtm
  493. *
  494. * NEEDSDOC ($objectName$) @return
  495. */
  496. public abstract int getDTMIdentity(DTM dtm);
  497. /**
  498. * %TBD% Doc
  499. *
  500. * NEEDSDOC ($objectName$) @return
  501. */
  502. public int getDTMIdentityMask()
  503. {
  504. return IDENT_DTM_DEFAULT;
  505. }
  506. /**
  507. * %TBD% Doc
  508. *
  509. * NEEDSDOC ($objectName$) @return
  510. */
  511. public int getNodeIdentityMask()
  512. {
  513. return IDENT_NODE_DEFAULT;
  514. }
  515. }