001package org.jsoup.nodes;
002
003import org.jsoup.internal.QuietAppendable;
004import org.jsoup.internal.StringUtil;
005
006
007/**
008 * An XML Declaration. Includes support for treating the declaration contents as pseudo attributes.
009 */
010public class XmlDeclaration extends LeafNode {
011
012    /**
013     First char is `!` if isDeclaration, like in {@code  <!ENTITY ...>}.
014     Otherwise, is `?`, a processing instruction, like {@code <?xml .... ?>} (and note trailing `?`).
015     */
016    private final boolean isDeclaration;
017
018    /**
019     * Create a new XML declaration
020     * @param name of declaration
021     * @param isDeclaration {@code true} if a declaration (first char is `!`), otherwise a processing instruction (first char is `?`).
022     */
023    public XmlDeclaration(String name, boolean isDeclaration) {
024        super(name);
025        this.isDeclaration = isDeclaration;
026    }
027
028    @Override public String nodeName() {
029        return "#declaration";
030    }
031
032    /**
033     * Get the name of this declaration.
034     * @return name of this declaration.
035     */
036    public String name() {
037        return coreValue();
038    }
039
040    /**
041     * Get the unencoded XML declaration.
042     * @return XML declaration
043     */
044    public String getWholeDeclaration() {
045        StringBuilder sb = StringUtil.borrowBuilder();
046        getWholeDeclaration(QuietAppendable.wrap(sb), new Document.OutputSettings());
047        return StringUtil.releaseBuilder(sb).trim();
048    }
049
050    private void getWholeDeclaration(QuietAppendable accum, Document.OutputSettings out) {
051        for (Attribute attribute : attributes()) {
052            String key = attribute.getKey();
053            String val = attribute.getValue();
054            if (!key.equals(nodeName())) { // skips coreValue (name)
055                accum.append(' ');
056                // basically like Attribute, but skip empty vals in XML
057                accum.append(key);
058                if (!val.isEmpty()) {
059                    accum.append("=\"");
060                    Entities.escape(accum, val, out, Entities.ForAttribute);
061                    accum.append('"');
062                }
063            }
064        }
065    }
066
067    @Override
068    void outerHtmlHead(QuietAppendable accum, Document.OutputSettings out) {
069        accum
070            .append("<")
071            .append(isDeclaration ? "!" : "?")
072            .append(coreValue());
073        getWholeDeclaration(accum, out);
074        accum
075            .append(isDeclaration ? "" : "?")
076            .append(">");
077    }
078
079    @Override
080    void outerHtmlTail(QuietAppendable accum, Document.OutputSettings out) {
081    }
082
083    @Override
084    public String toString() {
085        return outerHtml();
086    }
087
088    @Override
089    public XmlDeclaration clone() {
090        return (XmlDeclaration) super.clone();
091    }
092}