001package org.jsoup.nodes;
002
003import org.jsoup.internal.QuietAppendable;
004import org.jsoup.internal.StringUtil;
005
006
007/**
008 * An XML declaration, such as {@code <?xml version="1.0"?>}, or a markup declaration, such as {@code <!ELEMENT ...>}.
009 * Use {@link ProcessingInstruction} for other {@code <?target data?>} nodes.
010 * Declaration values are available through {@link #attributes()} and {@link #attr(String)}.
011 */
012public class XmlDeclaration extends LeafNode {
013
014    /** Whether this is a markup declaration. */
015    private final boolean isMarkupDeclaration;
016
017    /**
018     * Creates an XML declaration or markup declaration.
019     * @param name the declaration name, such as {@code xml} or {@code ELEMENT}
020     * @param isMarkupDeclaration {@code true} to create {@code <!name ...>}; {@code false} to create {@code <?name ...?>}
021     */
022    public XmlDeclaration(String name, boolean isMarkupDeclaration) {
023        super(name);
024        this.isMarkupDeclaration = isMarkupDeclaration;
025    }
026
027    @Override public String nodeName() {
028        return "#declaration";
029    }
030
031    /**
032     * Gets the declaration name.
033     * @return the declaration name
034     */
035    public String name() {
036        return coreValue();
037    }
038
039    /**
040     * Gets the declaration data, without its name or delimiters.
041     * @return the declaration data
042     */
043    public String getWholeDeclaration() {
044        StringBuilder sb = StringUtil.borrowBuilder();
045        getWholeDeclaration(QuietAppendable.wrap(sb), new Document.OutputSettings());
046        return StringUtil.releaseBuilder(sb).trim();
047    }
048
049    private void getWholeDeclaration(QuietAppendable accum, Document.OutputSettings out) {
050        for (Attribute attribute : attributes()) {
051            String key = attribute.getKey();
052            String val = attribute.getValue();
053            if (!key.equals(nodeName())) { // skips coreValue (name)
054                accum.append(' ');
055                // basically like Attribute, but skip empty vals in XML
056                accum.append(key);
057                if (!val.isEmpty()) {
058                    accum.append("=\"");
059                    Entities.escape(accum, val, out, Entities.ForAttribute);
060                    accum.append('"');
061                }
062            }
063        }
064    }
065
066    @Override
067    void outerHtmlHead(QuietAppendable accum, Document.OutputSettings out) {
068        accum
069            .append("<")
070            .append(isMarkupDeclaration ? "!" : "?")
071            .append(coreValue());
072        getWholeDeclaration(accum, out);
073        accum
074            .append(isMarkupDeclaration ? "" : "?")
075            .append(">");
076    }
077
078    @Override
079    void outerHtmlTail(QuietAppendable accum, Document.OutputSettings out) {
080    }
081
082    @Override
083    public String toString() {
084        return outerHtml();
085    }
086
087    @Override
088    public XmlDeclaration clone() {
089        return (XmlDeclaration) super.clone();
090    }
091}