001: package net.sf.saxon.event;
002:
003: import net.sf.saxon.trans.XPathException;
004: import net.sf.saxon.tinytree.TinyBuilder;
005: import net.sf.saxon.om.*;
006: import net.sf.saxon.type.Type;
007: import net.sf.saxon.value.AtomicValue;
008:
009: /**
010: * This outputter is used when writing a sequence of atomic values and nodes, for
011: * example, when xsl:variable is used with content and an "as" attribute. The outputter
012: * builds the sequence; the concrete subclass is responsible for deciding what to do with the
013: * resulting items.
014: *
015: * <p>This class is not used to build temporary trees. For that, the ComplexContentOutputter
016: * is used.</p>
017: *
018: *
019: * @author Michael H. Kay
020: */
021:
022: public abstract class SequenceWriter extends SequenceReceiver {
023: private String systemId;
024: private Receiver tree = null;
025: private TinyBuilder builder = null;
026: private int level = 0;
027: private boolean inStartTag = false;
028: private static final int[] treeSizeParameters = { 50, 10, 5, 200 };
029:
030: /**
031: * Abstract method to be supplied by subclasses: output one item in the sequence.
032: */
033:
034: public abstract void write(Item item) throws XPathException;
035:
036: /**
037: * Set the system ID
038: * @param systemId the URI used to identify the tree being passed across this interface
039: */
040:
041: public void setSystemId(String systemId) {
042: this .systemId = systemId;
043: }
044:
045: /**
046: * Get the system ID
047: * @return the system ID that was supplied using the setSystemId() method
048: */
049:
050: public String getSystemId() {
051: return systemId;
052: }
053:
054: /**
055: * Determine whether there are any open document or element nodes in the output
056: */
057:
058: public boolean hasOpenNodes() {
059: return level != 0;
060: }
061:
062: /**
063: * Start of a document node.
064: */
065:
066: public void startDocument(int properties) throws XPathException {
067: if (tree == null) {
068: createTree();
069: }
070: if (level++ == 0) {
071: tree.startDocument(properties);
072: }
073: }
074:
075: /**
076: * Create a TinyTree to hold a document or element node.
077: * @throws net.sf.saxon.trans.XPathException
078: */
079:
080: private void createTree() throws XPathException {
081: builder = new TinyBuilder();
082: builder.setPipelineConfiguration(getPipelineConfiguration());
083: builder.setSizeParameters(treeSizeParameters);
084:
085: NamespaceReducer reducer = new NamespaceReducer();
086: reducer.setUnderlyingReceiver(builder);
087: reducer.setPipelineConfiguration(getPipelineConfiguration());
088:
089: ComplexContentOutputter complex = new ComplexContentOutputter();
090: complex.setPipelineConfiguration(getPipelineConfiguration());
091: complex.setReceiver(reducer);
092: tree = complex;
093:
094: tree.setSystemId(systemId);
095: tree.setPipelineConfiguration(getPipelineConfiguration());
096: tree.open();
097: }
098:
099: /**
100: * Notify the end of a document node
101: */
102:
103: public void endDocument() throws XPathException {
104: if (--level == 0) {
105: tree.endDocument();
106: DocumentInfo doc = (DocumentInfo) builder.getCurrentRoot();
107: // add the constructed document to the result sequence
108: append(doc, 0, NodeInfo.ALL_NAMESPACES);
109: }
110: previousAtomic = false;
111: }
112:
113: /**
114: * Output an element start tag.
115: * @param nameCode The element name code - a code held in the Name Pool
116: * @param typeCode Integer code identifying the type of this element. Zero identifies the default
117: * type, that is xs:anyType
118: * @param properties bit-significant flags indicating any special information
119: */
120:
121: public void startElement(int nameCode, int typeCode,
122: int locationId, int properties) throws XPathException {
123:
124: if (inStartTag) {
125: startContent();
126: }
127:
128: if (tree == null) {
129: createTree();
130: }
131:
132: tree.startElement(nameCode, typeCode, locationId, properties);
133: level++;
134: inStartTag = true;
135: previousAtomic = false;
136: }
137:
138: /**
139: * Output an element end tag.
140: */
141:
142: public void endElement() throws XPathException {
143: if (inStartTag) {
144: startContent();
145: }
146: tree.endElement();
147: if (--level == 0) {
148: NodeInfo element = builder.getCurrentRoot();
149: append(element, 0, NodeInfo.ALL_NAMESPACES);
150: }
151: previousAtomic = false;
152: }
153:
154: /**
155: * Output a namespace declaration. <br>
156: * This is added to a list of pending namespaces for the current start tag.
157: * If there is already another declaration of the same prefix, this one is
158: * ignored.
159: * Note that unlike SAX2 startPrefixMapping(), this call is made AFTER writing the start tag.
160: * @param nscode The namespace code
161: * @param properties Allows special properties to be passed if required
162: * @throws net.sf.saxon.trans.XPathException if there is no start tag to write to (created using writeStartTag),
163: * or if character content has been written since the start tag was written.
164: */
165:
166: public void namespace(int nscode, int properties)
167: throws XPathException {
168: if (level == 0) {
169: NamePool namePool = getNamePool();
170: Orphan o = new Orphan(getConfiguration());
171: o.setNodeKind(Type.NAMESPACE);
172: o.setNameCode(namePool.allocate("", "", namePool
173: .getPrefixFromNamespaceCode(nscode)));
174: o.setStringValue(namePool.getURIFromNamespaceCode(nscode));
175: append(o, 0, NodeInfo.ALL_NAMESPACES);
176: } else {
177: tree.namespace(nscode, properties);
178: }
179: previousAtomic = false;
180: }
181:
182: /**
183: * Output an attribute value. <br>
184: * @param nameCode An integer code representing the name of the attribute, as held in the Name Pool
185: * @param typeCode Integer code identifying the type annotation of the attribute; zero represents
186: * the default type (xs:untypedAtomic)
187: * @param value The value of the attribute
188: * @param properties Bit significant flags for passing extra information to the serializer, e.g.
189: * to disable escaping
190: * @throws net.sf.saxon.trans.XPathException if there is no start tag to write to (created using writeStartTag),
191: * or if character content has been written since the start tag was written.
192: */
193:
194: public void attribute(int nameCode, int typeCode,
195: CharSequence value, int locationId, int properties)
196: throws XPathException {
197: if (level == 0) {
198: Orphan o = new Orphan(getConfiguration());
199: o.setNodeKind(Type.ATTRIBUTE);
200: o.setNameCode(nameCode);
201: o.setStringValue(value);
202: o.setTypeAnnotation(typeCode);
203: append(o, locationId, NodeInfo.ALL_NAMESPACES);
204: } else {
205: tree.attribute(nameCode, typeCode, value, locationId,
206: properties);
207: }
208: previousAtomic = false;
209: }
210:
211: /**
212: * The startContent() event is notified after all namespaces and attributes of an element
213: * have been notified, and before any child nodes are notified.
214: * @throws net.sf.saxon.trans.XPathException for any failure
215: */
216:
217: public void startContent() throws XPathException {
218: inStartTag = false;
219: tree.startContent();
220: previousAtomic = false;
221: }
222:
223: /**
224: * Produce text content output. <BR>
225: * @param s The String to be output
226: * @param properties bit-significant flags for extra information, e.g. disable-output-escaping
227: * @throws net.sf.saxon.trans.XPathException for any failure
228: */
229:
230: public void characters(CharSequence s, int locationId,
231: int properties) throws XPathException {
232: if (level == 0) {
233: Orphan o = new Orphan(getConfiguration());
234: o.setNodeKind(Type.TEXT);
235: o.setStringValue(s);
236: append(o, locationId, NodeInfo.ALL_NAMESPACES);
237: } else {
238: if (s.length() > 0) {
239: if (inStartTag) {
240: startContent();
241: }
242: tree.characters(s, locationId, properties);
243: }
244: }
245: previousAtomic = false;
246: }
247:
248: /**
249: * Write a comment.
250: */
251:
252: public void comment(CharSequence comment, int locationId,
253: int properties) throws XPathException {
254: if (inStartTag) {
255: startContent();
256: }
257: if (level == 0) {
258: Orphan o = new Orphan(getConfiguration());
259: o.setNodeKind(Type.COMMENT);
260: o.setStringValue(comment);
261: append(o, locationId, NodeInfo.ALL_NAMESPACES);
262: } else {
263: tree.comment(comment, locationId, properties);
264: }
265: previousAtomic = false;
266: }
267:
268: /**
269: * Write a processing instruction
270: * No-op in this implementation
271: */
272:
273: public void processingInstruction(String target, CharSequence data,
274: int locationId, int properties) throws XPathException {
275: if (inStartTag) {
276: startContent();
277: }
278: if (level == 0) {
279: Orphan o = new Orphan(getConfiguration());
280: o.setNameCode(getNamePool().allocate("", "", target));
281: o.setNodeKind(Type.PROCESSING_INSTRUCTION);
282: o.setStringValue(data);
283: append(o, locationId, NodeInfo.ALL_NAMESPACES);
284: } else {
285: tree.processingInstruction(target, data, locationId,
286: properties);
287: }
288: previousAtomic = false;
289: }
290:
291: /**
292: * Close the output
293: */
294:
295: public void close() throws XPathException {
296: previousAtomic = false;
297: if (tree != null) {
298: tree.close();
299: }
300: }
301:
302: /**
303: * Append an item to the sequence, performing any necessary type-checking and conversion
304: */
305:
306: public void append(Item item, int locationId, int copyNamespaces)
307: throws XPathException {
308:
309: if (item == null) {
310: return;
311: }
312:
313: if (level == 0) {
314: write(item);
315: previousAtomic = false;
316: } else {
317: if (item instanceof AtomicValue) {
318: // If an atomic value is written to a tree, and the previous item was also
319: // an atomic value, then add a single space to separate them
320: if (previousAtomic) {
321: tree.characters(" ", 0, 0);
322: }
323: tree.characters(item.getStringValueCS(), 0, 0);
324: previousAtomic = true;
325: } else {
326: ((NodeInfo) item).copy(tree, NodeInfo.ALL_NAMESPACES,
327: true, locationId);
328: previousAtomic = false;
329: }
330: }
331: }
332: }
333:
334: //
335: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
336: // you may not use this file except in compliance with the License. You may obtain a copy of the
337: // License at http://www.mozilla.org/MPL/
338: //
339: // Software distributed under the License is distributed on an "AS IS" basis,
340: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
341: // See the License for the specific language governing rights and limitations under the License.
342: //
343: // The Original Code is: all this file.
344: //
345: // The Initial Developer of the Original Code is Michael H. Kay
346: //
347: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
348: //
349: // Contributor(s): none.
350: //
|