001: package net.sf.saxon.functions;
002:
003: import net.sf.saxon.expr.Expression;
004: import net.sf.saxon.om.NamespaceConstant;
005: import net.sf.saxon.trans.StaticError;
006: import net.sf.saxon.trans.XPathException;
007: import net.sf.saxon.Err;
008:
009: /**
010: * The SystemFunctionLibrary represents the collection of functions in the fn: namespace. That is, the
011: * functions defined in the "Functions and Operators" specification, optionally augmented by the additional
012: * functions defined in XSLT.
013: */
014:
015: public class SystemFunctionLibrary implements FunctionLibrary {
016:
017: private int functionSet;
018:
019: public static final int XPATH_ONLY = 0;
020: public static final int FULL_XSLT = 1;
021: public static final int USE_WHEN = 2;
022:
023: /**
024: * Create a SystemFunctionLibrary
025: * @param functionSet determines the set of functions allowed. One of
026: * {@link #XPATH_ONLY}, {@link #FULL_XSLT}, {@link #USE_WHEN}
027: */
028:
029: public SystemFunctionLibrary(int functionSet) {
030: this .functionSet = functionSet;
031: }
032:
033: /**
034: * Test whether a system function with a given name and arity is available. This supports
035: * the function-available() function in XSLT. This method may be called either at compile time
036: * or at run time.
037: * @param uri The URI of the function name
038: * @param local The local part of the function name
039: * @param arity The number of arguments. This is set to -1 in the case of the single-argument
040: * function-available() function; in this case the method should return true if there is some
041: * matching extension function, regardless of its arity.
042: */
043:
044: public boolean isAvailable(int fingerprint, String uri,
045: String local, int arity) {
046: if (uri.equals(NamespaceConstant.FN)) {
047: StandardFunction.Entry entry = StandardFunction
048: .getFunction(local, arity);
049: if (entry == null) {
050: return false;
051: }
052: return (arity == -1 || (arity >= entry.minArguments && arity <= entry.maxArguments));
053: } else {
054: return false;
055: }
056: }
057:
058: /**
059: * Bind an extension function, given the URI and local parts of the function name,
060: * and the list of expressions supplied as arguments. This method is called at compile
061: * time.
062: * @param uri The URI of the function name
063: * @param local The local part of the function name
064: * @param staticArgs The expressions supplied statically in the function call. The intention is
065: * that the static type of the arguments (obtainable via getItemType() and getCardinality() may
066: * be used as part of the binding algorithm.
067: * @return An object representing the extension function to be called, if one is found;
068: * null if no extension function was found matching the required name and arity.
069: * @throws net.sf.saxon.trans.XPathException if a function is found with the required name and arity, but
070: * the implementation of the function cannot be loaded or used; or if an error occurs
071: * while searching for the function; or if this function library "owns" the namespace containing
072: * the function call, but no function was found.
073: */
074:
075: public Expression bind(int nameCode, String uri, String local,
076: Expression[] staticArgs) throws XPathException {
077: if (uri.equals(NamespaceConstant.FN)) {
078: StandardFunction.Entry entry = StandardFunction
079: .getFunction(local, staticArgs.length);
080: if (entry == null) {
081: if (StandardFunction.getFunction(local, -1) == null) {
082: StaticError err = new StaticError(
083: "Unknown system function " + local + "()");
084: err.setErrorCode("XPST0017");
085: throw err;
086: } else {
087: StaticError err = new StaticError(
088: "System function "
089: + local
090: + "() cannot be called with "
091: + pluralArguments(staticArgs.length));
092: err.setErrorCode("XPST0017");
093: throw err;
094: }
095: }
096: Class functionClass = entry.implementationClass;
097: SystemFunction f;
098: try {
099: f = (SystemFunction) functionClass.newInstance();
100: } catch (Exception err) {
101: throw new AssertionError(
102: "Failed to load system function: "
103: + err.getMessage());
104: }
105: f.setDetails(entry);
106: f.setFunctionNameCode(nameCode);
107: if (functionSet != FULL_XSLT) {
108: if (f instanceof XSLTFunction
109: || (f instanceof NamePart && entry.opcode == NamePart.GENERATE_ID)) {
110: if (functionSet == XPATH_ONLY) {
111: StaticError err = new StaticError(
112: "Cannot use the "
113: + local
114: + "() function in a non-XSLT context");
115: err.setErrorCode("XPST0017");
116: throw err;
117: } else if (functionSet == USE_WHEN
118: && !(f instanceof Available || f instanceof SystemProperty)) {
119: StaticError err = new StaticError(
120: "Cannot use the "
121: + local
122: + "() function in a use-when expression");
123: err.setErrorCode("XPST0017");
124: throw err;
125: }
126: }
127: }
128: f.setArguments(staticArgs);
129: checkArgumentCount(staticArgs.length, entry.minArguments,
130: entry.maxArguments, local);
131: return f;
132: } else {
133: return null;
134: }
135: }
136:
137: /**
138: * Check number of arguments. <BR>
139: * A convenience routine for use in subclasses.
140: * @param min the minimum number of arguments allowed
141: * @param max the maximum number of arguments allowed
142: * @return the actual number of arguments
143: * @throws net.sf.saxon.trans.XPathException if the number of arguments is out of range
144: */
145:
146: private int checkArgumentCount(int numArgs, int min, int max,
147: String local) throws XPathException {
148: if (min == max && numArgs != min) {
149: throw new StaticError("Function "
150: + Err.wrap(local, Err.FUNCTION) + " must have "
151: + min + pluralArguments(min));
152: }
153: if (numArgs < min) {
154: throw new StaticError("Function "
155: + Err.wrap(local, Err.FUNCTION)
156: + " must have at least " + min
157: + pluralArguments(min));
158: }
159: if (numArgs > max) {
160: throw new StaticError("Function "
161: + Err.wrap(local, Err.FUNCTION)
162: + " must have no more than " + max
163: + pluralArguments(max));
164: }
165: return numArgs;
166: }
167:
168: /**
169: * Utility routine used in constructing error messages
170: */
171:
172: private static String pluralArguments(int num) {
173: if (num == 1)
174: return " argument";
175: return " arguments";
176: }
177:
178: /**
179: * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
180: * new functions to be added, then additions to this copy will not affect the original, or
181: * vice versa.
182: *
183: * @return a copy of this function library. This must be an instance of the original class.
184: */
185:
186: public FunctionLibrary copy() {
187: return this ;
188: }
189: }
190: //
191: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
192: // you may not use this file except in compliance with the License. You may obtain a copy of the
193: // License at http://www.mozilla.org/MPL/
194: //
195: // Software distributed under the License is distributed on an "AS IS" basis,
196: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
197: // See the License for the specific language governing rights and limitations under the License.
198: //
199: // The Original Code is: all this file.
200: //
201: // The Initial Developer of the Original Code is Michael H. Kay.
202: //
203: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
204: //
205: // Contributor(s): none.
206: //
|