001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath;
017:
018: import java.lang.reflect.Constructor;
019: import java.lang.reflect.Method;
020: import java.util.Collection;
021: import java.util.Collections;
022: import java.util.Iterator;
023: import java.util.Set;
024:
025: import org.apache.commons.jxpath.functions.ConstructorFunction;
026: import org.apache.commons.jxpath.functions.MethodFunction;
027: import org.apache.commons.jxpath.util.MethodLookupUtils;
028: import org.apache.commons.jxpath.util.TypeUtils;
029:
030: /**
031: * Extension functions provided by Java classes. The class prefix specified
032: * in the constructor is used when a constructor or a static method is called.
033: * Usually, a class prefix is a package name (hence the name of this class).
034: *
035: * Let's say, we declared a PackageFunction like this:
036: * <blockquote><pre>
037: * new PackageFunctions("java.util.", "util")
038: * </pre></blockquote>
039: *
040: * We can now use XPaths like:
041: * <dl>
042: * <dt><code>"util:Date.new()"</code></dt>
043: * <dd>Equivalent to <code>new java.util.Date()</code></dd>
044: * <dt><code>"util:Collections.singleton('foo')"</code></dt>
045: * <dd>Equivalent to <code>java.util.Collections.singleton("foo")</code></dd>
046: * <dt><code>"util:substring('foo', 1, 2)"</code></dt>
047: * <dd>Equivalent to <code>"foo".substring(1, 2)</code>. Note that in
048: * this case, the class prefix is not used. JXPath does not check that
049: * the first parameter of the function (the method target) is in fact
050: * a member of the package described by this PackageFunctions object.</dd>
051: * </dl>
052: *
053: * <p>
054: * If the first argument of a method or constructor is ExpressionContext, the
055: * expression context in which the function is evaluated is passed to
056: * the method.
057: * </p>
058: * <p>
059: * There is one PackageFunctions object registered by default with each
060: * JXPathContext. It does not have a namespace and uses no class prefix.
061: * The existence of this object allows us to use XPaths like:
062: * <code>"java.util.Date.new()"</code> and <code>"length('foo')"</code>
063: * without the explicit registration of any extension functions.
064: * </p>
065:
066: *
067: * @author Dmitri Plotnikov
068: * @version $Revision: 1.14 $ $Date: 2004/04/04 23:16:23 $
069: */
070: public class PackageFunctions implements Functions {
071: private String classPrefix;
072: private String namespace;
073: private static final Object[] EMPTY_ARRAY = new Object[0];
074:
075: public PackageFunctions(String classPrefix, String namespace) {
076: this .classPrefix = classPrefix;
077: this .namespace = namespace;
078: }
079:
080: /**
081: * Returns the namespace specified in the constructor
082: */
083: public Set getUsedNamespaces() {
084: return Collections.singleton(namespace);
085: }
086:
087: /**
088: * Returns a Function, if any, for the specified namespace,
089: * name and parameter types.
090: * <p>
091: * @param namespace - if it is not the same as specified in the
092: * construction, this method returns null
093: * @param name - name of the method, which can one these forms:
094: * <ul>
095: * <li><b>methodname</b>, if invoking a method on an object passed as the
096: * first parameter</li>
097: * <li><b>Classname.new</b>, if looking for a constructor</li>
098: * <li><b>subpackage.subpackage.Classname.new</b>, if looking for a
099: * constructor in a subpackage</li>
100: * <li><b>Classname.methodname</b>, if looking for a static method</li>
101: * <li><b>subpackage.subpackage.Classname.methodname</b>, if looking for a
102: * static method of a class in a subpackage</li>
103: * </ul>
104: *
105: * @return a MethodFunction, a ConstructorFunction or null if no function
106: * is found
107: */
108: public Function getFunction(String namespace, String name,
109: Object[] parameters) {
110: if ((namespace == null && this .namespace != null)
111: || (namespace != null && !namespace
112: .equals(this .namespace))) {
113: return null;
114: }
115:
116: if (parameters == null) {
117: parameters = EMPTY_ARRAY;
118: }
119:
120: if (parameters.length >= 1) {
121: Object target = TypeUtils.convert(parameters[0],
122: Object.class);
123: if (target != null) {
124: Method method = MethodLookupUtils.lookupMethod(target
125: .getClass(), name, parameters);
126: if (method != null) {
127: return new MethodFunction(method);
128: }
129:
130: if (target instanceof NodeSet) {
131: target = ((NodeSet) target).getPointers();
132: }
133:
134: method = MethodLookupUtils.lookupMethod(target
135: .getClass(), name, parameters);
136: if (method != null) {
137: return new MethodFunction(method);
138: }
139:
140: if (target instanceof Collection) {
141: Iterator iter = ((Collection) target).iterator();
142: if (iter.hasNext()) {
143: target = iter.next();
144: if (target instanceof Pointer) {
145: target = ((Pointer) target).getValue();
146: }
147: } else {
148: target = null;
149: }
150: }
151: }
152: if (target != null) {
153: Method method = MethodLookupUtils.lookupMethod(target
154: .getClass(), name, parameters);
155: if (method != null) {
156: return new MethodFunction(method);
157: }
158: }
159: }
160:
161: String fullName = classPrefix + name;
162: int inx = fullName.lastIndexOf('.');
163: if (inx == -1) {
164: return null;
165: }
166:
167: String className = fullName.substring(0, inx);
168: String methodName = fullName.substring(inx + 1);
169:
170: Class functionClass;
171: try {
172: functionClass = Class.forName(className);
173: } catch (ClassNotFoundException ex) {
174: throw new JXPathException(
175: "Cannot invoke extension function "
176: + (namespace != null ? namespace + ":"
177: + name : name), ex);
178: }
179:
180: if (methodName.equals("new")) {
181: Constructor constructor = MethodLookupUtils
182: .lookupConstructor(functionClass, parameters);
183: if (constructor != null) {
184: return new ConstructorFunction(constructor);
185: }
186: } else {
187: Method method = MethodLookupUtils.lookupStaticMethod(
188: functionClass, methodName, parameters);
189: if (method != null) {
190: return new MethodFunction(method);
191: }
192: }
193: return null;
194: }
195: }
|