/*
* Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import sun.awt.AppContext;
import java.io.PrintStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.util.Hashtable;
import java.util.Vector;
import java.util.BitSet;
import java.util.StringTokenizer;
import java.util.Enumeration;
import java.util.Properties;
import java.net.URL;
The representation of an SGML DTD. DTD describes a document
syntax and is used in parsing of HTML documents. It contains
a list of elements and their attributes as well as a list of
entities defined in the DTD.
Author: Arthur van Hoff See Also:
/**
* The representation of an SGML DTD. DTD describes a document
* syntax and is used in parsing of HTML documents. It contains
* a list of elements and their attributes as well as a list of
* entities defined in the DTD.
*
* @see Element
* @see AttributeList
* @see ContentModel
* @see Parser
* @author Arthur van Hoff
*/
public
class DTD implements DTDConstants {
the name of the DTD
/**
* the name of the DTD
*/
public String name;
The vector of elements
/**
* The vector of elements
*/
public Vector<Element> elements = new Vector<Element>();
The hash table contains the name of element and
the corresponding element.
/**
* The hash table contains the name of element and
* the corresponding element.
*/
public Hashtable<String,Element> elementHash
= new Hashtable<String,Element>();
The hash table contains an Object
and the corresponding Entity
/**
* The hash table contains an {@code Object} and the corresponding {@code Entity}
*/
public Hashtable<Object,Entity> entityHash
= new Hashtable<Object,Entity>();
The element corresponding to pcdata.
/**
* The element corresponding to pcdata.
*/
public final Element pcdata = getElement("#pcdata");
The element corresponding to html.
/**
* The element corresponding to html.
*/
public final Element html = getElement("html");
The element corresponding to meta.
/**
* The element corresponding to meta.
*/
public final Element meta = getElement("meta");
The element corresponding to base.
/**
* The element corresponding to base.
*/
public final Element base = getElement("base");
The element corresponding to isindex.
/**
* The element corresponding to isindex.
*/
public final Element isindex = getElement("isindex");
The element corresponding to head.
/**
* The element corresponding to head.
*/
public final Element head = getElement("head");
The element corresponding to body.
/**
* The element corresponding to body.
*/
public final Element body = getElement("body");
The element corresponding to applet.
/**
* The element corresponding to applet.
*/
public final Element applet = getElement("applet");
The element corresponding to param.
/**
* The element corresponding to param.
*/
public final Element param = getElement("param");
The element corresponding to p.
/**
* The element corresponding to p.
*/
public final Element p = getElement("p");
The element corresponding to title.
/**
* The element corresponding to title.
*/
public final Element title = getElement("title");
final Element style = getElement("style");
final Element link = getElement("link");
final Element script = getElement("script");
The version of a file
/**
* The version of a file
*/
public static final int FILE_VERSION = 1;
Creates a new DTD with the specified name.
Params: - name – the name, as a
String
of the new DTD
/**
* Creates a new DTD with the specified name.
* @param name the name, as a <code>String</code> of the new DTD
*/
protected DTD(String name) {
this.name = name;
defEntity("#RE", GENERAL, '\r');
defEntity("#RS", GENERAL, '\n');
defEntity("#SPACE", GENERAL, ' ');
defineElement("unknown", EMPTY, false, true, null, null, null, null);
}
Gets the name of the DTD.
Returns: the name of the DTD
/**
* Gets the name of the DTD.
* @return the name of the DTD
*/
public String getName() {
return name;
}
Gets an entity by name.
Params: - name – the entity name
Returns: the Entity
corresponding to the
name
String
/**
* Gets an entity by name.
* @param name the entity name
* @return the <code>Entity</code> corresponding to the
* <code>name</code> <code>String</code>
*/
public Entity getEntity(String name) {
return entityHash.get(name);
}
Gets a character entity.
Params: - ch – the character
Returns: the Entity
corresponding to the
ch
character
/**
* Gets a character entity.
* @param ch the character
* @return the <code>Entity</code> corresponding to the
* <code>ch</code> character
*/
public Entity getEntity(int ch) {
return entityHash.get(Integer.valueOf(ch));
}
Returns true
if the element is part of the DTD,
otherwise returns false
.
Params: - name – the requested
String
Returns: true
if name
exists as
part of the DTD, otherwise returns false
/**
* Returns <code>true</code> if the element is part of the DTD,
* otherwise returns <code>false</code>.
*
* @param name the requested <code>String</code>
* @return <code>true</code> if <code>name</code> exists as
* part of the DTD, otherwise returns <code>false</code>
*/
boolean elementExists(String name) {
return !"unknown".equals(name) && (elementHash.get(name) != null);
}
Gets an element by name. A new element is
created if the element doesn't exist.
Params: - name – the requested
String
Returns: the Element
corresponding to
name
, which may be newly created
/**
* Gets an element by name. A new element is
* created if the element doesn't exist.
*
* @param name the requested <code>String</code>
* @return the <code>Element</code> corresponding to
* <code>name</code>, which may be newly created
*/
public Element getElement(String name) {
Element e = elementHash.get(name);
if (e == null) {
e = new Element(name, elements.size());
elements.addElement(e);
elementHash.put(name, e);
}
return e;
}
Gets an element by index.
Params: - index – the requested index
Returns: the Element
corresponding to
index
/**
* Gets an element by index.
*
* @param index the requested index
* @return the <code>Element</code> corresponding to
* <code>index</code>
*/
public Element getElement(int index) {
return elements.elementAt(index);
}
Defines an entity. If the Entity
specified
by name
, type
, and data
exists, it is returned; otherwise a new Entity
is created and is returned.
Params: - name – the name of the
Entity
as a String
- type – the type of the
Entity
- data – the
Entity
's data
Returns: the Entity
requested or a new Entity
if not found
/**
* Defines an entity. If the <code>Entity</code> specified
* by <code>name</code>, <code>type</code>, and <code>data</code>
* exists, it is returned; otherwise a new <code>Entity</code>
* is created and is returned.
*
* @param name the name of the <code>Entity</code> as a <code>String</code>
* @param type the type of the <code>Entity</code>
* @param data the <code>Entity</code>'s data
* @return the <code>Entity</code> requested or a new <code>Entity</code>
* if not found
*/
public Entity defineEntity(String name, int type, char[] data) {
Entity ent = entityHash.get(name);
if (ent == null) {
ent = new Entity(name, type, data);
entityHash.put(name, ent);
if (((type & GENERAL) != 0) && (data.length == 1)) {
switch (type & ~GENERAL) {
case CDATA:
case SDATA:
entityHash.put(Integer.valueOf(data[0]), ent);
break;
}
}
}
return ent;
}
Returns the Element
which matches the
specified parameters. If one doesn't exist, a new
one is created and returned.
Params: - name – the name of the
Element
- type – the type of the
Element
- omitStart –
true
if start should be omitted - omitEnd –
true
if end should be omitted - content – the
ContentModel
- exclusions – the set of elements that must not occur inside the element
- inclusions – the set of elements that can occur inside the element
- atts – the
AttributeList
specifying the
Element
Returns: the Element
specified
/**
* Returns the <code>Element</code> which matches the
* specified parameters. If one doesn't exist, a new
* one is created and returned.
*
* @param name the name of the <code>Element</code>
* @param type the type of the <code>Element</code>
* @param omitStart <code>true</code> if start should be omitted
* @param omitEnd <code>true</code> if end should be omitted
* @param content the <code>ContentModel</code>
* @param exclusions the set of elements that must not occur inside the element
* @param inclusions the set of elements that can occur inside the element
* @param atts the <code>AttributeList</code> specifying the
* <code>Element</code>
* @return the <code>Element</code> specified
*/
public Element defineElement(String name, int type,
boolean omitStart, boolean omitEnd, ContentModel content,
BitSet exclusions, BitSet inclusions, AttributeList atts) {
Element e = getElement(name);
e.type = type;
e.oStart = omitStart;
e.oEnd = omitEnd;
e.content = content;
e.exclusions = exclusions;
e.inclusions = inclusions;
e.atts = atts;
return e;
}
Defines attributes for an Element
. Params: - name – the name of the
Element
- atts – the
AttributeList
specifying the
Element
/**
* Defines attributes for an {@code Element}.
*
* @param name the name of the <code>Element</code>
* @param atts the <code>AttributeList</code> specifying the
* <code>Element</code>
*/
public void defineAttributes(String name, AttributeList atts) {
Element e = getElement(name);
e.atts = atts;
}
Creates and returns a character Entity
.
Params: - name – the entity's name
- type – the entity's type
- ch – the entity's value (character)
Returns: the new character Entity
/**
* Creates and returns a character <code>Entity</code>.
* @param name the entity's name
* @param type the entity's type
* @param ch the entity's value (character)
* @return the new character <code>Entity</code>
*/
public Entity defEntity(String name, int type, int ch) {
char[] data = {(char)ch};
return defineEntity(name, type, data);
}
Creates and returns an Entity
.
Params: - name – the entity's name
- type – the entity's type
- str – the entity's data section
Returns: the new Entity
/**
* Creates and returns an <code>Entity</code>.
* @param name the entity's name
* @param type the entity's type
* @param str the entity's data section
* @return the new <code>Entity</code>
*/
protected Entity defEntity(String name, int type, String str) {
int len = str.length();
char[] data = new char[len];
str.getChars(0, len, data, 0);
return defineEntity(name, type, data);
}
Creates and returns an Element
.
Params: - name – the element's name
- type – the element's type
- omitStart –
true
if the element needs no starting tag - omitEnd –
true
if the element needs no closing tag - content – the element's content
- exclusions – the elements that must be excluded from the content of the element
- inclusions – the elements that can be included as the content of the element
- atts – the attributes of the element
Returns: the new Element
/**
* Creates and returns an <code>Element</code>.
* @param name the element's name
* @param type the element's type
* @param omitStart {@code true} if the element needs no starting tag
* @param omitEnd {@code true} if the element needs no closing tag
* @param content the element's content
* @param exclusions the elements that must be excluded from the content of the element
* @param inclusions the elements that can be included as the content of the element
* @param atts the attributes of the element
* @return the new <code>Element</code>
*/
protected Element defElement(String name, int type,
boolean omitStart, boolean omitEnd, ContentModel content,
String[] exclusions, String[] inclusions, AttributeList atts) {
BitSet excl = null;
if (exclusions != null && exclusions.length > 0) {
excl = new BitSet();
for (String str : exclusions) {
if (str.length() > 0) {
excl.set(getElement(str).getIndex());
}
}
}
BitSet incl = null;
if (inclusions != null && inclusions.length > 0) {
incl = new BitSet();
for (String str : inclusions) {
if (str.length() > 0) {
incl.set(getElement(str).getIndex());
}
}
}
return defineElement(name, type, omitStart, omitEnd, content, excl, incl, atts);
}
Creates and returns an AttributeList
responding to a new attribute.
Params: - name – the attribute's name
- type – the attribute's type
- modifier – the attribute's modifier
- value – the default value of the attribute
- values – the allowed values for the attribute (multiple values could be separated by '|')
- atts – the previous attribute of the element; to be placed to
AttributeList.next
, creating a linked list
Returns: the new AttributeList
/**
* Creates and returns an <code>AttributeList</code> responding to a new attribute.
* @param name the attribute's name
* @param type the attribute's type
* @param modifier the attribute's modifier
* @param value the default value of the attribute
* @param values the allowed values for the attribute (multiple values could be separated by '|')
* @param atts the previous attribute of the element; to be placed to {@code AttributeList.next},
* creating a linked list
* @return the new <code>AttributeList</code>
*/
protected AttributeList defAttributeList(String name, int type, int modifier,
String value, String values, AttributeList atts) {
Vector<String> vals = null;
if (values != null) {
vals = new Vector<String>();
for (StringTokenizer s = new StringTokenizer(values, "|") ; s.hasMoreTokens() ;) {
String str = s.nextToken();
if (str.length() > 0) {
vals.addElement(str);
}
}
}
return new AttributeList(name, type, modifier, value, vals, atts);
}
Creates and returns a new content model.
Params: - type – the type of the new content model
- obj – the content of the content model
- next – pointer to the next content model
Returns: the new ContentModel
/**
* Creates and returns a new content model.
* @param type the type of the new content model
* @param obj the content of the content model
* @param next pointer to the next content model
* @return the new <code>ContentModel</code>
*/
protected ContentModel defContentModel(int type, Object obj, ContentModel next) {
return new ContentModel(type, obj, next);
}
Returns a string representation of this DTD.
Returns: the string representation of this DTD
/**
* Returns a string representation of this DTD.
* @return the string representation of this DTD
*/
public String toString() {
return name;
}
The hashtable key of DTDs in AppContext.
/**
* The hashtable key of DTDs in AppContext.
*/
private static final Object DTD_HASH_KEY = new Object();
Put a name and appropriate DTD to hashtable.
Params: - name – the name of the DTD
- dtd – the DTD
/**
* Put a name and appropriate DTD to hashtable.
*
* @param name the name of the DTD
* @param dtd the DTD
*/
public static void putDTDHash(String name, DTD dtd) {
getDtdHash().put(name, dtd);
}
Returns a DTD with the specified name
. If
a DTD with that name doesn't exist, one is created
and returned. Any uppercase characters in the name
are converted to lowercase.
Params: - name – the name of the DTD
Throws: - IOException – if an I/O error occurs
Returns: the DTD which corresponds to name
/**
* Returns a DTD with the specified <code>name</code>. If
* a DTD with that name doesn't exist, one is created
* and returned. Any uppercase characters in the name
* are converted to lowercase.
*
* @param name the name of the DTD
* @return the DTD which corresponds to <code>name</code>
* @throws IOException if an I/O error occurs
*/
public static DTD getDTD(String name) throws IOException {
name = name.toLowerCase();
DTD dtd = getDtdHash().get(name);
if (dtd == null)
dtd = new DTD(name);
return dtd;
}
private static Hashtable<String, DTD> getDtdHash() {
AppContext appContext = AppContext.getAppContext();
@SuppressWarnings("unchecked")
Hashtable<String, DTD> result = (Hashtable<String, DTD>) appContext.get(DTD_HASH_KEY);
if (result == null) {
result = new Hashtable<String, DTD>();
appContext.put(DTD_HASH_KEY, result);
}
return result;
}
Recreates a DTD from an archived format.
Params: - in – the
DataInputStream
to read from
Throws: - IOException – if an I/O error occurs
/**
* Recreates a DTD from an archived format.
* @param in the <code>DataInputStream</code> to read from
* @throws IOException if an I/O error occurs
*/
public void read(DataInputStream in) throws IOException {
if (in.readInt() != FILE_VERSION) {
}
//
// Read the list of names
//
String[] names = new String[in.readShort()];
for (int i = 0; i < names.length; i++) {
names[i] = in.readUTF();
}
//
// Read the entities
//
int num = in.readShort();
for (int i = 0; i < num; i++) {
short nameId = in.readShort();
int type = in.readByte();
String name = in.readUTF();
defEntity(names[nameId], type | GENERAL, name);
}
// Read the elements
//
num = in.readShort();
for (int i = 0; i < num; i++) {
short nameId = in.readShort();
int type = in.readByte();
byte flags = in.readByte();
ContentModel m = readContentModel(in, names);
String[] exclusions = readNameArray(in, names);
String[] inclusions = readNameArray(in, names);
AttributeList atts = readAttributeList(in, names);
defElement(names[nameId], type,
((flags & 0x01) != 0), ((flags & 0x02) != 0),
m, exclusions, inclusions, atts);
}
}
private ContentModel readContentModel(DataInputStream in, String[] names)
throws IOException {
byte flag = in.readByte();
switch(flag) {
case 0: // null
return null;
case 1: { // content_c
int type = in.readByte();
ContentModel m = readContentModel(in, names);
ContentModel next = readContentModel(in, names);
return defContentModel(type, m, next);
}
case 2: { // content_e
int type = in.readByte();
Element el = getElement(names[in.readShort()]);
ContentModel next = readContentModel(in, names);
return defContentModel(type, el, next);
}
default:
throw new IOException("bad bdtd");
}
}
private String[] readNameArray(DataInputStream in, String[] names)
throws IOException {
int num = in.readShort();
if (num == 0) {
return null;
}
String[] result = new String[num];
for (int i = 0; i < num; i++) {
result[i] = names[in.readShort()];
}
return result;
}
private AttributeList readAttributeList(DataInputStream in, String[] names)
throws IOException {
AttributeList result = null;
for (int num = in.readByte(); num > 0; --num) {
short nameId = in.readShort();
int type = in.readByte();
int modifier = in.readByte();
short valueId = in.readShort();
String value = (valueId == -1) ? null : names[valueId];
Vector<String> values = null;
short numValues = in.readShort();
if (numValues > 0) {
values = new Vector<String>(numValues);
for (int i = 0; i < numValues; i++) {
values.addElement(names[in.readShort()]);
}
}
result = new AttributeList(names[nameId], type, modifier, value,
values, result);
// We reverse the order of the linked list by doing this, but
// that order isn't important.
}
return result;
}
}