001: package net.sf.saxon.functions;
002:
003: import net.sf.saxon.expr.Expression;
004: import net.sf.saxon.expr.StaticContext;
005: import net.sf.saxon.expr.StaticProperty;
006: import net.sf.saxon.om.NamespaceConstant;
007: import net.sf.saxon.pattern.NodeKindTest;
008: import net.sf.saxon.trans.StaticError;
009: import net.sf.saxon.trans.XPathException;
010: import net.sf.saxon.type.ItemType;
011: import net.sf.saxon.type.Type;
012: import net.sf.saxon.Err;
013:
014: import java.util.HashMap;
015:
016: /**
017: * The VendorFunctionLibrary represents specially-recognized functions in the Saxon namespace. It doesn't
018: * handle Saxon extension functions that are implemented as normal extension functions, which are bound using
019: * the {@link JavaExtensionLibrary}.
020: */
021:
022: public class VendorFunctionLibrary implements FunctionLibrary {
023:
024: private HashMap functionTable;
025:
026: public VendorFunctionLibrary() {
027: init();
028: }
029:
030: /**
031: * Register an extension function in the table of function details.
032: * @param name the function name
033: * @param implementationClass the class used to implement the function
034: * @param opcode identifies the function when a single class implements several functions
035: * @param minArguments the minimum number of arguments required
036: * @param maxArguments the maximum number of arguments allowed
037: * @param itemType the item type of the result of the function
038: * @param cardinality the cardinality of the result of the function
039: * @return the entry describing the function. The entry is incomplete, it does not yet contain information
040: * about the function arguments.
041: */
042:
043: protected StandardFunction.Entry register(String name,
044: Class implementationClass, int opcode, int minArguments,
045: int maxArguments, ItemType itemType, int cardinality) {
046: StandardFunction.Entry e = StandardFunction.makeEntry(name,
047: implementationClass, opcode, minArguments,
048: maxArguments, itemType, cardinality);
049: functionTable.put(name, e);
050: return e;
051: }
052:
053: protected void init() {
054: functionTable = new HashMap(30);
055: StandardFunction.Entry e;
056: e = register("evaluate", Evaluate.class, Evaluate.EVALUATE, 1,
057: 10, Type.ITEM_TYPE, StaticProperty.ALLOWS_ZERO_OR_MORE);
058: StandardFunction.arg(e, 0, Type.STRING_TYPE,
059: StaticProperty.EXACTLY_ONE);
060:
061: e = register("evaluate-node", Evaluate.class,
062: Evaluate.EVALUATE_NODE, 1, 1, Type.ITEM_TYPE,
063: StaticProperty.ALLOWS_ZERO_OR_MORE);
064: StandardFunction.arg(e, 0, Type.NODE_TYPE,
065: StaticProperty.EXACTLY_ONE);
066:
067: e = register("eval", Evaluate.class, Evaluate.EVAL, 1, 10,
068: Type.ITEM_TYPE, StaticProperty.ALLOWS_ZERO_OR_MORE);
069: StandardFunction.arg(e, 0, Type.ANY_ATOMIC_TYPE,
070: StaticProperty.EXACTLY_ONE);
071:
072: e = register("expression", Evaluate.class, Evaluate.EXPRESSION,
073: 1, 1, Type.ANY_ATOMIC_TYPE, StaticProperty.EXACTLY_ONE);
074: StandardFunction.arg(e, 0, Type.STRING_TYPE,
075: StaticProperty.EXACTLY_ONE);
076:
077: e = register("parse", Parse.class, 0, 1, 1,
078: NodeKindTest.DOCUMENT, StaticProperty.EXACTLY_ONE);
079: StandardFunction.arg(e, 0, Type.STRING_TYPE,
080: StaticProperty.EXACTLY_ONE);
081:
082: e = register("serialize", Serialize.class, 0, 2, 2,
083: Type.STRING_TYPE, StaticProperty.EXACTLY_ONE);
084: StandardFunction.arg(e, 0, Type.NODE_TYPE,
085: StaticProperty.ALLOWS_ZERO_OR_ONE);
086: StandardFunction.arg(e, 1, Type.STRING_TYPE,
087: StaticProperty.EXACTLY_ONE);
088:
089: }
090:
091: /**
092: * Test whether a Saxon function with a given name and arity is available. This supports
093: * the function-available() function in XSLT. This method may be called either at compile time
094: * or at run time.
095: * @param uri The URI of the function name
096: * @param local The local part of the function name
097: * @param arity The number of arguments. This is set to -1 in the case of the single-argument
098: * function-available() function; in this case the method should return true if there is some
099: * matching extension function, regardless of its arity.
100: */
101:
102: public boolean isAvailable(int fingerprint, String uri,
103: String local, int arity) {
104: if (uri.equals(NamespaceConstant.SAXON)) {
105: StandardFunction.Entry entry = (StandardFunction.Entry) functionTable
106: .get(local);
107: if (entry == null) {
108: return false;
109: }
110: return (arity == -1 || (arity >= entry.minArguments && arity <= entry.maxArguments));
111: } else {
112: return false;
113: }
114: }
115:
116: /**
117: * Bind an extension function, given the URI and local parts of the function name,
118: * and the list of expressions supplied as arguments. This method is called at compile
119: * time.
120: * @param uri The URI of the function name
121: * @param local The local part of the function name
122: * @param staticArgs The expressions supplied statically in the function call. The intention is
123: * that the static type of the arguments (obtainable via getItemType() and getCardinality() may
124: * be used as part of the binding algorithm.
125: * @return An object representing the extension function to be called, if one is found;
126: * null if no extension function was found matching the required name and arity.
127: * @throws net.sf.saxon.trans.XPathException if a function is found with the required name and arity, but
128: * the implementation of the function cannot be loaded or used; or if an error occurs
129: * while searching for the function; or if this function library "owns" the namespace containing
130: * the function call, but no function was found.
131: */
132:
133: public Expression bind(int nameCode, String uri, String local,
134: Expression[] staticArgs) throws XPathException {
135: if (uri.equals(NamespaceConstant.SAXON)) {
136: StandardFunction.Entry entry = (StandardFunction.Entry) functionTable
137: .get(local);
138: if (entry == null) {
139: return null;
140: }
141: Class functionClass = entry.implementationClass;
142: SystemFunction f;
143: try {
144: f = (SystemFunction) functionClass.newInstance();
145: } catch (Exception err) {
146: throw new AssertionError(
147: "Failed to load Saxon extension function: "
148: + err.getMessage());
149: }
150: f.setDetails(entry);
151: f.setFunctionNameCode(nameCode);
152: f.setArguments(staticArgs);
153: checkArgumentCount(staticArgs.length, entry.minArguments,
154: entry.maxArguments, local);
155: return f;
156: } else {
157: return null;
158: }
159: }
160:
161: /**
162: * Make a Saxon function with a given name
163: */
164:
165: public Expression makeSaxonFunction(String localName,
166: StaticContext env, Expression[] arguments)
167: throws XPathException {
168: String uri = NamespaceConstant.SAXON;
169: int nameCode = env.getNamePool().allocate("saxon", uri,
170: localName);
171: return bind(nameCode, uri, localName, arguments);
172: }
173:
174: /**
175: * Check number of arguments. <BR>
176: * A convenience routine for use in subclasses.
177: * @param min the minimum number of arguments allowed
178: * @param max the maximum number of arguments allowed
179: * @return the actual number of arguments
180: * @throws net.sf.saxon.trans.XPathException if the number of arguments is out of range
181: */
182:
183: private int checkArgumentCount(int numArgs, int min, int max,
184: String local) throws XPathException {
185: if (min == max && numArgs != min) {
186: throw new StaticError("Function "
187: + Err.wrap("saxon:" + local, Err.FUNCTION)
188: + " must have " + min + pluralArguments(min));
189: }
190: if (numArgs < min) {
191: throw new StaticError("Function "
192: + Err.wrap("saxon:" + local, Err.FUNCTION)
193: + " must have at least " + min
194: + pluralArguments(min));
195: }
196: if (numArgs > max) {
197: throw new StaticError("Function "
198: + Err.wrap("saxon:" + local, Err.FUNCTION)
199: + " must have no more than " + max
200: + pluralArguments(max));
201: }
202: return numArgs;
203: }
204:
205: /**
206: * Utility routine used in constructing error messages
207: */
208:
209: public static String pluralArguments(int num) {
210: if (num == 1)
211: return " argument";
212: return " arguments";
213: }
214:
215: /**
216: * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
217: * new functions to be added, then additions to this copy will not affect the original, or
218: * vice versa.
219: *
220: * @return a copy of this function library. This must be an instance of the original class.
221: */
222:
223: public FunctionLibrary copy() {
224: return this ;
225: }
226: }
227: //
228: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
229: // you may not use this file except in compliance with the License. You may obtain a copy of the
230: // License at http://www.mozilla.org/MPL/
231: //
232: // Software distributed under the License is distributed on an "AS IS" basis,
233: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
234: // See the License for the specific language governing rights and limitations under the License.
235: //
236: // The Original Code is: all this file.
237: //
238: // The Initial Developer of the Original Code is Michael H. Kay.
239: //
240: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
241: //
242: // Contributor(s): none.
243: //
|