| /* |
| |
| Copyright 2001-2002 The Apache Software Foundation |
| |
| Licensed 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. |
| |
| */ |
| package org.apache.batik.transcoder.svg2svg; |
| |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.apache.batik.xml.XMLUtilities; |
| |
| /** |
| * This class is responsible of the output of XML constructs. |
| * |
| * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a> |
| * @version $Id$ |
| */ |
| public class OutputManager { |
| |
| /** |
| * The pretty printer. |
| */ |
| protected PrettyPrinter prettyPrinter; |
| |
| /** |
| * The writer used to output the tokens. |
| */ |
| protected Writer writer; |
| |
| /** |
| * The indentation level. |
| */ |
| protected int level; |
| |
| /** |
| * The margin. |
| */ |
| protected StringBuffer margin = new StringBuffer(); |
| |
| /** |
| * The current line. |
| */ |
| protected int line = 1; |
| |
| /** |
| * The current column. |
| */ |
| protected int column; |
| |
| /** |
| * The xml:space values. |
| */ |
| protected List xmlSpace = new LinkedList(); |
| { |
| xmlSpace.add(Boolean.FALSE); |
| } |
| |
| /** |
| * Whether the next markup can be indented. |
| */ |
| protected boolean canIndent = true; |
| |
| /** |
| * The elements starting lines. |
| */ |
| protected List startingLines = new LinkedList(); |
| |
| /** |
| * Whether the attribute layout must be done on a single line. |
| */ |
| protected boolean lineAttributes = false; |
| |
| /** |
| * Creates a new output manager. |
| * @param pp The PrettyPrinter used for formatting the output. |
| * @param w The Writer to write the output to. |
| */ |
| public OutputManager(PrettyPrinter pp, Writer w) { |
| prettyPrinter = pp; |
| writer = w; |
| } |
| |
| /** |
| * Prints a single character. |
| */ |
| public void printCharacter(char c) throws IOException { |
| if (c == 10) { |
| printNewline(); |
| } else { |
| column++; |
| writer.write(c); |
| } |
| } |
| |
| /** |
| * Prints a newline. |
| */ |
| public void printNewline() throws IOException { |
| String nl = prettyPrinter.getNewline(); |
| for (int i = 0; i < nl.length(); i++) { |
| writer.write(nl.charAt(i)); |
| } |
| column = 0; |
| line++; |
| } |
| |
| /** |
| * Prints a string. |
| */ |
| public void printString(String s) throws IOException { |
| for (int i = 0; i < s.length(); i++) { |
| printCharacter(s.charAt(i)); |
| } |
| } |
| |
| /** |
| * Prints a char array. |
| */ |
| public void printCharacters(char[] ca) throws IOException { |
| for (int i = 0; i < ca.length; i++) { |
| printCharacter(ca[i]); |
| } |
| } |
| |
| /** |
| * Prints white spaces. |
| * @param text The space text. |
| * @param opt whether the space is optional. |
| */ |
| public void printSpaces(char[] text, boolean opt) throws IOException { |
| if (prettyPrinter.getFormat()) { |
| if (!opt) { |
| printCharacter(' '); |
| } |
| } else { |
| printCharacters(text); |
| } |
| } |
| |
| /** |
| * Prints top level white spaces. |
| * @param text The space text. |
| */ |
| public void printTopSpaces(char[] text) throws IOException { |
| if (prettyPrinter.getFormat()) { |
| int nl = newlines(text); |
| for (int i = 0; i < nl; i++) { |
| printNewline(); |
| } |
| } else { |
| printCharacters(text); |
| } |
| } |
| |
| /** |
| * Prints a comment. |
| * @param text The comment text. |
| */ |
| public void printComment(char[] text) throws IOException { |
| if (prettyPrinter.getFormat()) { |
| if (canIndent) { |
| printNewline(); |
| printString(margin.toString()); |
| } |
| printString("<!--"); |
| if (column + text.length + 3 < prettyPrinter.getDocumentWidth()) { |
| printCharacters(text); |
| } else { |
| formatText(text, margin.toString(), false); |
| printCharacter(' '); |
| } |
| if (column + 3 > prettyPrinter.getDocumentWidth()) { |
| printNewline(); |
| printString(margin.toString()); |
| } |
| printString("-->"); |
| } else { |
| printString("<!--"); |
| printCharacters(text); |
| printString("-->"); |
| } |
| } |
| |
| /** |
| * Prints an XML declaration. |
| */ |
| public void printXMLDecl(char[] space1, |
| char[] space2, |
| char[] space3, |
| char[] version, char versionDelim, |
| char[] space4, |
| char[] space5, |
| char[] space6, |
| char[] encoding, char encodingDelim, |
| char[] space7, |
| char[] space8, |
| char[] space9, |
| char[] standalone, char standaloneDelim, |
| char[] space10) |
| throws IOException { |
| printString("<?xml"); |
| |
| printSpaces(space1, false); |
| |
| printString("version"); |
| |
| if (space2 != null) { |
| printSpaces(space2, true); |
| } |
| |
| printCharacter('='); |
| |
| if (space3 != null) { |
| printSpaces(space3, true); |
| } |
| |
| printCharacter(versionDelim); |
| printCharacters(version); |
| printCharacter(versionDelim); |
| |
| if (space4 != null) { |
| printSpaces(space4, false); |
| |
| if (encoding != null) { |
| printString("encoding"); |
| |
| if (space5 != null) { |
| printSpaces(space5, true); |
| } |
| |
| printCharacter('='); |
| |
| if (space6 != null) { |
| printSpaces(space6, true); |
| } |
| |
| printCharacter(encodingDelim); |
| printCharacters(encoding); |
| printCharacter(encodingDelim); |
| |
| if (space7 != null) { |
| printSpaces(space7, standalone == null); |
| } |
| } |
| |
| if (standalone != null) { |
| printString("standalone"); |
| |
| if (space8 != null) { |
| printSpaces(space8, true); |
| } |
| |
| printCharacter('='); |
| |
| if (space9 != null) { |
| printSpaces(space9, true); |
| } |
| |
| printCharacter(standaloneDelim); |
| printCharacters(standalone); |
| printCharacter(standaloneDelim); |
| |
| if (space10 != null) { |
| printSpaces(space10, true); |
| } |
| } |
| } |
| |
| printString("?>"); |
| } |
| |
| /** |
| * Prints a processing instruction. |
| */ |
| public void printPI(char[] target, char[] space, char[] data) throws IOException { |
| if (prettyPrinter.getFormat()) { |
| if (canIndent) { |
| printNewline(); |
| printString(margin.toString()); |
| } |
| } |
| printString("<?"); |
| printCharacters(target); |
| printSpaces(space, false); |
| printCharacters(data); |
| printString("?>"); |
| } |
| |
| /** |
| * Prints the portion of the doctype before '['. |
| */ |
| public void printDoctypeStart(char[] space1, |
| char[] root, |
| char[] space2, |
| String externalId, |
| char[] space3, |
| char[] string1, char string1Delim, |
| char[] space4, |
| char[] string2, char string2Delim, |
| char[] space5) throws IOException { |
| if (prettyPrinter.getFormat()) { |
| printString("<!DOCTYPE"); |
| |
| printCharacter(' '); |
| printCharacters(root); |
| |
| if (space2 != null) { |
| printCharacter(' '); |
| printString(externalId); |
| printCharacter(' '); |
| |
| printCharacter(string1Delim); |
| printCharacters(string1); |
| printCharacter(string1Delim); |
| |
| if (space4 != null) { |
| if (string2 != null) { |
| if (column + string2.length + 3 > |
| prettyPrinter.getDocumentWidth()) { |
| printNewline(); |
| for (int i = 0; |
| i < prettyPrinter.getTabulationWidth(); |
| i++) { |
| printCharacter(' '); |
| } |
| } else { |
| printCharacter(' '); |
| } |
| printCharacter(string2Delim); |
| printCharacters(string2); |
| printCharacter(string2Delim); |
| printCharacter(' '); |
| } |
| } |
| } |
| } else { |
| printString("<!DOCTYPE"); |
| |
| printSpaces(space1, false); |
| printCharacters(root); |
| |
| if (space2 != null) { |
| printSpaces(space2, false); |
| printString(externalId); |
| printSpaces(space3, false); |
| |
| printCharacter(string1Delim); |
| printCharacters(string1); |
| printCharacter(string1Delim); |
| |
| if (space4 != null) { |
| printSpaces(space4, string2 == null); |
| |
| if (string2 != null) { |
| printCharacter(string2Delim); |
| printCharacters(string2); |
| printCharacter(string2Delim); |
| |
| if (space5 != null) { |
| printSpaces(space5, true); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Prints the portion of the doctype after ']'. |
| */ |
| public void printDoctypeEnd(char[] space) throws IOException { |
| if (space != null) { |
| printSpaces(space, true); |
| } |
| printCharacter('>'); |
| } |
| |
| /** |
| * Prints a parameter entity reference. |
| */ |
| public void printParameterEntityReference(char[] name) throws IOException { |
| printCharacter('%'); |
| printCharacters(name); |
| printCharacter(';'); |
| } |
| |
| /** |
| * Prints an entity reference. |
| */ |
| public void printEntityReference(char[] name, |
| boolean first) throws IOException { |
| if ((prettyPrinter.getFormat()) && |
| (xmlSpace.get(0) != Boolean.TRUE) && |
| first) { |
| printNewline(); |
| printString(margin.toString()); |
| } |
| printCharacter('&'); |
| printCharacters(name); |
| printCharacter(';'); |
| } |
| |
| /** |
| * Prints a character entity reference. |
| */ |
| public void printCharacterEntityReference |
| (char[] code, boolean first, boolean preceedingSpace) |
| throws IOException { |
| if ((prettyPrinter.getFormat()) && |
| (xmlSpace.get(0) != Boolean.TRUE)) { |
| |
| if (first) { |
| printNewline(); |
| printString(margin.toString()); |
| } else if (preceedingSpace) { |
| int endCol = column + code.length + 3; |
| if (endCol > prettyPrinter.getDocumentWidth()){ |
| printNewline(); |
| printString(margin.toString()); |
| } else { |
| printCharacter(' '); |
| } |
| } |
| } |
| printString("&#"); |
| printCharacters(code); |
| printCharacter(';'); |
| } |
| |
| /** |
| * Prints the start of an element. |
| */ |
| public void printElementStart(char[] name, List attributes, char[] space) |
| throws IOException { |
| xmlSpace.add(0, xmlSpace.get(0)); |
| |
| startingLines.add(0, new Integer(line)); |
| |
| if (prettyPrinter.getFormat()) { |
| if (canIndent) { |
| printNewline(); |
| printString(margin.toString()); |
| } |
| } |
| printCharacter('<'); |
| printCharacters(name); |
| |
| if (prettyPrinter.getFormat()) { |
| Iterator it = attributes.iterator(); |
| if (it.hasNext()) { |
| AttributeInfo ai = (AttributeInfo)it.next(); |
| |
| if (ai.isAttribute("xml:space")) { |
| xmlSpace.set(0, (ai.value.equals("preserve") |
| ? Boolean.TRUE |
| : Boolean.FALSE)); |
| } |
| |
| printCharacter(' '); |
| printCharacters(ai.name); |
| printCharacter('='); |
| printCharacter(ai.delimiter); |
| printString(ai.value); |
| printCharacter(ai.delimiter); |
| } |
| while (it.hasNext()) { |
| AttributeInfo ai = (AttributeInfo)it.next(); |
| |
| if (ai.isAttribute("xml:space")) { |
| xmlSpace.set(0, (ai.value.equals("preserve") |
| ? Boolean.TRUE |
| : Boolean.FALSE)); |
| } |
| |
| int len = ai.name.length + ai.value.length() + 4; |
| if (lineAttributes || |
| len + column > prettyPrinter.getDocumentWidth()) { |
| printNewline(); |
| printString(margin.toString()); |
| for (int i = 0; i < name.length + 2; i++) { |
| printCharacter(' '); |
| } |
| } else { |
| printCharacter(' '); |
| } |
| printCharacters(ai.name); |
| printCharacter('='); |
| printCharacter(ai.delimiter); |
| printString(ai.value); |
| printCharacter(ai.delimiter); |
| } |
| } else { |
| Iterator it = attributes.iterator(); |
| while (it.hasNext()) { |
| AttributeInfo ai = (AttributeInfo)it.next(); |
| |
| if (ai.isAttribute("xml:space")) { |
| xmlSpace.set(0, (ai.value.equals("preserve") |
| ? Boolean.TRUE |
| : Boolean.FALSE)); |
| } |
| |
| printSpaces(ai.space, false); |
| printCharacters(ai.name); |
| |
| if (ai.space1 != null) { |
| printSpaces(ai.space1, true); |
| } |
| printCharacter('='); |
| if (ai.space2 != null) { |
| printSpaces(ai.space2, true); |
| } |
| |
| printCharacter(ai.delimiter); |
| printString(ai.value); |
| printCharacter(ai.delimiter); |
| } |
| } |
| |
| if (space != null) { |
| printSpaces(space, true); |
| } |
| level++; |
| for (int i = 0; i < prettyPrinter.getTabulationWidth(); i++) { |
| margin.append(' '); |
| } |
| canIndent = true; |
| } |
| |
| /** |
| * Prints the end of an element. |
| */ |
| public void printElementEnd(char[] name, char[] space) throws IOException { |
| for (int i = 0; i < prettyPrinter.getTabulationWidth(); i++) { |
| margin.deleteCharAt(0); |
| } |
| level--; |
| if (name != null) { |
| if (prettyPrinter.getFormat()) { |
| if (xmlSpace.get(0) != Boolean.TRUE && |
| (line != ((Integer)startingLines.get(0)).intValue() || |
| column + name.length + 3 >= prettyPrinter.getDocumentWidth())) { |
| printNewline(); |
| printString(margin.toString()); |
| } |
| } |
| printString("</"); |
| printCharacters(name); |
| if (space != null) { |
| printSpaces(space, true); |
| } |
| printCharacter('>'); |
| } else { |
| printString("/>"); |
| } |
| startingLines.remove(0); |
| xmlSpace.remove(0); |
| } |
| |
| /** |
| * Prints the character data of an element content. |
| */ |
| public boolean printCharacterData(char[] data, |
| boolean first, |
| boolean preceedingSpace) |
| throws IOException { |
| if (!prettyPrinter.getFormat()) { |
| printCharacters(data); |
| return false; |
| } |
| |
| canIndent = true; |
| if (isWhiteSpace(data)) { |
| int nl = newlines(data); |
| for (int i = 0; i < nl - 1; i++) { |
| printNewline(); |
| } |
| return true; |
| } |
| |
| if (xmlSpace.get(0) == Boolean.TRUE) { |
| printCharacters(data); |
| canIndent = false; |
| return false; |
| } |
| |
| if (first) { |
| printNewline(); |
| printString(margin.toString()); |
| } |
| return formatText(data, margin.toString(), preceedingSpace); |
| } |
| |
| /** |
| * Prints a CDATA section. |
| */ |
| public void printCDATASection(char[] data) throws IOException { |
| printString("<![CDATA["); |
| printCharacters(data); |
| printString("]]>"); |
| } |
| |
| /** |
| * Prints a notation declaration. |
| */ |
| public void printNotation(char[] space1, |
| char[] name, |
| char[] space2, |
| String externalId, |
| char[] space3, |
| char[] string1, char string1Delim, |
| char[] space4, |
| char[] string2, char string2Delim, |
| char[] space5) |
| throws IOException { |
| writer.write("<!NOTATION"); |
| printSpaces(space1, false); |
| writer.write(name); |
| printSpaces(space2, false); |
| writer.write(externalId); |
| printSpaces(space3, false); |
| |
| writer.write(string1Delim); |
| writer.write(string1); |
| writer.write(string1Delim); |
| |
| if (space4 != null) { |
| printSpaces(space4, false); |
| |
| if (string2 != null) { |
| writer.write(string2Delim); |
| writer.write(string2); |
| writer.write(string2Delim); |
| } |
| } |
| if (space5 != null) { |
| printSpaces(space5, true); |
| } |
| writer.write('>'); |
| } |
| |
| /** |
| * Prints an attribute list declaration start. |
| */ |
| public void printAttlistStart(char[] space, char[] name) throws IOException { |
| writer.write("<!ATTLIST"); |
| printSpaces(space, false); |
| writer.write(name); |
| } |
| |
| /** |
| * Prints an attribute list declaration end. |
| */ |
| public void printAttlistEnd(char[] space) throws IOException { |
| if (space != null) { |
| printSpaces(space, false); |
| } |
| writer.write('>'); |
| } |
| |
| /** |
| * Prints an attribute declaration start. |
| */ |
| public void printAttName(char[] space1, char[] name, char[] space2) |
| throws IOException { |
| printSpaces(space1, false); |
| writer.write(name); |
| printSpaces(space2, false); |
| } |
| |
| /** |
| * Prints an enumeration. |
| */ |
| public void printEnumeration(List names) throws IOException { |
| writer.write('('); |
| |
| Iterator it = names.iterator(); |
| NameInfo ni = (NameInfo)it.next(); |
| if (ni.space1 != null) { |
| printSpaces(ni.space1, true); |
| } |
| |
| writer.write(ni.name); |
| |
| if (ni.space2 != null) { |
| printSpaces(ni.space2, true); |
| } |
| while (it.hasNext()) { |
| writer.write('|'); |
| |
| ni = (NameInfo)it.next(); |
| if (ni.space1 != null) { |
| printSpaces(ni.space1, true); |
| } |
| |
| writer.write(ni.name); |
| |
| if (ni.space2 != null) { |
| printSpaces(ni.space2, true); |
| } |
| } |
| |
| writer.write(')'); |
| } |
| |
| /** |
| * Returns the number of newlines in the given char array. |
| */ |
| protected int newlines(char[] text) { |
| int result = 0; |
| for (int i = 0; i < text.length; i++) { |
| if (text[i] == 10) { |
| result++; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Tells whether the given character represents white spaces. |
| */ |
| protected boolean isWhiteSpace(char[] text) { |
| for (int i = 0; i < text.length; i++) { |
| if (!XMLUtilities.isXMLSpace(text[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Formats the given text. |
| */ |
| protected boolean formatText(char[] text, String margin, |
| boolean preceedingSpace) throws IOException { |
| int i = 0; |
| boolean startsWithSpace = preceedingSpace; |
| loop: while (i < text.length) { |
| for (;;) { |
| if (i >= text.length) { |
| break loop; |
| } |
| if (!XMLUtilities.isXMLSpace(text[i])) { |
| break; |
| } |
| startsWithSpace = true; |
| i++; |
| } |
| StringBuffer sb = new StringBuffer(); |
| for (;;) { |
| if (i >= text.length || XMLUtilities.isXMLSpace(text[i])) { |
| break; |
| } |
| sb.append(text[i++]); |
| } |
| if (sb.length() == 0) { |
| return startsWithSpace; |
| } |
| if (startsWithSpace) { |
| // Consider reformatting ws so things look nicer. |
| int endCol = column + sb.length(); |
| if ((endCol >= prettyPrinter.getDocumentWidth() - 1) && |
| ((margin.length() + sb.length() < |
| prettyPrinter.getDocumentWidth() - 1) || |
| (margin.length() < column))) { |
| printNewline(); |
| printString(margin); |
| } else if (column > margin.length()) { |
| // Don't print space at start of new line. |
| printCharacter(' '); |
| } |
| } |
| printString(sb.toString()); |
| startsWithSpace = false; |
| } |
| return startsWithSpace; |
| } |
| |
| /** |
| * To store the informations about a name. |
| */ |
| public static class NameInfo { |
| |
| /** |
| * The space before the name. |
| */ |
| public char[] space1; |
| |
| /** |
| * The name. |
| */ |
| public char[] name; |
| |
| /** |
| * The space after the name |
| */ |
| public char[] space2; |
| |
| /** |
| * Creates a new NameInfo. |
| */ |
| public NameInfo(char[] sp1, char[] nm, char[] sp2) { |
| space1 = sp1; |
| name = nm; |
| space2 = sp2; |
| } |
| } |
| |
| /** |
| * To store the informations about an attribute. |
| */ |
| public static class AttributeInfo { |
| |
| /** |
| * The space before the name. |
| */ |
| public char[] space; |
| |
| /** |
| * The attribute name. |
| */ |
| public char[] name; |
| |
| /** |
| * The space before '='. |
| */ |
| public char[] space1; |
| |
| /** |
| * The space after '='. |
| */ |
| public char[] space2; |
| |
| /** |
| * The attribute value. |
| */ |
| public String value; |
| |
| /** |
| * The attribute value delimiter. |
| */ |
| public char delimiter; |
| |
| /** |
| * Whether the attribute value contains entity references. |
| */ |
| public boolean entityReferences; |
| |
| /** |
| * Creates a new AttributeInfo. |
| */ |
| public AttributeInfo(char[] sp, char[] n, char[] sp1, char[] sp2, |
| String val, char delim, boolean entity) { |
| space = sp; |
| name = n; |
| space1 = sp1; |
| space2 = sp2; |
| value = val; |
| delimiter = delim; |
| entityReferences = entity; |
| } |
| |
| /** |
| * Tells whether the name of the attribute represented by this class |
| * equals the given string. |
| */ |
| public boolean isAttribute(String s) { |
| if (name.length == s.length()) { |
| for (int i = 0; i < name.length; i++) { |
| if (name[i] != s.charAt(i)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| } |
| } |