0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: * Free SoftwareFoundation, Inc.
0023: * 59 Temple Place, Suite 330
0024: * Boston, MA 02111-1307 USA
0025: *
0026: * @author Scott Ferguson
0027: */
0028:
0029: package com.caucho.xsl;
0030:
0031: import com.caucho.log.Log;
0032: import com.caucho.util.CharBuffer;
0033: import com.caucho.util.IntArray;
0034: import com.caucho.util.L10N;
0035: import com.caucho.vfs.Path;
0036: import com.caucho.xml.*;
0037: import com.caucho.xpath.Expr;
0038: import com.caucho.xpath.ExprEnvironment;
0039: import com.caucho.xpath.NamespaceContext;
0040: import com.caucho.xpath.XPathException;
0041:
0042: import org.w3c.dom.*;
0043: import org.xml.sax.SAXException;
0044:
0045: import java.io.IOException;
0046: import java.io.OutputStream;
0047: import java.io.Writer;
0048: import java.util.ArrayList;
0049: import java.util.HashMap;
0050: import java.util.Iterator;
0051: import java.util.logging.Level;
0052: import java.util.logging.Logger;
0053:
0054: /**
0055: * Writer stream for generating stylesheet output.
0056: *
0057: * <p>Because XSL produces an XML tree, XslWriter contains extra
0058: * methods for constructing the tree.
0059: *
0060: * <p>The writer methods, e.g. println, add to the current text node.
0061: *
0062: * <p>In addition, stylesheets can access variables through getPwd and
0063: * getPage.
0064: */
0065: public class XslWriter extends Writer implements ExtendedLocator {
0066: static final Logger log = Log.open(XslWriter.class);
0067: static final L10N L = new L10N(XslWriter.class);
0068:
0069: // This is the value Axis wants
0070: private final static String XMLNS = "http://www.w3.org/2000/xmlns/";
0071:
0072: private final static XMLWriter ATTR_WRITER = new DOMBuilder();
0073:
0074: private XMLWriter _xmlWriter;
0075:
0076: String _systemId;
0077: String _filename;
0078: int _line;
0079: int _tailLine;
0080:
0081: private IntArray flags = new IntArray();
0082:
0083: private CharBuffer _text = new CharBuffer();
0084: private String elementName;
0085:
0086: private String _attributeURL;
0087: private String _attributePrefix;
0088: private String _attributeLocalName;
0089: private String _attributeName;
0090:
0091: private ArrayList depends = new ArrayList();
0092: private boolean _isCacheable = true;
0093: private boolean _disableEscaping;
0094:
0095: private boolean generateLocation;
0096:
0097: private Document _document;
0098:
0099: private StylesheetImpl _stylesheet;
0100: private TransformerImpl _transformer;
0101:
0102: private HashMap<String, String> _cdataElements;
0103: private boolean isCdata;
0104:
0105: private HashMap<String, String> _namespaces;
0106: private ArrayList<String> _topNamespaces;
0107: private ArrayList<StackItem> _elementStack;
0108: private int _depth;
0109:
0110: private ExtendedLocator _locator = null;
0111:
0112: XslWriter(HashMap env, StylesheetImpl stylesheet,
0113: TransformerImpl transformer) {
0114: _stylesheet = stylesheet;
0115: _transformer = transformer;
0116:
0117: ArrayList<String> cdata = stylesheet.getOutputFormat()
0118: .getCdataSectionElements();
0119: if (cdata != null) {
0120: _cdataElements = new HashMap<String, String>();
0121:
0122: for (int i = 0; i < cdata.size(); i++) {
0123: String element = cdata.get(i);
0124:
0125: _cdataElements.put(element, element);
0126: }
0127: }
0128: }
0129:
0130: void init(XMLWriter xmlWriter) {
0131: _xmlWriter = xmlWriter;
0132: _namespaces = new HashMap<String, String>();
0133: _topNamespaces = new ArrayList<String>();
0134: _elementStack = new ArrayList<StackItem>();
0135:
0136: _document = null;
0137:
0138: _locator = this ;
0139: xmlWriter.setDocumentLocator(_locator);
0140: }
0141:
0142: public TransformerImpl getTransformer() {
0143: return _transformer;
0144: }
0145:
0146: /**
0147: * Returns true if the generated stylesheet is currently cacheable.
0148: */
0149: boolean isCacheable() {
0150: return _isCacheable;
0151: }
0152:
0153: /**
0154: * Returns the Path dependency list of the generated stylesheet.
0155: */
0156: ArrayList getDepends() {
0157: return depends;
0158: }
0159:
0160: /**
0161: * Indicate that the result document is not cacheable.
0162: */
0163: public void setNotCacheable() {
0164: _isCacheable = false;
0165: }
0166:
0167: /**
0168: * Add a dependency to the result document. When the result is checked
0169: * for modification, this path will also be checked.
0170: */
0171: public void addCacheDepend(Path path) {
0172: _transformer.addCacheDepend(path);
0173: }
0174:
0175: /**
0176: * Implementation function so jsp:decl tags aren't repeated.
0177: */
0178: public boolean isFlagFirst(int id) {
0179: while (flags.size() <= id)
0180: flags.add(0);
0181:
0182: int value = flags.get(id);
0183: flags.set(id, 1);
0184:
0185: return value == 0;
0186: }
0187:
0188: /**
0189: * Adds a byte to the current text node.
0190: */
0191: public void write(int ch) {
0192: _text.append((char) ch);
0193: }
0194:
0195: /**
0196: * Adds a byte buffer to the current text node.
0197: */
0198: public void write(byte[] buf, int offset, int length) {
0199: for (int i = 0; i < length; i++)
0200: write(buf[offset + i]);
0201: }
0202:
0203: /**
0204: * Adds a char buffer to the current text node.
0205: */
0206: public void write(char[] buf, int offset, int length) {
0207: _text.append(buf, offset, length);
0208: }
0209:
0210: /**
0211: * Adds a string to the current text node.
0212: */
0213: public void print(String string) {
0214: if (string == null) {
0215: _text.append("null");
0216: return;
0217: }
0218:
0219: _text.append(string);
0220: }
0221:
0222: /**
0223: * Adds a boolean to the current text node.
0224: */
0225: public void print(boolean b) {
0226: _text.append(b);
0227: }
0228:
0229: /**
0230: * Adds a character to the current text node.
0231: */
0232: public void print(char ch) {
0233: _text.append(ch);
0234: }
0235:
0236: /**
0237: * Adds an integer to the current text node.
0238: */
0239: public void print(int i) {
0240: _text.append(i);
0241: }
0242:
0243: /**
0244: * Adds an integer to the current text node.
0245: */
0246: public void print(long l) {
0247: _text.append(l);
0248: }
0249:
0250: /**
0251: * Adds a float to the current text node.
0252: */
0253: public void print(float f) {
0254: _text.append(f);
0255: }
0256:
0257: /**
0258: * Adds a double to the current text node.
0259: */
0260: public void print(double d) {
0261: _text.append(d);
0262: }
0263:
0264: /**
0265: * Adds an object to the current text node, converted by
0266: * String.valueOf.
0267: */
0268: public void print(Object o) {
0269: _text.append(o);
0270: }
0271:
0272: /**
0273: * Adds a newline to the current text node.
0274: */
0275: public void println() {
0276: _text.append('\n');
0277: _tailLine++;
0278: }
0279:
0280: /**
0281: * Adds a boolean to the current text node.
0282: */
0283: public void println(boolean b) {
0284: _text.append(b);
0285: println();
0286: }
0287:
0288: /**
0289: * Adds a string to the current text node.
0290: */
0291: public void println(String s) {
0292: print(s);
0293: println();
0294: }
0295:
0296: /**
0297: * Adds a character to the current text node.
0298: */
0299: public void println(char ch) {
0300: print(ch);
0301: println();
0302: }
0303:
0304: /**
0305: * Adds an integer to the current text node.
0306: */
0307: public void println(int i) {
0308: _text.append(i);
0309: println();
0310: }
0311:
0312: /**
0313: * Adds a long to the current text node.
0314: */
0315: public void println(long l) {
0316: _text.append(l);
0317: println();
0318: }
0319:
0320: /**
0321: * Adds a double to the current text node.
0322: */
0323: public void println(double d) {
0324: _text.append(d);
0325: println();
0326: }
0327:
0328: /**
0329: * Adds a float to the current text node.
0330: */
0331: public void println(float f) {
0332: _text.append(f);
0333: println();
0334: }
0335:
0336: /**
0337: * Adds an object to the current text node, converted by String.valueOf.
0338: */
0339: public void println(Object o) {
0340: _text.append(o);
0341: println();
0342: }
0343:
0344: /**
0345: * flush is meaningless for XslWriter. It's only added to conform to Writer.
0346: */
0347: public void flush() {
0348: }
0349:
0350: public void close() throws IOException {
0351: try {
0352: for (int i = 0; i < _topNamespaces.size(); i++) {
0353: String topPrefix = _topNamespaces.get(i);
0354: String topUrl = _namespaces.get(topPrefix);
0355:
0356: if (topPrefix.equals(""))
0357: _xmlWriter.endPrefixMapping(null);
0358: else
0359: _xmlWriter.endPrefixMapping(topPrefix);
0360: }
0361:
0362: popText();
0363: _xmlWriter.endDocument();
0364: } catch (SAXException e) {
0365: throw new IOException(e.toString());
0366: }
0367: }
0368:
0369: public boolean getDisableEscaping() {
0370: return _disableEscaping;
0371: }
0372:
0373: public boolean disableEscaping(boolean disable) throws IOException,
0374: SAXException {
0375: if (disable != _disableEscaping) {
0376: popText();
0377: _xmlWriter.setEscapeText(!disable);
0378: }
0379:
0380: boolean old = _disableEscaping;
0381: _disableEscaping = disable;
0382:
0383: return old;
0384: }
0385:
0386: public void setLocation(String systemId, String filename, int line)
0387: throws IOException, SAXException {
0388: // Don't need to pop the text if the line # matches
0389: if (filename == null || !filename.equals(_filename)
0390: || line != _tailLine)
0391: popText();
0392:
0393: _systemId = systemId;
0394: _filename = filename;
0395: _line = line;
0396: _tailLine = line;
0397: }
0398:
0399: /**
0400: * Adds a new element to the current node, making the new element the
0401: * current node.
0402: *
0403: * <p>Each pushElement should be matched by a popElement.
0404: *
0405: * @param name name of the element
0406: */
0407: public void pushElement(String name) throws IOException,
0408: SAXException {
0409: popText();
0410:
0411: String local;
0412: int p = name.lastIndexOf(':');
0413: if (p > 0)
0414: local = name.substring(p + 1);
0415: else
0416: local = name;
0417:
0418: startElement(null, null, local, name);
0419: }
0420:
0421: /**
0422: * Adds a new element to the current node, making the new element the
0423: * current node.
0424: *
0425: * <p>Each pushElement should be matched by a popElement.
0426: *
0427: * @param name name of the element
0428: * @param namespace namespace context
0429: */
0430: public void pushElement(String name, NamespaceContext namespace)
0431: throws IOException, SAXException {
0432: popText();
0433:
0434: // Look up the proper namespace for the element.
0435: int p = name.indexOf(':');
0436: if (p <= 0) {
0437: startElement(null, null, name, name);
0438: return;
0439: }
0440:
0441: String prefix = name.substring(0, p);
0442: String url = namespace.find(namespace, prefix);
0443:
0444: if (url != null)
0445: startElement(url, prefix, name.substring(p + 1), name);
0446: else
0447: startElement(null, null, name, name);
0448: }
0449:
0450: /**
0451: * Adds a new element to the current node, making the new element the
0452: * current node.
0453: *
0454: * <p>Each pushElement should be matched by a popElement.
0455: *
0456: * @param name name of the element
0457: * @param url namespace url
0458: */
0459: public void pushElementNs(String name, String url)
0460: throws IOException, SAXException {
0461: popText();
0462:
0463: // Look up the proper namespace for the element.
0464: int p = name.indexOf(':');
0465: if (p <= 0) {
0466: startElement(url, "", name, name);
0467: return;
0468: }
0469:
0470: String prefix = name.substring(0, p);
0471: String local = name.substring(p + 1);
0472:
0473: startElement(url, prefix, local, name);
0474: }
0475:
0476: /**
0477: * Adds a namespace-aware element to the current node, making the
0478: * new element the current node.
0479: *
0480: * <p>Each pushElement should be matched by a popElement.
0481: *
0482: * @param prefix the prefix of the element name, e.g. xsl
0483: * @param local the local part of the element name, e.g. template
0484: * @param url the namespace url, e.g. http://www.xml.org/...
0485: */
0486: public void pushElement(String url, String prefix, String local,
0487: String name) throws IOException, SAXException {
0488: popText();
0489:
0490: /*
0491: if (url != null && url.startsWith("quote:"))
0492: url = url.substring(6);
0493: */
0494:
0495: startElement(url, prefix, local, name);
0496: }
0497:
0498: /**
0499: * Adds a new attribute with the given name to the current node, making
0500: * the attribute the current node.
0501: */
0502: public XMLWriter pushAttribute(String name) throws IOException,
0503: SAXException {
0504: popText();
0505:
0506: XMLWriter oldWriter = _xmlWriter;
0507: _xmlWriter = ATTR_WRITER;
0508:
0509: _attributeURL = null;
0510: _attributePrefix = null;
0511: _attributeLocalName = null;
0512: _attributeName = name;
0513:
0514: return oldWriter;
0515: }
0516:
0517: /**
0518: * Adds a new attribute with the given name to the current node, making
0519: * the attribute the current node.
0520: */
0521: public XMLWriter pushAttribute(String name,
0522: NamespaceContext namespace) throws IOException,
0523: SAXException {
0524: popText();
0525:
0526: XMLWriter oldWriter = _xmlWriter;
0527: _xmlWriter = ATTR_WRITER;
0528:
0529: // Look up the proper namespace for the element.
0530: int p = name.indexOf(':');
0531: String prefix = null;
0532: if (p > 0)
0533: prefix = name.substring(0, p);
0534: String url = namespace.find(namespace, prefix);
0535: Attr attr;
0536:
0537: if (url != null) {
0538: _attributeURL = url;
0539: _attributePrefix = prefix;
0540: _attributeLocalName = name.substring(p + 1);
0541: _attributeName = name;
0542: } else {
0543: _attributeURL = null;
0544: _attributePrefix = null;
0545: _attributeLocalName = null;
0546: _attributeName = name;
0547: }
0548:
0549: return oldWriter;
0550: }
0551:
0552: /**
0553: * Adds a new attribute to the current node, making the new attribute the
0554: * current node.
0555: *
0556: * <p>Each pushAttributeNs should be matched by a popAttribute.
0557: *
0558: * @param name name of the element
0559: * @param url namespace url
0560: */
0561: public XMLWriter pushAttributeNs(String name, String url)
0562: throws IOException, SAXException {
0563: popText();
0564:
0565: XMLWriter oldWriter = _xmlWriter;
0566: _xmlWriter = ATTR_WRITER;
0567:
0568: Attr attr;
0569:
0570: // Look up the proper namespace for the element.
0571: int p = name.indexOf(':');
0572: String prefix = null;
0573: String local = name;
0574: if (p > 0) {
0575: prefix = name.substring(0, p);
0576: local = name.substring(p + 1);
0577: }
0578:
0579: _attributeURL = url;
0580: _attributePrefix = prefix;
0581: _attributeLocalName = local;
0582: _attributeName = name;
0583:
0584: return oldWriter;
0585: }
0586:
0587: /**
0588: * Adds a namespace-aware attribute to the current node, making the
0589: * new attribute the current node.
0590: *
0591: * <p>Each pushAttribute should be matched by a popAttribute.
0592: *
0593: * @param prefix the prefix of the element name, e.g. xsl
0594: * @param local the local part of the element name, e.g. template
0595: * @param url the namespace url, e.g. http://www.xml.org/...
0596: */
0597: public XMLWriter pushAttribute(String prefix, String local,
0598: String url) throws IOException, SAXException {
0599: popText();
0600:
0601: XMLWriter oldWriter = _xmlWriter;
0602: _xmlWriter = ATTR_WRITER;
0603:
0604: /*
0605: if (url != null && url.startsWith("quote:"))
0606: url = url.substring(6);
0607: */
0608:
0609: _attributeURL = url;
0610: _attributePrefix = prefix;
0611: _attributeLocalName = local;
0612:
0613: if (prefix != null && !prefix.equals(""))
0614: _attributeName = prefix + ":" + local;
0615: else
0616: _attributeName = local;
0617:
0618: return oldWriter;
0619: }
0620:
0621: /**
0622: * Adds a namespace-aware attribute to the current node, making the
0623: * new attribute the current node.
0624: *
0625: * <p>Each pushAttribute should be matched by a popAttribute.
0626: *
0627: * @param prefix the prefix of the element name, e.g. xsl
0628: * @param local the local part of the element name, e.g. template
0629: * @param url the namespace url, e.g. http://www.xml.org/...
0630: */
0631: public void setAttribute(String prefix, String local, String url,
0632: String value) throws IOException, SAXException {
0633: popText();
0634:
0635: /*
0636: if (url != null && url.startsWith("quote:"))
0637: url = url.substring(6);
0638: */
0639:
0640: String attributeName;
0641: if (prefix != null && !prefix.equals(""))
0642: attributeName = prefix + ":" + local;
0643: else
0644: attributeName = local;
0645:
0646: attribute(url, prefix, local, attributeName, value);
0647: }
0648:
0649: /**
0650: * Adds a new attribute with the given name to the current node, making
0651: * the attribute the current node.
0652: */
0653: public void setAttribute(String name, NamespaceContext namespace,
0654: String value) throws IOException, SAXException {
0655: popText();
0656:
0657: // Look up the proper namespace for the element.
0658: int p = name.indexOf(':');
0659: String prefix = null;
0660: if (p > 0)
0661: prefix = name.substring(0, p);
0662: String url = namespace.find(namespace, prefix);
0663: Attr attr;
0664:
0665: if (url != null) {
0666: attribute(url, prefix, name.substring(p + 1), name, value);
0667: } else {
0668: attribute(null, null, null, name, value);
0669: }
0670: }
0671:
0672: /**
0673: * Sets the attribute value to the current text, and sets the current node
0674: * to the parent.
0675: */
0676: public void popAttribute(XMLWriter writer) throws IOException,
0677: SAXException {
0678: _xmlWriter = writer;
0679:
0680: attribute(_attributeURL, _attributePrefix, _attributeLocalName,
0681: _attributeName, _text.toString());
0682:
0683: _text.clear();
0684: _attributeName = null;
0685: }
0686:
0687: /**
0688: * Directly sets an attribute with a value.
0689: */
0690: public void setAttribute(String name, String value)
0691: throws IOException, SAXException {
0692: attribute(null, null, name, name, value);
0693: }
0694:
0695: /**
0696: * Copies the node without attributes or children.
0697: */
0698: public void pushCopy(Node copyNode) throws IOException,
0699: SAXException {
0700: popText();
0701:
0702: switch (copyNode.getNodeType()) {
0703: case Node.ATTRIBUTE_NODE:
0704: Node oldNode = copyNode;
0705: attribute(oldNode.getNamespaceURI(), oldNode.getPrefix(),
0706: oldNode.getLocalName(), oldNode.getNodeName(),
0707: oldNode.getNodeValue());
0708: break;
0709:
0710: case Node.DOCUMENT_NODE:
0711: return;
0712:
0713: case Node.ELEMENT_NODE:
0714: Element oldElt = (Element) copyNode;
0715:
0716: /*
0717: String oldSystemId = _systemId;
0718: String oldFilename = _filename;
0719: int oldLine = _line;
0720:
0721: _systemId = oldElt.getBaseURI();
0722: _filename = oldElt.getFilename();
0723: _line = oldElt.getLine();
0724:
0725: if (generateLocation)
0726: _xmlWriter.setLocation(.getFilename(), oldElt.getLine(), 0);
0727: */
0728:
0729: startElement(oldElt.getNamespaceURI(), oldElt.getPrefix(),
0730: oldElt.getLocalName(), oldElt.getNodeName());
0731:
0732: /*
0733: _systemId = oldSystemId;
0734: _filename = oldFilename;
0735: _line = oldLine;
0736: */
0737: break;
0738:
0739: case Node.COMMENT_NODE:
0740: _xmlWriter.comment(((Comment) copyNode).getData());
0741: break;
0742:
0743: case Node.TEXT_NODE:
0744: /*
0745: if (generateLocation)
0746: _xmlWriter.setLocation(((QAbstractNode) copyNode).getFilename(),
0747: ((QAbstractNode) copyNode).getLine(),
0748: 0);
0749: */
0750: _text.append(((Text) copyNode).getData());
0751: break;
0752:
0753: case Node.PROCESSING_INSTRUCTION_NODE:
0754: ProcessingInstruction oldPi = (ProcessingInstruction) copyNode;
0755:
0756: _xmlWriter.processingInstruction(oldPi.getNodeName(), oldPi
0757: .getNodeValue());
0758: break;
0759: }
0760: }
0761:
0762: /**
0763: * Pops the copy.
0764: */
0765: public void popCopy(Node copyNode) throws IOException, SAXException {
0766: if (copyNode.getNodeType() == Node.ELEMENT_NODE) {
0767: popText();
0768: popElement();
0769: }
0770: }
0771:
0772: public void pushPi() throws IOException, SAXException {
0773: popText();
0774: }
0775:
0776: /**
0777: * Sets the PI data to the current text, and sets the current node
0778: * to the parent.
0779: */
0780: public void popPi(String name) throws IOException, SAXException {
0781: _xmlWriter.processingInstruction(name, _text.toString());
0782: _text.clear();
0783: }
0784:
0785: /**
0786: * Adds an empty comment to the current node, making
0787: * the attribute the current node.
0788: */
0789: public void pushComment() throws IOException, SAXException {
0790: popText();
0791: }
0792:
0793: /**
0794: * Sets the comment data to the current text, and sets the current
0795: * to the the parent.
0796: */
0797: public void popComment() throws IOException, SAXException {
0798: _xmlWriter.comment(_text.toString());
0799:
0800: _text.clear();
0801: }
0802:
0803: /**
0804: * Starts a fragment. The fragment becomes the current node.
0805: */
0806: public XMLWriter pushFragment() throws IOException, SAXException {
0807: popText();
0808:
0809: DOMBuilder domBuilder = new DOMBuilder();
0810:
0811: if (_document == null)
0812: _document = Xml.createDocument();
0813: domBuilder.init(_document.createDocumentFragment());
0814: domBuilder.setDocumentLocator(_locator);
0815:
0816: XMLWriter oldWriter = _xmlWriter;
0817: _xmlWriter = domBuilder;
0818:
0819: return oldWriter;
0820: }
0821:
0822: /**
0823: * Returns the generated fragment. The current node does not contain
0824: * the new fragment.
0825: *
0826: * @return the generated fragment.
0827: */
0828: public Node popFragment(XMLWriter oldWriter) throws IOException,
0829: SAXException {
0830: popText();
0831:
0832: DOMBuilder domBuilder = (DOMBuilder) _xmlWriter;
0833:
0834: _xmlWriter = oldWriter;
0835:
0836: domBuilder.endDocument();
0837: Node node = domBuilder.getNode();
0838:
0839: return node;
0840: }
0841:
0842: /**
0843: * Adds a the contents of the node to the current node.
0844: *
0845: * @param node node to print
0846: */
0847: public void valueOf(Object node) throws IOException, SAXException {
0848: if (node == null)
0849: return;
0850: else if (node instanceof Element
0851: || node instanceof DocumentFragment) {
0852: Node elt = (Node) node;
0853: for (Node child = elt.getFirstChild(); child != null; child = child
0854: .getNextSibling()) {
0855: elementValueOf(child);
0856: }
0857: } else if (node instanceof Text) {
0858: String data = ((Text) node).getNodeValue();
0859: for (int i = 0; i < data.length(); i++) {
0860: if (!XmlChar.isWhitespace(data.charAt(i))) {
0861: print(data);
0862: return;
0863: }
0864: }
0865: /*
0866: if (! _stylesheet.stripSpaces(((Node) node).getParentNode()))
0867: */
0868: print(data);
0869: } else if (node instanceof Node) {
0870: print(((QAbstractNode) node).getNodeValue());
0871: } else if (node instanceof NodeList) {
0872: NodeList list = (NodeList) node;
0873: Node value = list.item(0);
0874:
0875: if (value != null)
0876: valueOf(value);
0877: } else if (node instanceof ArrayList) {
0878: ArrayList list = (ArrayList) node;
0879: if (list.size() > 0)
0880: valueOf(list.get(0));
0881: } else if (node instanceof Iterator) {
0882: Iterator list = (Iterator) node;
0883: valueOf(list.next());
0884: } else if (node instanceof Double) {
0885: Double d = (Double) node;
0886: double dValue = d.doubleValue();
0887:
0888: if ((int) dValue == dValue)
0889: print((int) dValue);
0890: else
0891: print(dValue);
0892: } else
0893: print(node);
0894: }
0895:
0896: /**
0897: * Adds a the contents of the node to the current node.
0898: *
0899: * @param node node to print
0900: */
0901: private void elementValueOf(Node node) throws IOException,
0902: SAXException {
0903: if (node == null)
0904: return;
0905: else if (node instanceof Element) {
0906: Element elt = (Element) node;
0907: for (Node child = elt.getFirstChild(); child != null; child = child
0908: .getNextSibling()) {
0909: elementValueOf(child);
0910: }
0911: } else if (node instanceof Text) {
0912: String data = ((Text) node).getNodeValue();
0913: for (int i = 0; i < data.length(); i++) {
0914: if (!XmlChar.isWhitespace(data.charAt(i))) {
0915: print(data);
0916: return;
0917: }
0918: }
0919: /*
0920: if (! _stylesheet.stripSpaces(node.getParentNode()))
0921: */
0922: print(data);
0923: }
0924: }
0925:
0926: /**
0927: * Adds a deep copy of the node to the current node.
0928: *
0929: * @param XPath node to be copied to the destination.
0930: */
0931: public void copyOf(Object value) throws IOException, SAXException,
0932: XPathException {
0933: popText();
0934:
0935: if (value instanceof NodeList) {
0936: NodeList list = (NodeList) value;
0937:
0938: int length = list.getLength();
0939: for (int i = 0; i < length; i++) {
0940: Node child = list.item(i);
0941:
0942: copyOf(child);
0943: }
0944: } else if (value instanceof ArrayList) {
0945: ArrayList list = (ArrayList) value;
0946:
0947: for (int i = 0; i < list.size(); i++) {
0948: Node child = (Node) list.get(i);
0949:
0950: copyOf(child);
0951: }
0952: } else if (value instanceof Iterator) {
0953: Iterator iter = (Iterator) value;
0954:
0955: while (iter.hasNext()) {
0956: Node child = (Node) iter.next();
0957:
0958: copyOf(child);
0959: }
0960: } else if (value instanceof Attr) {
0961: Attr child = (Attr) value;
0962:
0963: attribute(child.getNamespaceURI(), child.getPrefix(), child
0964: .getLocalName(), child.getNodeName(), child
0965: .getNodeValue());
0966: } else if (value instanceof QElement) {
0967: QElement child = (QElement) value;
0968:
0969: String oldSystemId = _systemId;
0970: String oldFilename = _filename;
0971: int oldLine = _line;
0972:
0973: _systemId = child.getBaseURI();
0974: _filename = child.getFilename();
0975: _line = child.getLine();
0976:
0977: startElement(child.getNamespaceURI(), child.getPrefix(),
0978: child.getLocalName(), child.getNodeName());
0979: Node subNode = child.getFirstAttribute();
0980: for (; subNode != null; subNode = subNode.getNextSibling()) {
0981: QAttr attr = (QAttr) subNode;
0982:
0983: attribute(attr.getNamespaceURI(), attr.getPrefix(),
0984: attr.getLocalName(), attr.getNodeName(), attr
0985: .getNodeValue());
0986: }
0987:
0988: for (subNode = child.getFirstChild(); subNode != null; subNode = subNode
0989: .getNextSibling()) {
0990: copyOf(subNode);
0991: }
0992:
0993: popElement();
0994:
0995: _systemId = oldSystemId;
0996: _filename = oldFilename;
0997: _line = oldLine;
0998: } else if (value instanceof DocumentFragment) {
0999: for (Node subNode = ((Node) value).getFirstChild(); subNode != null; subNode = subNode
1000: .getNextSibling()) {
1001: copyOf(subNode);
1002: }
1003: } else if (value instanceof Text) {
1004: Text child = (Text) value;
1005:
1006: _text.append(child.getNodeValue());
1007: } else if (value instanceof Comment) {
1008: Comment child = (Comment) value;
1009:
1010: _xmlWriter.comment(child.getNodeValue());
1011: } else if (value instanceof ProcessingInstruction) {
1012: ProcessingInstruction pi = (ProcessingInstruction) value;
1013:
1014: _xmlWriter.processingInstruction(pi.getNodeName(), pi
1015: .getNodeValue());
1016: } else if (value instanceof EntityReference) {
1017: EntityReference child = (EntityReference) value;
1018:
1019: _text.append("&" + child.getNodeName() + ";");
1020: } else if (value instanceof Node) {
1021: Node child = (Node) value;
1022:
1023: _text.append(child.getNodeValue());
1024: } else {
1025: print(Expr.toString(value));
1026: }
1027: }
1028:
1029: public void addNamespace(String prefix, String url) {
1030: /*
1031: if (url.startsWith("quote:"))
1032: url = url.substring(6);
1033: */
1034: if (!url.equals("")) {
1035: _namespaces.put(prefix, url);
1036:
1037: _topNamespaces.add(prefix);
1038: }
1039: }
1040:
1041: void startElement(String url, String prefix, String local,
1042: String qName) throws IOException, SAXException {
1043: if (_attributeName != null)
1044: throw error(L
1045: .l(
1046: "element `{0}' is not allowed inside attribute `{1}'. xsl:attribute must contain text only.",
1047: qName, _attributeName));
1048: popText();
1049:
1050: StackItem item = null;
1051: if (_elementStack.size() <= _depth) {
1052: item = new StackItem();
1053: _elementStack.add(item);
1054: } else
1055: item = _elementStack.get(_depth);
1056:
1057: item.init(url, prefix, local, qName, isCdata);
1058:
1059: if (_cdataElements != null && _cdataElements.get(qName) != null)
1060: isCdata = true;
1061:
1062: _depth++;
1063:
1064: _xmlWriter.startElement(url, local, qName);
1065:
1066: // Initialize top-level namespaces
1067: if (_depth == 1) {
1068: for (int i = 0; i < _topNamespaces.size(); i++) {
1069: String topPrefix = _topNamespaces.get(i);
1070: String topUrl = _namespaces.get(topPrefix);
1071:
1072: if (topPrefix.equals("")) {
1073: _xmlWriter.startPrefixMapping(null, topUrl);
1074: _xmlWriter.attribute(XMLNS, null, "xmlns", topUrl);
1075: } else {
1076: _xmlWriter.startPrefixMapping(topPrefix, topUrl);
1077: _xmlWriter.attribute(XMLNS, topPrefix, "xmlns:"
1078: + topPrefix, topUrl);
1079: }
1080: }
1081: }
1082:
1083: if (url == null)
1084: return;
1085:
1086: bindNamespace(prefix, url);
1087: }
1088:
1089: public void popElement() throws IOException, SAXException {
1090: popText();
1091: _depth--;
1092:
1093: StackItem item = _elementStack.get(_depth);
1094:
1095: try {
1096: _xmlWriter.endElement(item.getNamespace(), item
1097: .getLocalName(), item.getName());
1098:
1099: // If this element bound namespaces, pop the old values
1100: for (int i = 0; i < item.nsSize(); i++) {
1101: String oldPrefix = item.getNSPrefix(i);
1102: String oldUrl = item.getNSUrl(i);
1103:
1104: if (oldUrl == null)
1105: _namespaces.remove(oldPrefix);
1106: else
1107: _namespaces.put(oldPrefix, oldUrl);
1108:
1109: _xmlWriter.endPrefixMapping(oldPrefix);
1110: }
1111:
1112: isCdata = item.getCdata();
1113: } catch (Throwable e) {
1114: log.log(Level.FINE, e.toString(), e);
1115: }
1116: }
1117:
1118: /**
1119: * Sends the attribute to the output
1120: *
1121: * @param url the namespace for the attribute name
1122: * @param prefix the prefix for the attribute name
1123: * @param local the local attribute name
1124: * @param qName the full qualified name
1125: * @param value the attribute's value
1126: */
1127: public void attribute(String url, String prefix, String local,
1128: String qName, String value) throws IOException,
1129: SAXException {
1130: if (qName.startsWith("xmlns:"))
1131: bindNamespace(qName.substring("xmlns:".length()), value);
1132: else if (qName.equals("xmlns"))
1133: bindNamespace(null, value);
1134: else {
1135: _xmlWriter.attribute(url, local, qName, value);
1136:
1137: // null namespace binding doesn't add binding
1138: if (url != null && !url.equals("") && !prefix.equals(""))
1139: bindNamespace(prefix, url);
1140: }
1141: }
1142:
1143: /**
1144: * Sends the attribute to the output
1145: *
1146: * @param url the namespace for the attribute name
1147: * @param prefix the prefix for the attribute name
1148: * @param local the local attribute name
1149: * @param qName the full qualified name
1150: * @param value the attribute's value
1151: */
1152: public void attribute(String qName, String value)
1153: throws IOException, SAXException {
1154: _xmlWriter.attribute(null, null, qName, value);
1155: }
1156:
1157: public void bindNamespace(String prefix, String url)
1158: throws IOException, SAXException {
1159: String oldUrl = _namespaces.get(prefix);
1160:
1161: // If the namespace matches, return
1162: if (oldUrl == null && url.equals("") || oldUrl != null
1163: && url.equals(oldUrl))
1164: return;
1165:
1166: // Send the namespace declaration to the writer
1167: if (prefix != null) {
1168: _xmlWriter.startPrefixMapping(prefix, url);
1169: _xmlWriter.attribute(XMLNS, prefix, "xmlns:" + prefix, url);
1170: _namespaces.put(prefix, url);
1171: } else {
1172: _xmlWriter.startPrefixMapping(null, url);
1173: _xmlWriter.attribute(XMLNS, null, "xmlns", url);
1174: _namespaces.put(null, url);
1175: }
1176:
1177: StackItem item = _elementStack.get(_depth - 1);
1178: item.addNamespace(prefix, oldUrl);
1179: }
1180:
1181: /**
1182: * Pop the accumulated text to the DOM.
1183: */
1184: public void popText() throws IOException, SAXException {
1185: if (_xmlWriter == ATTR_WRITER)
1186: return;
1187:
1188: Text textNode = null;
1189:
1190: if (_text.length() == 0)
1191: return;
1192:
1193: if (_filename != null)
1194: _line = _tailLine;
1195:
1196: if (isCdata)
1197: _xmlWriter.cdata(_text.getBuffer(), 0, _text.getLength());
1198: else
1199: _xmlWriter.text(_text.getBuffer(), 0, _text.getLength());
1200:
1201: _text.clear();
1202: }
1203:
1204: /**
1205: * Returns the attribute with the given name.
1206: */
1207: public Object getProperty(String name) {
1208: return _transformer.getProperty(name);
1209: }
1210:
1211: /**
1212: * Sets the attribute with the given name.
1213: */
1214: public void setProperty(String name, Object value) {
1215: _transformer.setProperty(name, value);
1216: }
1217:
1218: /**
1219: * removes the attribute with the given name.
1220: */
1221: public void removeProperty(String name) {
1222: }
1223:
1224: /**
1225: * Lists the names of all the attributes.
1226: */
1227: public Iterator getPropertyNames() {
1228: return null;
1229: }
1230:
1231: public Object getParameter(String name) {
1232: return _transformer.getParameter(name);
1233: }
1234:
1235: public Path getPwd() {
1236: return (Path) getProperty("caucho.pwd");
1237: }
1238:
1239: public OutputStream openWrite(ExprEnvironment env, String href)
1240: throws IOException {
1241: if (_xmlWriter instanceof XmlPrinter) {
1242: XmlPrinter printer = (XmlPrinter) _xmlWriter;
1243:
1244: Path path = printer.getPath();
1245:
1246: if (path != null) {
1247: Path dst = path.getParent().lookup(href);
1248: dst.getParent().mkdirs();
1249:
1250: return dst.openWrite();
1251: }
1252: }
1253:
1254: Path stylesheetPath = env.getStylesheetEnv().getPath();
1255:
1256: return stylesheetPath.getParent().lookup(href).openWrite();
1257: }
1258:
1259: public XslWriter openResultDocument(OutputStream os)
1260: throws IOException, SAXException {
1261: XMLWriter writer = new XmlPrinter(os);
1262: XslWriter out = new XslWriter(null, _stylesheet, _transformer);
1263: out.init(writer);
1264:
1265: writer.startDocument();
1266:
1267: return out;
1268: }
1269:
1270: /**
1271: * @deprecated
1272: */
1273: public javax.servlet.jsp.PageContext getPage() {
1274: return (javax.servlet.jsp.PageContext) getProperty("caucho.page.context");
1275: }
1276:
1277: private IOException error(String message) {
1278: if (_filename != null)
1279: return new IOException(_filename + ":" + _line + ": "
1280: + message);
1281: else
1282: return new IOException(message);
1283: }
1284:
1285: public String getSystemId() {
1286: if (_systemId != null)
1287: return _systemId;
1288: else
1289: return _filename;
1290: }
1291:
1292: public String getFilename() {
1293: if (_filename != null)
1294: return _filename;
1295: else
1296: return _systemId;
1297: }
1298:
1299: public String getPublicId() {
1300: return null;
1301: }
1302:
1303: public int getLineNumber() {
1304: return _line;
1305: }
1306:
1307: public int getColumnNumber() {
1308: return 0;
1309: }
1310:
1311: static class StackItem {
1312: String _url;
1313: String _prefix;
1314: String _local;
1315: String _qName;
1316: boolean _isCdata;
1317:
1318: ArrayList<String> _nsPrefixes;
1319: ArrayList<String> _nsUrls;
1320:
1321: void clear() {
1322: }
1323:
1324: void init(String url, String prefix, String local,
1325: String qName, boolean isCdata) {
1326: if (_nsPrefixes != null) {
1327: _nsPrefixes.clear();
1328: _nsUrls.clear();
1329: }
1330:
1331: _url = url;
1332: _prefix = prefix;
1333: _local = local;
1334: _qName = qName;
1335:
1336: _isCdata = isCdata;
1337: }
1338:
1339: String getNamespace() {
1340: return _url;
1341: }
1342:
1343: String getPrefix() {
1344: return _prefix;
1345: }
1346:
1347: String getLocalName() {
1348: return _local;
1349: }
1350:
1351: String getName() {
1352: return _qName;
1353: }
1354:
1355: boolean getCdata() {
1356: return _isCdata;
1357: }
1358:
1359: int nsSize() {
1360: return _nsPrefixes == null ? 0 : _nsPrefixes.size();
1361: }
1362:
1363: String getNSPrefix(int i) {
1364: return _nsPrefixes.get(i);
1365: }
1366:
1367: String getNSUrl(int i) {
1368: return _nsUrls.get(i);
1369: }
1370:
1371: void addNamespace(String prefix, String oldUrl) {
1372: if (_nsPrefixes == null) {
1373: _nsPrefixes = new ArrayList<String>();
1374: _nsUrls = new ArrayList<String>();
1375: }
1376:
1377: _nsPrefixes.add(prefix);
1378: _nsUrls.add(oldUrl);
1379: }
1380: }
1381: }
|