blob: cdab247e930ce23e0f92a735643c8270b49da33d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2015 David Green and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* David Green - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.wikitext.util;
import java.util.Stack;
/**
* An {@link XmlStreamWriter} that creates formatted output by emitting whitespace into the document output stream.
*
* @author David Green
* @since 3.0
*/
public class FormattingXMLStreamWriter extends XmlStreamWriter {
private final XmlStreamWriter delegate;
private int indentLevel;
private final Stack<Integer> childCounts = new Stack<Integer>();
private int childCount;
private final Stack<String> elements = new Stack<String>();
private int lineOffset = 0;
public FormattingXMLStreamWriter(XmlStreamWriter delegate) {
this.delegate = delegate;
}
@Override
public void close() {
delegate.close();
}
@Override
public void flush() {
delegate.flush();
}
@Override
public String getPrefix(String uri) {
return delegate.getPrefix(uri);
}
@Override
public String getNamespaceURI(String prefix) {
return delegate.getNamespaceURI(prefix);
}
@Override
public void setDefaultNamespace(String uri) {
delegate.setDefaultNamespace(uri);
}
@Override
public void setPrefix(String prefix, String uri) {
delegate.setPrefix(prefix, uri);
}
@Override
public void writeAttribute(String prefix, String namespaceURI, String localName, String value) {
if (value == null) {
value = ""; //$NON-NLS-1$
}
delegate.writeAttribute(prefix, namespaceURI, localName, value);
}
@Override
public void writeAttribute(String namespaceURI, String localName, String value) {
if (value == null) {
value = ""; //$NON-NLS-1$
}
delegate.writeAttribute(namespaceURI, localName, value);
}
@Override
public void writeAttribute(String localName, String value) {
if (value == null) {
value = ""; //$NON-NLS-1$
}
delegate.writeAttribute(localName, value);
}
@Override
public void writeCData(String data) {
++lineOffset;
delegate.writeCData(data);
}
@Override
public void writeCharacters(char[] text, int start, int len) {
int lineStart = start;
int length = 0;
for (int x = 0; x < len; ++x) {
final int charOffset = start + x;
++length;
if (lineOffset == 0 && text[charOffset] != '\n') {
maybeIndent(false, true);
}
++lineOffset;
if (text[charOffset] == '\n') {
delegate.writeCharacters(text, lineStart, length);
length = 0;
lineOffset = 0;
lineStart = charOffset;
}
}
if (length > 0) {
delegate.writeCharacters(text, lineStart, length);
lineOffset += length;
}
}
@Override
public void writeCharacters(String text) {
if (text == null) {
return;
}
char[] chars = text.toCharArray();
writeCharacters(chars, 0, chars.length);
}
@Override
public void writeLiteral(String literal) {
++lineOffset;
delegate.writeLiteral(literal);
}
@Override
public void writeComment(String data) {
if (data == null) {
data = ""; //$NON-NLS-1$
}
++childCount;
maybeIndent();
++lineOffset;
delegate.writeComment(data);
}
@Override
public void writeDefaultNamespace(String namespaceURI) {
delegate.writeDefaultNamespace(namespaceURI);
}
@Override
public void writeDTD(String dtd) {
++lineOffset;
delegate.writeDTD(dtd);
}
@Override
public void writeEmptyElement(String prefix, String localName, String namespaceURI) {
++childCount;
maybeIndent();
++lineOffset;
delegate.writeEmptyElement(prefix, localName, namespaceURI);
}
@Override
public void writeEmptyElement(String namespaceURI, String localName) {
++childCount;
maybeIndent();
++lineOffset;
delegate.writeEmptyElement(namespaceURI, localName);
}
@Override
public void writeEmptyElement(String localName) {
++childCount;
maybeIndent();
++lineOffset;
delegate.writeEmptyElement(localName);
}
@Override
public void writeEndDocument() {
delegate.writeEndDocument();
}
@Override
public void writeEndElement() {
--indentLevel;
maybeIndent();
elements.pop();
delegate.writeEndElement();
childCount = childCounts.pop();
}
@Override
public void writeEntityRef(String name) {
++lineOffset;
delegate.writeEntityRef(name);
}
@Override
public void writeNamespace(String prefix, String namespaceURI) {
delegate.writeNamespace(prefix, namespaceURI);
}
@Override
public void writeProcessingInstruction(String target, String data) {
++childCount;
++lineOffset;
delegate.writeProcessingInstruction(target, data);
}
@Override
public void writeProcessingInstruction(String target) {
++childCount;
++lineOffset;
delegate.writeProcessingInstruction(target);
}
@Override
public void writeStartDocument() {
++childCount;
++lineOffset;
delegate.writeStartDocument();
}
@Override
public void writeStartDocument(String encoding, String version) {
++childCount;
++lineOffset;
delegate.writeStartDocument(encoding, version);
}
@Override
public void writeStartDocument(String version) {
++childCount;
++lineOffset;
delegate.writeStartDocument(version);
}
@Override
public void writeStartElement(String prefix, String localName, String namespaceURI) {
++childCount;
maybeIndent();
elements.push(localName);
childCounts.push(childCount);
childCount = 0;
++indentLevel;
++lineOffset;
delegate.writeStartElement(prefix, localName, namespaceURI);
}
@Override
public void writeStartElement(String namespaceURI, String localName) {
++childCount;
maybeIndent();
elements.push(localName);
childCounts.push(childCount);
childCount = 0;
++indentLevel;
++lineOffset;
delegate.writeStartElement(namespaceURI, localName);
}
@Override
public void writeStartElement(String localName) {
++childCount;
maybeIndent();
elements.push(localName);
childCounts.push(childCount);
childCount = 0;
++indentLevel;
++lineOffset;
delegate.writeStartElement(localName);
}
private void maybeIndent() {
maybeIndent(true, false);
}
private void maybeIndent(boolean withNewline, boolean force) {
if ((childCount == 0 && !force) || preserveWhitespace()) {
return;
}
StringBuilder buf = new StringBuilder();
if (withNewline && (childCount > 1 || !childCounts.isEmpty())) {
buf.append('\n');
lineOffset = 0;
}
for (int x = 0; x < indentLevel; ++x) {
buf.append('\t');
}
lineOffset += indentLevel;
if (buf.length() > 0) {
delegate.writeCharacters(buf.toString().toCharArray(), 0, buf.length());
}
}
private boolean preserveWhitespace() {
for (int x = elements.size() - 1; x >= 0; --x) {
if (preserveWhitespace(elements.get(x))) {
return true;
}
}
return false;
}
/**
* Override this method to indicate which elements must have whitespace preserved.
*
* @param elementName
* the local name of the element
*/
protected boolean preserveWhitespace(String elementName) {
return false;
}
}