001package org.jsoup.nodes;
002
003import org.jsoup.helper.StringUtil;
004import org.jsoup.helper.Validate;
005import org.jsoup.nodes.Document.OutputSettings.Syntax;
006
007import java.io.IOException;
008
009/**
010 * A {@code <!DOCTYPE>} node.
011 */
012public class DocumentType extends LeafNode {
013    // todo needs a bit of a chunky cleanup. this level of detail isn't needed
014    public static final String PUBLIC_KEY = "PUBLIC";
015    public static final String SYSTEM_KEY = "SYSTEM";
016    private static final String NAME = "name";
017    private static final String PUB_SYS_KEY = "pubSysKey"; // PUBLIC or SYSTEM
018    private static final String PUBLIC_ID = "publicId";
019    private static final String SYSTEM_ID = "systemId";
020    // todo: quirk mode from publicId and systemId
021
022    /**
023     * Create a new doctype element.
024     * @param name the doctype's name
025     * @param publicId the doctype's public ID
026     * @param systemId the doctype's system ID
027     */
028    public DocumentType(String name, String publicId, String systemId) {
029        Validate.notNull(name);
030        Validate.notNull(publicId);
031        Validate.notNull(systemId);
032        attr(NAME, name);
033        attr(PUBLIC_ID, publicId);
034        if (has(PUBLIC_ID)) {
035            attr(PUB_SYS_KEY, PUBLIC_KEY);
036        }
037        attr(SYSTEM_ID, systemId);
038    }
039
040    /**
041     * Create a new doctype element.
042     * @param name the doctype's name
043     * @param publicId the doctype's public ID
044     * @param systemId the doctype's system ID
045     * @param baseUri unused
046     * @deprecated
047     */
048    public DocumentType(String name, String publicId, String systemId, String baseUri) {
049        attr(NAME, name);
050        attr(PUBLIC_ID, publicId);
051        if (has(PUBLIC_ID)) {
052            attr(PUB_SYS_KEY, PUBLIC_KEY);
053        }
054        attr(SYSTEM_ID, systemId);
055    }
056
057    /**
058     * Create a new doctype element.
059     * @param name the doctype's name
060     * @param publicId the doctype's public ID
061     * @param systemId the doctype's system ID
062     * @param baseUri unused
063     * @deprecated
064     */
065    public DocumentType(String name, String pubSysKey, String publicId, String systemId, String baseUri) {
066        attr(NAME, name);
067        if (pubSysKey != null) {
068            attr(PUB_SYS_KEY, pubSysKey);
069        }
070        attr(PUBLIC_ID, publicId);
071        attr(SYSTEM_ID, systemId);
072    }
073    public void setPubSysKey(String value) {
074        if (value != null)
075            attr(PUB_SYS_KEY, value);
076    }
077
078    @Override
079    public String nodeName() {
080        return "#doctype";
081    }
082
083    @Override
084    void outerHtmlHead(Appendable accum, int depth, Document.OutputSettings out) throws IOException {
085        if (out.syntax() == Syntax.html && !has(PUBLIC_ID) && !has(SYSTEM_ID)) {
086            // looks like a html5 doctype, go lowercase for aesthetics
087            accum.append("<!doctype");
088        } else {
089            accum.append("<!DOCTYPE");
090        }
091        if (has(NAME))
092            accum.append(" ").append(attr(NAME));
093        if (has(PUB_SYS_KEY))
094            accum.append(" ").append(attr(PUB_SYS_KEY));
095        if (has(PUBLIC_ID))
096            accum.append(" \"").append(attr(PUBLIC_ID)).append('"');
097        if (has(SYSTEM_ID))
098            accum.append(" \"").append(attr(SYSTEM_ID)).append('"');
099        accum.append('>');
100    }
101
102    @Override
103    void outerHtmlTail(Appendable accum, int depth, Document.OutputSettings out) {
104    }
105
106    private boolean has(final String attribute) {
107        return !StringUtil.isBlank(attr(attribute));
108    }
109}