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.pattern.NodeKindTest;
007: import net.sf.saxon.trans.XPathException;
008: import net.sf.saxon.type.ItemType;
009: import net.sf.saxon.type.Type;
010: import net.sf.saxon.value.Cardinality;
011: import net.sf.saxon.value.EmptySequence;
012: import net.sf.saxon.value.SequenceType;
013: import net.sf.saxon.value.StringValue;
014:
015: /**
016: * This class defines common behaviour across xsl:variable, xsl:param, and xsl:with-param
017: */
018:
019: public abstract class XSLGeneralVariable extends StyleElement {
020:
021: protected Expression select = null;
022: protected SequenceType requiredType = null;
023: protected String constantText = null;
024: protected boolean global;
025: protected SlotManager slotManager = null; // used only for global variable declarations
026: protected boolean assignable = false;
027: protected boolean redundant = false;
028: protected boolean requiredParam = false;
029: protected boolean tunnel = false;
030: private boolean textonly;
031:
032: /**
033: * Determine the type of item returned by this instruction (only relevant if
034: * it is an instruction).
035: * @return the item type returned. This is null for a variable: we are not
036: * interested in the type of the variable, but in what the xsl:variable constributes
037: * to the result of the sequence constructor it is part of.
038: */
039:
040: protected ItemType getReturnedItemType() {
041: return null;
042: }
043:
044: /**
045: * Determine whether this type of element is allowed to contain a template-body
046: * @return true: yes, it may contain a template-body
047: */
048:
049: public boolean mayContainSequenceConstructor() {
050: return true;
051: }
052:
053: protected boolean allowsAsAttribute() {
054: return true;
055: }
056:
057: protected boolean allowsTunnelAttribute() {
058: return false;
059: }
060:
061: protected boolean allowsValue() {
062: return true;
063: }
064:
065: protected boolean allowsRequired() {
066: return false;
067: }
068:
069: /**
070: * Test whether it is permitted to assign to the variable using the saxon:assign
071: * extension element. This will only be true if the extra attribute saxon:assignable="yes"
072: * is present.
073: */
074:
075: public boolean isAssignable() {
076: return assignable;
077: }
078:
079: public boolean isTunnelParam() {
080: return tunnel;
081: }
082:
083: public boolean isRequiredParam() {
084: return requiredParam;
085: }
086:
087: public boolean isGlobal() {
088: return isTopLevel();
089: // might be called before the "global" field is initialized
090: }
091:
092: /**
093: * Get the display name of the variable.
094: */
095:
096: public String getVariableName() {
097: return getAttributeValue(StandardNames.NAME);
098: }
099:
100: /**
101: * Mark this global variable as redundant. This is done before prepareAttributes is called.
102: */
103:
104: public void setRedundant() {
105: redundant = true;
106: }
107:
108: /**
109: * Get the fingerprint of the variable name
110: */
111:
112: public int getVariableFingerprint() {
113:
114: // if an expression has a forwards reference to this variable, getVariableFingerprint() can be
115: // called before prepareAttributes() is called. We need to allow for this. But we'll
116: // deal with any errors when we come round to processing this attribute, to avoid
117: // duplicate error messages
118:
119: if (getObjectNameCode() == -1) {
120: String nameAttribute = getAttributeValue(StandardNames.NAME);
121: if (nameAttribute == null) {
122: return -1; // we'll report the error later
123: }
124: try {
125: setObjectNameCode(makeNameCode(nameAttribute.trim()));
126: } catch (NamespaceException err) {
127: setObjectNameCode(-1);
128: } catch (XPathException err) {
129: setObjectNameCode(-1);
130: }
131: }
132: return getObjectFingerprint();
133: }
134:
135: public void prepareAttributes() throws XPathException {
136:
137: getVariableFingerprint();
138:
139: AttributeCollection atts = getAttributeList();
140:
141: String selectAtt = null;
142: String assignAtt = null;
143: String nameAtt = null;
144: String asAtt = null;
145: String requiredAtt = null;
146: String tunnelAtt = null;
147:
148: for (int a = 0; a < atts.getLength(); a++) {
149: int nc = atts.getNameCode(a);
150: String f = getNamePool().getClarkName(nc);
151: if (f == StandardNames.NAME) {
152: nameAtt = atts.getValue(a).trim();
153: } else if (f == StandardNames.SELECT) {
154: selectAtt = atts.getValue(a);
155: } else if (f == StandardNames.AS && allowsAsAttribute()) {
156: asAtt = atts.getValue(a);
157: } else if (f == StandardNames.REQUIRED && allowsRequired()) {
158: requiredAtt = atts.getValue(a).trim();
159: } else if (f == StandardNames.TUNNEL
160: && allowsTunnelAttribute()) {
161: tunnelAtt = atts.getValue(a).trim();
162: } else if (f == StandardNames.SAXON_ASSIGNABLE
163: && this instanceof XSLVariableDeclaration) {
164: assignAtt = atts.getValue(a).trim();
165: } else {
166: checkUnknownAttribute(nc);
167: }
168: }
169:
170: if (nameAtt == null) {
171: reportAbsence("name");
172: } else {
173: try {
174: setObjectNameCode(makeNameCode(nameAtt.trim()));
175: } catch (NamespaceException err) {
176: compileError(err.getMessage());
177: } catch (XPathException err) {
178: compileError(err);
179: }
180: }
181:
182: if (selectAtt != null) {
183: if (!allowsValue()) {
184: compileError(
185: "Function parameters cannot have a default value",
186: "XTSE0760");
187: }
188: select = makeExpression(selectAtt);
189: }
190:
191: if (assignAtt != null && assignAtt.equals("yes")) {
192: assignable = true;
193: }
194:
195: if (requiredAtt != null) {
196: if (requiredAtt.equals("yes")) {
197: requiredParam = true;
198: } else if (requiredAtt.equals("no")) {
199: requiredParam = false;
200: } else {
201: compileError(
202: "The attribute 'required' must be set to 'yes' or 'no'",
203: "XTSE0020");
204: }
205: }
206:
207: if (tunnelAtt != null) {
208: if (tunnelAtt.equals("yes")) {
209: tunnel = true;
210: } else if (tunnelAtt.equals("no")) {
211: tunnel = false;
212: } else {
213: compileError(
214: "The attribute 'tunnel' must be set to 'yes' or 'no'",
215: "XTSE0020");
216: }
217: }
218:
219: if (asAtt != null) {
220: requiredType = makeSequenceType(asAtt);
221: }
222: }
223:
224: public void validate() throws XPathException {
225: global = isTopLevel();
226:
227: if (global) {
228: slotManager = getConfiguration().makeSlotManager();
229: }
230: if (select != null && hasChildNodes()) {
231: compileError("An " + getDisplayName()
232: + " element with a select attribute must be empty",
233: "XTSE0620");
234: }
235:
236: if (assignable && !global) {
237: compileError("saxon:assignable='yes' is no longer permitted for local variables");
238: }
239:
240: checkAgainstRequiredType(requiredType);
241:
242: if (select == null && allowsValue()) {
243: textonly = true;
244: AxisIterator kids = iterateAxis(Axis.CHILD);
245: NodeInfo first = (NodeInfo) kids.next();
246: if (first == null) {
247: if (requiredType == null) {
248: select = StringValue.EMPTY_STRING;
249: } else {
250: if (this instanceof XSLParam) {
251: if (!requiredParam) {
252: if (Cardinality.allowsZero(requiredType
253: .getCardinality())) {
254: select = EmptySequence.getInstance();
255: } else {
256: // The implicit default value () is not valid for the required type, so
257: // it is treated as if there is no default
258: requiredParam = true;
259: }
260: }
261: } else {
262: if (Cardinality.allowsZero(requiredType
263: .getCardinality())) {
264: select = EmptySequence.getInstance();
265: } else {
266: compileError(
267: "The implicit value () is not valid for the declared type",
268: "XTTE0570");
269: }
270: }
271: }
272: } else {
273: if (kids.next() == null) {
274: // there is exactly one child node
275: if (first.getNodeKind() == Type.TEXT) {
276: // it is a text node: optimize for this case
277: constantText = first.getStringValue();
278: }
279: }
280:
281: // Determine if the temporary tree can only contain text nodes
282: textonly = (getCommonChildItemType() == NodeKindTest.TEXT);
283: }
284: }
285: select = typeCheck("select", select);
286: }
287:
288: /**
289: * Check the supplied select expression against the required type.
290: * @param required The type required by the variable declaration, or in the case
291: * of xsl:with-param, the signature of the called template
292: */
293:
294: protected void checkAgainstRequiredType(SequenceType required)
295: throws XPathException {
296: try {
297:
298: if (required != null) {
299: // check that the expression is consistent with the required type
300: if (select != null) {
301: RoleLocator role = new RoleLocator(
302: RoleLocator.VARIABLE, getVariableName(), 0,
303: null);
304: role.setSourceLocator(new ExpressionLocation(this ));
305: role.setErrorCode("XTTE0570");
306: select = TypeChecker.staticTypeCheck(select,
307: required, false, role, getStaticContext());
308: } else {
309: // do the check later
310: }
311: }
312: } catch (XPathException err) {
313: err.setLocator(this ); // because the expression wasn't yet linked into the module
314: compileError(err);
315: select = new ErrorExpression(err);
316: }
317: }
318:
319: /**
320: * Initialize - common code called from the compile() method of all subclasses
321: */
322:
323: protected void initializeInstruction(Executable exec,
324: GeneralVariable var) throws XPathException {
325:
326: var.init(select, getObjectNameCode());
327: var.setAssignable(assignable);
328: var.setRequiredParam(requiredParam);
329: var.setRequiredType(requiredType);
330: var.setTunnel(tunnel);
331:
332: // handle the "temporary tree" case by creating a Document sub-instruction
333: // to construct and return a document node.
334: if (hasChildNodes()) {
335: if (requiredType == null) {
336: DocumentInstr doc = new DocumentInstr(textonly,
337: constantText, getBaseURI());
338: doc.setParentExpression(var);
339: Expression b = compileSequenceConstructor(exec,
340: iterateAxis(Axis.CHILD), true);
341: if (b == null) {
342: b = EmptySequence.getInstance();
343: }
344: doc.setContentExpression(b);
345: select = doc;
346: var.setSelectExpression(doc);
347: } else {
348: select = compileSequenceConstructor(exec,
349: iterateAxis(Axis.CHILD), true);
350: var.adoptChildExpression(select);
351: if (select == null) {
352: select = EmptySequence.getInstance();
353: }
354: try {
355: if (requiredType != null) {
356: var.setParentExpression(this ); //temporarily
357: RoleLocator role = new RoleLocator(
358: RoleLocator.VARIABLE,
359: getVariableName(), 0, null);
360: role.setErrorCode("XTTE0570");
361: role.setSourceLocator(new ExpressionLocation(
362: this ));
363: select = select.simplify(getStaticContext());
364: select = TypeChecker.staticTypeCheck(select,
365: requiredType, false, role,
366: getStaticContext());
367: }
368: } catch (XPathException err) {
369: err.setLocator(this );
370: compileError(err);
371: select = new ErrorExpression(err);
372: }
373: var.setSelectExpression(select);
374: }
375: }
376: if (global) {
377: final GlobalVariable gvar = (GlobalVariable) var;
378: Expression exp2 = select;
379: if (exp2 != null) {
380: try {
381: exp2 = select.simplify(staticContext).typeCheck(
382: staticContext, Type.NODE_TYPE);
383: exp2 = exp2.optimize(getConfiguration()
384: .getOptimizer(), staticContext,
385: Type.NODE_TYPE);
386: } catch (XPathException err) {
387: compileError(err);
388: }
389:
390: if (getConfiguration().getTraceListener() != null) {
391: TraceWrapper trace = new TraceInstruction(exp2,
392: this );
393: trace.setLocationId(allocateLocationId(
394: getSystemId(), getLineNumber()));
395: exp2 = trace;
396: }
397:
398: allocateSlots(exp2);
399: }
400: if (slotManager != null
401: && slotManager.getNumberOfVariables() > 0) {
402: gvar.setContainsLocals(slotManager);
403: }
404: exec.registerGlobalVariable(gvar);
405: setReferenceCount(gvar);
406:
407: if (exp2 != select) {
408: gvar.setSelectExpression(exp2);
409: }
410: }
411: }
412:
413: protected void setReferenceCount(GeneralVariable var) {
414: // overridden in subclass
415: }
416:
417: /**
418: * Get the type of construct. This will be a constant in
419: * class {@link net.sf.saxon.trace.Location}. This method is part of the
420: * {@link net.sf.saxon.trace.InstructionInfo} interface
421: */
422:
423: public int getConstructType() {
424: return StandardNames.XSL_VARIABLE;
425: }
426:
427: }
428:
429: //
430: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
431: // you may not use this file except in compliance with the License. You may obtain a copy of the
432: // License at http://www.mozilla.org/MPL/
433: //
434: // Software distributed under the License is distributed on an "AS IS" basis,
435: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
436: // See the License for the specific language governing rights and limitations under the License.
437: //
438: // The Original Code is: all this file.
439: //
440: // The Initial Developer of the Original Code is Michael H. Kay.
441: //
442: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
443: //
444: // Contributor(s): none.
445: //
|