1. /*
  2. * @(#)DTD.java 1.10 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text.html.parser;
  8. import java.io.PrintStream;
  9. import java.io.File;
  10. import java.io.FileInputStream;
  11. import java.io.InputStream;
  12. import java.io.IOException;
  13. import java.io.FileNotFoundException;
  14. import java.io.BufferedInputStream;
  15. import java.io.DataInputStream;
  16. import java.util.Hashtable;
  17. import java.util.Vector;
  18. import java.util.BitSet;
  19. import java.util.StringTokenizer;
  20. import java.util.Enumeration;
  21. import java.util.Properties;
  22. import java.net.URL;
  23. /**
  24. * The representation of an SGML DTD. This is produced by the DTDParser.
  25. * The resulting DTD object describes a document syntax and is needed
  26. * to parser HTML documents using the Parser. It contains a list of
  27. * elements and their attributes as well as a list of entities defined
  28. * in the DTD.
  29. *
  30. * @see Element
  31. * @see AttributeList
  32. * @see ContentModel
  33. * @see DTDParser
  34. * @see Parser
  35. * @author Arthur van Hoff
  36. * @version 1.10 11/29/01
  37. */
  38. public
  39. class DTD implements DTDConstants {
  40. public String name;
  41. public Vector elements = new Vector();
  42. public Hashtable elementHash = new Hashtable();
  43. public Hashtable entityHash = new Hashtable();
  44. public final Element pcdata = getElement("#pcdata");
  45. public final Element html = getElement("html");
  46. public final Element meta = getElement("meta");
  47. public final Element base = getElement("base");
  48. public final Element isindex = getElement("isindex");
  49. public final Element head = getElement("head");
  50. public final Element body = getElement("body");
  51. public final Element applet = getElement("applet");
  52. public final Element param = getElement("param");
  53. public final Element p = getElement("p");
  54. public final Element title = getElement("title");
  55. final Element style = getElement("style");
  56. final Element link = getElement("link");
  57. public static int FILE_VERSION = 1;
  58. /**
  59. * Create a new DTD.
  60. */
  61. protected DTD(String name) {
  62. this.name = name;
  63. defEntity("#RE", GENERAL, '\r');
  64. defEntity("#RS", GENERAL, '\n');
  65. defEntity("#SPACE", GENERAL, ' ');
  66. defineElement("unknown", EMPTY, false, true, null, null, null, null);
  67. }
  68. /**
  69. * Get the name of the DTD.
  70. */
  71. public String getName() {
  72. return name;
  73. }
  74. /**
  75. * Get an entity by name.
  76. */
  77. public Entity getEntity(String name) {
  78. return (Entity)entityHash.get(name);
  79. }
  80. /**
  81. * Get a character entity.
  82. */
  83. public Entity getEntity(int ch) {
  84. return (Entity)entityHash.get(new Integer(ch));
  85. }
  86. /**
  87. * Return true if the element is part of the dtd
  88. * else false.
  89. */
  90. boolean elementExists(String name) {
  91. Element e = (Element)elementHash.get(name);
  92. return ((e == null) ? false : true);
  93. }
  94. /**
  95. * Get an element by name. A new element is
  96. * created if the element doesn't exist.
  97. */
  98. public Element getElement(String name) {
  99. Element e = (Element)elementHash.get(name);
  100. if (e == null) {
  101. e = new Element(name, elements.size());
  102. elements.addElement(e);
  103. elementHash.put(name, e);
  104. }
  105. return e;
  106. }
  107. /**
  108. * Get an element by index.
  109. */
  110. public Element getElement(int index) {
  111. return (Element)elements.elementAt(index);
  112. }
  113. /**
  114. * Define an entity.
  115. */
  116. public Entity defineEntity(String name, int type, char data[]) {
  117. Entity ent = (Entity)entityHash.get(name);
  118. if (ent == null) {
  119. ent = new Entity(name, type, data);
  120. entityHash.put(name, ent);
  121. if (((type & GENERAL) != 0) && (data.length == 1)) {
  122. switch (type & ~GENERAL) {
  123. case CDATA:
  124. case SDATA:
  125. entityHash.put(new Integer(data[0]), ent);
  126. break;
  127. }
  128. }
  129. }
  130. return ent;
  131. }
  132. /**
  133. * Define an element.
  134. */
  135. public Element defineElement(String name, int type,
  136. boolean omitStart, boolean omitEnd, ContentModel content,
  137. BitSet exclusions, BitSet inclusions, AttributeList atts) {
  138. Element e = getElement(name);
  139. e.type = type;
  140. e.oStart = omitStart;
  141. e.oEnd = omitEnd;
  142. e.content = content;
  143. e.exclusions = exclusions;
  144. e.inclusions = inclusions;
  145. e.atts = atts;
  146. return e;
  147. }
  148. /**
  149. * Define the attributes of an element.
  150. */
  151. public void defineAttributes(String name, AttributeList atts) {
  152. Element e = getElement(name);
  153. e.atts = atts;
  154. }
  155. /**
  156. * Define a character entity.
  157. */
  158. public Entity defEntity(String name, int type, int ch) {
  159. char data[] = {(char)ch};
  160. return defineEntity(name, type, data);
  161. }
  162. /**
  163. * Define an entity.
  164. */
  165. protected Entity defEntity(String name, int type, String str) {
  166. int len = str.length();
  167. char data[] = new char[len];
  168. str.getChars(0, len, data, 0);
  169. return defineEntity(name, type, data);
  170. }
  171. /**
  172. * Define an element.
  173. */
  174. protected Element defElement(String name, int type,
  175. boolean omitStart, boolean omitEnd, ContentModel content,
  176. String[] exclusions, String[] inclusions, AttributeList atts) {
  177. BitSet excl = null;
  178. if (exclusions != null && exclusions.length > 0) {
  179. excl = new BitSet();
  180. for (int i = 0; i < exclusions.length; i++) {
  181. String str = exclusions[i];
  182. if (str.length() > 0) {
  183. excl.set(getElement(str).getIndex());
  184. }
  185. }
  186. }
  187. BitSet incl = null;
  188. if (inclusions != null && inclusions.length > 0) {
  189. incl = new BitSet();
  190. for (int i = 0; i < inclusions.length; i++) {
  191. String str = inclusions[i];
  192. if (str.length() > 0) {
  193. incl.set(getElement(str).getIndex());
  194. }
  195. }
  196. }
  197. return defineElement(name, type, omitStart, omitEnd, content, excl, incl, atts);
  198. }
  199. /**
  200. * Define an attribute list
  201. */
  202. protected AttributeList defAttributeList(String name, int type, int modifier, String value, String values, AttributeList atts) {
  203. Vector vals = null;
  204. if (values != null) {
  205. vals = new Vector();
  206. for (StringTokenizer s = new StringTokenizer(values, "|") ; s.hasMoreTokens() ;) {
  207. String str = s.nextToken();
  208. if (str.length() > 0) {
  209. vals.addElement(str);
  210. }
  211. }
  212. }
  213. return new AttributeList(name, type, modifier, value, vals, atts);
  214. }
  215. /**
  216. * Define a content model
  217. */
  218. protected ContentModel defContentModel(int type, Object obj, ContentModel next) {
  219. return new ContentModel(type, obj, next);
  220. }
  221. /**
  222. * Return a string representation.
  223. */
  224. public String toString() {
  225. return name;
  226. }
  227. /**
  228. * The hashtable of DTDs.
  229. */
  230. static Hashtable dtdHash = new Hashtable();
  231. public static void putDTDHash(String name, DTD dtd) {
  232. dtdHash.put(name, dtd);
  233. }
  234. /**
  235. * Get a DTD.
  236. */
  237. public static DTD getDTD(String name) throws IOException {
  238. name = name.toLowerCase();
  239. DTD dtd = (DTD)dtdHash.get(name);
  240. if (dtd == null)
  241. dtd = new DTD(name);
  242. return dtd;
  243. }
  244. public void read(DataInputStream in) throws IOException {
  245. if (in.readInt() != FILE_VERSION) {
  246. }
  247. //
  248. // Read the list of names
  249. //
  250. String[] names = new String[in.readShort()];
  251. for (int i = 0; i < names.length; i++) {
  252. names[i] = in.readUTF();
  253. }
  254. //
  255. // Read the entities
  256. //
  257. int num = in.readShort();
  258. for (int i = 0; i < num; i++) {
  259. short nameId = in.readShort();
  260. int type = in.readByte();
  261. String name = in.readUTF();
  262. defEntity(names[nameId], type | GENERAL, name);
  263. }
  264. // Read the elements
  265. //
  266. num = in.readShort();
  267. for (int i = 0; i < num; i++) {
  268. short nameId = in.readShort();
  269. int type = in.readByte();
  270. byte flags = in.readByte();
  271. ContentModel m = readContentModel(in, names);
  272. String[] exclusions = readNameArray(in, names);
  273. String[] inclusions = readNameArray(in, names);
  274. AttributeList atts = readAttributeList(in, names);
  275. defElement(names[nameId], type,
  276. ((flags & 0x01) != 0), ((flags & 0x02) != 0),
  277. m, exclusions, inclusions, atts);
  278. }
  279. }
  280. private ContentModel readContentModel(DataInputStream in, String[] names)
  281. throws IOException {
  282. byte flag = in.readByte();
  283. switch(flag) {
  284. case 0: // null
  285. return null;
  286. case 1: { // content_c
  287. int type = in.readByte();
  288. ContentModel m = readContentModel(in, names);
  289. ContentModel next = readContentModel(in, names);
  290. return defContentModel(type, m, next);
  291. }
  292. case 2: { // content_e
  293. int type = in.readByte();
  294. Element el = getElement(names[in.readShort()]);
  295. ContentModel next = readContentModel(in, names);
  296. return defContentModel(type, el, next);
  297. }
  298. default:
  299. throw new IOException("bad bdtd");
  300. }
  301. }
  302. private String[] readNameArray(DataInputStream in, String[] names)
  303. throws IOException {
  304. int num = in.readShort();
  305. if (num == 0) {
  306. return null;
  307. }
  308. String[] result = new String[num];
  309. for (int i = 0; i < num; i++) {
  310. result[i] = names[in.readShort()];
  311. }
  312. return result;
  313. }
  314. private AttributeList readAttributeList(DataInputStream in, String[] names)
  315. throws IOException {
  316. AttributeList result = null;
  317. for (int num = in.readByte(); num > 0; --num) {
  318. short nameId = in.readShort();
  319. int type = in.readByte();
  320. int modifier = in.readByte();
  321. short valueId = in.readShort();
  322. String value = (valueId == -1) ? null : names[valueId];
  323. Vector values = null;
  324. short numValues = in.readShort();
  325. if (numValues > 0) {
  326. values = new Vector(numValues);
  327. for (int i = 0; i < numValues; i++) {
  328. values.addElement(names[in.readShort()]);
  329. }
  330. }
  331. result = new AttributeList(names[nameId], type, modifier, value,
  332. values, result);
  333. // We reverse the order of the linked list by doing this, but
  334. // that order isn't important.
  335. }
  336. return result;
  337. }
  338. }