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.xalan.templates;
  58. import java.util.Vector;
  59. import java.io.StringWriter;
  60. import java.io.PrintWriter;
  61. import java.io.IOException;
  62. import org.apache.xml.dtm.DTM;
  63. import org.apache.xml.dtm.DTMIterator;
  64. import org.apache.xml.dtm.DTMManager;
  65. import org.apache.xpath.NodeSetDTM;
  66. import org.apache.xpath.functions.Function;
  67. import org.apache.xpath.functions.Function2Args;
  68. import org.apache.xpath.functions.WrongNumberArgsException;
  69. import org.apache.xpath.objects.XObject;
  70. import org.apache.xpath.objects.XNodeSet;
  71. import org.apache.xpath.XPath;
  72. import org.apache.xpath.XPathContext;
  73. import org.apache.xpath.SourceTreeManager;
  74. import org.apache.xpath.Expression;
  75. import org.apache.xpath.XPathContext;
  76. import org.apache.xalan.res.XSLMessages;
  77. import org.apache.xalan.res.XSLTErrorResources;
  78. import org.apache.xpath.XPathContext;
  79. import org.apache.xalan.transformer.TransformerImpl;
  80. import org.xml.sax.InputSource;
  81. import org.xml.sax.Locator;
  82. import javax.xml.transform.TransformerException;
  83. import javax.xml.transform.SourceLocator;
  84. import javax.xml.transform.ErrorListener;
  85. import javax.xml.transform.Source;
  86. import org.apache.xml.utils.SAXSourceLocator;
  87. import org.apache.xml.utils.XMLString;
  88. /**
  89. * <meta name="usage" content="advanced"/>
  90. * Execute the Doc() function.
  91. *
  92. * When the document function has exactly one argument and the argument
  93. * is a node-set, then the result is the union, for each node in the
  94. * argument node-set, of the result of calling the document function with
  95. * the first argument being the string-value of the node, and the second
  96. * argument being a node-set with the node as its only member. When the
  97. * document function has two arguments and the first argument is a node-set,
  98. * then the result is the union, for each node in the argument node-set,
  99. * of the result of calling the document function with the first argument
  100. * being the string-value of the node, and with the second argument being
  101. * the second argument passed to the document function.
  102. */
  103. public class FuncDocument extends Function2Args
  104. {
  105. /**
  106. * Execute the function. The function must return
  107. * a valid object.
  108. * @param xctxt The current execution context.
  109. * @return A valid XObject.
  110. *
  111. * @throws javax.xml.transform.TransformerException
  112. */
  113. public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
  114. {
  115. int context = xctxt.getCurrentNode();
  116. DTM dtm = xctxt.getDTM(context);
  117. int docContext = dtm.getDocumentRoot(context);
  118. XObject arg = (XObject) this.getArg0().execute(xctxt);
  119. String base = "";
  120. Expression arg1Expr = this.getArg1();
  121. if (null != arg1Expr)
  122. {
  123. // The URI reference may be relative. The base URI (see [3.2 Base URI])
  124. // of the node in the second argument node-set that is first in document
  125. // order is used as the base URI for resolving the
  126. // relative URI into an absolute URI.
  127. XObject arg2 = arg1Expr.execute(xctxt);
  128. if (XObject.CLASS_NODESET == arg2.getType())
  129. {
  130. int baseNode = arg2.iter().nextNode();
  131. if (baseNode == DTM.NULL)
  132. warn(xctxt, XSLTErrorResources.WG_EMPTY_SECOND_ARG, null);
  133. DTM baseDTM = xctxt.getDTM(baseNode);
  134. base = baseDTM.getDocumentBaseURI();
  135. // %REVIEW% This doesn't seem to be a problem with the conformance
  136. // suite, but maybe it's just not doing a good test?
  137. // int baseDoc = baseDTM.getDocument();
  138. //
  139. // if (baseDoc == DTM.NULL /* || baseDoc instanceof Stylesheet -->What to do?? */)
  140. // {
  141. //
  142. // // base = ((Stylesheet)baseDoc).getBaseIdentifier();
  143. // base = xctxt.getNamespaceContext().getBaseIdentifier();
  144. // }
  145. // else
  146. // base = xctxt.getSourceTreeManager().findURIFromDoc(baseDoc);
  147. }
  148. else
  149. {
  150. base = arg2.str();
  151. }
  152. }
  153. else
  154. {
  155. // If the second argument is omitted, then it defaults to
  156. // the node in the stylesheet that contains the expression that
  157. // includes the call to the document function. Note that a
  158. // zero-length URI reference is a reference to the document
  159. // relative to which the URI reference is being resolved; thus
  160. // document("") refers to the root node of the stylesheet;
  161. // the tree representation of the stylesheet is exactly
  162. // the same as if the XML document containing the stylesheet
  163. // was the initial source document.
  164. assertion(null != xctxt.getNamespaceContext(), "Namespace context can not be null!");
  165. base = xctxt.getNamespaceContext().getBaseIdentifier();
  166. }
  167. XNodeSet nodes = new XNodeSet(xctxt.getDTMManager());
  168. NodeSetDTM mnl = nodes.mutableNodeset();
  169. DTMIterator iterator = (XObject.CLASS_NODESET == arg.getType())
  170. ? arg.iter() : null;
  171. int pos = DTM.NULL;
  172. while ((null == iterator) || (DTM.NULL != (pos = iterator.nextNode())))
  173. {
  174. XMLString ref = (null != iterator)
  175. ? xctxt.getDTM(pos).getStringValue(pos) : arg.xstr();
  176. // The first and only argument was a nodeset, the base in that
  177. // case is the base URI of the node from the first argument nodeset.
  178. // Remember, when the document function has exactly one argument and
  179. // the argument is a node-set, then the result is the union, for each
  180. // node in the argument node-set, of the result of calling the document
  181. // function with the first argument being the string-value of the node,
  182. // and the second argument being a node-set with the node as its only
  183. // member.
  184. if (null == arg1Expr && DTM.NULL != pos)
  185. {
  186. DTM baseDTM = xctxt.getDTM(pos);
  187. base = baseDTM.getDocumentBaseURI();
  188. }
  189. if (null == ref)
  190. continue;
  191. if (DTM.NULL == docContext)
  192. {
  193. error(xctxt, XSLTErrorResources.ER_NO_CONTEXT_OWNERDOC, null); //"context does not have an owner document!");
  194. }
  195. // From http://www.ics.uci.edu/pub/ietf/uri/rfc1630.txt
  196. // A partial form can be distinguished from an absolute form in that the
  197. // latter must have a colon and that colon must occur before any slash
  198. // characters. Systems not requiring partial forms should not use any
  199. // unencoded slashes in their naming schemes. If they do, absolute URIs
  200. // will still work, but confusion may result.
  201. int indexOfColon = ref.indexOf(':');
  202. int indexOfSlash = ref.indexOf('/');
  203. if ((indexOfColon != -1) && (indexOfSlash != -1)
  204. && (indexOfColon < indexOfSlash))
  205. {
  206. // The url (or filename, for that matter) is absolute.
  207. base = null;
  208. }
  209. int newDoc = getDoc(xctxt, context, ref.toString(), base);
  210. // nodes.mutableNodeset().addNode(newDoc);
  211. if (DTM.NULL != newDoc)
  212. {
  213. // TODO: mnl.addNodeInDocOrder(newDoc, true, xctxt); ??
  214. if (!mnl.contains(newDoc))
  215. {
  216. mnl.addElement(newDoc);
  217. }
  218. }
  219. if (null == iterator || newDoc == DTM.NULL)
  220. break;
  221. }
  222. return nodes;
  223. }
  224. /**
  225. * Get the document from the given URI and base
  226. *
  227. * @param xctxt The XPath runtime state.
  228. * @param context The current context node
  229. * @param uri Relative(?) URI of the document
  230. * @param base Base to resolve relative URI from.
  231. *
  232. * @return The document Node pointing to the document at the given URI
  233. * or null
  234. *
  235. * @throws javax.xml.transform.TransformerException
  236. */
  237. int getDoc(XPathContext xctxt, int context, String uri, String base)
  238. throws javax.xml.transform.TransformerException
  239. {
  240. // System.out.println("base: "+base+", uri: "+uri);
  241. SourceTreeManager treeMgr = xctxt.getSourceTreeManager();
  242. Source source;
  243. int newDoc;
  244. try
  245. {
  246. source = treeMgr.resolveURI(base, uri, xctxt.getSAXLocator());
  247. newDoc = treeMgr.getNode(source);
  248. }
  249. catch (IOException ioe)
  250. {
  251. throw new TransformerException(ioe.getMessage(),
  252. (SourceLocator)xctxt.getSAXLocator(), ioe);
  253. }
  254. catch(TransformerException te)
  255. {
  256. throw new TransformerException(te);
  257. }
  258. if (DTM.NULL != newDoc)
  259. return newDoc;
  260. // If the uri length is zero, get the uri of the stylesheet.
  261. if (uri.length() == 0)
  262. {
  263. // Hmmm... this seems pretty bogus to me... -sb
  264. uri = xctxt.getNamespaceContext().getBaseIdentifier();
  265. try
  266. {
  267. source = treeMgr.resolveURI(base, uri, xctxt.getSAXLocator());
  268. }
  269. catch (IOException ioe)
  270. {
  271. throw new TransformerException(ioe.getMessage(),
  272. (SourceLocator)xctxt.getSAXLocator(), ioe);
  273. }
  274. }
  275. String diagnosticsString = null;
  276. try
  277. {
  278. if ((null != uri) && (uri.toString().length() > 0))
  279. {
  280. newDoc = treeMgr.getSourceTree(source, xctxt.getSAXLocator(), xctxt);
  281. // System.out.println("newDoc: "+((Document)newDoc).getDocumentElement().getNodeName());
  282. }
  283. else
  284. warn(xctxt, XSLTErrorResources.WG_CANNOT_MAKE_URL_FROM,
  285. new Object[]{ ((base == null) ? "" : base) + uri }); //"Can not make URL from: "+((base == null) ? "" : base )+uri);
  286. }
  287. catch (Throwable throwable)
  288. {
  289. // throwable.printStackTrace();
  290. newDoc = DTM.NULL;
  291. // path.warn(XSLTErrorResources.WG_ENCODING_NOT_SUPPORTED_USING_JAVA, new Object[]{((base == null) ? "" : base )+uri}); //"Can not load requested doc: "+((base == null) ? "" : base )+uri);
  292. while (throwable
  293. instanceof org.apache.xml.utils.WrappedRuntimeException)
  294. {
  295. throwable =
  296. ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
  297. }
  298. if ((throwable instanceof NullPointerException)
  299. || (throwable instanceof ClassCastException))
  300. {
  301. throw new org.apache.xml.utils.WrappedRuntimeException(
  302. (Exception) throwable);
  303. }
  304. StringWriter sw = new StringWriter();
  305. PrintWriter diagnosticsWriter = new PrintWriter(sw);
  306. if (throwable instanceof TransformerException)
  307. {
  308. TransformerException spe = (TransformerException) throwable;
  309. {
  310. Throwable e = spe;
  311. while (null != e)
  312. {
  313. if (null != e.getMessage())
  314. {
  315. diagnosticsWriter.println(" (" + e.getClass().getName() + "): "
  316. + e.getMessage());
  317. }
  318. if (e instanceof TransformerException)
  319. {
  320. TransformerException spe2 = (TransformerException) e;
  321. SourceLocator locator = spe2.getLocator();
  322. if ((null != locator) && (null != locator.getSystemId()))
  323. diagnosticsWriter.println(" ID: " + locator.getSystemId()
  324. + " Line #" + locator.getLineNumber()
  325. + " Column #"
  326. + locator.getColumnNumber());
  327. e = spe2.getException();
  328. if (e instanceof org.apache.xml.utils.WrappedRuntimeException)
  329. e = ((org.apache.xml.utils.WrappedRuntimeException) e).getException();
  330. }
  331. else
  332. e = null;
  333. }
  334. }
  335. }
  336. else
  337. {
  338. diagnosticsWriter.println(" (" + throwable.getClass().getName()
  339. + "): " + throwable.getMessage());
  340. }
  341. diagnosticsString = throwable.getMessage(); //sw.toString();
  342. }
  343. if (DTM.NULL == newDoc)
  344. {
  345. // System.out.println("what?: "+base+", uri: "+uri);
  346. if (null != diagnosticsString)
  347. {
  348. warn(xctxt, XSLTErrorResources.WG_CANNOT_LOAD_REQUESTED_DOC,
  349. new Object[]{ diagnosticsString }); //"Can not load requested doc: "+((base == null) ? "" : base )+uri);
  350. }
  351. else
  352. warn(xctxt, XSLTErrorResources.WG_CANNOT_LOAD_REQUESTED_DOC,
  353. new Object[]{
  354. uri == null
  355. ? ((base == null) ? "" : base) + uri : uri.toString() }); //"Can not load requested doc: "+((base == null) ? "" : base )+uri);
  356. }
  357. else
  358. {
  359. // %REVIEW%
  360. // TBD: What to do about XLocator?
  361. // xctxt.getSourceTreeManager().associateXLocatorToNode(newDoc, url, null);
  362. }
  363. return newDoc;
  364. }
  365. /**
  366. * Tell the user of an error, and probably throw an
  367. * exception.
  368. *
  369. * @param xctxt The XPath runtime state.
  370. * @param msg The error message key
  371. * @param args Arguments to be used in the error message
  372. * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
  373. * the error condition is severe enough to halt processing.
  374. *
  375. * @throws javax.xml.transform.TransformerException
  376. */
  377. public void error(XPathContext xctxt, String msg, Object args[])
  378. throws javax.xml.transform.TransformerException
  379. {
  380. String formattedMsg = XSLMessages.createMessage(msg, args);
  381. ErrorListener errHandler = xctxt.getErrorListener();
  382. TransformerException spe = new TransformerException(formattedMsg,
  383. (SourceLocator)xctxt.getSAXLocator());
  384. if (null != errHandler)
  385. errHandler.error(spe);
  386. else
  387. System.out.println(formattedMsg);
  388. }
  389. /**
  390. * Warn the user of a problem.
  391. *
  392. * @param xctxt The XPath runtime state.
  393. * @param msg Warning message key
  394. * @param args Arguments to be used in the warning message
  395. * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
  396. * the error condition is severe enough to halt processing.
  397. *
  398. * @throws javax.xml.transform.TransformerException
  399. */
  400. public void warn(XPathContext xctxt, String msg, Object args[])
  401. throws javax.xml.transform.TransformerException
  402. {
  403. String formattedMsg = XSLMessages.createWarning(msg, args);
  404. ErrorListener errHandler = xctxt.getErrorListener();
  405. TransformerException spe = new TransformerException(formattedMsg,
  406. (SourceLocator)xctxt.getSAXLocator());
  407. if (null != errHandler)
  408. errHandler.warning(spe);
  409. else
  410. System.out.println(formattedMsg);
  411. }
  412. /**
  413. * Overide the superclass method to allow one or two arguments.
  414. *
  415. *
  416. * @param argNum Number of arguments passed in to this function
  417. *
  418. * @throws WrongNumberArgsException
  419. */
  420. public void checkNumberArgs(int argNum) throws WrongNumberArgsException
  421. {
  422. if ((argNum < 1) || (argNum > 2))
  423. reportWrongNumberArgs();
  424. }
  425. /**
  426. * Constructs and throws a WrongNumberArgException with the appropriate
  427. * message for this function object.
  428. *
  429. * @throws WrongNumberArgsException
  430. */
  431. protected void reportWrongNumberArgs() throws WrongNumberArgsException {
  432. throw new WrongNumberArgsException(XSLMessages.createMessage(XSLTErrorResources.ER_ONE_OR_TWO, null)); //"1 or 2");
  433. }
  434. /**
  435. * Tell if the expression is a nodeset expression. In other words, tell
  436. * if you can execute {@link asNode() asNode} without an exception.
  437. * @return true if the expression can be represented as a nodeset.
  438. */
  439. public boolean isNodesetExpr()
  440. {
  441. return true;
  442. }
  443. }