001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: AVT.java,v 1.26 2005/01/23 00:27:29 mcnamara Exp $
018: */
019: package org.apache.xalan.templates;
020:
021: import java.util.StringTokenizer;
022: import java.util.Vector;
023:
024: import javax.xml.transform.TransformerException;
025:
026: import org.apache.xalan.processor.StylesheetHandler;
027: import org.apache.xalan.res.XSLMessages;
028: import org.apache.xalan.res.XSLTErrorResources;
029: import org.apache.xml.utils.FastStringBuffer;
030: import org.apache.xml.utils.StringBufferPool;
031: import org.apache.xpath.XPath;
032: import org.apache.xpath.XPathContext;
033:
034: /**
035: * Class to hold an Attribute Value Template.
036: * @xsl.usage advanced
037: */
038: public class AVT implements java.io.Serializable, XSLTVisitable {
039: static final long serialVersionUID = 5167607155517042691L;
040:
041: /**
042: *We are not going to use the object pool if USE_OBJECT_POOL == false.
043: */
044: private final static boolean USE_OBJECT_POOL = false;
045:
046: /**
047: * INIT_BUFFER_CHUNK_BITS is used to set initial size of
048: * of the char m_array in FastStringBuffer if USE_OBJECT_POOL == false.
049: * size = 2^ INIT_BUFFER_CHUNK_BITS, INIT_BUFFER_CHUNK_BITS = 7
050: * corresponds size = 256.
051: */
052: private final static int INIT_BUFFER_CHUNK_BITS = 8;
053:
054: /**
055: * We are caching FastStringBuffer objects if if USE_OBJECT_POOL == false
056: */
057: private transient FastStringBuffer m_cachedBuf;
058:
059: /**
060: * If the AVT is not complex, just hold the simple string.
061: * @serial
062: */
063: private String m_simpleString = null;
064:
065: /**
066: * If the AVT is complex, hold a Vector of AVTParts.
067: * @serial
068: */
069: private Vector m_parts = null;
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: return m_rawName;
084: }
085:
086: /**
087: * Get the raw name of the attribute, with the prefix unprocessed.
088: *
089: * @param rawName non-null reference to prefixed name.
090: */
091: public void setRawName(String rawName) {
092: m_rawName = rawName;
093: }
094:
095: /**
096: * The name of the attribute.
097: * @serial
098: */
099: private String m_name;
100:
101: /**
102: * Get the local name of the attribute.
103: *
104: * @return non-null reference to name string.
105: */
106: public String getName() {
107: return m_name;
108: }
109:
110: /**
111: * Set the local name of the attribute.
112: *
113: * @param name non-null reference to name string.
114: */
115: public void setName(String name) {
116: m_name = name;
117: }
118:
119: /**
120: * The namespace URI of the owning attribute.
121: * @serial
122: */
123: private String m_uri;
124:
125: /**
126: * Get the namespace URI of the attribute.
127: *
128: * @return non-null reference to URI, "" if null namespace.
129: */
130: public String getURI() {
131: return m_uri;
132: }
133:
134: /**
135: * Get the namespace URI of the attribute.
136: *
137: * @param uri non-null reference to URI, "" if null namespace.
138: */
139: public void setURI(String uri) {
140: m_uri = uri;
141: }
142:
143: /**
144: * Construct an AVT by parsing the string, and either
145: * constructing a vector of AVTParts, or simply hold
146: * on to the string if the AVT is simple.
147: *
148: * @param handler non-null reference to StylesheetHandler that is constructing.
149: * @param uri non-null reference to URI, "" if null namespace.
150: * @param name non-null reference to name string.
151: * @param rawName prefixed name.
152: * @param stringedValue non-null raw string value.
153: *
154: * @throws javax.xml.transform.TransformerException
155: */
156: public AVT(StylesheetHandler handler, String uri, String name,
157: String rawName, String stringedValue,
158: ElemTemplateElement owner)
159: throws javax.xml.transform.TransformerException {
160:
161: m_uri = uri;
162: m_name = name;
163: m_rawName = rawName;
164:
165: StringTokenizer tokenizer = new StringTokenizer(stringedValue,
166: "{}\"\'", true);
167: int nTokens = tokenizer.countTokens();
168:
169: if (nTokens < 2) {
170: m_simpleString = stringedValue; // then do the simple thing
171: } else {
172: FastStringBuffer buffer = null;
173: FastStringBuffer exprBuffer = null;
174: if (USE_OBJECT_POOL) {
175: buffer = StringBufferPool.get();
176: exprBuffer = StringBufferPool.get();
177: } else {
178: buffer = new FastStringBuffer(6);
179: exprBuffer = new FastStringBuffer(6);
180: }
181: try {
182: m_parts = new Vector(nTokens + 1);
183:
184: String t = null; // base token
185: String lookahead = null; // next token
186: String error = null; // if non-null, break from loop
187:
188: while (tokenizer.hasMoreTokens()) {
189: if (lookahead != null) {
190: t = lookahead;
191: lookahead = null;
192: } else
193: t = tokenizer.nextToken();
194:
195: if (t.length() == 1) {
196: switch (t.charAt(0)) {
197: case ('\"'):
198: case ('\''): {
199:
200: // just keep on going, since we're not in an attribute template
201: buffer.append(t);
202:
203: break;
204: }
205: case ('{'): {
206:
207: try {
208: // Attribute Value Template start
209: lookahead = tokenizer.nextToken();
210:
211: if (lookahead.equals("{")) {
212:
213: // Double curlys mean escape to show curly
214: buffer.append(lookahead);
215:
216: lookahead = null;
217:
218: break; // from switch
219: }
220:
221: /*
222: else if(lookahead.equals("\"") || lookahead.equals("\'"))
223: {
224: // Error. Expressions can not begin with quotes.
225: error = "Expressions can not begin with quotes.";
226: break; // from switch
227: }
228: */
229: else {
230: if (buffer.length() > 0) {
231: m_parts
232: .addElement(new AVTPartSimple(
233: buffer
234: .toString()));
235: buffer.setLength(0);
236: }
237:
238: exprBuffer.setLength(0);
239:
240: while (null != lookahead) {
241: if (lookahead.length() == 1) {
242: switch (lookahead.charAt(0)) {
243: case '\'':
244: case '\"': {
245:
246: // String start
247: exprBuffer
248: .append(lookahead);
249:
250: String quote = lookahead;
251:
252: // Consume stuff 'till next quote
253: lookahead = tokenizer
254: .nextToken();
255:
256: while (!lookahead
257: .equals(quote)) {
258: exprBuffer
259: .append(lookahead);
260:
261: lookahead = tokenizer
262: .nextToken();
263: }
264:
265: exprBuffer
266: .append(lookahead);
267:
268: lookahead = tokenizer
269: .nextToken();
270:
271: break;
272: }
273: case '{': {
274:
275: // What's another curly doing here?
276: error = XSLMessages
277: .createMessage(
278: XSLTErrorResources.ER_NO_CURLYBRACE,
279: null); //"Error: Can not have \"{\" within expression.";
280:
281: lookahead = null; // breaks out of inner while loop
282:
283: break;
284: }
285: case '}': {
286:
287: // Proper close of attribute template.
288: // Evaluate the expression.
289: buffer.setLength(0);
290:
291: XPath xpath = handler
292: .createXPath(
293: exprBuffer
294: .toString(),
295: owner);
296:
297: m_parts
298: .addElement(new AVTPartXPath(
299: xpath));
300:
301: lookahead = null; // breaks out of inner while loop
302:
303: break;
304: }
305: default: {
306:
307: // part of the template stuff, just add it.
308: exprBuffer
309: .append(lookahead);
310:
311: lookahead = tokenizer
312: .nextToken();
313: }
314: } // end inner switch
315: } // end if lookahead length == 1
316: else {
317:
318: // part of the template stuff, just add it.
319: exprBuffer
320: .append(lookahead);
321:
322: lookahead = tokenizer
323: .nextToken();
324: }
325: } // end while(!lookahead.equals("}"))
326:
327: if (error != null) {
328: break; // from inner while loop
329: }
330: }
331:
332: break;
333: } catch (java.util.NoSuchElementException ex) {
334: error = XSLMessages
335: .createMessage(
336: XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
337: new Object[] { name,
338: stringedValue });
339: break;
340: }
341: }
342: case ('}'): {
343: lookahead = tokenizer.nextToken();
344:
345: if (lookahead.equals("}")) {
346:
347: // Double curlys mean escape to show curly
348: buffer.append(lookahead);
349:
350: lookahead = null; // swallow
351: } else {
352:
353: // Illegal, I think...
354: try {
355: handler
356: .warn(
357: XSLTErrorResources.WG_FOUND_CURLYBRACE,
358: null); //"Found \"}\" but no attribute template open!");
359: } catch (org.xml.sax.SAXException se) {
360: throw new TransformerException(se);
361: }
362:
363: buffer.append("}");
364:
365: // leave the lookahead to be processed by the next round.
366: }
367:
368: break;
369: }
370: default: {
371:
372: // Anything else just add to string.
373: buffer.append(t);
374: }
375: } // end switch t
376: } // end if length == 1
377: else {
378:
379: // Anything else just add to string.
380: buffer.append(t);
381: }
382:
383: if (null != error) {
384: try {
385: handler
386: .warn(
387: XSLTErrorResources.WG_ATTR_TEMPLATE,
388: new Object[] { error }); //"Attr Template, "+error);
389: } catch (org.xml.sax.SAXException se) {
390: throw new TransformerException(se);
391: }
392:
393: break;
394: }
395: } // end while(tokenizer.hasMoreTokens())
396:
397: if (buffer.length() > 0) {
398: m_parts.addElement(new AVTPartSimple(buffer
399: .toString()));
400: buffer.setLength(0);
401: }
402: } finally {
403: if (USE_OBJECT_POOL) {
404: StringBufferPool.free(buffer);
405: StringBufferPool.free(exprBuffer);
406: } else {
407: buffer = null;
408: exprBuffer = null;
409: }
410: ;
411: }
412: } // end else nTokens > 1
413:
414: if (null == m_parts && (null == m_simpleString)) {
415:
416: // Error?
417: m_simpleString = "";
418: }
419: }
420:
421: /**
422: * Get the AVT as the original string.
423: *
424: * @return The AVT as the original string
425: */
426: public String getSimpleString() {
427:
428: if (null != m_simpleString) {
429: return m_simpleString;
430: } else if (null != m_parts) {
431: final FastStringBuffer buf = getBuffer();
432: String out = null;
433:
434: int n = m_parts.size();
435: try {
436: for (int i = 0; i < n; i++) {
437: AVTPart part = (AVTPart) m_parts.elementAt(i);
438: buf.append(part.getSimpleString());
439: }
440: out = buf.toString();
441: } finally {
442: if (USE_OBJECT_POOL) {
443: StringBufferPool.free(buf);
444: } else {
445: buf.setLength(0);
446: }
447: ;
448: }
449: return out;
450: } else {
451: return "";
452: }
453: }
454:
455: /**
456: * Evaluate the AVT and return a String.
457: *
458: * @param xctxt Te XPathContext to use to evaluate this.
459: * @param context The current source tree context.
460: * @param nsNode The current namespace context (stylesheet tree context).
461: *
462: * @return The AVT evaluated as a string
463: *
464: * @throws javax.xml.transform.TransformerException
465: */
466: public String evaluate(XPathContext xctxt, int context,
467: org.apache.xml.utils.PrefixResolver nsNode)
468: throws javax.xml.transform.TransformerException {
469: if (null != m_simpleString) {
470: return m_simpleString;
471: } else if (null != m_parts) {
472: final FastStringBuffer buf = getBuffer();
473: String out = null;
474: int n = m_parts.size();
475: try {
476: for (int i = 0; i < n; i++) {
477: AVTPart part = (AVTPart) m_parts.elementAt(i);
478: part.evaluate(xctxt, buf, context, nsNode);
479: }
480: out = buf.toString();
481: } finally {
482: if (USE_OBJECT_POOL) {
483: StringBufferPool.free(buf);
484: } else {
485: buf.setLength(0);
486: }
487: }
488: return out;
489: } else {
490: return "";
491: }
492: }
493:
494: /**
495: * Test whether the AVT is insensitive to the context in which
496: * it is being evaluated. This is intended to facilitate
497: * compilation of templates, by allowing simple AVTs to be
498: * converted back into strings.
499: *
500: * Currently the only case we recognize is simple strings.
501: * ADDED 9/5/2000 to support compilation experiment
502: *
503: * @return True if the m_simpleString member of this AVT is not null
504: */
505: public boolean isContextInsensitive() {
506: return null != m_simpleString;
507: }
508:
509: /**
510: * Tell if this expression or it's subexpressions can traverse outside
511: * the current subtree.
512: *
513: * @return true if traversal outside the context node's subtree can occur.
514: */
515: public boolean canTraverseOutsideSubtree() {
516:
517: if (null != m_parts) {
518: int n = m_parts.size();
519:
520: for (int i = 0; i < n; i++) {
521: AVTPart part = (AVTPart) m_parts.elementAt(i);
522:
523: if (part.canTraverseOutsideSubtree())
524: return true;
525: }
526: }
527:
528: return false;
529: }
530:
531: /**
532: * This function is used to fixup variables from QNames to stack frame
533: * indexes at stylesheet build time.
534: * @param vars List of QNames that correspond to variables. This list
535: * should be searched backwards for the first qualified name that
536: * corresponds to the variable reference qname. The position of the
537: * QName in the vector from the start of the vector will be its position
538: * in the stack frame (but variables above the globalsTop value will need
539: * to be offset to the current stack frame).
540: */
541: public void fixupVariables(java.util.Vector vars, int globalsSize) {
542: if (null != m_parts) {
543: int n = m_parts.size();
544:
545: for (int i = 0; i < n; i++) {
546: AVTPart part = (AVTPart) m_parts.elementAt(i);
547:
548: part.fixupVariables(vars, globalsSize);
549: }
550: }
551: }
552:
553: /**
554: * @see XSLTVisitable#callVisitors(XSLTVisitor)
555: */
556: public void callVisitors(XSLTVisitor visitor) {
557: if (visitor.visitAVT(this ) && (null != m_parts)) {
558: int n = m_parts.size();
559:
560: for (int i = 0; i < n; i++) {
561: AVTPart part = (AVTPart) m_parts.elementAt(i);
562:
563: part.callVisitors(visitor);
564: }
565: }
566: }
567:
568: /**
569: * Returns true if this AVT is simple
570: */
571: public boolean isSimple() {
572: return m_simpleString != null;
573: }
574:
575: private final FastStringBuffer getBuffer() {
576: if (USE_OBJECT_POOL) {
577: return StringBufferPool.get();
578: } else if (m_cachedBuf == null) {
579: m_cachedBuf = new FastStringBuffer(INIT_BUFFER_CHUNK_BITS);
580: return m_cachedBuf;
581: } else if (m_cachedBuf.length() != 0) {
582: return new FastStringBuffer(INIT_BUFFER_CHUNK_BITS);
583: } else {
584: return m_cachedBuf;
585: }
586: }
587: }
|