KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > style > XSLFunction


1 package net.sf.saxon.style;
2 import net.sf.saxon.expr.*;
3 import net.sf.saxon.instruct.*;
4 import net.sf.saxon.om.*;
5 import net.sf.saxon.trans.XPathException;
6 import net.sf.saxon.value.EmptySequence;
7 import net.sf.saxon.value.SequenceType;
8
9 import java.util.ArrayList JavaDoc;
10 import java.util.Iterator JavaDoc;
11 import java.util.List JavaDoc;
12
13 /**
14 * Handler for xsl:function elements in stylesheet (XSLT 2.0). <BR>
15 * Attributes: <br>
16 * name gives the name of the function
17 * saxon:memo-function=yes|no indicates whether it acts as a memo function.
18 */

19
20 public class XSLFunction extends StyleElement implements StylesheetProcedure {
21
22     private String JavaDoc nameAtt = null;
23     private String JavaDoc asAtt = null;
24     private String JavaDoc overrideAtt = null;
25
26     private SequenceType resultType;
27     private String JavaDoc functionName;
28     private SlotManager stackFrameMap;
29     private boolean memoFunction = false;
30     private boolean override = true;
31     private int numberOfArguments = -1; // -1 means not yet known
32
private UserFunction compiledFunction;
33
34     // List of UserFunctionCall objects that reference this XSLFunction
35
List JavaDoc references = new ArrayList JavaDoc(10);
36
37     /**
38     * Method called by UserFunctionCall to register the function call for
39     * subsequent fixup.
40      * @param ref the UserFunctionCall to be registered
41     */

42
43     public void registerReference(UserFunctionCall ref) {
44         references.add(ref);
45     }
46
47     public void prepareAttributes() throws XPathException {
48
49         AttributeCollection atts = getAttributeList();
50
51         for (int a=0; a<atts.getLength(); a++) {
52             int nc = atts.getNameCode(a);
53             String JavaDoc f = getNamePool().getClarkName(nc);
54             if (f==StandardNames.NAME) {
55                 nameAtt = atts.getValue(a).trim();
56                 if (nameAtt.indexOf(':')<0) {
57                     compileError("Function name must have a namespace prefix", "XTSE0740");
58                 }
59                 try {
60                     setObjectNameCode(makeNameCode(nameAtt.trim()));
61                 } catch (NamespaceException err) {
62                     compileError(err.getMessage(), "XTSE0280");
63                 } catch (XPathException err) {
64                     compileError(err);
65                 }
66             } else if (f==StandardNames.AS) {
67                 asAtt = atts.getValue(a);
68             } else if (f==StandardNames.OVERRIDE) {
69                 overrideAtt = atts.getValue(a).trim();
70                 if (overrideAtt.equals("yes")) {
71                     override = true;
72                 } else if (overrideAtt.equals("no")) {
73                     override = false;
74                 } else {
75                     compileError("override must be 'yes' or 'no'", "XTSE0020");
76                 }
77             } else if (f==StandardNames.SAXON_MEMO_FUNCTION) {
78                 String JavaDoc memoAtt = atts.getValue(a).trim();
79                 if (memoAtt.equals("yes")) {
80                     memoFunction = true;
81                 } else if (memoAtt.equals("no")) {
82                     memoFunction = false;
83                 } else {
84                     compileError("saxon:memo-function must be 'yes' or 'no'", "XTSE0020");
85                 }
86             } else {
87                 checkUnknownAttribute(nc);
88             }
89         }
90
91         if (nameAtt == null) {
92             reportAbsence("name");
93         }
94
95         if (asAtt == null) {
96             resultType = SequenceType.ANY_SEQUENCE;
97         } else {
98             resultType = makeSequenceType(asAtt);
99         }
100
101         functionName = nameAtt;
102     }
103
104     /**
105     * Determine whether this type of element is allowed to contain a template-body.
106     * @return true: yes, it may contain a general template-body
107     */

108
109     public boolean mayContainSequenceConstructor() {
110         return true;
111     }
112
113     /**
114      * Specify that xsl:param is a permitted child
115      */

116
117     protected boolean isPermittedChild(StyleElement child) {
118         return (child instanceof XSLParam);
119     }
120     /**
121     * Is override="yes"?.
122     * @return true if override="yes" was specified, otherwise false
123     */

124
125     public boolean isOverriding() {
126         return override;
127     }
128
129     /**
130     * Notify all references to this function of the data type.
131      * @throws XPathException
132     */

133
134     public void fixupReferences() throws XPathException {
135         Iterator JavaDoc iter = references.iterator();
136         while (iter.hasNext()) {
137             ((UserFunctionCall)iter.next()).setStaticType(resultType);
138         }
139         super.fixupReferences();
140     }
141
142     public void validate() throws XPathException {
143
144         stackFrameMap = getConfiguration().makeSlotManager();
145
146         // check the element is at the top level of the stylesheet
147

148         checkTopLevel(null);
149         getNumberOfArguments();
150
151         // check that this function is not a duplicate of another
152

153         XSLStylesheet root = getPrincipalStylesheet();
154         List JavaDoc toplevel = root.getTopLevel();
155         boolean isDuplicate = false;
156         for (int i=toplevel.size()-1; i>=0; i--) {
157             Object JavaDoc child = toplevel.get(i);
158             if (child instanceof XSLFunction &&
159                     !(child == this) &&
160                     ((XSLFunction)child).getFunctionFingerprint() == getFunctionFingerprint() &&
161                     ((XSLFunction)child).getNumberOfArguments() == numberOfArguments) {
162                 if (((XSLFunction)child).getPrecedence() == getPrecedence()) {
163                     isDuplicate = true;
164                 }
165                 if (((XSLFunction)child).getPrecedence() > getPrecedence()) {
166                     // it's not an error to have duplicates if there is another with higher precedence
167
isDuplicate = false;
168                     break;
169                 }
170             }
171         }
172         if (isDuplicate) {
173             compileError("Duplicate function declaration", "XTSE0770");
174         }
175     }
176
177
178     /**
179      * Compile the function definition to create an executable representation
180      * @return an Instruction, or null. The instruction returned is actually
181      * rather irrelevant; the compile() method has the side-effect of binding
182      * all references to the function to the executable representation
183      * (a UserFunction object)
184      * @throws XPathException
185      */

186
187     public Expression compile(Executable exec) throws XPathException {
188         compileAsExpression(exec);
189         return null;
190     }
191
192     /**
193      * Compile the function into a UserFunction object, which treats the function
194      * body as a single XPath expression. This involves recursively translating
195      * xsl:variable declarations into let expressions, withe the action part of the
196      * let expression containing the rest of the function body.
197      * The UserFunction that is created will be linked from all calls to
198      * this function, so nothing else needs to be done with the result. If there are
199      * no calls to it, the compiled function will be garbage-collected away.
200      * @throws XPathException
201      */

202
203     private void compileAsExpression(Executable exec) throws XPathException {
204         //Block body = new Block();
205
//compileChildren(exec, body, false);
206
Expression exp = compileSequenceConstructor(exec, iterateAxis(Axis.CHILD), false);
207         if (exp == null) {
208             exp = EmptySequence.getInstance();
209         }
210
211         //Expression exp = body;
212

213         UserFunction fn = new UserFunction();
214         fn.setBody(exp);
215         fn.setFunctionNameCode(getObjectNameCode());
216         setParameterDefinitions(fn);
217         //fn.setParameterDefinitions(getParameterDefinitions());
218
//fn.setArgumentTypes(getArgumentTypes());
219
fn.setResultType(getResultType());
220         fn.setLineNumber(getLineNumber());
221         fn.setSystemId(getSystemId());
222         fn.setStackFrameMap(stackFrameMap);
223         fn.setMemoFunction(memoFunction);
224         fn.setExecutable(exec);
225
226         Expression exp2 = exp;
227         try {
228             // We've already done the typecheck of each XPath expression, but it's worth doing again at this
229
// level because we have more information now.
230
exp2 = exp.typeCheck(staticContext, null);
231             exp2 = exp2.optimize(getConfiguration().getOptimizer(), staticContext, null);
232             if (resultType != null) {
233                 RoleLocator role =
234                         new RoleLocator(RoleLocator.FUNCTION_RESULT, functionName, 0, null);
235                 role.setSourceLocator(new ExpressionLocation(this));
236                 exp2 = TypeChecker.staticTypeCheck(exp2, resultType, false, role, getStaticContext());
237             }
238
239         } catch (XPathException err) {
240             compileError(err);
241         }
242
243         if (getConfiguration().getTraceListener() != null) {
244             TraceWrapper trace = new TraceInstruction(exp2, this);
245             trace.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
246             exp2 = trace;
247         }
248
249         allocateSlots(exp2);
250         if (exp2 != exp) {
251             fn.setBody(exp2);
252         }
253
254         boolean tailCalls = ExpressionTool.markTailFunctionCalls(exp2);
255         fn.setTailRecursive(tailCalls);
256         fixupInstruction(fn, getStaticContext());
257         compiledFunction = fn;
258
259         if (isExplaining()) {
260             System.err.println("Optimized expression tree for function "
261                     + functionName + " at line " +
262                     getLineNumber() + " in " + getSystemId() + ":");
263             exp2.display(10, getNamePool(), System.err);
264         }
265     }
266
267     /**
268     * Fixup all function references.
269      * @param compiledFunction the Instruction representing this function in the compiled code
270      * @throws XPathException if an error occurs.
271     */

272
273     private void fixupInstruction(UserFunction compiledFunction, StaticContext env)
274     throws XPathException {
275         try {
276             Iterator JavaDoc iter = references.iterator();
277             while (iter.hasNext()) {
278                 UserFunctionCall call = ((UserFunctionCall)iter.next());
279                 call.setFunction(compiledFunction, env);
280                 call.checkFunctionCall(compiledFunction, env);
281             }
282         } catch (XPathException err) {
283             compileError(err);
284         }
285     }
286
287     /**
288      * Get associated Procedure (for details of stack frame).
289      * @return the associated Procedure object
290      */

291
292     public SlotManager getSlotManager() {
293         return stackFrameMap;
294     }
295
296     /**
297      * Get the fingerprint of the name of this function.
298      * @return the fingerprint of the name
299      */

300
301     public int getFunctionFingerprint() {
302         if (getObjectFingerprint()==-1) {
303             // this is a forwards reference to the function
304
try {
305                 prepareAttributes();
306             } catch (XPathException err) {
307                 return -1; // we'll report the error later
308
}
309         }
310         return getObjectFingerprint();
311     }
312
313     /**
314      * Get the type of value returned by this function
315      * @return the declared result type, or the inferred result type
316      * if this is more precise
317      */

318     public SequenceType getResultType() {
319         return resultType;
320     }
321
322     /**
323      * Get the number of arguments declared by this function (that is, its arity).
324      * @return the arity of the function
325      */

326
327     public int getNumberOfArguments() {
328         if (numberOfArguments == -1) {
329             numberOfArguments = 0;
330             AxisIterator kids = iterateAxis(Axis.CHILD);
331             while (true) {
332                 Item child = kids.next();
333                 if (child instanceof XSLParam) {
334                     numberOfArguments++;
335                 } else {
336                     return numberOfArguments;
337                 }
338             }
339         }
340         return numberOfArguments;
341     }
342
343     /**
344      * Set the definitions of the parameters in the compiled function, as an array.
345      */

346
347     public void setParameterDefinitions(UserFunction fn) {
348         UserFunctionParameter[] params = new UserFunctionParameter[getNumberOfArguments()];
349         fn.setParameterDefinitions(params);
350         int count = 0;
351         AxisIterator kids = iterateAxis(Axis.CHILD);
352         while (true) {
353             NodeInfo node = (NodeInfo)kids.next();
354             if (node == null) {
355                 return;
356             }
357             if (node instanceof XSLParam) {
358                 UserFunctionParameter param = new UserFunctionParameter();
359                 params[count++] = param;
360                 param.setRequiredType(((XSLParam)node).getRequiredType());
361                 param.setSlotNumber(((XSLParam)node).getSlotNumber());
362                 ((XSLParam)node).fixupBinding(param);
363                 List JavaDoc references = ((XSLParam)node).getReferences();
364                 int refs = RangeVariableDeclaration.getReferenceCount(references, param);
365                 param.setReferenceCount(refs);
366             }
367         }
368     }
369
370     /**
371      * Get the compiled function
372      */

373
374     public UserFunction getCompiledFunction() {
375         return compiledFunction;
376     }
377
378     /**
379      * Get the type of construct. This will be a constant in
380      * class {@link net.sf.saxon.trace.Location}. This method is part of the
381      * {@link net.sf.saxon.trace.InstructionInfo} interface
382      */

383
384     public int getConstructType() {
385         return StandardNames.XSL_FUNCTION;
386     }
387
388 }
389
390 //
391
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
392
// you may not use this file except in compliance with the License. You may obtain a copy of the
393
// License at http://www.mozilla.org/MPL/
394
//
395
// Software distributed under the License is distributed on an "AS IS" basis,
396
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
397
// See the License for the specific language governing rights and limitations under the License.
398
//
399
// The Original Code is: all this file.
400
//
401
// The Initial Developer of the Original Code is Michael H. Kay.
402
//
403
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
404
//
405
// Contributor(s):
406
// Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
407
//
408
Popular Tags