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