
org.apache.xml.serializer.ToXMLStream Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: ToXMLStream.java 469359 2006-10-31 03:43:19Z minchau $
*/
package org.apache.xml.serializer;
import java.io.IOException;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import org.apache.xml.serializer.utils.MsgKey;
import org.apache.xml.serializer.utils.Utils;
import org.xml.sax.SAXException;
import com.sta.mlogger.MLogger;
/**
* Name: ToXMLStream
* Description: Klasse wg. Zeilenumbruch-Bug ersetzt.
*
* Comment: ...
* This class converts SAX or SAX-like calls to a serialized xml document. The xsl:output method is "xml".
* This class is used explicitly in code generated by XSLTC, so it is "public", but it should
* be viewed as internal or package private, this is not an API.
* _xsl.usage internal
*
* Copyright: Copyright (c) 2019, 2021
* Company: >StA-Soft<
* @author StA
* @version 1.0
*/
public class ToXMLStream extends ToStream
{
/**
* Map that tells which XML characters should have special treatment, and it
* provides character to entity name lookup.
*/
private CharInfo m_xmlcharInfo =
CharInfo.getCharInfo(CharInfo.XML_ENTITIES_RESOURCE, Method.XML);
/**
* Default constructor.
*/
public ToXMLStream()
{
m_charInfo = m_xmlcharInfo;
initCDATA();
// initialize namespaces
m_prefixMap = new NamespaceMappings();
}
/**
* Copy properties from another SerializerToXML.
* @param xmlListener non-null reference to a SerializerToXML object.
*/
public void copyFrom(ToXMLStream xmlListener)
{
setWriter(xmlListener.m_writer);
// m_outputStream = xmlListener.m_outputStream;
String encoding = xmlListener.getEncoding();
setEncoding(encoding);
setOmitXMLDeclaration(xmlListener.getOmitXMLDeclaration());
m_ispreserve = xmlListener.m_ispreserve;
m_preserves = xmlListener.m_preserves;
m_isprevtext = xmlListener.m_isprevtext;
m_doIndent = xmlListener.m_doIndent;
setIndentAmount(xmlListener.getIndentAmount());
m_startNewLine = xmlListener.m_startNewLine;
m_needToOutputDocTypeDecl = xmlListener.m_needToOutputDocTypeDecl;
setDoctypeSystem(xmlListener.getDoctypeSystem());
setDoctypePublic(xmlListener.getDoctypePublic());
setStandalone(xmlListener.getStandalone());
setMediaType(xmlListener.getMediaType());
m_encodingInfo = xmlListener.m_encodingInfo;
m_spaceBeforeClose = xmlListener.m_spaceBeforeClose;
m_cdataStartCalled = xmlListener.m_cdataStartCalled;
}
@Override
public void startDocumentInternal() throws org.xml.sax.SAXException
{
if (m_needToCallStartDocument)
{
super.startDocumentInternal();
m_needToCallStartDocument = false;
if (m_inEntityRef)
{
return;
}
m_needToOutputDocTypeDecl = true;
m_startNewLine = false;
/* The call to getXMLVersion() might emit an error message
* and we should emit this message regardless of if we are
* writing out an XML header or not.
*/
final String version = getXMLVersion();
if (!getOmitXMLDeclaration())
{
String encoding = Encodings.getMimeEncoding(getEncoding());
String standalone;
if (m_standaloneWasSpecified)
{
standalone = " standalone=\"" + getStandalone() + "\"";
}
else
{
standalone = "";
}
try
{
final java.io.Writer writer = m_writer;
writer.write("");
if (m_doIndent)
{
// if (m_standaloneWasSpecified || (getDoctypePublic() != null) || (getDoctypeSystem() != null) || true)
// {
// We almost never put a newline after the XML
// header because this XML could be used as
// an extenal general parsed entity
// and we don't know the context into which it
// will be used in the future. Only when
// standalone, or a doctype system or public is
// specified are we free to insert a new line
// after the header. Is it even worth bothering
// in these rare cases?
writer.write(m_lineSep, 0, m_lineSepLen);
// }
}
}
catch (IOException e)
{
throw new SAXException(e);
}
}
}
}
@Override
public void endDocument() throws org.xml.sax.SAXException
{
flushPending();
if (m_doIndent && !m_isprevtext)
{
try
{
outputLineSep();
}
catch (IOException e)
{
throw new SAXException(e);
}
}
flushWriter();
if (m_tracer != null)
{
super.fireEndDoc();
}
}
/**
* Starts a whitespace preserving section. All characters printed
* within a preserving section are printed without indentation and
* without consolidating multiple spaces. This is equivalent to
* the xml:space="preserve" attribute. Only XML
* and HTML serializers need to support this method.
*
* The contents of the whitespace preserving section will be delivered
* through the regular characters event.
* @throws org.xml.sax.SAXException in case of an error
*/
public void startPreserving() throws org.xml.sax.SAXException
{
// Not sure this is really what we want. -sb
m_preserves.push(true);
m_ispreserve = true;
}
/**
* Ends a whitespace preserving section.
* @throws org.xml.sax.SAXException in case of an error
* @see #startPreserving
*/
public void endPreserving() throws org.xml.sax.SAXException
{
// Not sure this is really what we want. -sb
m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
}
@Override
public void processingInstruction(String target, String data)
throws org.xml.sax.SAXException
{
if (m_inEntityRef)
{
return;
}
flushPending();
if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING))
{
startNonEscaping();
}
else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING))
{
endNonEscaping();
}
else
{
try
{
if (m_elemContext.m_startTagOpen)
{
closeStartTag();
m_elemContext.m_startTagOpen = false;
}
else if (m_needToCallStartDocument)
{
startDocumentInternal();
}
if (shouldIndent())
{
indent();
}
final java.io.Writer writer = m_writer;
writer.write("");
writer.write(target);
if ((data.length() > 0) && !Character.isSpaceChar(data.charAt(0)))
{
writer.write(' ');
}
int indexOfQLT = data.indexOf("?>");
if (indexOfQLT >= 0)
{
// See XSLT spec on error recovery of "?>" in PIs.
if (indexOfQLT > 0)
{
writer.write(data.substring(0, indexOfQLT));
}
writer.write("? >"); // add space between.
if ((indexOfQLT + 2) < data.length())
{
writer.write(data.substring(indexOfQLT + 2));
}
}
else
{
writer.write(data);
}
writer.write('?');
writer.write('>');
/*
* Don't write out any indentation whitespace now,
* because there may be non-whitespace text after this.
*
* Simply mark that at this point if we do decide
* to indent that we should
* add a newline on the end of the current line before
* the indentation at the start of the next line.
*/
m_startNewLine = true;
}
catch (IOException e)
{
throw new SAXException(e);
}
}
if (m_tracer != null)
{
super.fireEscapingEvent(target, data);
}
}
@Override
public void entityReference(String name) throws org.xml.sax.SAXException
{
if (m_elemContext.m_startTagOpen)
{
closeStartTag();
m_elemContext.m_startTagOpen = false;
}
try
{
if (shouldIndent())
{
indent();
}
final java.io.Writer writer = m_writer;
writer.write('&');
writer.write(name);
writer.write(';');
}
catch (IOException e)
{
throw new SAXException(e);
}
if (m_tracer != null)
{
super.fireEntityReference(name);
}
}
@Override
public void addUniqueAttribute(String name, String value, int flags) throws SAXException
{
if (m_elemContext.m_startTagOpen)
{
try
{
final String patchedName = patchName(name);
final java.io.Writer writer = m_writer;
if ((flags & NO_BAD_CHARS) > 0 && m_xmlcharInfo.onlyQuotAmpLtGt)
{
// "flags" has indicated that the characters
// '>' '<' '&' and '"' are not in the value and
// m_htmlcharInfo has recorded that there are no other
// entities in the range 32 to 127 so we write out the
// value directly
writer.write(' ');
writer.write(patchedName);
writer.write("=\"");
writer.write(value);
writer.write('"');
}
else
{
writer.write(' ');
writer.write(patchedName);
writer.write("=\"");
writeAttrString(writer, value, this.getEncoding());
writer.write('"');
}
}
catch (IOException e)
{
throw new SAXException(e);
}
}
}
@Override
public void addAttribute(
String uri,
String localName,
String rawName,
String type,
String value,
boolean xslAttribute)
throws SAXException
{
if (m_elemContext.m_startTagOpen)
{
boolean was_added = addAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
/*
* We don't run this block of code if:
* 1. The attribute value was only replaced (was_added is false).
* 2. The attribute is from an xsl:attribute element (that is handled
* in the addAttributeAlways() call just above.
* 3. The name starts with "xmlns", i.e. it is a namespace declaration.
*/
if (was_added && !xslAttribute && !rawName.startsWith("xmlns"))
{
String prefixUsed = ensureAttributesNamespaceIsDeclared(uri, localName, rawName);
if ((prefixUsed != null) && (rawName != null) && !rawName.startsWith(prefixUsed))
{
// use a different raw name, with the prefix used in the
// generated namespace declaration
rawName = prefixUsed + ":" + localName;
}
}
addAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
}
else
{
/*
* The startTag is closed, yet we are adding an attribute?
*
* Section: 7.1.3 Creating Attributes Adding an attribute to an
* element after a PI (for example) has been added to it is an
* error. The attributes can be ignored. The spec doesn't explicitly
* say this is disallowed, as it does for child elements, but it
* makes sense to have the same treatment.
*
* We choose to ignore the attribute which is added too late.
*/
// Generate a warning of the ignored attributes
// Create the warning message
String msg = Utils.messages.createMessage(
MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION, new Object[]{localName});
try
{
// Prepare to issue the warning message
Transformer tran = super.getTransformer();
ErrorListener errHandler = tran.getErrorListener();
// Issue the warning message
if (null != errHandler && m_sourceLocator != null)
{
errHandler.warning(new TransformerException(msg, m_sourceLocator));
}
else
{
MLogger.inf(msg);
}
}
catch (TransformerException e)
{
// A user defined error handler, errHandler, may throw
// a TransformerException if it chooses to, and if it does
// we will wrap it with a SAXException and re-throw.
// Of course if the handler throws another type of
// exception, like a RuntimeException, then that is OK too.
SAXException se = new SAXException(e);
throw se;
}
}
}
@Override
public void endElement(String elemName) throws SAXException
{
endElement(null, null, elemName);
}
@Override
public void namespaceAfterStartElement(final String prefix, final String uri) throws SAXException
{
// hack for XSLTC with finding URI for default namespace
if (m_elemContext.m_elementURI == null)
{
String prefix1 = getPrefixPart(m_elemContext.m_elementName);
if (prefix1 == null && EMPTYSTRING.equals(prefix))
{
// the elements URI is not known yet, and it
// doesn't have a prefix, and we are currently
// setting the uri for prefix "", so we have
// the uri for the element... lets remember it
m_elemContext.m_elementURI = uri;
}
}
startPrefixMapping(prefix, uri, false);
return;
}
/**
* From XSLTC
* Declare a prefix to point to a namespace URI. Inform SAX handler
* if this is a new prefix mapping.
* @param prefix prefix
* @param uri uri
* @return boolean
*/
protected boolean pushNamespace(String prefix, String uri)
{
try
{
if (m_prefixMap.pushNamespace(
prefix, uri, m_elemContext.m_currentElemDepth))
{
startPrefixMapping(prefix, uri);
return true;
}
}
catch (SAXException e)
{
// falls through
}
return false;
}
@Override
public boolean reset()
{
boolean wasReset = false;
if (super.reset())
{
// Make this call when resetToXMLStream does
// something.
// resetToXMLStream();
wasReset = true;
}
return wasReset;
}
/**
* Reset all of the fields owned by ToStream class.
*/
private void resetToXMLStream()
{
// This is an empty method, but is kept for future use
// as a place holder for a location to reset fields
// defined within this class
return;
}
/**
* This method checks for the XML version of output document.
* If XML version of output document is not specified, then output
* document is of version XML 1.0.
* If XML version of output doucment is specified, but it is not either
* XML 1.0 or XML 1.1, a warning message is generated, the XML Version of
* output document is set to XML 1.0 and processing continues.
* @return string (XML version)
*/
private String getXMLVersion()
{
String xmlVersion = getVersion();
if (xmlVersion == null || xmlVersion.equals(XMLVERSION10))
{
xmlVersion = XMLVERSION10;
}
else if (xmlVersion.equals(XMLVERSION11))
{
xmlVersion = XMLVERSION11;
}
else
{
String msg = Utils.messages.createMessage(
MsgKey.ER_XML_VERSION_NOT_SUPPORTED, new Object[]{xmlVersion});
try
{
// Prepare to issue the warning message
Transformer tran = super.getTransformer();
ErrorListener errHandler = tran.getErrorListener();
// Issue the warning message
if (null != errHandler && m_sourceLocator != null)
{
errHandler.warning(new TransformerException(msg, m_sourceLocator));
}
else
{
MLogger.inf(msg);
}
}
catch (Exception e)
{
}
xmlVersion = XMLVERSION10;
}
return xmlVersion;
}
}