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 */ 21package org.apache.xalan.templates; 22 23import java.util.StringTokenizer; 24import java.util.Vector; 25 26import javax.xml.transform.TransformerException; 27 28import org.apache.xalan.processor.StylesheetHandler; 29import org.apache.xalan.res.XSLMessages; 30import org.apache.xalan.res.XSLTErrorResources; 31import org.apache.xml.utils.FastStringBuffer; 32import org.apache.xml.utils.StringBufferPool; 33import org.apache.xpath.XPath; 34import org.apache.xpath.XPathContext; 35 36/** 37 * Class to hold an Attribute Value Template. 38 * @xsl.usage advanced 39 */ 40public 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