001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: ElemVariable.java,v 1.29 2005/01/23 00:27:29 mcnamara Exp $
018: */
019: package org.apache.xalan.templates;
020:
021: import javax.xml.transform.TransformerException;
022:
023: import org.apache.xalan.transformer.TransformerImpl;
024: import org.apache.xml.utils.QName;
025: import org.apache.xpath.XPath;
026: import org.apache.xpath.XPathContext;
027: import org.apache.xpath.objects.XObject;
028: import org.apache.xpath.objects.XRTreeFrag;
029: import org.apache.xpath.objects.XRTreeFragSelectWrapper;
030: import org.apache.xpath.objects.XString;
031: import org.apache.xalan.res.XSLTErrorResources;
032:
033: /**
034: * Implement xsl:variable.
035: * <pre>
036: * <!ELEMENT xsl:variable %template;>
037: * <!ATTLIST xsl:variable
038: * name %qname; #REQUIRED
039: * select %expr; #IMPLIED
040: * >
041: * </pre>
042: * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
043: * @xsl.usage advanced
044: */
045: public class ElemVariable extends ElemTemplateElement {
046: static final long serialVersionUID = 9111131075322790061L;
047:
048: /**
049: * Constructor ElemVariable
050: *
051: */
052: public ElemVariable() {
053: }
054:
055: /**
056: * This is the index into the stack frame.
057: */
058: protected int m_index;
059:
060: /**
061: * The stack frame size for this variable if it is a global variable
062: * that declares an RTF, which is equal to the maximum number
063: * of variables that can be declared in the variable at one time.
064: */
065: int m_frameSize = -1;
066:
067: /**
068: * Sets the relative position of this variable within the stack frame (if local)
069: * or the global area (if global). Note that this should be called only for
070: * global variables since the local position is computed in the compose() method.
071: */
072: public void setIndex(int index) {
073: m_index = index;
074: }
075:
076: /**
077: * If this element is not at the top-level, get the relative position of the
078: * variable into the stack frame. If this variable is at the top-level, get
079: * the relative position within the global area.
080: */
081: public int getIndex() {
082: return m_index;
083: }
084:
085: /**
086: * The value of the "select" attribute.
087: * @serial
088: */
089: private XPath m_selectPattern;
090:
091: /**
092: * Set the "select" attribute.
093: * If the variable-binding element has a select attribute,
094: * then the value of the attribute must be an expression and
095: * the value of the variable is the object that results from
096: * evaluating the expression. In this case, the content
097: * of the variable must be empty.
098: *
099: * @param v Value to set for the "select" attribute.
100: */
101: public void setSelect(XPath v) {
102: m_selectPattern = v;
103: }
104:
105: /**
106: * Get the "select" attribute.
107: * If the variable-binding element has a select attribute,
108: * then the value of the attribute must be an expression and
109: * the value of the variable is the object that results from
110: * evaluating the expression. In this case, the content
111: * of the variable must be empty.
112: *
113: * @return Value of the "select" attribute.
114: */
115: public XPath getSelect() {
116: return m_selectPattern;
117: }
118:
119: /**
120: * The value of the "name" attribute.
121: * @serial
122: */
123: protected QName m_qname;
124:
125: /**
126: * Set the "name" attribute.
127: * Both xsl:variable and xsl:param have a required name
128: * attribute, which specifies the name of the variable. The
129: * value of the name attribute is a QName, which is expanded
130: * as described in [2.4 Qualified Names].
131: * @see <a href="http://www.w3.org/TR/xslt#qname">qname in XSLT Specification</a>
132: *
133: * @param v Value to set for the "name" attribute.
134: */
135: public void setName(QName v) {
136: m_qname = v;
137: }
138:
139: /**
140: * Get the "name" attribute.
141: * Both xsl:variable and xsl:param have a required name
142: * attribute, which specifies the name of the variable. The
143: * value of the name attribute is a QName, which is expanded
144: * as described in [2.4 Qualified Names].
145: * @see <a href="http://www.w3.org/TR/xslt#qname">qname in XSLT Specification</a>
146: *
147: * @return Value of the "name" attribute.
148: */
149: public QName getName() {
150: return m_qname;
151: }
152:
153: /**
154: * Tells if this is a top-level variable or param, or not.
155: * @serial
156: */
157: private boolean m_isTopLevel = false;
158:
159: /**
160: * Set if this is a top-level variable or param, or not.
161: * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
162: *
163: * @param v Boolean indicating whether this is a top-level variable
164: * or param, or not.
165: */
166: public void setIsTopLevel(boolean v) {
167: m_isTopLevel = v;
168: }
169:
170: /**
171: * Get if this is a top-level variable or param, or not.
172: * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
173: *
174: * @return Boolean indicating whether this is a top-level variable
175: * or param, or not.
176: */
177: public boolean getIsTopLevel() {
178: return m_isTopLevel;
179: }
180:
181: /**
182: * Get an integer representation of the element type.
183: *
184: * @return An integer representation of the element, defined in the
185: * Constants class.
186: * @see org.apache.xalan.templates.Constants
187: */
188: public int getXSLToken() {
189: return Constants.ELEMNAME_VARIABLE;
190: }
191:
192: /**
193: * Return the node name.
194: *
195: * @return The node name
196: */
197: public String getNodeName() {
198: return Constants.ELEMNAME_VARIABLE_STRING;
199: }
200:
201: /**
202: * Copy constructor.
203: *
204: * @param param An element created from an xsl:variable
205: *
206: * @throws TransformerException
207: */
208: public ElemVariable(ElemVariable param) throws TransformerException {
209:
210: m_selectPattern = param.m_selectPattern;
211: m_qname = param.m_qname;
212: m_isTopLevel = param.m_isTopLevel;
213:
214: // m_value = param.m_value;
215: // m_varContext = param.m_varContext;
216: }
217:
218: /**
219: * Execute a variable declaration and push it onto the variable stack.
220: * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
221: *
222: * @param transformer non-null reference to the the current transform-time state.
223: *
224: * @throws TransformerException
225: */
226: public void execute(TransformerImpl transformer)
227: throws TransformerException {
228:
229: if (transformer.getDebug())
230: transformer.getTraceManager().fireTraceEvent(this );
231:
232: int sourceNode = transformer.getXPathContext().getCurrentNode();
233:
234: XObject var = getValue(transformer, sourceNode);
235:
236: // transformer.getXPathContext().getVarStack().pushVariable(m_qname, var);
237: transformer.getXPathContext().getVarStack().setLocalVariable(
238: m_index, var);
239:
240: if (transformer.getDebug())
241: transformer.getTraceManager().fireTraceEndEvent(this );
242: }
243:
244: /**
245: * Get the XObject representation of the variable.
246: *
247: * @param transformer non-null reference to the the current transform-time state.
248: * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
249: *
250: * @return the XObject representation of the variable.
251: *
252: * @throws TransformerException
253: */
254: public XObject getValue(TransformerImpl transformer, int sourceNode)
255: throws TransformerException {
256:
257: XObject var;
258: XPathContext xctxt = transformer.getXPathContext();
259:
260: xctxt.pushCurrentNode(sourceNode);
261:
262: try {
263: if (null != m_selectPattern) {
264: var = m_selectPattern.execute(xctxt, sourceNode, this );
265:
266: var.allowDetachToRelease(false);
267:
268: if (transformer.getDebug())
269: transformer.getTraceManager().fireSelectedEvent(
270: sourceNode, this , "select",
271: m_selectPattern, var);
272: } else if (null == getFirstChildElem()) {
273: var = XString.EMPTYSTRING;
274: } else {
275:
276: // Use result tree fragment.
277: // Global variables may be deferred (see XUnresolvedVariable) and hence
278: // need to be assigned to a different set of DTMs than local variables
279: // so they aren't popped off the stack on return from a template.
280: int df;
281:
282: // Bugzilla 7118: A variable set via an RTF may create local
283: // variables during that computation. To keep them from overwriting
284: // variables at this level, push a new variable stack.
285: ////// PROBLEM: This is provoking a variable-used-before-set
286: ////// problem in parameters. Needs more study.
287: try {
288: //////////xctxt.getVarStack().link(0);
289: if (m_parentNode instanceof Stylesheet) // Global variable
290: df = transformer.transformToGlobalRTF(this );
291: else
292: df = transformer.transformToRTF(this );
293: } finally {
294: //////////////xctxt.getVarStack().unlink();
295: }
296:
297: var = new XRTreeFrag(df, xctxt, this );
298: }
299: } finally {
300: xctxt.popCurrentNode();
301: }
302:
303: return var;
304: }
305:
306: /**
307: * This function is called after everything else has been
308: * recomposed, and allows the template to set remaining
309: * values that may be based on some other property that
310: * depends on recomposition.
311: */
312: public void compose(StylesheetRoot sroot)
313: throws TransformerException {
314: // See if we can reduce an RTF to a select with a string expression.
315: if (null == m_selectPattern && sroot.getOptimizer()) {
316: XPath newSelect = rewriteChildToExpression(this );
317: if (null != newSelect)
318: m_selectPattern = newSelect;
319: }
320:
321: StylesheetRoot.ComposeState cstate = sroot.getComposeState();
322:
323: // This should be done before addVariableName, so we don't have visibility
324: // to the variable now being defined.
325: java.util.Vector vnames = cstate.getVariableNames();
326: if (null != m_selectPattern)
327: m_selectPattern.fixupVariables(vnames, cstate
328: .getGlobalsSize());
329:
330: // Only add the variable if this is not a global. If it is a global,
331: // it was already added by stylesheet root.
332: if (!(m_parentNode instanceof Stylesheet) && m_qname != null) {
333: m_index = cstate.addVariableName(m_qname)
334: - cstate.getGlobalsSize();
335: } else if (m_parentNode instanceof Stylesheet) {
336: // If this is a global, then we need to treat it as if it's a xsl:template,
337: // and count the number of variables it contains. So we set the count to
338: // zero here.
339: cstate.resetStackFrameSize();
340: }
341:
342: // This has to be done after the addVariableName, so that the variable
343: // pushed won't be immediately popped again in endCompose.
344: super .compose(sroot);
345: }
346:
347: /**
348: * This after the template's children have been composed. We have to get
349: * the count of how many variables have been declared, so we can do a link
350: * and unlink.
351: */
352: public void endCompose(StylesheetRoot sroot)
353: throws TransformerException {
354: super .endCompose(sroot);
355: if (m_parentNode instanceof Stylesheet) {
356: StylesheetRoot.ComposeState cstate = sroot
357: .getComposeState();
358: m_frameSize = cstate.getFrameSize();
359: cstate.resetStackFrameSize();
360: }
361: }
362:
363: // /**
364: // * This after the template's children have been composed.
365: // */
366: // public void endCompose() throws TransformerException
367: // {
368: // super.endCompose();
369: // }
370:
371: /**
372: * If the children of a variable is a single xsl:value-of or text literal,
373: * it is cheaper to evaluate this as an expression, so try and adapt the
374: * child an an expression.
375: *
376: * @param varElem Should be a ElemParam, ElemVariable, or ElemWithParam.
377: *
378: * @return An XPath if rewrite is possible, else null.
379: *
380: * @throws TransformerException
381: */
382: static XPath rewriteChildToExpression(ElemTemplateElement varElem)
383: throws TransformerException {
384:
385: ElemTemplateElement t = varElem.getFirstChildElem();
386:
387: // Down the line this can be done with multiple string objects using
388: // the concat function.
389: if (null != t && null == t.getNextSiblingElem()) {
390: int etype = t.getXSLToken();
391:
392: if (Constants.ELEMNAME_VALUEOF == etype) {
393: ElemValueOf valueof = (ElemValueOf) t;
394:
395: // %TBD% I'm worried about extended attributes here.
396: if (valueof.getDisableOutputEscaping() == false
397: && valueof.getDOMBackPointer() == null) {
398: varElem.m_firstChild = null;
399:
400: return new XPath(new XRTreeFragSelectWrapper(
401: valueof.getSelect().getExpression()));
402: }
403: } else if (Constants.ELEMNAME_TEXTLITERALRESULT == etype) {
404: ElemTextLiteral lit = (ElemTextLiteral) t;
405:
406: if (lit.getDisableOutputEscaping() == false
407: && lit.getDOMBackPointer() == null) {
408: String str = lit.getNodeValue();
409: XString xstr = new XString(str);
410:
411: varElem.m_firstChild = null;
412:
413: return new XPath(new XRTreeFragSelectWrapper(xstr));
414: }
415: }
416: }
417:
418: return null;
419: }
420:
421: /**
422: * This function is called during recomposition to
423: * control how this element is composed.
424: * @param root The root stylesheet for this transformation.
425: */
426: public void recompose(StylesheetRoot root) {
427: root.recomposeVariables(this );
428: }
429:
430: /**
431: * Set the parent as an ElemTemplateElement.
432: *
433: * @param p This node's parent as an ElemTemplateElement
434: */
435: public void setParentElem(ElemTemplateElement p) {
436: super .setParentElem(p);
437: p.m_hasVariableDecl = true;
438: }
439:
440: /**
441: * Accept a visitor and call the appropriate method
442: * for this class.
443: *
444: * @param visitor The visitor whose appropriate method will be called.
445: * @return true if the children of the object should be visited.
446: */
447: protected boolean accept(XSLTVisitor visitor) {
448: return visitor.visitVariableOrParamDecl(this );
449: }
450:
451: /**
452: * Call the children visitors.
453: * @param visitor The visitor whose appropriate method will be called.
454: */
455: protected void callChildVisitors(XSLTVisitor visitor,
456: boolean callAttrs) {
457: if (null != m_selectPattern)
458: m_selectPattern.getExpression().callVisitors(
459: m_selectPattern, visitor);
460: super .callChildVisitors(visitor, callAttrs);
461: }
462:
463: /**
464: * Tell if this is a psuedo variable reference, declared by Xalan instead
465: * of by the user.
466: */
467: public boolean isPsuedoVar() {
468: java.lang.String ns = m_qname.getNamespaceURI();
469: if ((null != ns)
470: && ns
471: .equals(RedundentExprEliminator.PSUEDOVARNAMESPACE)) {
472: if (m_qname.getLocalName().startsWith("#"))
473: return true;
474: }
475: return false;
476: }
477:
478: /**
479: * Add a child to the child list. If the select attribute
480: * is present, an error will be raised.
481: *
482: * @param elem New element to append to this element's children list
483: *
484: * @return null if the select attribute was present, otherwise the
485: * child just added to the child list
486: */
487: public ElemTemplateElement appendChild(ElemTemplateElement elem) {
488: // cannot have content and select
489: if (m_selectPattern != null) {
490: error(XSLTErrorResources.ER_CANT_HAVE_CONTENT_AND_SELECT,
491: new Object[] { "xsl:" + this.getNodeName() });
492: return null;
493: }
494: return super.appendChild(elem);
495: }
496:
497: }
|