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

net.sf.saxon.serialize.Emitter Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2022 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.serialize;

import net.sf.saxon.event.ReceiverOption;
import net.sf.saxon.event.ReceiverWithOutputProperties;
import net.sf.saxon.event.SequenceReceiver;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.s9api.Location;
import net.sf.saxon.serialize.charcode.CharacterSet;
import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
import net.sf.saxon.serialize.charcode.UTF8CharacterSet;
import net.sf.saxon.str.UnicodeWriter;
import net.sf.saxon.trans.XPathException;

import java.io.IOException;
import java.util.Properties;


/**
 * Emitter: This abstract class defines methods that must be implemented by
 * components that format SAXON output. There is one emitter for XML,
 * one for HTML, and so on. Additional methods are concerned with
 * setting options and providing a Writer.
 * 

The interface is deliberately designed to be as close as possible to the * standard SAX2 ContentHandler interface, however, it allows additional * information to be made available.

*

An Emitter is a Receiver, specifically it is a Receiver that can direct output * to a Writer or OutputStream, using serialization properties defined in a Properties * object.

*

The Emitter (from 11.0 onwards) writes to a UnicodeWriter, which may itself * bridge to a Writer or OutputStream.

*/ public abstract class Emitter extends SequenceReceiver implements ReceiverWithOutputProperties { // protected StreamResult streamResult; // protected Writer writer; // protected OutputStream outputStream; protected UnicodeWriter writer; protected Properties outputProperties; protected CharacterSet characterSet; protected boolean allCharactersEncodable = false; private boolean mustClose = false; public Emitter() { super(null); } /** * Set output properties * * @param details the output serialization properties * @throws net.sf.saxon.trans.XPathException * if an error occurs finding the encoding property */ public void setOutputProperties(Properties details) throws XPathException { if (characterSet == null) { characterSet = getConfiguration().getCharacterSetFactory().getCharacterSet(details); allCharactersEncodable = (characterSet instanceof UTF8CharacterSet || characterSet instanceof UTF16CharacterSet); } outputProperties = details; } /** * Get the output properties * * @return the output serialization properties. The returned value will be null if setOutputProperties() * has not been called */ @Override public Properties getOutputProperties() { return outputProperties; } public void setUnicodeWriter(UnicodeWriter unicodeWriter) { this.writer = unicodeWriter; } // /** // * Set the StreamResult acting as the output destination of the Emitter // * // * @param result the output destination // * @throws net.sf.saxon.trans.XPathException // * if an error occurs // */ // // public void setStreamResult(StreamResult result) throws XPathException { // streamResult = result; // if (systemId == null) { // systemId = result.getSystemId(); // } // } // /** // * Make a Writer for this Emitter to use, given a StreamResult. // * // * @throws net.sf.saxon.trans.XPathException // * if an error occurs // */ // // protected void makeWriter() throws XPathException { // if (writer != null) { // return; // } // if (streamResult == null) { // throw new IllegalStateException("Emitter must have either a Writer or a StreamResult to write to"); // } // writer = streamResult.getWriter(); // if (writer == null) { // OutputStream os = streamResult.getOutputStream(); // if (os != null) { // setOutputStream(os); // } // } // if (writer == null) { // makeOutputStream(); // } // } // // @SuppressWarnings({"ResultOfMethodCallIgnored"}) // protected OutputStream makeOutputStream() throws XPathException { // String uriString = streamResult.getSystemId(); // if (uriString == null) { // throw new XPathException("Result has no system ID, writer, or output stream defined", SaxonErrorCode.SXRD0004); // } // // try { // File file = ExpandedStreamResult.makeWritableOutputFile(uriString); // setOutputStream(new FileOutputStream(file)); // // Set the outputstream in the StreamResult object so that the // // call on OutputURIResolver.close() can close it // streamResult.setOutputStream(outputStream); // mustClose = true; // } catch (FileNotFoundException | URISyntaxException | IllegalArgumentException fnf) { // // for example, the system ID doesn't use the file: scheme // XPathException err = new XPathException("Unable to write to output destination", fnf); // err.setErrorCode(SaxonErrorCode.SXRD0004); // throw err; // } // return outputStream; // } // // /** // * Determine whether the Emitter wants a Writer for character output or // * an OutputStream for binary output. The standard Emitters all use a Writer, so // * this returns true; but a subclass can override this if it wants to use an OutputStream // * // * @return true if a Writer is needed, as distinct from an OutputStream // */ // // public boolean usesWriter() { // return true; // } // // /** // * Set the output destination as a character stream // * // * @param writer the Writer to use as an output destination // * @throws net.sf.saxon.trans.XPathException // * if an error occurs // */ // // public void setWriter(Writer writer) throws XPathException { // this.writer = writer; // // // If the writer uses a known encoding, change the encoding in the XML declaration // // to match. Any encoding actually specified in xsl:output is ignored, because encoding // // is being done by the user-supplied Writer, and not by Saxon itself. // // if (writer instanceof OutputStreamWriter && outputProperties != null) { // String enc = ((OutputStreamWriter) writer).getEncoding(); // outputProperties.setProperty(OutputKeys.ENCODING, enc); // characterSet = getConfiguration().getCharacterSetFactory().getCharacterSet(outputProperties); // allCharactersEncodable = (characterSet instanceof UTF8CharacterSet || // characterSet instanceof UTF16CharacterSet); // } // } // // /** // * Get the output writer // * // * @return the Writer being used as an output destination, if any // */ // // public Writer getWriter() { // return writer; // } // // /** // * Set the output destination as a byte stream. // *

Note that if a specific encoding (other than the default, UTF-8) is required, then // * {@link #setOutputProperties(java.util.Properties)} must be called before calling // * this method.

// * // * @param stream the OutputStream being used as an output destination // * @throws net.sf.saxon.trans.XPathException // * if an error occurs // */ // // public void setOutputStream(OutputStream stream) throws XPathException { // outputStream = stream; // // // If the user supplied an OutputStream, but the Emitter is written to // // use a Writer (this is the most common case), then we create a Writer // // to wrap the supplied OutputStream; the complications are to ensure that // // the character encoding is correct. // // if (usesWriter()) { // // if (outputProperties == null) { // outputProperties = new Properties(); // } // // String encoding = outputProperties.getProperty(OutputKeys.ENCODING); // if (encoding == null) { // encoding = "UTF8"; // allCharactersEncodable = true; // } else if (encoding.equalsIgnoreCase("UTF-8")) { // encoding = "UTF8"; // allCharactersEncodable = true; // } else if (encoding.equalsIgnoreCase("UTF-16")) { // encoding = "UTF16"; // } // // if (characterSet == null) { // characterSet = getConfiguration().getCharacterSetFactory().getCharacterSet(outputProperties); // } // // String byteOrderMark = outputProperties.getProperty(SaxonOutputKeys.BYTE_ORDER_MARK); // if ("no".equals(byteOrderMark) && "UTF16".equals(encoding)) { // // Java always writes a bom for UTF-16, so if the user doesn't want one, use utf16-be // encoding = "UTF-16BE"; // } else if (!(characterSet instanceof UTF8CharacterSet)) { // // //if (characterSet instanceof PluggableCharacterSet) { // encoding = characterSet.getCanonicalName(); // } // // while (true) { // try { // String javaEncoding = encoding; // if (encoding.equalsIgnoreCase("iso-646") || encoding.equalsIgnoreCase("iso646")) { // javaEncoding = "US-ASCII"; // } // if (encoding.equalsIgnoreCase("UTF8")) { // writer = new UTF8Writer(outputStream); // } else { // writer = new BufferedWriter( // new OutputStreamWriter( // outputStream, javaEncoding)); // } // break; // } catch (Exception err) { // if (encoding.equalsIgnoreCase("UTF8")) { // throw new XPathException("Failed to create a UTF8 output writer"); // } // XmlProcessingIncident de = new XmlProcessingIncident("Encoding " + encoding + " is not supported: using UTF8", "SESU0007"); // getPipelineConfiguration().getErrorReporter().report(de); // encoding = "UTF8"; // characterSet = UTF8CharacterSet.getInstance(); // allCharactersEncodable = true; // outputProperties.setProperty(OutputKeys.ENCODING, "UTF-8"); // } // } // } // // } // // /** // * Get the output stream // * // * @return the OutputStream being used as an output destination, if any // */ // // public OutputStream getOutputStream() { // return outputStream; // } /** * Set unparsed entity URI. Needed to satisfy the Receiver interface, but not used, * because unparsed entities can occur only in input documents, not in output documents. * * @param name the entity name * @param uri the entity system ID * @param publicId the entity public ID */ @Override public void setUnparsedEntity(String name, String uri, String publicId) throws XPathException { } /** * Notify the end of the event stream */ @Override public void close() throws XPathException { if (mustClose && writer != null) { try { writer.close(); } catch (IOException e) { throw new XPathException("Failed to close output stream"); } } } /** * 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 (or conversely, it may * avoid stripping unwanted type annotations) */ @Override public boolean usesTypeAnnotations() { return false; } /** * Append an arbitrary item (node or atomic value) to the output */ @Override public void append(/*@Nullable*/ Item item, Location locationId, int copyNamespaces) throws XPathException { if (item instanceof NodeInfo) { decompose(item, locationId, copyNamespaces); } else { characters(item.getUnicodeStringValue(), locationId, ReceiverOption.NONE); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy