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: AVT.java 469221 2006-10-30 18:26:44Z minchau $
020 */
021 package org.apache.xalan.templates;
022
023 import java.util.StringTokenizer;
024 import java.util.Vector;
025
026 import javax.xml.transform.TransformerException;
027
028 import org.apache.xalan.processor.StylesheetHandler;
029 import org.apache.xalan.res.XSLMessages;
030 import org.apache.xalan.res.XSLTErrorResources;
031 import org.apache.xml.utils.FastStringBuffer;
032 import org.apache.xml.utils.StringBufferPool;
033 import org.apache.xpath.XPath;
034 import org.apache.xpath.XPathContext;
035
036 /**
037 * Class to hold an Attribute Value Template.
038 * @xsl.usage advanced
039 */
040 public class AVT implements java.io.Serializable, XSLTVisitable
041 {
042 static final long serialVersionUID = 5167607155517042691L;
043
044 /**
045 *We are not going to use the object pool if USE_OBJECT_POOL == false.
046 */
047 private final static boolean USE_OBJECT_POOL = false;
048
049 /**
050 * INIT_BUFFER_CHUNK_BITS is used to set initial size of
051 * of the char m_array in FastStringBuffer if USE_OBJECT_POOL == false.
052 * size = 2^ INIT_BUFFER_CHUNK_BITS, INIT_BUFFER_CHUNK_BITS = 7
053 * corresponds size = 256.
054 */
055 private final static int INIT_BUFFER_CHUNK_BITS = 8;
056
057 /**
058 * If the AVT is not complex, just hold the simple string.
059 * @serial
060 */
061 private String m_simpleString = null;
062
063 /**
064 * If the AVT is complex, hold a Vector of AVTParts.
065 * @serial
066 */
067 private Vector m_parts = null;
068
069
070
071 /**
072 * The name of the attribute.
073 * @serial
074 */
075 private String m_rawName;
076
077 /**
078 * Get the raw name of the attribute, with the prefix unprocessed.
079 *
080 * @return non-null reference to prefixed name.
081 */
082 public String getRawName()
083 {
084 return m_rawName;
085 }
086
087 /**
088 * Get the raw name of the attribute, with the prefix unprocessed.
089 *
090 * @param rawName non-null reference to prefixed name.
091 */
092 public void setRawName(String rawName)
093 {
094 m_rawName = rawName;
095 }
096
097 /**
098 * The name of the attribute.
099 * @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 }