Home | History | Annotate | Download | only in templates
      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    */
     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    */
     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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    603   public boolean isSimple() {
    604   	return m_simpleString != null;
    605   }
    606 
    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