All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.event.SequenceWriter Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.event;

import net.sf.saxon.Configuration;
import net.sf.saxon.expr.parser.ExplicitLocation;
import net.sf.saxon.expr.parser.Location;
import net.sf.saxon.ma.arrays.ArrayItem;
import net.sf.saxon.om.*;
import net.sf.saxon.trans.Err;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.linked.LinkedTreeBuilder;
import net.sf.saxon.tree.util.Orphan;
import net.sf.saxon.type.SchemaType;
import net.sf.saxon.type.SimpleType;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.AtomicValue;
import net.sf.saxon.value.ObjectValue;

/**
 * The SequenceWriter is used when writing a sequence of atomic values and nodes, for
 * example, when xsl:variable is used with content and an "as" attribute. The SequenceWriter
 * builds the sequence; the concrete subclass is responsible for deciding what to do with the
 * resulting items.
 * 

*

This class is not used to build temporary trees. For that, the ComplexContentOutputter * is used.

* * @author Michael H. Kay */ public abstract class SequenceWriter extends SequenceReceiver { private Receiver outputter = null; private TreeModel treeModel = null; private Builder builder = null; private int level = 0; private boolean inStartTag = false; public SequenceWriter(/*@NotNull*/ PipelineConfiguration pipe) { super(pipe); //System.err.println("SequenceWriter init"); } /** * Abstract method to be supplied by subclasses: output one item in the sequence. * * @param item the item to be written to the sequence * @throws net.sf.saxon.trans.XPathException * if any failure occurs while writing the item */ public abstract void write(Item item) throws XPathException; /** * Start of a document node. */ public void startDocument(int properties) throws XPathException { if (outputter == null) { createTree((properties & ReceiverOptions.MUTABLE_TREE) != 0); } if (level++ == 0) { outputter.startDocument(properties); } } /** * Notify an unparsed entity URI. * * @param name The name of the unparsed entity * @param systemID The system identifier of the unparsed entity * @param publicID The public identifier of the unparsed entity */ @Override public void setUnparsedEntity(String name, String systemID, String publicID) throws XPathException { if (builder != null) { builder.setUnparsedEntity(name, systemID, publicID); } } /** * Create a (skeletal) tree to hold a document or element node. * * @param mutable set to true if the tree is required to support in-situ updates (other that the initial * sequential writing of nodes to construct the tree) * @throws net.sf.saxon.trans.XPathException * if any error occurs while creating the tree */ private void createTree(boolean mutable) throws XPathException { PipelineConfiguration pipe = getPipelineConfiguration(); if (treeModel != null) { builder = treeModel.makeBuilder(pipe); } else { if (mutable) { TreeModel model = pipe.getController().getModel(); if (model.isMutable()) { builder = pipe.getController().makeBuilder(); } else { builder = new LinkedTreeBuilder(pipe); } } else { builder = pipe.getController().makeBuilder(); } } builder.setPipelineConfiguration(pipe); builder.setSystemId(systemId); builder.setBaseURI(systemId); builder.setTiming(false); NamespaceReducer reducer = new NamespaceReducer(builder); ComplexContentOutputter cco = new ComplexContentOutputter(pipe); cco.setHostLanguage(pipe.getHostLanguage()); cco.setReceiver(reducer); outputter = cco; outputter.setSystemId(systemId); outputter.setPipelineConfiguration(pipe); outputter.open(); } /** * Get the tree model that will be used for creating trees when events are written to the sequence * @return the tree model, if one has been set using setTreeModel(); otherwise null */ public TreeModel getTreeModel() { return treeModel; } /** * Set the tree model that will be used for creating trees when events are written to the sequence * @param treeModel the tree model to be used. If none has been set, the default tree model for the configuration * is used, unless a mutable tree is required and the default tree model is not mutable, in which case a linked * tree is used. */ public void setTreeModel(TreeModel treeModel) { this.treeModel = treeModel; } /** * Notify the end of a document node */ public void endDocument() throws XPathException { if (--level == 0) { outputter.endDocument(); outputter = null; NodeInfo doc = builder.getCurrentRoot(); // add the constructed document to the result sequence append(doc, ExplicitLocation.UNKNOWN_LOCATION, NodeInfo.ALL_NAMESPACES); } previousAtomic = false; } /** * Output an element start tag. * @param elemName The element name code - a code held in the Name Pool * @param typeCode Integer code identifying the type of this element. Zero identifies the default * type, that is xs:anyType * @param location * @param properties bit-significant flags indicating any special information */ public void startElement(NodeName elemName, SchemaType typeCode, Location location, int properties) throws XPathException { if (inStartTag) { startContent(); } if (outputter == null) { createTree((properties & ReceiverOptions.MUTABLE_TREE) != 0); } //System.err.println("SEQUENCE_WRITER startElement " + this); outputter.startElement(elemName, typeCode, location, properties); level++; inStartTag = true; previousAtomic = false; } /** * Output an element end tag. */ public void endElement() throws XPathException { if (inStartTag) { startContent(); } //System.err.println("SEQUENCE_WRITER endElement " + this); outputter.endElement(); if (--level == 0) { outputter.close(); outputter = null; NodeInfo element = builder.getCurrentRoot(); append(element, ExplicitLocation.UNKNOWN_LOCATION, NodeInfo.ALL_NAMESPACES); } previousAtomic = false; } /** * Output a namespace declaration.
* This is added to a list of pending namespaces for the current start tag. * If there is already another declaration of the same prefix, this one is * ignored. * Note that unlike SAX2 startPrefixMapping(), this call is made AFTER writing the start tag. * * @param namespaceBinding The namespace binding * @param properties Allows special properties to be passed if required * @throws net.sf.saxon.trans.XPathException * if there is no start tag to write to (created using writeStartTag), * or if character content has been written since the start tag was written. */ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException { if (level == 0) { Orphan o = new Orphan(getConfiguration()); o.setNodeKind(Type.NAMESPACE); o.setNodeName(new NoNamespaceName(namespaceBinding.getPrefix())); o.setStringValue(namespaceBinding.getURI()); append(o, ExplicitLocation.UNKNOWN_LOCATION, NodeInfo.ALL_NAMESPACES); } else { outputter.namespace(namespaceBinding, properties); } previousAtomic = false; } /** * Output an attribute value.
* * @param attName An integer code representing the name of the attribute, as held in the Name Pool * @param typeCode Integer code identifying the type annotation of the attribute; zero represents * the default type (xs:untypedAtomic) * @param value The value of the attribute * @param locationId *@param properties Bit significant flags for passing extra information to the serializer, e.g. * to disable escaping @throws net.sf.saxon.trans.XPathException * if there is no start tag to write to (created using writeStartTag), * or if character content has been written since the start tag was written. */ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, Location locationId, int properties) throws XPathException { if (level == 0) { Orphan o = new Orphan(getConfiguration()); o.setNodeKind(Type.ATTRIBUTE); o.setNodeName(attName); o.setStringValue(value); o.setTypeAnnotation(typeCode); append(o, locationId, NodeInfo.ALL_NAMESPACES); } else { outputter.attribute(attName, typeCode, value, locationId, properties); } previousAtomic = false; } /** * The startContent() event is notified after all namespaces and attributes of an element * have been notified, and before any child nodes are notified. * * @throws net.sf.saxon.trans.XPathException * for any failure */ public void startContent() throws XPathException { inStartTag = false; outputter.startContent(); previousAtomic = false; } /** * Produce text content output.
* * @param s The String to be output * @param locationId *@param properties bit-significant flags for extra information, e.g. disable-output-escaping @throws net.sf.saxon.trans.XPathException * for any failure */ public void characters(CharSequence s, Location locationId, int properties) throws XPathException { if (level == 0) { Orphan o = new Orphan(getConfiguration()); o.setNodeKind(Type.TEXT); o.setStringValue(s.toString()); append(o, locationId, NodeInfo.ALL_NAMESPACES); } else { if (s.length() > 0) { if (inStartTag) { startContent(); } outputter.characters(s, locationId, properties); } } previousAtomic = false; } /** * Write a comment. */ public void comment(CharSequence comment, Location locationId, int properties) throws XPathException { if (inStartTag) { startContent(); } if (level == 0) { Orphan o = new Orphan(getConfiguration()); o.setNodeKind(Type.COMMENT); o.setStringValue(comment); append(o, locationId, NodeInfo.ALL_NAMESPACES); } else { outputter.comment(comment, locationId, properties); } previousAtomic = false; } /** * Write a processing instruction * No-op in this implementation */ public void processingInstruction(String target, CharSequence data, Location locationId, int properties) throws XPathException { if (inStartTag) { startContent(); } if (level == 0) { Orphan o = new Orphan(getConfiguration()); o.setNodeName(new NoNamespaceName(target)); o.setNodeKind(Type.PROCESSING_INSTRUCTION); o.setStringValue(data); append(o, locationId, NodeInfo.ALL_NAMESPACES); } else { outputter.processingInstruction(target, data, locationId, properties); } previousAtomic = false; } /** * Close the output */ public void close() throws XPathException { previousAtomic = false; if (outputter != null) { outputter.close(); } } /** * Append an item to the sequence, performing any necessary type-checking and conversion */ public void append(/*@Nullable*/ Item item, Location locationId, int copyNamespaces) throws XPathException { if (item == null) { return; } if (level == 0) { write(item); previousAtomic = false; } else { if (item instanceof AtomicValue || item instanceof ObjectValue) { // If an atomic value is written to a tree, and the previous item was also // an atomic value, then add a single space to separate them if (previousAtomic) { outputter.characters(" ", ExplicitLocation.UNKNOWN_LOCATION, 0); } outputter.characters(item.getStringValueCS(), ExplicitLocation.UNKNOWN_LOCATION, 0); previousAtomic = true; } else if (item instanceof ArrayItem) { for (Sequence member : (ArrayItem) item) { SequenceIterator iter = member.iterate(); Item it; while ((it = iter.next()) != null) { append(it, locationId, copyNamespaces); } } } else if (item instanceof Function) { XPathException err = new XPathException("Cannot write a function item to an XML tree", "FOTY0013"); err.setLocator(locationId.saveLocation()); throw err; } else { NodeInfo node = (NodeInfo) item; if (node.getNodeKind() == Type.ATTRIBUTE && ((SimpleType) node.getSchemaType()).isNamespaceSensitive()) { XPathException err = new XPathException("Cannot copy attributes whose type is namespace-sensitive (QName or NOTATION): " + Err.wrap(node.getDisplayName(), Err.ATTRIBUTE)); err.setErrorCode(getPipelineConfiguration().getHostLanguage() == Configuration.XSLT ? "XTTE0950" : "XQTY0086"); throw err; } ((NodeInfo) item).copy(outputter, CopyOptions.ALL_NAMESPACES | CopyOptions.TYPE_ANNOTATIONS, locationId); previousAtomic = false; } } } /** * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations * supplied on element and attribute events * * @return true if the Receiver makes any use of this information. If false, the caller * may supply untyped nodes instead of supplying the type annotation */ public boolean usesTypeAnnotations() { return outputter == null || outputter.usesTypeAnnotations(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy