/* * Copyright 2000-2009 JetBrains s.r.o. * * 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 com.intellij.util; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; import org.w3c.dom.*; import java.io.PrintWriter; class DOMWriter { private final PrintWriter myOut; private final boolean myCanonical; @NonNls private static final String XML_PROLOG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; @NonNls private static final String DOCTYPE = "<!DOCTYPE "; @NonNls private static final String PUBLIC = " PUBLIC '"; @NonNls private static final String SYSTEM = " SYSTEM '"; @NonNls private static final String QUOT = """; @NonNls private static final String AMP = "&"; @NonNls private static final String GT = ">"; @NonNls private static final String LT = "<"; @NonNls private static final String CDATA = "<![CDATA["; private static final int INDENT = 2; public DOMWriter(boolean canonical, PrintWriter printWriter) { myCanonical = canonical; myOut = printWriter; } public void write(Node node) { _write(node, 0); myOut.println(); //JDOM compatiblity } private void _write(final Node node, int indent) { if (node == null) return; short type = node.getNodeType(); switch (type) { case Node.DOCUMENT_NODE: { Document document = (Document)node; myOut.println(XML_PROLOG); _write(document.getDoctype(), indent); _write(document.getDocumentElement(), indent); break; } case Node.DOCUMENT_TYPE_NODE: { DocumentType doctype = (DocumentType)node; myOut.print(DOCTYPE); myOut.print(doctype.getName()); String publicId = doctype.getPublicId(); String systemId = doctype.getSystemId(); if (publicId != null) { myOut.print(PUBLIC); myOut.print(publicId); myOut.print("' '"); myOut.print(systemId); myOut.print('\''); } else { myOut.print(SYSTEM); myOut.print(systemId); myOut.print('\''); } String internalSubset = doctype.getInternalSubset(); if (internalSubset != null) { myOut.println(" ["); myOut.print(internalSubset); myOut.print(']'); } myOut.println('>'); break; } case Node.ELEMENT_NODE: { indent(indent); myOut.print('<'); myOut.print(node.getNodeName()); final NamedNodeMap attributes = node.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Attr attr = (Attr)attributes.item(i); myOut.print(' '); myOut.print(attr.getNodeName()); myOut.print("=\""); normalizeAndPrint(attr.getNodeValue()); myOut.print('"'); } Node child = node.getFirstChild(); if (child == null) { myOut.println(" />"); } else { myOut.print('>'); boolean firstChild = true; boolean indentNeeded = false; while (child != null) { if (child instanceof Element) { if (firstChild) { myOut.println(); indentNeeded = true; } firstChild = false; } else if (child.getNodeValue().trim().length() > 0){ firstChild = false; } _write(child, indent + INDENT); child = child.getNextSibling(); } if (indentNeeded) { indent(indent); } myOut.print("</"); myOut.print(node.getNodeName()); myOut.println('>'); break; } } case Node.ENTITY_REFERENCE_NODE: { if (myCanonical) { Node child = node.getFirstChild(); while (child != null) { _write(child, indent); child = child.getNextSibling(); } } else { myOut.print('&'); myOut.print(node.getNodeName()); myOut.print(';'); } break; } case Node.CDATA_SECTION_NODE: { if (myCanonical) { normalizeAndPrint(node.getNodeValue()); } else { myOut.print(CDATA); myOut.print(node.getNodeValue()); myOut.print("]]>"); } break; } case Node.TEXT_NODE: { final String nodeValue = node.getNodeValue(); if (nodeValue.trim().length() > 0) { normalizeAndPrint(nodeValue.trim()); } break; } case Node.PROCESSING_INSTRUCTION_NODE: { myOut.print("<?"); myOut.print(node.getNodeName()); String data = node.getNodeValue(); if (data != null && data.length() > 0) { myOut.print(' '); myOut.print(data); } myOut.println("?>"); break; } } } private void indent(final int indent) { for (int i = 0; i < indent; i++) { myOut.print(' '); } } @Nullable private static Attr[] sortAttributes(NamedNodeMap attrs) { if (attrs == null) return null; int len = attrs.getLength(); Attr[] array = new Attr[len]; for (int i = 0; i < len; i++) { array[i] = (Attr)attrs.item(i); } return array; } private void normalizeAndPrint(String s) { if (s == null) return; int len = s.length(); for (int i = 0; i < len; i++) { char c = s.charAt(i); normalizeAndPrint(c); } } private void normalizeAndPrint(char c) { switch (c) { case '<': { myOut.print(LT); break; } case '>': { myOut.print(GT); break; } case '&': { myOut.print(AMP); break; } case '"': { myOut.print(QUOT); break; } case '\r': case '\n': { if (myCanonical) { myOut.print("&#"); myOut.print(Integer.toString(c)); myOut.print(';'); break; } } default: { myOut.print(c); } } } }