001: package net.sf.saxon.event;
002:
003: import net.sf.saxon.om.NamespaceConstant;
004: import net.sf.saxon.om.NamePool;
005: import net.sf.saxon.trans.XPathException;
006: import net.sf.saxon.type.Type;
007: import net.sf.saxon.value.Whitespace;
008:
009: import javax.xml.transform.OutputKeys;
010: import javax.xml.transform.Result;
011: import java.util.ArrayList;
012: import java.util.List;
013: import java.util.Properties;
014:
015: /**
016: * This class is used when the decision on which serialization method to use has to be delayed until the first
017: * element is read. It buffers comments and processing instructions until that happens; then when the first
018: * element arrives it creates a real serialization pipeline and uses that for future output.
019: * @author Michael H. Kay
020: */
021:
022: public class UncommittedSerializer extends ProxyReceiver {
023:
024: boolean committed = false;
025: List pending = null;
026: Result finalResult;
027: Properties outputProperties;
028:
029: public UncommittedSerializer(Result finalResult,
030: Properties outputProperties) {
031: this .finalResult = finalResult;
032: this .outputProperties = outputProperties;
033: setUnderlyingReceiver(new Sink());
034: }
035:
036: public void open() throws XPathException {
037: committed = false;
038: }
039:
040: /**
041: * End of document
042: */
043:
044: public void close() throws XPathException {
045: // empty output: must send a beginDocument()/endDocument() pair to the content handler
046: if (!committed) {
047: switchToMethod("xml");
048: }
049: getUnderlyingReceiver().close();
050: }
051:
052: /**
053: * Produce character output using the current Writer. <BR>
054: */
055:
056: public void characters(CharSequence chars, int locationId,
057: int properties) throws XPathException {
058: if (committed) {
059: getUnderlyingReceiver().characters(chars, locationId,
060: properties);
061: } else {
062: if (pending == null) {
063: pending = new ArrayList(10);
064: }
065: PendingNode node = new PendingNode();
066: node.kind = Type.TEXT;
067: node.name = null;
068: node.content = chars;
069: node.locationId = locationId;
070: node.properties = properties;
071: pending.add(node);
072: if (!Whitespace.isWhite(chars)) {
073: switchToMethod("xml");
074: }
075: }
076: }
077:
078: /**
079: * Processing Instruction
080: */
081:
082: public void processingInstruction(String target, CharSequence data,
083: int locationId, int properties) throws XPathException {
084: if (committed) {
085: getUnderlyingReceiver().processingInstruction(target, data,
086: locationId, properties);
087: } else {
088: if (pending == null) {
089: pending = new ArrayList(10);
090: }
091: PendingNode node = new PendingNode();
092: node.kind = Type.PROCESSING_INSTRUCTION;
093: node.name = target;
094: node.content = data;
095: node.locationId = locationId;
096: node.properties = properties;
097: pending.add(node);
098: }
099: }
100:
101: /**
102: * Output a comment
103: */
104:
105: public void comment(CharSequence chars, int locationId,
106: int properties) throws XPathException {
107: if (committed) {
108: getUnderlyingReceiver().comment(chars, locationId,
109: properties);
110: } else {
111: if (pending == null) {
112: pending = new ArrayList(10);
113: }
114: PendingNode node = new PendingNode();
115: node.kind = Type.COMMENT;
116: node.name = null;
117: node.content = chars;
118: node.locationId = locationId;
119: node.properties = properties;
120: pending.add(node);
121: }
122: }
123:
124: /**
125: * Output an element start tag. <br>
126: * This can only be called once: it switches to a substitute output generator for XML, XHTML, or HTML,
127: * depending on the element name.
128: * @param nameCode The element name (tag)
129: * @param typeCode The type annotation
130: * @param properties Bit field holding special properties of the element
131: */
132:
133: public void startElement(int nameCode, int typeCode,
134: int locationId, int properties) throws XPathException {
135: if (!committed) {
136: NamePool namePool = getNamePool();
137: String name = namePool.getLocalName(nameCode);
138: short uriCode = namePool.getURICode(nameCode);
139: if (name.equalsIgnoreCase("html")
140: && uriCode == NamespaceConstant.NULL_CODE) {
141: switchToMethod("html");
142: } else if (name.equals("html")
143: && namePool.getURIFromURICode(uriCode).equals(
144: NamespaceConstant.XHTML)) {
145: String version = outputProperties
146: .getProperty(SaxonOutputKeys.STYLESHEET_VERSION);
147: if ("1".equals(version)) {
148: switchToMethod("xml");
149: } else {
150: switchToMethod("xhtml");
151: }
152: } else {
153: switchToMethod("xml");
154: }
155: }
156: getUnderlyingReceiver().startElement(nameCode, typeCode,
157: locationId, properties);
158: }
159:
160: /**
161: * Switch to a specific emitter once the output method is known
162: */
163:
164: private void switchToMethod(String method) throws XPathException {
165: Properties newProperties = new Properties(outputProperties);
166: newProperties.setProperty(OutputKeys.METHOD, method);
167: Receiver target = ResultWrapper.getReceiver(finalResult,
168: getPipelineConfiguration(), newProperties);
169: committed = true;
170: target.open();
171: target.startDocument(0);
172: if (pending != null) {
173: for (int i = 0; i < pending.size(); i++) {
174: PendingNode node = (PendingNode) pending.get(i);
175: switch (node.kind) {
176: case Type.COMMENT:
177: target.comment(node.content, node.locationId,
178: node.properties);
179: break;
180: case Type.PROCESSING_INSTRUCTION:
181: target.processingInstruction(node.name,
182: node.content, node.locationId,
183: node.properties);
184: break;
185: case Type.TEXT:
186: target.characters(node.content, node.locationId,
187: node.properties);
188: break;
189: }
190: }
191: pending = null;
192: }
193: setUnderlyingReceiver(target);
194: }
195:
196: /**
197: * A text, comment, or PI node that hasn't been output yet because we don't yet know what output
198: * method to use
199: */
200:
201: private static final class PendingNode {
202: int kind;
203: String name;
204: CharSequence content;
205: int properties;
206: int locationId;
207: }
208:
209: }
210:
211: //
212: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
213: // you may not use this file except in compliance with the License. You may obtain a copy of the
214: // License at http://www.mozilla.org/MPL/
215: //
216: // Software distributed under the License is distributed on an "AS IS" basis,
217: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
218: // See the License for the specific language governing rights and limitations under the License.
219: //
220: // The Original Code is: all this file.
221: //
222: // The Initial Developer of the Original Code is Michael H. Kay.
223: //
224: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
225: //
226: // Contributor(s): none.
227: //
|