001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.util;
018:
019: import java.io.OutputStreamWriter;
020: import java.io.PrintWriter;
021: import java.io.UnsupportedEncodingException;
022: import java.io.Writer;
023:
024: import org.w3c.dom.Attr;
025: import org.w3c.dom.Document;
026: import org.w3c.dom.NamedNodeMap;
027: import org.w3c.dom.Node;
028: import org.w3c.dom.NodeList;
029:
030: /**
031: * A sample DOM writer. This sample program illustrates how to
032: * traverse a DOM tree in order to print a document that is parsed.
033: */
034: public class DOMWriter {
035:
036: //
037: // Data
038: //
039:
040: /** Default Encoding */
041: private static String PRINTWRITER_ENCODING = "UTF8";
042:
043: private static String MIME2JAVA_ENCODINGS[] = { "Default", "UTF-8",
044: "US-ASCII", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3",
045: "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7",
046: "ISO-8859-8", "ISO-8859-9", "ISO-2022-JP", "SHIFT_JIS",
047: "EUC-JP", "GB2312", "BIG5", "EUC-KR", "ISO-2022-KR",
048: "KOI8-R", "EBCDIC-CP-US", "EBCDIC-CP-CA", "EBCDIC-CP-NL",
049: "EBCDIC-CP-DK", "EBCDIC-CP-NO", "EBCDIC-CP-FI",
050: "EBCDIC-CP-SE", "EBCDIC-CP-IT", "EBCDIC-CP-ES",
051: "EBCDIC-CP-GB", "EBCDIC-CP-FR", "EBCDIC-CP-AR1",
052: "EBCDIC-CP-HE", "EBCDIC-CP-CH", "EBCDIC-CP-ROECE",
053: "EBCDIC-CP-YU", "EBCDIC-CP-IS", "EBCDIC-CP-AR2", "UTF-16" };
054:
055: /** Output qualified names */
056: private boolean qualifiedNames = true;
057:
058: /** Print writer. */
059: protected PrintWriter out;
060:
061: /** Canonical output. */
062: protected boolean canonical;
063:
064: public DOMWriter(String encoding, boolean canonical)
065: throws UnsupportedEncodingException {
066: out = new PrintWriter(new OutputStreamWriter(System.out,
067: encoding));
068: this .canonical = canonical;
069: } // <init>(String,boolean)
070:
071: //
072: // Constructors
073: //
074:
075: /** Default constructor. */
076: public DOMWriter(boolean canonical)
077: throws UnsupportedEncodingException {
078: this (getWriterEncoding(), canonical);
079: }
080:
081: public DOMWriter(Writer writer, boolean canonical) {
082: out = new PrintWriter(writer);
083: this .canonical = canonical;
084: }
085:
086: public boolean getQualifiedNames() {
087: return this .qualifiedNames;
088: }
089:
090: public void setQualifiedNames(boolean qualifiedNames) {
091: this .qualifiedNames = qualifiedNames;
092: }
093:
094: public static String getWriterEncoding() {
095: return (PRINTWRITER_ENCODING);
096: }// getWriterEncoding
097:
098: public static void setWriterEncoding(String encoding) {
099: if (encoding.equalsIgnoreCase("DEFAULT"))
100: PRINTWRITER_ENCODING = "UTF8";
101: else if (encoding.equalsIgnoreCase("UTF-16"))
102: PRINTWRITER_ENCODING = "Unicode";
103: else
104: PRINTWRITER_ENCODING = MIME2Java.convert(encoding);
105: }// setWriterEncoding
106:
107: public static boolean isValidJavaEncoding(String encoding) {
108: for (int i = 0; i < MIME2JAVA_ENCODINGS.length; i++)
109: if (encoding.equals(MIME2JAVA_ENCODINGS[i]))
110: return (true);
111:
112: return (false);
113: }// isValidJavaEncoding
114:
115: /** Prints the specified node, recursively. */
116: public void print(Node node) {
117:
118: // is there anything to do?
119: if (node == null) {
120: return;
121: }
122:
123: int type = node.getNodeType();
124: switch (type) {
125: // print document
126: case Node.DOCUMENT_NODE: {
127: if (!canonical) {
128: String Encoding = getWriterEncoding();
129: if (Encoding.equalsIgnoreCase("DEFAULT"))
130: Encoding = "UTF-8";
131: else if (Encoding.equalsIgnoreCase("Unicode"))
132: Encoding = "UTF-16";
133: else
134: Encoding = MIME2Java.reverse(Encoding);
135:
136: out.println("<?xml version=\"1.0\" encoding=\""
137: + Encoding + "\"?>");
138: }
139: print(((Document) node).getDocumentElement());
140: out.flush();
141: break;
142: }
143:
144: // print element with attributes
145: case Node.ELEMENT_NODE: {
146: out.print('<');
147: if (this .qualifiedNames) {
148: out.print(node.getNodeName());
149: } else {
150: out.print(node.getLocalName());
151: }
152: Attr attrs[] = sortAttributes(node.getAttributes());
153: for (int i = 0; i < attrs.length; i++) {
154: Attr attr = attrs[i];
155: out.print(' ');
156: if (this .qualifiedNames) {
157: out.print(attr.getNodeName());
158: } else {
159: out.print(attr.getLocalName());
160: }
161:
162: out.print("=\"");
163: out.print(normalize(attr.getNodeValue()));
164: out.print('"');
165: }
166: out.print('>');
167: NodeList children = node.getChildNodes();
168: if (children != null) {
169: int len = children.getLength();
170: for (int i = 0; i < len; i++) {
171: print(children.item(i));
172: }
173: }
174: break;
175: }
176:
177: // handle entity reference nodes
178: case Node.ENTITY_REFERENCE_NODE: {
179: if (canonical) {
180: NodeList children = node.getChildNodes();
181: if (children != null) {
182: int len = children.getLength();
183: for (int i = 0; i < len; i++) {
184: print(children.item(i));
185: }
186: }
187: } else {
188: out.print('&');
189: if (this .qualifiedNames) {
190: out.print(node.getNodeName());
191: } else {
192: out.print(node.getLocalName());
193: }
194: out.print(';');
195: }
196: break;
197: }
198:
199: // print cdata sections
200: case Node.CDATA_SECTION_NODE: {
201: if (canonical) {
202: out.print(normalize(node.getNodeValue()));
203: } else {
204: out.print("<![CDATA[");
205: out.print(node.getNodeValue());
206: out.print("]]>");
207: }
208: break;
209: }
210:
211: // print text
212: case Node.TEXT_NODE: {
213: out.print(normalize(node.getNodeValue()));
214: break;
215: }
216:
217: // print processing instruction
218: case Node.PROCESSING_INSTRUCTION_NODE: {
219: out.print("<?");
220: if (this .qualifiedNames) {
221: out.print(node.getNodeName());
222: } else {
223: out.print(node.getLocalName());
224: }
225:
226: String data = node.getNodeValue();
227: if (data != null && data.length() > 0) {
228: out.print(' ');
229: out.print(data);
230: }
231: out.print("?>");
232: break;
233: }
234: }
235:
236: if (type == Node.ELEMENT_NODE) {
237: out.print("</");
238: if (this .qualifiedNames) {
239: out.print(node.getNodeName());
240: } else {
241: out.print(node.getLocalName());
242: }
243: out.print('>');
244: }
245:
246: out.flush();
247:
248: } // print(Node)
249:
250: /** Returns a sorted list of attributes. */
251: protected Attr[] sortAttributes(NamedNodeMap attrs) {
252:
253: int len = (attrs != null) ? attrs.getLength() : 0;
254: Attr array[] = new Attr[len];
255: for (int i = 0; i < len; i++) {
256: array[i] = (Attr) attrs.item(i);
257: }
258: for (int i = 0; i < len - 1; i++) {
259: String name = null;
260: if (this .qualifiedNames) {
261: name = array[i].getNodeName();
262: } else {
263: name = array[i].getLocalName();
264: }
265: int index = i;
266: for (int j = i + 1; j < len; j++) {
267: String curName = null;
268: if (this .qualifiedNames) {
269: curName = array[j].getNodeName();
270: } else {
271: curName = array[j].getLocalName();
272: }
273: if (curName.compareTo(name) < 0) {
274: name = curName;
275: index = j;
276: }
277: }
278: if (index != i) {
279: Attr temp = array[i];
280: array[i] = array[index];
281: array[index] = temp;
282: }
283: }
284:
285: return (array);
286:
287: } // sortAttributes(NamedNodeMap):Attr[]
288:
289: /** Normalizes the given string. */
290: protected String normalize(String s) {
291: StringBuffer str = new StringBuffer();
292:
293: int len = (s != null) ? s.length() : 0;
294: for (int i = 0; i < len; i++) {
295: char ch = s.charAt(i);
296: switch (ch) {
297: case '<': {
298: str.append("<");
299: break;
300: }
301: case '>': {
302: str.append(">");
303: break;
304: }
305: case '&': {
306: str.append("&");
307: break;
308: }
309: case '"': {
310: str.append(""");
311: break;
312: }
313: case '\r':
314: case '\n': {
315: if (canonical) {
316: str.append("&#");
317: str.append(Integer.toString(ch));
318: str.append(';');
319: break;
320: }
321: // else, default append char
322: }
323: default: {
324: str.append(ch);
325: }
326: }
327: }
328:
329: return (str.toString());
330:
331: } // normalize(String):String
332:
333: private static void printValidJavaEncoding() {
334: System.err.println(" ENCODINGS:");
335: System.err.print(" ");
336: for (int i = 0; i < MIME2JAVA_ENCODINGS.length; i++) {
337: System.err.print(MIME2JAVA_ENCODINGS[i] + " ");
338: if ((i % 7) == 0) {
339: System.err.println();
340: System.err.print(" ");
341: }
342: }
343:
344: } // printJavaEncoding()
345:
346: }
|