/******************************************************************************* | |
* Crown Copyright (c) 2006, 2007, Copyright (c) 2006, 2007 Jiva Medical. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* Jiva Medical - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.uomo.xml.impl; | |
import java.io.IOException; | |
import java.io.OutputStream; | |
import java.io.OutputStreamWriter; | |
import java.io.UnsupportedEncodingException; | |
import org.eclipse.uomo.xml.IXMLWriter; | |
import org.eclipse.uomo.xml.XMLUtil; | |
/** | |
* XML Writer class. | |
*/ | |
public class XMLWriter extends OutputStreamWriter implements IXMLWriter { | |
private boolean xmlHeader = true; | |
private String charset; | |
private boolean prettyBase; | |
private boolean prettyHeader; | |
private boolean pendingClose; | |
private boolean pendingOpen; | |
private String pendingComment; | |
private int lineType = LINE_UNIX; | |
private OutputStream stream; | |
private boolean started = false; | |
private String[] specialAttributeNames = new String[] {"id", "name" }; | |
private boolean sortAttributes; | |
private int attributeLineWrap; | |
public final static int LINE_UNIX = 0; | |
public final static int LINE_WINDOWS = 1; | |
public XMLWriter(OutputStream stream, String charset) throws UnsupportedEncodingException { | |
super(stream, charset); | |
this.stream = stream; | |
this.charset = charset; | |
} | |
protected boolean condition(boolean bTest, String message) throws IOException { | |
if (!bTest) | |
throw new IOException(message); | |
return bTest; | |
} | |
// -- writing context ------------------------------------------------ | |
/** | |
* Returns the encoding. | |
* | |
* @param charset | |
* @return encoding | |
* @throws IOException | |
*/ | |
public static String getXMLCharsetName(String charset) throws IOException { | |
if (charset == null || charset.equals("")) | |
return "UTF-8"; | |
else if (charset.equals("US-ASCII")) | |
return "UTF-8"; | |
else if (XMLUtil.charSetImpliesAscii(charset)) | |
return "ISO-8859-1"; | |
else if (charset.equals("UTF-8")) | |
return "UTF-8"; | |
else if (charset.equals("UTF-16") || charset.equals("UTF-16BE") || charset.equals("UTF-16LE")) | |
return "UTF-16"; | |
else | |
throw new IOException("Unknown charset encoding "+charset); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#start() | |
*/ | |
public void start() throws IOException { | |
condition(!started, "attempt to start after starting"); | |
levels.clear(); | |
attributes = null; | |
try { | |
if (xmlHeader) { | |
write("<?xml version=\"1.0\" encoding=\""+getXMLCharsetName(charset)+"\"?>"); | |
if (prettyBase || prettyHeader) | |
write(lineType == LINE_UNIX ? "\n" : "\r\n"); | |
} | |
} catch (UnsupportedEncodingException e) { | |
// TODO Auto-generated catch block | |
throw new IOException(e.getMessage()); | |
} | |
started = true; | |
} | |
private void checkStarted () throws IOException { | |
condition(started, "not started"); | |
} | |
private void checkInElement() throws IOException { | |
condition(levels.size() > 0, "not in an element"); | |
} | |
// -- attributes ---------------------------------------------------- | |
private String[][] attributes; | |
private void addAttribute(String name, String value) throws IOException { | |
addAttribute(name, value, false); | |
} | |
private void addAttribute(String name, String value, boolean noLines) throws IOException { | |
if (!XMLUtil.isNMToken(name)) | |
throw new IOException("XML name "+name+" is not valid"); | |
newLevelIfRequired(); | |
value = XMLUtil.escapeXML(value, charset, noLines); | |
if (attributes == null) | |
attributes = new String[][] {{name, value}}; | |
else { | |
String[][] newattr = new String[attributes.length+1][]; | |
for (int i = 0; i < attributes.length; i++) { | |
condition(!attributes[i][0].equals(name), "attempt to define attribute with name "+name+" more than once"); | |
newattr[i] = attributes[i]; | |
} | |
attributes = newattr; | |
attributes[attributes.length-1] = new String[] {name, value}; | |
} | |
} | |
protected String getAttribute(String name) { | |
if (attributes != null) { | |
for (int i = 0; i < attributes.length; i++) { | |
if (attributes[i][0].equals(name)) { | |
return attributes[i][1]; | |
} | |
} | |
} | |
return null; | |
} | |
protected void setAttribute(String name, String value) throws IOException { | |
newLevelIfRequired(); | |
if (attributes == null) | |
addAttribute(name, value, false); | |
else { | |
for (int i = 0; i < attributes.length; i++) { | |
if (attributes[i][0].equals(name)) { | |
attributes[i][1] = XMLUtil.escapeXML(value, charset, false); | |
return; | |
} | |
} | |
addAttribute(name, value); | |
} | |
} | |
protected void commitAttributes() throws IOException { | |
} | |
private boolean nameIsSpecial(String name) { | |
for (int i = 0; i < specialAttributeNames.length; i++) { | |
String n = specialAttributeNames[i]; | |
if (n.equalsIgnoreCase(name)) | |
return true; | |
} | |
return false; | |
} | |
private void writeAttributes(int col) throws IOException { | |
commitAttributes(); | |
if (attributes != null && sortAttributes) | |
sortAttributes(); | |
int c = col; | |
c = writeAttributeSet(true, c, col); | |
writeAttributeSet(false, c, col); | |
attributes = null; | |
} | |
private void sortAttributes() { | |
// bubble sort - look, it's easy | |
for (int i = 0; i < attributes.length - 1; i++) { | |
for (int j = 0; j < attributes.length - 1; j++) { | |
if (String.CASE_INSENSITIVE_ORDER.compare(attributes[j][0], attributes[j+1][0]) < 0) { | |
String[] t = attributes[j]; | |
attributes[j] = attributes[j+1]; | |
attributes[j+1] = t; | |
} | |
} | |
} | |
} | |
private int writeAttributeSet(boolean special, int col, int wrap) throws IOException { | |
// first pass: name, id | |
if (attributes != null) { | |
for (int i=0; i < attributes.length; i++) { | |
String[] element = attributes[i]; | |
if (nameIsSpecial(element[0]) == special) { | |
col = col + element[0].length()+element[1].length() + 4; | |
if (isPretty() && attributeLineWrap > 0 && col > attributeLineWrap && col > wrap) { | |
write(lineType == LINE_UNIX ? "\n" : "\r\n"); | |
for (int j = 0; j < wrap; j++) | |
write(" "); | |
col = wrap; | |
} | |
write(' '); | |
write(element[0]); | |
write("=\""); | |
if (element[1] != null) | |
write(element[1]); | |
write("\""); | |
} | |
} | |
} | |
return col; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#attribute(java.lang.String, java.lang.String, java.lang.String, boolean) | |
*/ | |
public void attribute(String namespace, String name, String value, boolean onlyIfNotEmpty) throws IOException { | |
if (!onlyIfNotEmpty || value != null && !value.equals("")) | |
attribute(namespace, name, value); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#attribute(java.lang.String, java.lang.String, java.lang.String) | |
*/ | |
public void attribute(String namespace, String name, String value) throws IOException { | |
checkStarted(); | |
if (namespace == null || namespace.equals("")) | |
addAttribute(name, value); | |
else | |
addAttribute(getNSAbbreviation(namespace)+name, value); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#attribute(java.lang.String, java.lang.String, boolean) | |
*/ | |
public void attribute(String name, String value, boolean onlyIfNotEmpty) throws IOException { | |
if (!onlyIfNotEmpty || value != null && !value.equals("")) | |
attribute(name, value); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#attribute(java.lang.String, java.lang.String) | |
*/ | |
public void attribute(String name, String value) throws IOException { | |
checkStarted(); | |
addAttribute(name, value); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#attributeNoLines(java.lang.String, java.lang.String) | |
*/ | |
public void attributeNoLines(String name, String value) throws IOException { | |
checkStarted(); | |
addAttribute(name, value, true); | |
} | |
// -- levels ------------------------------------------------- | |
private XMLWriterStateStack levels = new XMLWriterStateStack(); | |
private void newLevelIfRequired() throws IOException { | |
if (!pendingOpen) { | |
if (!levels.empty()) | |
levels.current().seeChild(); | |
XMLWriterState level = new XMLWriterState(); | |
level.setPretty(isPretty()); | |
levels.push(level); | |
pendingOpen = true; | |
} | |
} | |
// -- namespaces --------------------------------------------- | |
private void defineNamespace(String namespace, String abbrev) throws IOException { | |
checkStarted(); | |
if (namespace != null && !namespace.equals("")) { | |
if (abbrev.equals("")) | |
abbrev = null; | |
newLevelIfRequired(); | |
levels.current().addNamespaceDefn(namespace, abbrev); | |
if (abbrev == null) | |
addAttribute("xmlns", namespace); | |
else | |
addAttribute("xmlns:"+abbrev, namespace); | |
} | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#findByNamespace(java.lang.String) | |
*/ | |
public XMLNamespace findByNamespace(String namespace) { | |
for (int i = levels.size() - 1; i >= 0; i--) { | |
XMLNamespace ns = levels.item(i).getDefnByNamespace(namespace); | |
if (ns != null) | |
return ns; | |
} | |
return null; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#namespaceDefined(java.lang.String) | |
*/ | |
public boolean namespaceDefined(String namespace) { | |
return namespace == null || namespace.equals("") || findByNamespace(namespace) != null; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#findByAbbreviation(java.lang.String) | |
*/ | |
public XMLNamespace findByAbbreviation(String abbreviation) { | |
for (int i = levels.size() - 1; i >= 0; i--) { | |
XMLNamespace ns = levels.item(i).getDefnByAbbreviation(abbreviation); | |
if (ns != null) | |
return ns; | |
} | |
return null; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#abbreviationDefined(java.lang.String) | |
*/ | |
public boolean abbreviationDefined(String abbreviation) { | |
return findByAbbreviation(abbreviation) != null; | |
} | |
protected XMLNamespace findDefaultNamespace() { | |
for (int i = levels.size() - 1; i >= 0; i--) { | |
XMLNamespace ns = levels.item(i).getDefaultNamespace(); | |
if (ns != null) | |
return ns; | |
} | |
return null; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#getDefaultNamespace() | |
*/ | |
public String getDefaultNamespace() { | |
XMLNamespace ns = findDefaultNamespace(); | |
if (ns == null) | |
return null; | |
else | |
return ns.getNamespace(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#namespace(java.lang.String) | |
*/ | |
public void namespace(String namespace) throws IOException { | |
if (!namespaceDefined(namespace)) { | |
int index = 0; | |
while (abbreviationDefined("ns"+Integer.toString(index))) | |
index++; | |
defineNamespace(namespace, "ns"+Integer.toString(index)); | |
} | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#defaultNamespace(java.lang.String) | |
* | |
* Replace defaultNamespace() | |
*/ | |
public void setDefaultNamespace(String namespace) throws IOException { | |
if ((namespace == null && getDefaultNamespace() != null) || | |
(namespace != null && !namespace.equals(getDefaultNamespace()))) | |
defineNamespace(namespace, ""); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#namespace(java.lang.String, java.lang.String) | |
*/ | |
public void namespace(String namespace, String abbreviation) throws IOException { | |
XMLNamespace ns = findByAbbreviation(abbreviation); | |
if (ns == null || !ns.getNamespace().equals(namespace)) | |
defineNamespace(namespace, abbreviation); | |
} | |
private String getNSAbbreviation(String namespace) throws IOException { | |
if ("http://www.w3.org/XML/1998/namespace".equals(namespace)) | |
return "xml:"; | |
if (namespace == null || "".equals(namespace)) | |
return ""; | |
XMLNamespace ns = findByNamespace(namespace); | |
if (ns == null) | |
throw new IOException("Namespace "+namespace+" is not defined"); | |
else if (ns.getAbbreviation() == null) | |
return ""; | |
else | |
return ns.getAbbreviation()+":"; | |
} | |
// -- public API ----------------------------------------------------------- | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#comment(java.lang.String, boolean) | |
*/ | |
public void comment(String comment, boolean doPretty) throws IOException { | |
checkStarted(); | |
if (pendingClose) { | |
write('>'); | |
writePendingComment(); | |
pendingClose = false; | |
} | |
if (doPretty) { | |
writePretty(); | |
if (isPretty()) { | |
for (int i = 0; i < levels.size(); i++) | |
write(" "); | |
} | |
} | |
if (levels.inComment()) | |
write("<!-- "+comment+" -- >"); | |
else | |
write("<!-- "+comment+" -->"); | |
if (doPretty && !isPretty()) | |
writePretty(); | |
} | |
private void writePendingComment() throws IOException { | |
if (pendingComment != null) { | |
if (isPretty()) | |
write(" "); | |
if (levels.inComment()) | |
write("<!-- "+pendingComment+" -- >"); | |
else | |
write("<!-- "+pendingComment+" -->"); | |
} | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#open(java.lang.String, java.lang.String) | |
*/ | |
public void open(String namespace, String name) throws IOException { | |
open(namespace, name, null); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#open(java.lang.String, java.lang.String, java.lang.String) | |
*/ | |
public void open(String namespace, String name, String comment) throws IOException { | |
if (!XMLUtil.isNMToken(name)) | |
throw new IOException("XML name "+name+" is not valid"); | |
checkStarted(); | |
if (pendingClose) { | |
write('>'); | |
writePendingComment(); | |
pendingClose = false; | |
} | |
if (name == null) { | |
throw new IOException("name is null"); | |
} | |
newLevelIfRequired(); | |
levels.current().setName(name); | |
levels.current().setNamespace(namespace); | |
int col = writePretty(); | |
write('<'); | |
if (namespace == null) { | |
write(name); | |
col = col + name.length()+1; | |
} else { | |
String n = getNSAbbreviation(namespace)+name; | |
write(n); | |
col = col + n.length()+1; | |
} | |
writeAttributes(col); | |
pendingOpen = false; | |
pendingClose = true; | |
pendingComment = comment; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#close(java.lang.String) | |
*/ | |
public void close(String name) throws IOException { | |
checkStarted(); | |
if (levels.empty()) | |
throw new IOException("Unable to close null|"+name+", nothing to close"); | |
if (levels.current().getNamespace() != null || !levels.current().getName().equals(name)) | |
throw new IOException("Unable to close null|"+name+", found "+levels.current().getNamespace()+"|"+levels.current().getName()); | |
close(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#close(java.lang.String, java.lang.String) | |
*/ | |
public void close(String namespace, String name) throws IOException { | |
checkStarted(); | |
if (levels.empty()) | |
throw new IOException("Unable to close "+namespace+"|"+name+", nothing to close"); | |
if (!levels.current().getNamespace().equals(namespace) || !levels.current().getName().equals(name)) | |
throw new IOException("Unable to close "+namespace+"|"+name+", found "+levels.current().getNamespace()+"|"+levels.current().getName()); | |
close(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#closeToLevel(int) | |
*/ | |
public void closeToLevel(int count) throws IOException { | |
while (levels.size() > count) | |
close(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#close() | |
*/ | |
public void close() throws IOException { | |
checkStarted(); | |
if (levels.empty()) { | |
super.close(); | |
} else { | |
if (pendingClose) { | |
write("/>"); | |
writePendingComment(); | |
pendingClose = false; | |
} else { | |
if (levels.current().hasChildren()) | |
writePretty(); | |
write("</"); | |
if (levels.current().getNamespace() == null) | |
write(levels.current().getName()); | |
else | |
write(getNSAbbreviation(levels.current().getNamespace())+levels.current().getName()); | |
write('>'); | |
} | |
levels.pop(); | |
} | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#open(java.lang.String) | |
*/ | |
public void open(String name) throws IOException { | |
open(null, name); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#element(java.lang.String, java.lang.String, java.lang.String, boolean) | |
*/ | |
public void element(String namespace, String name, String content, boolean onlyIfNotEmpty) throws IOException { | |
if (!onlyIfNotEmpty || content != null && !content.equals("")) | |
element(namespace, name, content); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#element(java.lang.String, java.lang.String, java.lang.String, java.lang.String) | |
*/ | |
public void element(String namespace, String name, String content, String comment) throws IOException { | |
if (!XMLUtil.isNMToken(name)) | |
throw new IOException("XML name "+name+" is not valid"); | |
open(namespace, name, comment); | |
text(content); | |
close(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#element(java.lang.String, java.lang.String, java.lang.String) | |
*/ | |
public void element(String namespace, String name, String content) throws IOException { | |
if (!XMLUtil.isNMToken(name)) | |
throw new IOException("XML name "+name+" is not valid"); | |
open(namespace, name); | |
text(content); | |
close(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#element(java.lang.String, java.lang.String, boolean) | |
*/ | |
public void element(String name, String content, boolean onlyIfNotEmpty) throws IOException { | |
if (!onlyIfNotEmpty || content != null && !content.equals("")) | |
element(null, name, content); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#element(java.lang.String, java.lang.String) | |
*/ | |
public void element(String name, String content) throws IOException { | |
element(null, name, content); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#text(java.lang.String) | |
*/ | |
public void text(String content) throws IOException { | |
text(content, false); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#text(java.lang.String, boolean) | |
* | |
* Replace escapeText() | |
*/ | |
public void text(String content, boolean dontEscape) throws IOException { | |
checkInElement(); | |
if (content != null) { | |
if (pendingClose) { | |
write(">"); | |
writePendingComment(); | |
pendingClose = false; | |
} | |
if (dontEscape) | |
write(content); | |
else | |
write(XMLUtil.escapeXML(content, charset, false)); | |
} | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#cData(java.lang.String) | |
*/ | |
public void cData(String text) throws IOException { | |
text("<![CDATA["+text+"]]>"); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#writeBytes(byte[]) | |
*/ | |
public void writeBytes(byte[] bytes) throws IOException { | |
checkInElement(); | |
if (pendingClose) { | |
write(">"); | |
writePendingComment(); | |
pendingClose = false; | |
} | |
flush(); | |
stream.write(bytes); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#isPretty() | |
*/ | |
public boolean isPretty() throws IOException { | |
return (levels == null || levels.empty()) ? prettyBase : levels.current().isPretty(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#setPretty(boolean) | |
*/ | |
public void setPretty(boolean pretty) throws IOException { | |
if (levels == null || levels.empty()) | |
this.prettyBase = pretty; | |
else | |
levels.current().setPretty(pretty); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#startCommentBlock() | |
*/ | |
public void startCommentBlock() throws IOException { | |
if (levels.inComment()) | |
throw new IOException("cannot nest comments"); | |
levels.current().setInComment(true); | |
if (isPretty()) | |
writePretty(); | |
write("<!--"); | |
if (isPretty()) | |
writePretty(); | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.ohf.utilities.xml.IXMLWriter#endCommentBlock() | |
*/ | |
public void endCommentBlock() throws IOException { | |
if (!levels.inComment()) | |
throw new IOException("cannot close a comment block when it is open"); | |
if (!levels.current().isInComment()) | |
throw new IOException("cannot close a comment block when it is open"); | |
if (isPretty()) | |
writePretty(); | |
write("-->"); | |
if (isPretty()) | |
writePretty(); | |
levels.current().setInComment(false); | |
} | |
public boolean isSortAttributes() { | |
return sortAttributes; | |
} | |
public void setSortAttributes(boolean sortAttributes) { | |
this.sortAttributes = sortAttributes; | |
} | |
public boolean isPrettyHeader() { | |
return prettyHeader; | |
} | |
public void setPrettyHeader(boolean pretty) { | |
this.prettyHeader = pretty; | |
} | |
public int writePretty() throws IOException { | |
return writePretty(true); | |
} | |
public int writePretty(boolean eoln) throws IOException { | |
if (isPretty()) { | |
if (eoln) | |
write(lineType == LINE_UNIX ? "\n" : "\r\n"); | |
for (int i = 0; i < levels.size() - 1; i++) | |
write(" "); | |
return (levels.size() - 1) * 2; | |
} else | |
return 0; | |
} | |
public int getLineType() { | |
return lineType; | |
} | |
public void setLineType(int lineType) { | |
this.lineType = lineType; | |
} | |
public boolean isXmlHeader() { | |
return xmlHeader; | |
} | |
public void setXmlHeader(boolean xmlHeader) { | |
this.xmlHeader = xmlHeader; | |
} | |
public String[] getSpecialAttributeNames() { | |
return specialAttributeNames; | |
} | |
public void setSpecialAttributeNames(String[] specialAttributeNames) { | |
this.specialAttributeNames = specialAttributeNames; | |
} | |
public int getAttributeLineWrap() { | |
return attributeLineWrap; | |
} | |
public void setAttributeLineWrap(int attributeLineWrap) { | |
this.attributeLineWrap = attributeLineWrap; | |
} | |
public void escapedText(String content) throws IOException { | |
text(""); | |
int i = content.length(); | |
if (isPretty()) | |
while (i > 0 && (content.charAt(i-1) == '\r' || content.charAt(i-1) == '\n')) | |
i--; | |
write(content.substring(0, i)); | |
} | |
} | |