KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.style;
2 import net.sf.saxon.expr.*;
3 import net.sf.saxon.instruct.*;
4 import net.sf.saxon.om.AttributeCollection;
5 import net.sf.saxon.om.Axis;
6 import net.sf.saxon.om.NamespaceException;
7 import net.sf.saxon.pattern.NoNodeTest;
8 import net.sf.saxon.pattern.Pattern;
9 import net.sf.saxon.trans.Mode;
10 import net.sf.saxon.trans.RuleManager;
11 import net.sf.saxon.trans.XPathException;
12 import net.sf.saxon.type.ItemType;
13 import net.sf.saxon.type.Type;
14 import net.sf.saxon.value.EmptySequence;
15 import net.sf.saxon.value.SequenceType;
16
17 import javax.xml.transform.TransformerException JavaDoc;
18 import java.math.BigDecimal JavaDoc;
19 import java.util.StringTokenizer JavaDoc;
20
21 /**
22 * An xsl:template element in the style sheet.
23 */

24
25 public final class XSLTemplate extends StyleElement implements StylesheetProcedure {
26
27     private String JavaDoc matchAtt = null;
28     private String JavaDoc modeAtt = null;
29     private String JavaDoc nameAtt = null;
30     private String JavaDoc priorityAtt = null;
31     private String JavaDoc asAtt = null;
32
33     private int[] modeNameCodes;
34     private String JavaDoc diagnosticId;
35     private Pattern match;
36     private boolean prioritySpecified;
37     private double priority;
38     private SlotManager stackFrameMap;
39     private Template compiledTemplate = new Template();
40     private SequenceType requiredType = null;
41
42     /**
43     * Determine whether this type of element is allowed to contain a template-body
44     * @return true: yes, it may contain a template-body
45     */

46
47     public boolean mayContainSequenceConstructor() {
48         return true;
49     }
50
51     /**
52      * Specify that xsl:param is a permitted child
53      */

54
55     protected boolean isPermittedChild(StyleElement child) {
56         return (child instanceof XSLParam);
57     }
58
59     /**
60     * Return the fingerprint for the name of this template. Note that this may
61      * be called before prepareAttributes has been called.
62     */

63
64     public int getTemplateFingerprint() {
65
66         //We use -1 to mean "not yet evaluated"
67

68         try {
69             if (getObjectFingerprint()==-1) {
70                 // allow for forwards references
71
String JavaDoc nameAtt = getAttributeValue(StandardNames.NAME);
72                 if (nameAtt!=null) {
73                     setObjectNameCode(makeNameCode(nameAtt.trim()));
74                 }
75             }
76             return getObjectFingerprint();
77         } catch (NamespaceException err) {
78             return -1; // the errors will be picked up later
79
} catch (XPathException err) {
80             return -1;
81         }
82     }
83
84     /**
85      * Determine the type of item returned by this template
86      * @return the item type returned
87      */

88
89     protected ItemType getReturnedItemType() {
90         if (requiredType==null) {
91             return getCommonChildItemType();
92         } else {
93             return requiredType.getPrimaryType();
94         }
95     }
96
97     private int getMinImportPrecedence() {
98         return getContainingStylesheet().getMinImportPrecedence();
99     }
100
101     public void prepareAttributes() throws XPathException {
102
103         AttributeCollection atts = getAttributeList();
104
105         for (int a=0; a<atts.getLength(); a++) {
106             int nc = atts.getNameCode(a);
107             String JavaDoc f = getNamePool().getClarkName(nc);
108             if (f==StandardNames.MODE) {
109                 modeAtt = atts.getValue(a).trim();
110             } else if (f==StandardNames.NAME) {
111                 nameAtt = atts.getValue(a).trim();
112             } else if (f==StandardNames.MATCH) {
113                 matchAtt = atts.getValue(a);
114             } else if (f==StandardNames.PRIORITY) {
115                 priorityAtt = atts.getValue(a).trim();
116             } else if (f==StandardNames.AS) {
117                 asAtt = atts.getValue(a);
118             } else {
119                 checkUnknownAttribute(nc);
120             }
121         }
122         try {
123             if (modeAtt==null) {
124                 modeNameCodes = new int[1];
125                 modeNameCodes[0] = -1;
126             } else {
127                 if (matchAtt==null) {
128                     compileError("The mode attribute must be absent if the match attribute is absent", "XTSE0500");
129                 }
130                 // mode is a space-separated list of mode names, or "#default", or "#all"
131

132                 int count = 0;
133                 boolean allModes = false;
134                 StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(modeAtt);
135                 while (st.hasMoreTokens()) {
136                     st.nextToken();
137                     count++;
138                 }
139
140                 if (count==0) {
141                     compileError("The mode attribute must not be empty", "XTSE0550");
142                 }
143
144                 modeNameCodes = new int[count];
145                 count = 0;
146                 st = new StringTokenizer JavaDoc(modeAtt);
147                 while (st.hasMoreTokens()) {
148                     String JavaDoc s = st.nextToken();
149                     int code;
150                     if ("#default".equals(s)) {
151                         code = Mode.DEFAULT_MODE;
152                     } else if ("#all".equals(s)) {
153                         allModes = true;
154                         code = Mode.ALL_MODES;
155                     } else {
156                         code = makeNameCode(s);
157                     }
158                     for (int e=0; e < count; e++) {
159                         if (modeNameCodes[e] == code) {
160                             compileError("In the list of modes, the value " + s + " is duplicated", "XTSE0550");
161                         }
162                     }
163                     modeNameCodes[count++] = code;
164                 }
165                 if (allModes && (count>1)) {
166                     compileError("mode='#all' cannot be combined with other modes", "XTSE0550");
167                 }
168             }
169
170             if (nameAtt!=null) {
171                 setObjectNameCode(makeNameCode(nameAtt.trim()));
172                 diagnosticId = nameAtt;
173             }
174         } catch (NamespaceException err) {
175             compileError(err.getMessage(), "XTSE0280");
176         } catch (XPathException err) {
177             compileError(err.getMessage(), "XTSE0280");
178         }
179
180         prioritySpecified = (priorityAtt != null);
181         if (prioritySpecified) {
182             if (matchAtt==null) {
183                 compileError("The priority attribute must be absent if the match attribute is absent", "XTSE0500");
184             }
185             try {
186                 // it's got to be a valid decimal, but we want it as a double, so parse it twice
187
new BigDecimal JavaDoc(priorityAtt.trim());
188                 priority = Double.parseDouble(priorityAtt.trim());
189             } catch (NumberFormatException JavaDoc err) {
190                 compileError("Invalid numeric value for priority (" + priority + ')', "XTSE0530");
191             }
192         }
193
194         if (matchAtt != null) {
195             match = makePattern(matchAtt);
196             if (diagnosticId == null) {
197                 diagnosticId = "match=\"" + matchAtt + '\"';
198             }
199         }
200
201         if (match==null && nameAtt==null)
202             compileError("xsl:template must have a name or match attribute (or both)", "XTSE0010");
203
204         if (asAtt != null) {
205             requiredType = makeSequenceType(asAtt);
206         }
207
208     }
209
210     public void validate() throws XPathException {
211         stackFrameMap = getConfiguration().makeSlotManager();
212         checkTopLevel(null);
213
214         // the check for duplicates is now done in the buildIndexes() method of XSLStylesheet
215
if (match != null) {
216             typeCheck("match", match);
217             if (match.getNodeTest() instanceof NoNodeTest) {
218                 try {
219                     getConfiguration().getErrorListener().warning(
220                             new TransformerException JavaDoc("Match pattern cannot match any nodes", this));
221                 } catch (TransformerException JavaDoc e) {
222                     compileError(e);
223                 }
224             }
225         }
226         markTailCalls();
227     }
228
229     /**
230     * Mark tail-recursive calls on templates and functions.
231     */

232
233     public void markTailCalls() {
234         if (requiredType == null) {
235             // don't attempt tail call optimization if the return type needs checking
236
StyleElement last = getLastChildInstruction();
237             if (last != null) {
238                 last.markTailCalls();
239             }
240         }
241     }
242
243     /**
244     * Compile: this registers the template with the rule manager, and ensures
245     * space is available for local variables
246     */

247
248     public Expression compile(Executable exec) throws XPathException {
249
250         Expression block = compileSequenceConstructor(exec, iterateAxis(Axis.CHILD), true);
251         if (block == null) {
252             block = EmptySequence.getInstance();
253         }
254         compiledTemplate.setBody(block);
255         compiledTemplate.setStackFrameMap(stackFrameMap);
256         compiledTemplate.setExecutable(getExecutable());
257         compiledTemplate.setSystemId(getSystemId());
258         compiledTemplate.setLineNumber(getLineNumber());
259
260         Expression exp = null;
261         try {
262             exp = block.simplify(getStaticContext());
263         } catch (XPathException e) {
264             compileError(e);
265         }
266
267         try {
268             if (requiredType != null) {
269                 RoleLocator role =
270                         new RoleLocator(RoleLocator.TEMPLATE_RESULT, diagnosticId, 0, null);
271                 role.setSourceLocator(new ExpressionLocation(this));
272                 exp = TypeChecker.staticTypeCheck(exp, requiredType, false, role, getStaticContext());
273             }
274         } catch (XPathException err) {
275             compileError(err);
276         }
277
278         compiledTemplate.setBody(exp);
279         compiledTemplate.init ( getObjectFingerprint(),
280                                 getPrecedence(),
281                                 getMinImportPrecedence());
282
283         if (getConfiguration().getTraceListener() != null) {
284             TraceWrapper trace = new TraceInstruction(exp, this);
285             trace.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
286             trace.setParentExpression(compiledTemplate);
287             exp = trace;
288             compiledTemplate.setBody(exp);
289         }
290
291
292
293
294         ItemType contextItemType = Type.ITEM_TYPE;
295         if (getObjectFingerprint() == -1) {
296             // the template can't be called by name, so the context item must match the match pattern
297
contextItemType = match.getNodeTest();
298         }
299
300
301         try {
302             // We've already done the typecheck of each XPath expression, but it's worth doing again at this
303
// level because we have more information now.
304
Expression exp2 = exp.typeCheck(staticContext, contextItemType);
305             exp2 = exp2.optimize(getConfiguration().getOptimizer(), staticContext, contextItemType);
306             if (exp != exp2) {
307                 compiledTemplate.setBody(exp2);
308                 exp = exp2;
309             }
310         } catch (XPathException e) {
311             compileError(e);
312         }
313         super.allocateSlots(exp);
314         if (match!=null) {
315             RuleManager mgr = getPrincipalStylesheet().getRuleManager();
316             for (int i=0; i<modeNameCodes.length; i++) {
317                 int nc = modeNameCodes[i];
318                 Mode mode = mgr.getMode(nc);
319                 if (prioritySpecified) {
320                     mgr.setHandler(match, compiledTemplate, mode, getPrecedence(), priority);
321                 } else {
322                     mgr.setHandler(match, compiledTemplate, mode, getPrecedence());
323                 }
324             }
325         }
326
327         if (isExplaining()) {
328             System.err.println("Optimized expression tree for template at line " +
329                     getLineNumber() + " in " + getSystemId() + ":");
330             exp.display(10, getNamePool(), System.err);
331         }
332
333         return null;
334     }
335
336
337     /**
338     * Get associated Procedure (for details of stack frame)
339     */

340
341     public SlotManager getSlotManager() {
342         return stackFrameMap;
343     }
344
345     /**
346      * Allocate space for range variables within predicates in the match pattern. The xsl:template
347      * element has no XPath expressions among its attributes, so if this method is called on this
348      * object it can only be because there are variables used in the match pattern. We work out
349      * how many slots are needed for the match pattern in each template rule, and then apply-templates
350      * can allocate a stack frame that is large enough for the most demanding match pattern in the
351      * entire stylesheet.
352      * @param exp The expression containing range variables. This will be a predicate within a match pattern,
353      * or possibly an argument to id() or key() used in a match pattern.
354      */

355
356     public void allocateSlots(Expression exp) {
357         int highWater = ExpressionTool.allocateSlots(exp, 0, null);
358         getContainingStylesheet().allocatePatternSlots(highWater);
359     }
360     /**
361     * Get the compiled template
362     */

363
364     public Template getCompiledTemplate() {
365         return compiledTemplate;
366     }
367
368     /**
369      * Get the type of construct. This will be a constant in
370      * class {@link net.sf.saxon.trace.Location}. This method is part of the {@link net.sf.saxon.trace.InstructionInfo} interface
371      */

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