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. package org.apache.commons.jxpath;
  17. import java.lang.reflect.Constructor;
  18. import java.lang.reflect.Method;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.Iterator;
  22. import java.util.Set;
  23. import org.apache.commons.jxpath.functions.ConstructorFunction;
  24. import org.apache.commons.jxpath.functions.MethodFunction;
  25. import org.apache.commons.jxpath.util.MethodLookupUtils;
  26. import org.apache.commons.jxpath.util.TypeUtils;
  27. /**
  28. * Extension functions provided by Java classes. The class prefix specified
  29. * in the constructor is used when a constructor or a static method is called.
  30. * Usually, a class prefix is a package name (hence the name of this class).
  31. *
  32. * Let's say, we declared a PackageFunction like this:
  33. * <blockquote><pre>
  34. * new PackageFunctions("java.util.", "util")
  35. * </pre></blockquote>
  36. *
  37. * We can now use XPaths like:
  38. * <dl>
  39. * <dt><code>"util:Date.new()"</code></dt>
  40. * <dd>Equivalent to <code>new java.util.Date()</code></dd>
  41. * <dt><code>"util:Collections.singleton('foo')"</code></dt>
  42. * <dd>Equivalent to <code>java.util.Collections.singleton("foo")</code></dd>
  43. * <dt><code>"util:substring('foo', 1, 2)"</code></dt>
  44. * <dd>Equivalent to <code>"foo".substring(1, 2)</code>. Note that in
  45. * this case, the class prefix is not used. JXPath does not check that
  46. * the first parameter of the function (the method target) is in fact
  47. * a member of the package described by this PackageFunctions object.</dd>
  48. * </dl>
  49. *
  50. * <p>
  51. * If the first argument of a method or constructor is ExpressionContext, the
  52. * expression context in which the function is evaluated is passed to
  53. * the method.
  54. * </p>
  55. * <p>
  56. * There is one PackageFunctions object registered by default with each
  57. * JXPathContext. It does not have a namespace and uses no class prefix.
  58. * The existence of this object allows us to use XPaths like:
  59. * <code>"java.util.Date.new()"</code> and <code>"length('foo')"</code>
  60. * without the explicit registration of any extension functions.
  61. * </p>
  62. *
  63. * @author Dmitri Plotnikov
  64. * @version $Revision: 1.14 $ $Date: 2004/04/04 23:16:23 $
  65. */
  66. public class PackageFunctions implements Functions {
  67. private String classPrefix;
  68. private String namespace;
  69. private static final Object[] EMPTY_ARRAY = new Object[0];
  70. public PackageFunctions(String classPrefix, String namespace) {
  71. this.classPrefix = classPrefix;
  72. this.namespace = namespace;
  73. }
  74. /**
  75. * Returns the namespace specified in the constructor
  76. */
  77. public Set getUsedNamespaces() {
  78. return Collections.singleton(namespace);
  79. }
  80. /**
  81. * Returns a Function, if any, for the specified namespace,
  82. * name and parameter types.
  83. * <p>
  84. * @param namespace - if it is not the same as specified in the
  85. * construction, this method returns null
  86. * @param name - name of the method, which can one these forms:
  87. * <ul>
  88. * <li><b>methodname</b>, if invoking a method on an object passed as the
  89. * first parameter</li>
  90. * <li><b>Classname.new</b>, if looking for a constructor</li>
  91. * <li><b>subpackage.subpackage.Classname.new</b>, if looking for a
  92. * constructor in a subpackage</li>
  93. * <li><b>Classname.methodname</b>, if looking for a static method</li>
  94. * <li><b>subpackage.subpackage.Classname.methodname</b>, if looking for a
  95. * static method of a class in a subpackage</li>
  96. * </ul>
  97. *
  98. * @return a MethodFunction, a ConstructorFunction or null if no function
  99. * is found
  100. */
  101. public Function getFunction(
  102. String namespace,
  103. String name,
  104. Object[] parameters)
  105. {
  106. if ((namespace == null && this.namespace != null)
  107. || (namespace != null && !namespace.equals(this.namespace))) {
  108. return null;
  109. }
  110. if (parameters == null) {
  111. parameters = EMPTY_ARRAY;
  112. }
  113. if (parameters.length >= 1) {
  114. Object target = TypeUtils.convert(parameters[0], Object.class);
  115. if (target != null) {
  116. Method method =
  117. MethodLookupUtils.lookupMethod(
  118. target.getClass(),
  119. name,
  120. parameters);
  121. if (method != null) {
  122. return new MethodFunction(method);
  123. }
  124. if (target instanceof NodeSet) {
  125. target = ((NodeSet) target).getPointers();
  126. }
  127. method =
  128. MethodLookupUtils.lookupMethod(
  129. target.getClass(),
  130. name,
  131. parameters);
  132. if (method != null) {
  133. return new MethodFunction(method);
  134. }
  135. if (target instanceof Collection) {
  136. Iterator iter = ((Collection) target).iterator();
  137. if (iter.hasNext()) {
  138. target = iter.next();
  139. if (target instanceof Pointer) {
  140. target = ((Pointer) target).getValue();
  141. }
  142. }
  143. else {
  144. target = null;
  145. }
  146. }
  147. }
  148. if (target != null) {
  149. Method method =
  150. MethodLookupUtils.lookupMethod(
  151. target.getClass(),
  152. name,
  153. parameters);
  154. if (method != null) {
  155. return new MethodFunction(method);
  156. }
  157. }
  158. }
  159. String fullName = classPrefix + name;
  160. int inx = fullName.lastIndexOf('.');
  161. if (inx == -1) {
  162. return null;
  163. }
  164. String className = fullName.substring(0, inx);
  165. String methodName = fullName.substring(inx + 1);
  166. Class functionClass;
  167. try {
  168. functionClass = Class.forName(className);
  169. }
  170. catch (ClassNotFoundException ex) {
  171. throw new JXPathException(
  172. "Cannot invoke extension function "
  173. + (namespace != null ? namespace + ":" + name : name),
  174. ex);
  175. }
  176. if (methodName.equals("new")) {
  177. Constructor constructor =
  178. MethodLookupUtils.lookupConstructor(functionClass, parameters);
  179. if (constructor != null) {
  180. return new ConstructorFunction(constructor);
  181. }
  182. }
  183. else {
  184. Method method =
  185. MethodLookupUtils.lookupStaticMethod(
  186. functionClass,
  187. methodName,
  188. parameters);
  189. if (method != null) {
  190. return new MethodFunction(method);
  191. }
  192. }
  193. return null;
  194. }
  195. }