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.extensions;
  58. import java.util.Vector;
  59. import java.lang.reflect.Method;
  60. import java.lang.reflect.Modifier;
  61. import java.lang.reflect.Constructor;
  62. import java.lang.reflect.InvocationTargetException;
  63. import java.io.IOException;
  64. //import org.w3c.dom.Element;
  65. //import org.w3c.dom.Node;
  66. import org.apache.xalan.transformer.TransformerImpl;
  67. import org.apache.xalan.templates.Stylesheet;
  68. import org.apache.xalan.templates.ElemTemplateElement;
  69. import org.apache.xml.utils.QName;
  70. import org.apache.xpath.objects.XObject;
  71. import javax.xml.transform.TransformerException;
  72. /**
  73. * <meta name="usage" content="internal"/>
  74. * Represents an extension namespace for XPath that handles java classes.
  75. * It is recommended that the class URI be of the form:
  76. * <pre>
  77. * xalan://fully.qualified.class.name
  78. * </pre>
  79. * However, we do not enforce this. If the class name contains a
  80. * a /, we only use the part to the right of the rightmost slash.
  81. * In addition, we ignore any "class:" prefix.
  82. * Provides functions to test a function's existence and call a function.
  83. * Also provides functions to test an element's existence and call an
  84. * element.
  85. *
  86. * @author <a href="mailto:garyp@firstech.com">Gary L Peskin</a>
  87. */
  88. public class ExtensionHandlerJavaClass extends ExtensionHandlerJava
  89. {
  90. private Class m_classObj = null;
  91. /**
  92. * Provides a default Instance for use by elements that need to call
  93. * an instance method.
  94. */
  95. private Object m_defaultInstance = null;
  96. /**
  97. * Construct a new extension namespace handler given all the information
  98. * needed.
  99. * @param namespaceUri the extension namespace URI that I'm implementing
  100. * @param scriptLang language of code implementing the extension
  101. * @param className the fully qualified class name of the class
  102. */
  103. public ExtensionHandlerJavaClass(String namespaceUri,
  104. String scriptLang,
  105. String className)
  106. {
  107. super(namespaceUri, scriptLang, className);
  108. try
  109. {
  110. m_classObj = getClassForName(className);
  111. }
  112. catch (ClassNotFoundException e)
  113. {
  114. // For now, just let this go. We'll catch it when we try to invoke a method.
  115. }
  116. }
  117. /**
  118. * Tests whether a certain function name is known within this namespace.
  119. * Simply looks for a method with the appropriate name. There is
  120. * no information regarding the arguments to the function call or
  121. * whether the method implementing the function is a static method or
  122. * an instance method.
  123. * @param function name of the function being tested
  124. * @return true if its known, false if not.
  125. */
  126. public boolean isFunctionAvailable(String function)
  127. {
  128. Method[] methods = m_classObj.getMethods();
  129. int nMethods = methods.length;
  130. for (int i = 0; i < nMethods; i++)
  131. {
  132. if (methods[i].getName().equals(function))
  133. return true;
  134. }
  135. return false;
  136. }
  137. /**
  138. * Tests whether a certain element name is known within this namespace.
  139. * Looks for a method with the appropriate name and signature.
  140. * This method examines both static and instance methods.
  141. * @param element name of the element being tested
  142. * @return true if its known, false if not.
  143. */
  144. public boolean isElementAvailable(String element)
  145. {
  146. Method[] methods = m_classObj.getMethods();
  147. int nMethods = methods.length;
  148. for (int i = 0; i < nMethods; i++)
  149. {
  150. if (methods[i].getName().equals(element))
  151. {
  152. Class[] paramTypes = methods[i].getParameterTypes();
  153. if ( (paramTypes.length == 2)
  154. && paramTypes[0].isAssignableFrom(
  155. org.apache.xalan.extensions.XSLProcessorContext.class)
  156. && paramTypes[1].isAssignableFrom(org.apache.xalan.templates.ElemExtensionCall.class) )
  157. {
  158. return true;
  159. }
  160. }
  161. }
  162. return false;
  163. }
  164. /**
  165. * Process a call to a function in the java class represented by
  166. * this <code>ExtensionHandlerJavaClass<code>.
  167. * There are three possible types of calls:
  168. * <pre>
  169. * Constructor:
  170. * classns:new(arg1, arg2, ...)
  171. *
  172. * Static method:
  173. * classns:method(arg1, arg2, ...)
  174. *
  175. * Instance method:
  176. * classns:method(obj, arg1, arg2, ...)
  177. * </pre>
  178. * We use the following rules to determine the type of call made:
  179. * <ol type="1">
  180. * <li>If the function name is "new", call the best constructor for
  181. * class represented by the namespace URI</li>
  182. * <li>If the first argument to the function is of the class specified
  183. * in the namespace or is a subclass of that class, look for the best
  184. * method of the class specified in the namespace with the specified
  185. * arguments. Compare all static and instance methods with the correct
  186. * method name. For static methods, use all arguments in the compare.
  187. * For instance methods, use all arguments after the first.</li>
  188. * <li>Otherwise, select the best static or instance method matching
  189. * all of the arguments. If the best method is an instance method,
  190. * call the function using a default object, creating it if needed.</li>
  191. * </ol>
  192. *
  193. * @param funcName Function name.
  194. * @param args The arguments of the function call.
  195. * @param methodKey A key that uniquely identifies this class and method call.
  196. * @param exprContext The context in which this expression is being executed.
  197. * @return the return value of the function evaluation.
  198. * @throws TransformerException
  199. */
  200. public Object callFunction (String funcName,
  201. Vector args,
  202. Object methodKey,
  203. ExpressionContext exprContext)
  204. throws TransformerException
  205. {
  206. Object[] methodArgs;
  207. Object[][] convertedArgs;
  208. Class[] paramTypes;
  209. try
  210. {
  211. if (funcName.equals("new")) { // Handle constructor call
  212. methodArgs = new Object[args.size()];
  213. convertedArgs = new Object[1][];
  214. for (int i = 0; i < methodArgs.length; i++)
  215. {
  216. methodArgs[i] = args.elementAt(i);
  217. }
  218. Constructor c = (Constructor) getFromCache(methodKey, null, methodArgs);
  219. if (c != null)
  220. {
  221. try
  222. {
  223. paramTypes = c.getParameterTypes();
  224. MethodResolver.convertParams(methodArgs, convertedArgs, paramTypes, exprContext);
  225. return c.newInstance(convertedArgs[0]);
  226. }
  227. catch (InvocationTargetException ite)
  228. {
  229. throw ite;
  230. }
  231. catch(Exception e)
  232. {
  233. // Must not have been the right one
  234. }
  235. }
  236. c = MethodResolver.getConstructor(m_classObj,
  237. methodArgs,
  238. convertedArgs,
  239. exprContext);
  240. putToCache(methodKey, null, methodArgs, c);
  241. return c.newInstance(convertedArgs[0]);
  242. }
  243. else
  244. {
  245. int resolveType;
  246. Object targetObject = null;
  247. methodArgs = new Object[args.size()];
  248. convertedArgs = new Object[1][];
  249. for (int i = 0; i < methodArgs.length; i++)
  250. {
  251. methodArgs[i] = args.elementAt(i);
  252. }
  253. Method m = (Method) getFromCache(methodKey, null, methodArgs);
  254. if (m != null)
  255. {
  256. try
  257. {
  258. paramTypes = m.getParameterTypes();
  259. MethodResolver.convertParams(methodArgs, convertedArgs, paramTypes, exprContext);
  260. if (Modifier.isStatic(m.getModifiers()))
  261. return m.invoke(null, convertedArgs[0]);
  262. else
  263. {
  264. // This is tricky. We get the actual number of target arguments (excluding any
  265. // ExpressionContext). If we passed in the same number, we need the implied object.
  266. int nTargetArgs = convertedArgs[0].length;
  267. if (ExpressionContext.class.isAssignableFrom(paramTypes[0]))
  268. nTargetArgs--;
  269. if (methodArgs.length <= nTargetArgs)
  270. return m.invoke(m_defaultInstance, convertedArgs[0]);
  271. else
  272. {
  273. targetObject = methodArgs[0];
  274. if (targetObject instanceof XObject)
  275. targetObject = ((XObject) targetObject).object();
  276. return m.invoke(targetObject, convertedArgs[0]);
  277. }
  278. }
  279. }
  280. catch (InvocationTargetException ite)
  281. {
  282. throw ite;
  283. }
  284. catch(Exception e)
  285. {
  286. // Must not have been the right one
  287. }
  288. }
  289. if (args.size() > 0)
  290. {
  291. targetObject = methodArgs[0];
  292. if (targetObject instanceof XObject)
  293. targetObject = ((XObject) targetObject).object();
  294. if (m_classObj.isAssignableFrom(targetObject.getClass()))
  295. resolveType = MethodResolver.DYNAMIC;
  296. else
  297. resolveType = MethodResolver.STATIC_AND_INSTANCE;
  298. }
  299. else
  300. {
  301. targetObject = null;
  302. resolveType = MethodResolver.STATIC_AND_INSTANCE;
  303. }
  304. m = MethodResolver.getMethod(m_classObj,
  305. funcName,
  306. methodArgs,
  307. convertedArgs,
  308. exprContext,
  309. resolveType);
  310. putToCache(methodKey, null, methodArgs, m);
  311. if (MethodResolver.DYNAMIC == resolveType) // First argument was object type
  312. return m.invoke(targetObject, convertedArgs[0]);
  313. else // First arg was not object. See if we need the implied object.
  314. {
  315. if (Modifier.isStatic(m.getModifiers()))
  316. return m.invoke(null, convertedArgs[0]);
  317. else
  318. {
  319. if (null == m_defaultInstance)
  320. {
  321. m_defaultInstance = m_classObj.newInstance();
  322. }
  323. return m.invoke(m_defaultInstance, convertedArgs[0]);
  324. }
  325. }
  326. }
  327. }
  328. catch (InvocationTargetException ite)
  329. {
  330. Throwable resultException = ite;
  331. Throwable targetException = ite.getTargetException();
  332. if (targetException instanceof TransformerException)
  333. throw ((TransformerException)targetException);
  334. else if (targetException != null)
  335. resultException = targetException;
  336. throw new TransformerException(resultException);
  337. }
  338. catch (Exception e)
  339. {
  340. // e.printStackTrace();
  341. throw new TransformerException(e);
  342. }
  343. }
  344. /**
  345. * Process a call to this extension namespace via an element. As a side
  346. * effect, the results are sent to the TransformerImpl's result tree.
  347. * We invoke the static or instance method in the class represented by
  348. * by the namespace URI. If we don't already have an instance of this class,
  349. * we create one upon the first call.
  350. *
  351. * @param localPart Element name's local part.
  352. * @param element The extension element being processed.
  353. * @param transformer Handle to TransformerImpl.
  354. * @param stylesheetTree The compiled stylesheet tree.
  355. * @param sourceTree The root of the source tree (but don't assume
  356. * it's a Document).
  357. * @param sourceNode The current context node.
  358. * @param mode The current mode.
  359. * @param methodKey A key that uniquely identifies this element call.
  360. * @throws IOException if loading trouble
  361. * @throws TransformerException if parsing trouble
  362. */
  363. public void processElement(String localPart,
  364. ElemTemplateElement element,
  365. TransformerImpl transformer,
  366. Stylesheet stylesheetTree,
  367. Object methodKey)
  368. throws TransformerException, IOException
  369. {
  370. Object result = null;
  371. Method m = (Method) getFromCache(methodKey, null, null);
  372. if (null == m)
  373. {
  374. try
  375. {
  376. m = MethodResolver.getElementMethod(m_classObj, localPart);
  377. if ( (null == m_defaultInstance) && !Modifier.isStatic(m.getModifiers()) )
  378. m_defaultInstance = m_classObj.newInstance();
  379. }
  380. catch (Exception e)
  381. {
  382. // e.printStackTrace ();
  383. throw new TransformerException (e.getMessage (), e);
  384. }
  385. putToCache(methodKey, null, null, m);
  386. }
  387. XSLProcessorContext xpc = new XSLProcessorContext(transformer,
  388. stylesheetTree);
  389. try
  390. {
  391. result = m.invoke(m_defaultInstance, new Object[] {xpc, element});
  392. }
  393. catch (InvocationTargetException e)
  394. {
  395. Throwable targetException = e.getTargetException();
  396. if (targetException instanceof TransformerException)
  397. throw (TransformerException)targetException;
  398. else if (targetException != null)
  399. throw new TransformerException (targetException.getMessage (), targetException);
  400. else
  401. throw new TransformerException (e.getMessage (), e);
  402. }
  403. catch (Exception e)
  404. {
  405. // e.printStackTrace ();
  406. throw new TransformerException (e.getMessage (), e);
  407. }
  408. if (result != null)
  409. {
  410. xpc.outputToResultTree (stylesheetTree, result);
  411. }
  412. }
  413. }