001: /*
002: * XML input/output support for FindBugs
003: * Copyright (C) 2004,2005 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.xml;
021:
022: import java.io.IOException;
023: import java.io.OutputStream;
024: import java.io.OutputStreamWriter;
025: import java.io.Writer;
026: import java.nio.charset.Charset;
027:
028: /**
029: * Write XML to an output stream.
030: *
031: * @author David Hovemeyer
032: */
033: public class OutputStreamXMLOutput implements XMLOutput {
034: private static final String OPENING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
035:
036: private static String getStylesheetCode(String stylesheet) {
037: if (stylesheet == null)
038: return "";
039: return "<?xml-stylesheet type=\"text/xsl\" href=\""
040: + stylesheet + "\"?>\n";
041: }
042:
043: private static final MetaCharacterMap textMetaCharacterMap = new MetaCharacterMap();
044: static {
045: textMetaCharacterMap.addMeta('<', "<");
046: textMetaCharacterMap.addMeta('>', ">");
047: textMetaCharacterMap.addMeta('&', "&");
048: }
049:
050: private class WriterQuoteMetaCharacters extends QuoteMetaCharacters {
051: public WriterQuoteMetaCharacters(String text) {
052: super (text, textMetaCharacterMap);
053: }
054:
055: @Override
056: public void emitLiteral(String s) throws IOException {
057: out.write(s);
058: newLine = s.endsWith("\n");
059: }
060: }
061:
062: private Writer out;
063: private int nestingLevel;
064: private boolean newLine;
065: private String stylesheet;
066:
067: /**
068: * Constructor.
069: * @param os OutputStream to write XML output to
070: */
071: public OutputStreamXMLOutput(OutputStream os) {
072: this (os, null);
073: }
074:
075: public OutputStreamXMLOutput(OutputStream os, String stylesheet) {
076: this .out = new OutputStreamWriter(os, Charset.forName("UTF-8"));
077: this .nestingLevel = 0;
078: this .newLine = true;
079: this .stylesheet = stylesheet;
080: }
081:
082: public void beginDocument() throws IOException {
083: out.write(OPENING);
084: out.write(getStylesheetCode(stylesheet));
085: out.write("\n");
086: newLine = true;
087: }
088:
089: public void openTag(String tagName) throws IOException {
090: emitTag(tagName, false);
091: }
092:
093: public void openTag(String tagName, XMLAttributeList attributeList)
094: throws IOException {
095: emitTag(tagName, attributeList.toString(), false);
096: }
097:
098: public void openCloseTag(String tagName) throws IOException {
099: emitTag(tagName, true);
100: }
101:
102: public void openCloseTag(String tagName,
103: XMLAttributeList attributeList) throws IOException {
104: emitTag(tagName, attributeList.toString(), true);
105: }
106:
107: public void startTag(String tagName) throws IOException {
108: indent();
109: ++nestingLevel;
110: out.write("<" + tagName);
111: }
112:
113: public void addAttribute(String name, String value)
114: throws IOException {
115: out.write(' ');
116: out.write(name);
117: out.write('=');
118: out.write(XMLAttributeList.getQuotedAttributeValue(value));
119: }
120:
121: public void stopTag(boolean close) throws IOException {
122: if (close) {
123: out.write("/>\n");
124: --nestingLevel;
125: newLine = true;
126: } else {
127: out.write(">");
128: newLine = false;
129: }
130: }
131:
132: private void emitTag(String tagName, boolean close)
133: throws IOException {
134: startTag(tagName);
135: stopTag(close);
136: }
137:
138: private void emitTag(String tagName, String attributes,
139: boolean close) throws IOException {
140: startTag(tagName);
141: attributes = attributes.trim();
142: if (attributes.length() > 0) {
143: out.write(" ");
144: out.write(attributes);
145: }
146: stopTag(close);
147: }
148:
149: public void closeTag(String tagName) throws IOException {
150: --nestingLevel;
151: if (newLine)
152: indent();
153: out.write("</" + tagName + ">\n");
154: newLine = true;
155: }
156:
157: public void writeText(String text) throws IOException {
158: new WriterQuoteMetaCharacters(text).process();
159: }
160:
161: public void writeCDATA(String cdata) throws IOException {
162: // FIXME: We just trust fate that the characters being written
163: // don't contain the string "]]>"
164: out.write("<![CDATA[");
165: out.write(cdata);
166: out.write("]]>");
167: newLine = false;
168: }
169:
170: public void finish() throws IOException {
171: out.close();
172: }
173:
174: private void indent() throws IOException {
175: if (!newLine)
176: out.write("\n");
177: for (int i = 0; i < nestingLevel; ++i) {
178: out.write(" ");
179: }
180: }
181: }
182:
183: // vim:ts=4
|