1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: AVT.java 469221 2006-10-30 18:26:44Z minchau $
20  */
21 package org.apache.xalan.templates;
22 
23 import java.util.StringTokenizer;
24 import java.util.Vector;
25 
26 import javax.xml.transform.TransformerException;
27 
28 import org.apache.xalan.processor.StylesheetHandler;
29 import org.apache.xalan.res.XSLMessages;
30 import org.apache.xalan.res.XSLTErrorResources;
31 import org.apache.xml.utils.FastStringBuffer;
32 import org.apache.xml.utils.StringBufferPool;
33 import org.apache.xpath.XPath;
34 import org.apache.xpath.XPathContext;
35 
36 /**
37  * Class to hold an Attribute Value Template.
38  * @xsl.usage advanced
39  */
40 public class AVT implements java.io.Serializable, XSLTVisitable
41 {
42     static final long serialVersionUID = 5167607155517042691L;
43 
44   /**
45     *We are not going to use the object pool if USE_OBJECT_POOL == false.
46   */
47   private final static boolean USE_OBJECT_POOL = false;
48 
49   /**
50     * INIT_BUFFER_CHUNK_BITS is used to set initial size of
51     * of the char m_array in FastStringBuffer if USE_OBJECT_POOL == false.
52     * size = 2^ INIT_BUFFER_CHUNK_BITS, INIT_BUFFER_CHUNK_BITS = 7
53     * corresponds size = 256.
54   */
55   private final static int INIT_BUFFER_CHUNK_BITS = 8;
56 
57   /**
58    * If the AVT is not complex, just hold the simple string.
59    * @serial
60    */
61   private String m_simpleString = null;
62 
63   /**
64    * If the AVT is complex, hold a Vector of AVTParts.
65    * @serial
66    */
67   private Vector m_parts = null;
68 
69 
70 
71   /**
72    * The name of the attribute.
73    * @serial
74    */
75   private String m_rawName;
76 
77   /**
78    * Get the raw name of the attribute, with the prefix unprocessed.
79    *
80    * @return non-null reference to prefixed name.
81    */
getRawName()82   public String getRawName()
83   {
84     return m_rawName;
85   }
86 
87   /**
88    * Get the raw name of the attribute, with the prefix unprocessed.
89    *
90    * @param rawName non-null reference to prefixed name.
91    */
setRawName(String rawName)92   public void setRawName(String rawName)
93   {
94     m_rawName = rawName;
95   }
96 
97   /**
98    * The name of the attribute.
99    * @serial
100    */
101   private String m_name;
102 
103   /**
104    * Get the local name of the attribute.
105    *
106    * @return non-null reference to name string.
107    */
getName()108   public String getName()
109   {
110     return m_name;
111   }
112 
113   /**
114    * Set the local name of the attribute.
115    *
116    * @param name non-null reference to name string.
117    */
setName(String name)118   public void setName(String name)
119   {
120     m_name = name;
121   }
122 
123   /**
124    * The namespace URI of the owning attribute.
125    * @serial
126    */
127   private String m_uri;
128 
129   /**
130    * Get the namespace URI of the attribute.
131    *
132    * @return non-null reference to URI, "" if null namespace.
133    */
getURI()134   public String getURI()
135   {
136     return m_uri;
137   }
138 
139   /**
140    * Get the namespace URI of the attribute.
141    *
142    * @param uri non-null reference to URI, "" if null namespace.
143    */
setURI(String uri)144   public void setURI(String uri)
145   {
146     m_uri = uri;
147   }
148 
149   /**
150    * Construct an AVT by parsing the string, and either
151    * constructing a vector of AVTParts, or simply hold
152    * on to the string if the AVT is simple.
153    *
154    * @param handler non-null reference to StylesheetHandler that is constructing.
155    * @param uri non-null reference to URI, "" if null namespace.
156    * @param name  non-null reference to name string.
157    * @param rawName prefixed name.
158    * @param stringedValue non-null raw string value.
159    *
160    * @throws javax.xml.transform.TransformerException
161    */
AVT(StylesheetHandler handler, String uri, String name, String rawName, String stringedValue, ElemTemplateElement owner)162   public AVT(StylesheetHandler handler, String uri, String name,
163              String rawName, String stringedValue,
164              ElemTemplateElement owner)
165           throws javax.xml.transform.TransformerException
166   {
167 
168     m_uri = uri;
169     m_name = name;
170     m_rawName = rawName;
171 
172     StringTokenizer tokenizer = new StringTokenizer(stringedValue, "{}\"\'",
173                                   true);
174     int nTokens = tokenizer.countTokens();
175 
176     if (nTokens < 2)
177     {
178       m_simpleString = stringedValue;  // then do the simple thing
179     }
180     else
181     {
182       FastStringBuffer buffer = null;
183       FastStringBuffer exprBuffer = null;
184       if(USE_OBJECT_POOL){
185         buffer = StringBufferPool.get();
186         exprBuffer = StringBufferPool.get();
187       }else{
188         buffer = new FastStringBuffer(6);
189         exprBuffer = new FastStringBuffer(6);
190       }
191       try
192       {
193         m_parts = new Vector(nTokens + 1);
194 
195         String t = null;  // base token
196         String lookahead = null;  // next token
197         String error = null;  // if non-null, break from loop
198 
199         while (tokenizer.hasMoreTokens())
200         {
201           if (lookahead != null)
202           {
203             t = lookahead;
204             lookahead = null;
205           }
206           else
207             t = tokenizer.nextToken();
208 
209           if (t.length() == 1)
210           {
211             switch (t.charAt(0))
212             {
213             case ('\"') :
214             case ('\'') :
215             {
216 
217               // just keep on going, since we're not in an attribute template
218               buffer.append(t);
219 
220               break;
221             }
222             case ('{') :
223             {
224 
225               try
226               {
227                 // Attribute Value Template start
228                 lookahead = tokenizer.nextToken();
229 
230                 if (lookahead.equals("{"))
231                 {
232 
233                   // Double curlys mean escape to show curly
234                   buffer.append(lookahead);
235 
236                   lookahead = null;
237 
238                   break;  // from switch
239                 }
240 
241                 /*
242                 else if(lookahead.equals("\"") || lookahead.equals("\'"))
243                 {
244                 // Error. Expressions can not begin with quotes.
245                 error = "Expressions can not begin with quotes.";
246                 break; // from switch
247                 }
248                 */
249                 else
250                 {
251                   if (buffer.length() > 0)
252                   {
253                     m_parts.addElement(new AVTPartSimple(buffer.toString()));
254                     buffer.setLength(0);
255                   }
256 
257                   exprBuffer.setLength(0);
258 
259                   while (null != lookahead)
260                   {
261                     if (lookahead.length() == 1)
262                     {
263                       switch (lookahead.charAt(0))
264                       {
265                       case '\'' :
266                       case '\"' :
267                         {
268 
269                           // String start
270                           exprBuffer.append(lookahead);
271 
272                           String quote = lookahead;
273 
274                           // Consume stuff 'till next quote
275                           lookahead = tokenizer.nextToken();
276 
277                           while (!lookahead.equals(quote))
278                           {
279                             exprBuffer.append(lookahead);
280 
281                             lookahead = tokenizer.nextToken();
282                           }
283 
284                           exprBuffer.append(lookahead);
285 
286                           lookahead = tokenizer.nextToken();
287 
288                           break;
289                         }
290                       case '{' :
291                         {
292 
293                           // What's another curly doing here?
294                           error = XSLMessages.createMessage(
295                                                             XSLTErrorResources.ER_NO_CURLYBRACE, null);  //"Error: Can not have \"{\" within expression.";
296 
297                           lookahead = null;  // breaks out of inner while loop
298 
299                           break;
300                         }
301                       case '}' :
302                         {
303 
304                           // Proper close of attribute template.
305                           // Evaluate the expression.
306                           buffer.setLength(0);
307 
308                           XPath xpath =
309                                        handler.createXPath(exprBuffer.toString(), owner);
310 
311                           m_parts.addElement(new AVTPartXPath(xpath));
312 
313                           lookahead = null;  // breaks out of inner while loop
314 
315                           break;
316                         }
317                       default :
318                         {
319 
320                           // part of the template stuff, just add it.
321                           exprBuffer.append(lookahead);
322 
323                           lookahead = tokenizer.nextToken();
324                         }
325                       }  // end inner switch
326                     }  // end if lookahead length == 1
327                     else
328                     {
329 
330                       // part of the template stuff, just add it.
331                       exprBuffer.append(lookahead);
332 
333                       lookahead = tokenizer.nextToken();
334                     }
335                   }  // end while(!lookahead.equals("}"))
336 
337                   if (error != null)
338                   {
339                     break;  // from inner while loop
340                   }
341                 }
342 
343                 break;
344               }
345               catch (java.util.NoSuchElementException ex)
346               {
347                 error = XSLMessages.createMessage(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE, new Object[]{ name, stringedValue });
348                 break;
349               }
350             }
351             case ('}') :
352             {
353               lookahead = tokenizer.nextToken();
354 
355               if (lookahead.equals("}"))
356               {
357 
358                 // Double curlys mean escape to show curly
359                 buffer.append(lookahead);
360 
361                 lookahead = null;  // swallow
362               }
363               else
364               {
365 
366                 // Illegal, I think...
367                 try
368                 {
369                   handler.warn(XSLTErrorResources.WG_FOUND_CURLYBRACE, null);  //"Found \"}\" but no attribute template open!");
370                 }
371                 catch (org.xml.sax.SAXException se)
372                 {
373                   throw new TransformerException(se);
374                 }
375 
376                 buffer.append("}");
377 
378                 // leave the lookahead to be processed by the next round.
379               }
380 
381               break;
382             }
383             default :
384             {
385 
386               // Anything else just add to string.
387               buffer.append(t);
388             }
389             }  // end switch t
390           }  // end if length == 1
391           else
392           {
393 
394             // Anything else just add to string.
395             buffer.append(t);
396           }
397 
398           if (null != error)
399           {
400             try
401             {
402               handler.warn(XSLTErrorResources.WG_ATTR_TEMPLATE,
403                            new Object[]{ error });  //"Attr Template, "+error);
404             }
405             catch (org.xml.sax.SAXException se)
406             {
407               throw new TransformerException(se);
408             }
409 
410             break;
411           }
412         }  // end while(tokenizer.hasMoreTokens())
413 
414         if (buffer.length() > 0)
415         {
416           m_parts.addElement(new AVTPartSimple(buffer.toString()));
417           buffer.setLength(0);
418         }
419       }
420       finally
421       {
422         if(USE_OBJECT_POOL){
423              StringBufferPool.free(buffer);
424              StringBufferPool.free(exprBuffer);
425          }else{
426             buffer = null;
427             exprBuffer = null;
428          };
429       }
430     }  // end else nTokens > 1
431 
432     if (null == m_parts && (null == m_simpleString))
433     {
434 
435       // Error?
436       m_simpleString = "";
437     }
438   }
439 
440   /**
441    * Get the AVT as the original string.
442    *
443    * @return The AVT as the original string
444    */
getSimpleString()445   public String getSimpleString()
446   {
447 
448     if (null != m_simpleString){
449       return m_simpleString;
450     }
451     else if (null != m_parts){
452      final FastStringBuffer buf = getBuffer();
453      String out = null;
454 
455     int n = m_parts.size();
456     try{
457       for (int i = 0; i < n; i++){
458         AVTPart part = (AVTPart) m_parts.elementAt(i);
459         buf.append(part.getSimpleString());
460       }
461       out = buf.toString();
462     }finally{
463       if(USE_OBJECT_POOL){
464          StringBufferPool.free(buf);
465      }else{
466         buf.setLength(0);
467      };
468     }
469     return out;
470   }else{
471       return "";
472   }
473 }
474 
475   /**
476    * Evaluate the AVT and return a String.
477    *
478    * @param xctxt Te XPathContext to use to evaluate this.
479    * @param context The current source tree context.
480    * @param nsNode The current namespace context (stylesheet tree context).
481    *
482    * @return The AVT evaluated as a string
483    *
484    * @throws javax.xml.transform.TransformerException
485    */
evaluate( XPathContext xctxt, int context, org.apache.xml.utils.PrefixResolver nsNode)486   public String evaluate(
487           XPathContext xctxt, int context, org.apache.xml.utils.PrefixResolver nsNode)
488             throws javax.xml.transform.TransformerException
489   {
490     if (null != m_simpleString){
491         return m_simpleString;
492     }else if (null != m_parts){
493       final FastStringBuffer buf =getBuffer();
494       String out = null;
495       int n = m_parts.size();
496       try{
497         for (int i = 0; i < n; i++){
498           AVTPart part = (AVTPart) m_parts.elementAt(i);
499           part.evaluate(xctxt, buf, context, nsNode);
500         }
501        out = buf.toString();
502       }finally{
503           if(USE_OBJECT_POOL){
504              StringBufferPool.free(buf);
505          }else{
506            buf.setLength(0);
507          }
508       }
509      return out;
510     }else{
511       return "";
512     }
513   }
514 
515   /**
516    * Test whether the AVT is insensitive to the context in which
517    *  it is being evaluated. This is intended to facilitate
518    *  compilation of templates, by allowing simple AVTs to be
519    *  converted back into strings.
520    *
521    *  Currently the only case we recognize is simple strings.
522    * ADDED 9/5/2000 to support compilation experiment
523    *
524    * @return True if the m_simpleString member of this AVT is not null
525    */
isContextInsensitive()526   public boolean isContextInsensitive()
527   {
528     return null != m_simpleString;
529   }
530 
531   /**
532    * Tell if this expression or it's subexpressions can traverse outside
533    * the current subtree.
534    *
535    * @return true if traversal outside the context node's subtree can occur.
536    */
canTraverseOutsideSubtree()537   public boolean canTraverseOutsideSubtree()
538   {
539 
540     if (null != m_parts)
541     {
542       int n = m_parts.size();
543 
544       for (int i = 0; i < n; i++)
545       {
546         AVTPart part = (AVTPart) m_parts.elementAt(i);
547 
548         if (part.canTraverseOutsideSubtree())
549           return true;
550       }
551     }
552 
553     return false;
554   }
555 
556   /**
557    * This function is used to fixup variables from QNames to stack frame
558    * indexes at stylesheet build time.
559    * @param vars List of QNames that correspond to variables.  This list
560    * should be searched backwards for the first qualified name that
561    * corresponds to the variable reference qname.  The position of the
562    * QName in the vector from the start of the vector will be its position
563    * in the stack frame (but variables above the globalsTop value will need
564    * to be offset to the current stack frame).
565    */
fixupVariables(java.util.Vector vars, int globalsSize)566   public void fixupVariables(java.util.Vector vars, int globalsSize)
567   {
568     if (null != m_parts)
569     {
570       int n = m_parts.size();
571 
572       for (int i = 0; i < n; i++)
573       {
574         AVTPart part = (AVTPart) m_parts.elementAt(i);
575 
576         part.fixupVariables(vars, globalsSize);
577       }
578     }
579   }
580 
581   /**
582    * @see XSLTVisitable#callVisitors(XSLTVisitor)
583    */
callVisitors(XSLTVisitor visitor)584   public void callVisitors(XSLTVisitor visitor)
585   {
586   	if(visitor.visitAVT(this) && (null != m_parts))
587   	{
588       int n = m_parts.size();
589 
590       for (int i = 0; i < n; i++)
591       {
592         AVTPart part = (AVTPart) m_parts.elementAt(i);
593 
594         part.callVisitors(visitor);
595       }
596   	}
597   }
598 
599 
600   /**
601    * Returns true if this AVT is simple
602    */
isSimple()603   public boolean isSimple() {
604   	return m_simpleString != null;
605   }
606 
getBuffer()607   private final FastStringBuffer getBuffer(){
608     if(USE_OBJECT_POOL){
609       return StringBufferPool.get();
610     }else{
611       return new FastStringBuffer(INIT_BUFFER_CHUNK_BITS);
612     }
613   }
614 }
615