| /******************************************************************************* |
| * Copyright (c) 2008, 2017 xored software, Inc. and others. |
| * |
| * 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: |
| * xored software, Inc. - initial API and Implementation (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.core.tests.xml; |
| |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.StringWriter; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentType; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * Own implementation to have the same output on any platform. |
| */ |
| public class DOMSerializer { |
| |
| /** Indentation to use (default is no indentation) */ |
| private String indent = ""; |
| |
| /** Line separator to use */ |
| private String lineSeparator = "\n"; |
| |
| /** Encoding for output (default is UTF-8) */ |
| private String encoding = "UTF8"; |
| |
| /** Attributes will be displayed on separate lines */ |
| private boolean displayAttributesOnSeperateLine = true; |
| |
| private boolean skipEmptyText = true; |
| |
| public void setLineSeparator(String lineSeparator) { |
| this.lineSeparator = lineSeparator; |
| } |
| |
| public void setEncoding(String encoding) { |
| this.encoding = encoding; |
| } |
| |
| public void setIndent(int numSpaces) { |
| final char[] buffer = new char[numSpaces]; |
| Arrays.fill(buffer, ' '); |
| this.indent = new String(buffer); |
| } |
| |
| public void serialize(Document doc, OutputStream out) throws IOException { |
| Writer writer = new OutputStreamWriter(out, encoding); |
| serialize(doc, writer); |
| } |
| |
| public void serialize(Document doc, File file) throws IOException { |
| Writer writer = new FileWriter(file); |
| serialize(doc, writer); |
| } |
| |
| public void serialize(Document doc, Writer writer) throws IOException { |
| doc.normalize(); |
| // Start serialization recursion with no indenting |
| serializeNode(doc, writer, ""); |
| writer.flush(); |
| } |
| |
| public String serialize(Document doc) throws IOException { |
| final StringWriter writer = new StringWriter(); |
| serialize(doc, writer); |
| return writer.toString(); |
| } |
| |
| private void serializeNode(Node node, Writer writer, String indentLevel) |
| throws IOException { |
| // Determine action based on node type |
| switch (node.getNodeType()) { |
| case Node.DOCUMENT_NODE: |
| // Document doc = (Document) node; |
| // writer.write("<?xml version=\""); |
| // writer.write(doc.getXmlVersion()); |
| // writer.write("\" encoding=\"UTF-8\" standalone=\""); |
| // if (doc.getXmlStandalone()) |
| // writer.write("yes"); |
| // else |
| // writer.write("no"); |
| // writer.write("\""); |
| // writer.write("?>"); |
| // writer.write(lineSeparator); |
| // recurse on each top-level node |
| Node[] nodes = collectChildren(node); |
| if (nodes != null) { |
| for (int i = 0; i < nodes.length; i++) { |
| serializeNode(nodes[i], writer, ""); |
| } |
| } |
| break; |
| case Node.ELEMENT_NODE: |
| final String name = node.getNodeName(); |
| writer.write(indentLevel + "<" + name); |
| writeAttributes(node, writer, indentLevel); |
| |
| // recurse on each child |
| final Node[] children = collectChildren(node); |
| if (children != null) { |
| // Close the open tag |
| writer.write(">"); |
| if (children[0].getNodeType() == Node.ELEMENT_NODE) { |
| writer.write(lineSeparator); |
| } |
| for (int i = 0; i < children.length; i++) { |
| serializeNode(children[i], writer, indentLevel + indent); |
| } |
| if (children[children.length - 1] |
| .getNodeType() == Node.ELEMENT_NODE) { |
| writer.write(indentLevel); |
| } |
| writer.write("</" + name + ">"); |
| } else { |
| // Close this element without making a frivolous full close tag |
| writer.write("/>"); |
| } |
| writer.write(lineSeparator); |
| break; |
| case Node.TEXT_NODE: |
| final String value = node.getNodeValue(); |
| if (!skipEmptyText || !isBlank(value)) { |
| print(writer, value); |
| } |
| break; |
| case Node.CDATA_SECTION_NODE: |
| writer.write("<![CDATA["); |
| print(writer, node.getNodeValue()); |
| writer.write("]]>"); |
| break; |
| case Node.COMMENT_NODE: |
| writer.write(indentLevel + "<!-- " + node.getNodeValue() + " -->"); |
| writer.write(lineSeparator); |
| break; |
| case Node.PROCESSING_INSTRUCTION_NODE: |
| writer.write("<?" + node.getNodeName() + " " + node.getNodeValue() |
| + "?>"); |
| writer.write(lineSeparator); |
| break; |
| case Node.ENTITY_REFERENCE_NODE: |
| writer.write("&" + node.getNodeName() + ";"); |
| break; |
| case Node.DOCUMENT_TYPE_NODE: |
| DocumentType docType = (DocumentType) node; |
| String publicId = docType.getPublicId(); |
| String systemId = docType.getSystemId(); |
| String internalSubset = docType.getInternalSubset(); |
| writer.write("<!DOCTYPE " + docType.getName()); |
| if (publicId != null) |
| writer.write(" PUBLIC \"" + publicId + "\" "); |
| else |
| writer.write(" SYSTEM "); |
| writer.write("\"" + systemId + "\""); |
| if (internalSubset != null) |
| writer.write(" [" + internalSubset + "]"); |
| writer.write(">"); |
| writer.write(lineSeparator); |
| break; |
| } |
| } |
| |
| private void writeAttributes(Node node, Writer writer, String indentLevel) |
| throws IOException { |
| NamedNodeMap attributes = node.getAttributes(); |
| final String[] attributeNames = new String[attributes.getLength()]; |
| for (int i = 0; i < attributes.getLength(); i++) { |
| attributeNames[i] = attributes.item(i).getNodeName(); |
| } |
| Arrays.sort(attributeNames); |
| for (int i = 0; i < attributes.getLength(); i++) { |
| Node current = attributes.getNamedItem(attributeNames[i]); |
| String attributeSeperator = " "; |
| // If we only have one attribute write it on the same line |
| // otherwise indent |
| if (displayAttributesOnSeperateLine |
| && attributes.getLength() != 1) { |
| attributeSeperator = lineSeparator + indentLevel + indent; |
| } |
| // Double indentLevel to match parent element and then one |
| // indentation to format below parent |
| String attributeStr = attributeSeperator + current.getNodeName() |
| + "=\""; |
| writer.write(attributeStr); |
| print(writer, current.getNodeValue()); |
| writer.write("\""); |
| } |
| } |
| |
| private Node[] collectChildren(Node parent) { |
| final NodeList children = parent.getChildNodes(); |
| if (children != null && children.getLength() > 0) { |
| final List<Node> result = new ArrayList<>(); |
| for (int i = 0; i < children.getLength(); i++) { |
| final Node child = children.item(i); |
| if (child == null) { |
| continue; |
| } |
| if (skipEmptyText && child.getNodeType() == Node.TEXT_NODE |
| && isBlank(child.getNodeValue())) { |
| continue; |
| } |
| result.add(child); |
| } |
| if (!result.isEmpty()) { |
| return result.toArray(new Node[result.size()]); |
| } |
| } |
| return null; |
| } |
| |
| private static boolean isBlank(String str) { |
| if (str == null) { |
| return true; |
| } |
| final int strLen = str.length(); |
| if (strLen == 0) { |
| return true; |
| } |
| for (int i = 0; i < strLen; i++) { |
| if ((Character.isWhitespace(str.charAt(i)) == false)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void print(Writer writer, String s) throws IOException { |
| if (s == null) |
| return; |
| for (int i = 0, len = s.length(); i < len; i++) { |
| char c = s.charAt(i); |
| switch (c) { |
| case '<': |
| writer.write("<"); |
| break; |
| case '>': |
| writer.write(">"); |
| break; |
| case '&': |
| writer.write("&"); |
| break; |
| case '\r': |
| writer.write("
"); |
| break; |
| default: |
| writer.write(c); |
| } |
| } |
| } |
| |
| public boolean isDisplayAttributesOnSeperateLine() { |
| return displayAttributesOnSeperateLine; |
| } |
| |
| public void setDisplayAttributesOnSeperateLine( |
| boolean displayAttributesOnSeperateLine) { |
| this.displayAttributesOnSeperateLine = displayAttributesOnSeperateLine; |
| } |
| |
| } |