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