XmlNode.java
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2025, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.klimt.drawing.svg;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* A minimal, dependency-free DOM element, replicating just the subset of
* {@code org.w3c.dom.Element} that the SVG generation code relies on.
* <p>
* It exists so that an SVG document tree can be built and serialized under
* TeaVM, where {@code javax.xml} / {@code org.w3c.dom} are not available. The
* tree is built eagerly (like a DOM) so that attributes set late — once the
* final canvas size is known — are honored, then serialized to text in one pass
* via {@link XmlWriter}.
* <p>
* Attribute order is preserved (insertion order) to keep the output
* deterministic.
*/
public class XmlNode implements XmlContent, IElement {
private final String tagName;
private final Map<String, String> attributes = new LinkedHashMap<>();
private final List<XmlContent> children = new ArrayList<>();
XmlNode(String tagName) {
this.tagName = tagName;
}
public String getTagName() {
return tagName;
}
public void setAttribute(String name, String value) {
attributes.put(name, value);
}
public void appendChild(IElement child) {
children.add((XmlNode) child);
}
/**
* Replaces all current content with a single text node, mirroring
* {@code org.w3c.dom.Node.setTextContent(String)}.
*/
public void setTextContent(String value) {
children.clear();
appendText(value);
}
public void appendText(String value) {
children.add(XmlLeaf.text(value));
}
public void appendComment(String value) {
children.add(XmlLeaf.comment(value));
}
public void appendProcessingInstruction(String target, String data) {
children.add(XmlLeaf.processingInstruction(target, data));
}
public void appendCData(String value) {
children.add(XmlLeaf.cdata(value));
}
/**
* Appends already-serialized markup, spliced verbatim (no escaping). Used for
* inlined SVG images.
*/
public void appendRaw(String markup) {
children.add(XmlLeaf.raw(markup));
}
/**
* Returns the first child if this element has any content, else {@code null}.
* Mirrors {@code org.w3c.dom.Node.getFirstChild()}, used only to test whether
* an element is empty (so empty groups/links can be dropped).
*/
public XmlContent getFirstChild() {
return children.isEmpty() ? null : children.get(0);
}
/** Serializes this element and its subtree into the given writer. */
@Override
public void writeTo(XmlWriter w) {
w.startElement(tagName);
for (Map.Entry<String, String> ent : attributes.entrySet())
w.attribute(ent.getKey(), ent.getValue());
for (XmlContent child : children)
child.writeTo(w);
w.endElement();
}
/** Serializes this element as a standalone document fragment. */
public String toXml(int indentSpaces) {
final XmlWriter w = new XmlWriter(indentSpaces);
writeTo(w);
return w.getXml();
}
}