001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.expr.*;
004: import net.sf.saxon.instruct.*;
005: import net.sf.saxon.om.*;
006: import net.sf.saxon.trans.XPathException;
007: import net.sf.saxon.value.EmptySequence;
008: import net.sf.saxon.value.SequenceType;
009:
010: import java.util.ArrayList;
011: import java.util.Iterator;
012: import java.util.List;
013:
014: /**
015: * Handler for xsl:function elements in stylesheet (XSLT 2.0). <BR>
016: * Attributes: <br>
017: * name gives the name of the function
018: * saxon:memo-function=yes|no indicates whether it acts as a memo function.
019: */
020:
021: public class XSLFunction extends StyleElement implements
022: StylesheetProcedure {
023:
024: private String nameAtt = null;
025: private String asAtt = null;
026: private String overrideAtt = null;
027:
028: private SequenceType resultType;
029: private String functionName;
030: private SlotManager stackFrameMap;
031: private boolean memoFunction = false;
032: private boolean override = true;
033: private int numberOfArguments = -1; // -1 means not yet known
034: private UserFunction compiledFunction;
035:
036: // List of UserFunctionCall objects that reference this XSLFunction
037: List references = new ArrayList(10);
038:
039: /**
040: * Method called by UserFunctionCall to register the function call for
041: * subsequent fixup.
042: * @param ref the UserFunctionCall to be registered
043: */
044:
045: public void registerReference(UserFunctionCall ref) {
046: references.add(ref);
047: }
048:
049: public void prepareAttributes() throws XPathException {
050:
051: AttributeCollection atts = getAttributeList();
052:
053: for (int a = 0; a < atts.getLength(); a++) {
054: int nc = atts.getNameCode(a);
055: String f = getNamePool().getClarkName(nc);
056: if (f == StandardNames.NAME) {
057: nameAtt = atts.getValue(a).trim();
058: if (nameAtt.indexOf(':') < 0) {
059: compileError(
060: "Function name must have a namespace prefix",
061: "XTSE0740");
062: }
063: try {
064: setObjectNameCode(makeNameCode(nameAtt.trim()));
065: } catch (NamespaceException err) {
066: compileError(err.getMessage(), "XTSE0280");
067: } catch (XPathException err) {
068: compileError(err);
069: }
070: } else if (f == StandardNames.AS) {
071: asAtt = atts.getValue(a);
072: } else if (f == StandardNames.OVERRIDE) {
073: overrideAtt = atts.getValue(a).trim();
074: if (overrideAtt.equals("yes")) {
075: override = true;
076: } else if (overrideAtt.equals("no")) {
077: override = false;
078: } else {
079: compileError("override must be 'yes' or 'no'",
080: "XTSE0020");
081: }
082: } else if (f == StandardNames.SAXON_MEMO_FUNCTION) {
083: String memoAtt = atts.getValue(a).trim();
084: if (memoAtt.equals("yes")) {
085: memoFunction = true;
086: } else if (memoAtt.equals("no")) {
087: memoFunction = false;
088: } else {
089: compileError(
090: "saxon:memo-function must be 'yes' or 'no'",
091: "XTSE0020");
092: }
093: } else {
094: checkUnknownAttribute(nc);
095: }
096: }
097:
098: if (nameAtt == null) {
099: reportAbsence("name");
100: }
101:
102: if (asAtt == null) {
103: resultType = SequenceType.ANY_SEQUENCE;
104: } else {
105: resultType = makeSequenceType(asAtt);
106: }
107:
108: functionName = nameAtt;
109: }
110:
111: /**
112: * Determine whether this type of element is allowed to contain a template-body.
113: * @return true: yes, it may contain a general template-body
114: */
115:
116: public boolean mayContainSequenceConstructor() {
117: return true;
118: }
119:
120: /**
121: * Specify that xsl:param is a permitted child
122: */
123:
124: protected boolean isPermittedChild(StyleElement child) {
125: return (child instanceof XSLParam);
126: }
127:
128: /**
129: * Is override="yes"?.
130: * @return true if override="yes" was specified, otherwise false
131: */
132:
133: public boolean isOverriding() {
134: return override;
135: }
136:
137: /**
138: * Notify all references to this function of the data type.
139: * @throws XPathException
140: */
141:
142: public void fixupReferences() throws XPathException {
143: Iterator iter = references.iterator();
144: while (iter.hasNext()) {
145: ((UserFunctionCall) iter.next()).setStaticType(resultType);
146: }
147: super .fixupReferences();
148: }
149:
150: public void validate() throws XPathException {
151:
152: stackFrameMap = getConfiguration().makeSlotManager();
153:
154: // check the element is at the top level of the stylesheet
155:
156: checkTopLevel(null);
157: getNumberOfArguments();
158:
159: // check that this function is not a duplicate of another
160:
161: XSLStylesheet root = getPrincipalStylesheet();
162: List toplevel = root.getTopLevel();
163: boolean isDuplicate = false;
164: for (int i = toplevel.size() - 1; i >= 0; i--) {
165: Object child = toplevel.get(i);
166: if (child instanceof XSLFunction
167: && !(child == this )
168: && ((XSLFunction) child).getFunctionFingerprint() == getFunctionFingerprint()
169: && ((XSLFunction) child).getNumberOfArguments() == numberOfArguments) {
170: if (((XSLFunction) child).getPrecedence() == getPrecedence()) {
171: isDuplicate = true;
172: }
173: if (((XSLFunction) child).getPrecedence() > getPrecedence()) {
174: // it's not an error to have duplicates if there is another with higher precedence
175: isDuplicate = false;
176: break;
177: }
178: }
179: }
180: if (isDuplicate) {
181: compileError("Duplicate function declaration", "XTSE0770");
182: }
183: }
184:
185: /**
186: * Compile the function definition to create an executable representation
187: * @return an Instruction, or null. The instruction returned is actually
188: * rather irrelevant; the compile() method has the side-effect of binding
189: * all references to the function to the executable representation
190: * (a UserFunction object)
191: * @throws XPathException
192: */
193:
194: public Expression compile(Executable exec) throws XPathException {
195: compileAsExpression(exec);
196: return null;
197: }
198:
199: /**
200: * Compile the function into a UserFunction object, which treats the function
201: * body as a single XPath expression. This involves recursively translating
202: * xsl:variable declarations into let expressions, withe the action part of the
203: * let expression containing the rest of the function body.
204: * The UserFunction that is created will be linked from all calls to
205: * this function, so nothing else needs to be done with the result. If there are
206: * no calls to it, the compiled function will be garbage-collected away.
207: * @throws XPathException
208: */
209:
210: private void compileAsExpression(Executable exec)
211: throws XPathException {
212: //Block body = new Block();
213: //compileChildren(exec, body, false);
214: Expression exp = compileSequenceConstructor(exec,
215: iterateAxis(Axis.CHILD), false);
216: if (exp == null) {
217: exp = EmptySequence.getInstance();
218: }
219:
220: //Expression exp = body;
221:
222: UserFunction fn = new UserFunction();
223: fn.setBody(exp);
224: fn.setFunctionNameCode(getObjectNameCode());
225: setParameterDefinitions(fn);
226: //fn.setParameterDefinitions(getParameterDefinitions());
227: //fn.setArgumentTypes(getArgumentTypes());
228: fn.setResultType(getResultType());
229: fn.setLineNumber(getLineNumber());
230: fn.setSystemId(getSystemId());
231: fn.setStackFrameMap(stackFrameMap);
232: fn.setMemoFunction(memoFunction);
233: fn.setExecutable(exec);
234:
235: Expression exp2 = exp;
236: try {
237: // We've already done the typecheck of each XPath expression, but it's worth doing again at this
238: // level because we have more information now.
239: exp2 = exp.typeCheck(staticContext, null);
240: exp2 = exp2.optimize(getConfiguration().getOptimizer(),
241: staticContext, null);
242: if (resultType != null) {
243: RoleLocator role = new RoleLocator(
244: RoleLocator.FUNCTION_RESULT, functionName, 0,
245: null);
246: role.setSourceLocator(new ExpressionLocation(this ));
247: exp2 = TypeChecker.staticTypeCheck(exp2, resultType,
248: false, role, getStaticContext());
249: }
250:
251: } catch (XPathException err) {
252: compileError(err);
253: }
254:
255: if (getConfiguration().getTraceListener() != null) {
256: TraceWrapper trace = new TraceInstruction(exp2, this );
257: trace.setLocationId(allocateLocationId(getSystemId(),
258: getLineNumber()));
259: exp2 = trace;
260: }
261:
262: allocateSlots(exp2);
263: if (exp2 != exp) {
264: fn.setBody(exp2);
265: }
266:
267: boolean tailCalls = ExpressionTool.markTailFunctionCalls(exp2);
268: fn.setTailRecursive(tailCalls);
269: fixupInstruction(fn, getStaticContext());
270: compiledFunction = fn;
271:
272: if (isExplaining()) {
273: System.err
274: .println("Optimized expression tree for function "
275: + functionName + " at line "
276: + getLineNumber() + " in " + getSystemId()
277: + ':');
278: exp2.display(10, getNamePool(), System.err);
279: }
280: }
281:
282: /**
283: * Fixup all function references.
284: * @param compiledFunction the Instruction representing this function in the compiled code
285: * @throws XPathException if an error occurs.
286: */
287:
288: private void fixupInstruction(UserFunction compiledFunction,
289: StaticContext env) throws XPathException {
290: try {
291: Iterator iter = references.iterator();
292: while (iter.hasNext()) {
293: UserFunctionCall call = ((UserFunctionCall) iter.next());
294: call.setFunction(compiledFunction, env);
295: call.checkFunctionCall(compiledFunction, env);
296: }
297: } catch (XPathException err) {
298: compileError(err);
299: }
300: }
301:
302: /**
303: * Get associated Procedure (for details of stack frame).
304: * @return the associated Procedure object
305: */
306:
307: public SlotManager getSlotManager() {
308: return stackFrameMap;
309: }
310:
311: /**
312: * Get the fingerprint of the name of this function.
313: * @return the fingerprint of the name
314: */
315:
316: public int getFunctionFingerprint() {
317: if (getObjectFingerprint() == -1) {
318: // this is a forwards reference to the function
319: try {
320: prepareAttributes();
321: } catch (XPathException err) {
322: return -1; // we'll report the error later
323: }
324: }
325: return getObjectFingerprint();
326: }
327:
328: /**
329: * Get the type of value returned by this function
330: * @return the declared result type, or the inferred result type
331: * if this is more precise
332: */
333: public SequenceType getResultType() {
334: return resultType;
335: }
336:
337: /**
338: * Get the number of arguments declared by this function (that is, its arity).
339: * @return the arity of the function
340: */
341:
342: public int getNumberOfArguments() {
343: if (numberOfArguments == -1) {
344: numberOfArguments = 0;
345: AxisIterator kids = iterateAxis(Axis.CHILD);
346: while (true) {
347: Item child = kids.next();
348: if (child instanceof XSLParam) {
349: numberOfArguments++;
350: } else {
351: return numberOfArguments;
352: }
353: }
354: }
355: return numberOfArguments;
356: }
357:
358: /**
359: * Set the definitions of the parameters in the compiled function, as an array.
360: */
361:
362: public void setParameterDefinitions(UserFunction fn) {
363: UserFunctionParameter[] params = new UserFunctionParameter[getNumberOfArguments()];
364: fn.setParameterDefinitions(params);
365: int count = 0;
366: AxisIterator kids = iterateAxis(Axis.CHILD);
367: while (true) {
368: NodeInfo node = (NodeInfo) kids.next();
369: if (node == null) {
370: return;
371: }
372: if (node instanceof XSLParam) {
373: UserFunctionParameter param = new UserFunctionParameter();
374: params[count++] = param;
375: param.setRequiredType(((XSLParam) node)
376: .getRequiredType());
377: param.setSlotNumber(((XSLParam) node).getSlotNumber());
378: ((XSLParam) node).fixupBinding(param);
379: List references = ((XSLParam) node).getReferences();
380: int refs = RangeVariableDeclaration.getReferenceCount(
381: references, param, getStaticContext(), true);
382: param.setReferenceCount(refs);
383: }
384: }
385: }
386:
387: /**
388: * Get the compiled function
389: */
390:
391: public UserFunction getCompiledFunction() {
392: return compiledFunction;
393: }
394:
395: /**
396: * Get the type of construct. This will be a constant in
397: * class {@link net.sf.saxon.trace.Location}. This method is part of the
398: * {@link net.sf.saxon.trace.InstructionInfo} interface
399: */
400:
401: public int getConstructType() {
402: return StandardNames.XSL_FUNCTION;
403: }
404:
405: }
406:
407: //
408: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
409: // you may not use this file except in compliance with the License. You may obtain a copy of the
410: // License at http://www.mozilla.org/MPL/
411: //
412: // Software distributed under the License is distributed on an "AS IS" basis,
413: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
414: // See the License for the specific language governing rights and limitations under the License.
415: //
416: // The Original Code is: all this file.
417: //
418: // The Initial Developer of the Original Code is Michael H. Kay.
419: //
420: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
421: //
422: // Contributor(s):
423: // Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
424: //
|