1. /*
  2. * @(#)DTD.java 1.16 03/01/23
  3. *
  4. * Copyright 2003 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. DTD describes a document
  25. * syntax and is used in parsing of HTML documents. It contains
  26. * a list of elements and their attributes as well as a list of
  27. * entities defined in the DTD.
  28. *
  29. * @see Element
  30. * @see AttributeList
  31. * @see ContentModel
  32. * @see Parser
  33. * @author Arthur van Hoff
  34. * @version 1.16 01/23/03
  35. */
  36. public
  37. class DTD implements DTDConstants {
  38. public String name;
  39. public Vector elements = new Vector();
  40. public Hashtable elementHash = new Hashtable();
  41. public Hashtable entityHash = new Hashtable();
  42. public final Element pcdata = getElement("#pcdata");
  43. public final Element html = getElement("html");
  44. public final Element meta = getElement("meta");
  45. public final Element base = getElement("base");
  46. public final Element isindex = getElement("isindex");
  47. public final Element head = getElement("head");
  48. public final Element body = getElement("body");
  49. public final Element applet = getElement("applet");
  50. public final Element param = getElement("param");
  51. public final Element p = getElement("p");
  52. public final Element title = getElement("title");
  53. final Element style = getElement("style");
  54. final Element link = getElement("link");
  55. public static int FILE_VERSION = 1;
  56. /**
  57. * Creates a new DTD with the specified name.
  58. * @param name the name, as a <code>String</code> of the new DTD
  59. */
  60. protected DTD(String name) {
  61. this.name = name;
  62. defEntity("#RE", GENERAL, '\r');
  63. defEntity("#RS", GENERAL, '\n');
  64. defEntity("#SPACE", GENERAL, ' ');
  65. defineElement("unknown", EMPTY, false, true, null, null, null, null);
  66. }
  67. /**
  68. * Gets the name of the DTD.
  69. * @return the name of the DTD
  70. */
  71. public String getName() {
  72. return name;
  73. }
  74. /**
  75. * Gets an entity by name.
  76. * @return the <code>Entity</code> corresponding to the
  77. * <code>name</code> <code>String</code>
  78. */
  79. public Entity getEntity(String name) {
  80. return (Entity)entityHash.get(name);
  81. }
  82. /**
  83. * Gets a character entity.
  84. * @return the <code>Entity</code> corresponding to the
  85. * <code>ch</code> character
  86. */
  87. public Entity getEntity(int ch) {
  88. return (Entity)entityHash.get(new Integer(ch));
  89. }
  90. /**
  91. * Returns <code>true</code> if the element is part of the DTD,
  92. * otherwise returns <code>false</code>.
  93. *
  94. * @param name the requested <code>String</code>
  95. * @return <code>true</code> if <code>name</code> exists as
  96. * part of the DTD, otherwise returns <code>false</code>
  97. */
  98. boolean elementExists(String name) {
  99. Element e = (Element)elementHash.get(name);
  100. return ((e == null) ? false : true);
  101. }
  102. /**
  103. * Gets an element by name. A new element is
  104. * created if the element doesn't exist.
  105. *
  106. * @param name the requested <code>String</code>
  107. * @return the <code>Element</code> corresponding to
  108. * <code>name</code>, which may be newly created
  109. */
  110. public Element getElement(String name) {
  111. Element e = (Element)elementHash.get(name);
  112. if (e == null) {
  113. e = new Element(name, elements.size());
  114. elements.addElement(e);
  115. elementHash.put(name, e);
  116. }
  117. return e;
  118. }
  119. /**
  120. * Gets an element by index.
  121. *
  122. * @param index the requested index
  123. * @return the <code>Element</code> corresponding to
  124. * <code>index</code>
  125. */
  126. public Element getElement(int index) {
  127. return (Element)elements.elementAt(index);
  128. }
  129. /**
  130. * Defines an entity. If the <code>Entity</code> specified
  131. * by <code>name</code>, <code>type</code>, and <code>data</code>
  132. * exists, it is returned; otherwise a new <code>Entity</code>
  133. * is created and is returned.
  134. *
  135. * @param name the name of the <code>Entity</code> as a <code>String</code>
  136. * @param type the type of the <code>Entity</code>
  137. * @param data the <code>Entity</code>'s data
  138. * @return the <code>Entity</code> requested or a new <code>Entity</code>
  139. * if not found
  140. */
  141. public Entity defineEntity(String name, int type, char data[]) {
  142. Entity ent = (Entity)entityHash.get(name);
  143. if (ent == null) {
  144. ent = new Entity(name, type, data);
  145. entityHash.put(name, ent);
  146. if (((type & GENERAL) != 0) && (data.length == 1)) {
  147. switch (type & ~GENERAL) {
  148. case CDATA:
  149. case SDATA:
  150. entityHash.put(new Integer(data[0]), ent);
  151. break;
  152. }
  153. }
  154. }
  155. return ent;
  156. }
  157. /**
  158. * Returns the <code>Element</code> which matches the
  159. * specified parameters. If one doesn't exist, a new
  160. * one is created and returned.
  161. *
  162. * @param name the name of the <code>Element</code>
  163. * @param type the type of the <code>Element</code>
  164. * @param omitStart <code>true</code if start should be omitted
  165. * @param omitEnd <code>true</code> if end should be omitted
  166. * @param content the <code>ContentModel</code>
  167. * @param atts the <code>AttributeList</code> specifying the
  168. * <code>Element</code>
  169. * @return the <code>Element</code> specified
  170. */
  171. public Element defineElement(String name, int type,
  172. boolean omitStart, boolean omitEnd, ContentModel content,
  173. BitSet exclusions, BitSet inclusions, AttributeList atts) {
  174. Element e = getElement(name);
  175. e.type = type;
  176. e.oStart = omitStart;
  177. e.oEnd = omitEnd;
  178. e.content = content;
  179. e.exclusions = exclusions;
  180. e.inclusions = inclusions;
  181. e.atts = atts;
  182. return e;
  183. }
  184. /**
  185. * Returns the <code>Element</code> which matches the
  186. * specified <code>AttributeList</code>.
  187. * If one doesn't exist, a new one is created and returned.
  188. *
  189. * @param name the name of the <code>Element</code>
  190. * @param atts the <code>AttributeList</code> specifying the
  191. * <code>Element</code>
  192. * @return the <code>Element</code> specified
  193. */
  194. public void defineAttributes(String name, AttributeList atts) {
  195. Element e = getElement(name);
  196. e.atts = atts;
  197. }
  198. /**
  199. * Creates and returns a character <code>Entity</code>.
  200. * @param name the entity's name
  201. * @return the new character <code>Entity</code>
  202. */
  203. public Entity defEntity(String name, int type, int ch) {
  204. char data[] = {(char)ch};
  205. return defineEntity(name, type, data);
  206. }
  207. /**
  208. * Creates and returns an <code>Entity</code>.
  209. * @param name the entity's name
  210. * @return the new <code>Entity</code>
  211. */
  212. protected Entity defEntity(String name, int type, String str) {
  213. int len = str.length();
  214. char data[] = new char[len];
  215. str.getChars(0, len, data, 0);
  216. return defineEntity(name, type, data);
  217. }
  218. /**
  219. * Creates and returns an <code>Element</code>.
  220. * @param name the element's name
  221. * @return the new <code>Element</code>
  222. */
  223. protected Element defElement(String name, int type,
  224. boolean omitStart, boolean omitEnd, ContentModel content,
  225. String[] exclusions, String[] inclusions, AttributeList atts) {
  226. BitSet excl = null;
  227. if (exclusions != null && exclusions.length > 0) {
  228. excl = new BitSet();
  229. for (int i = 0; i < exclusions.length; i++) {
  230. String str = exclusions[i];
  231. if (str.length() > 0) {
  232. excl.set(getElement(str).getIndex());
  233. }
  234. }
  235. }
  236. BitSet incl = null;
  237. if (inclusions != null && inclusions.length > 0) {
  238. incl = new BitSet();
  239. for (int i = 0; i < inclusions.length; i++) {
  240. String str = inclusions[i];
  241. if (str.length() > 0) {
  242. incl.set(getElement(str).getIndex());
  243. }
  244. }
  245. }
  246. return defineElement(name, type, omitStart, omitEnd, content, excl, incl, atts);
  247. }
  248. /**
  249. * Creates and returns an <code>AttributeList</code>.
  250. * @param name the attribute list's name
  251. * @return the new <code>AttributeList</code>
  252. */
  253. protected AttributeList defAttributeList(String name, int type, int modifier, String value, String values, AttributeList atts) {
  254. Vector vals = null;
  255. if (values != null) {
  256. vals = new Vector();
  257. for (StringTokenizer s = new StringTokenizer(values, "|") ; s.hasMoreTokens() ;) {
  258. String str = s.nextToken();
  259. if (str.length() > 0) {
  260. vals.addElement(str);
  261. }
  262. }
  263. }
  264. return new AttributeList(name, type, modifier, value, vals, atts);
  265. }
  266. /**
  267. * Creates and returns a new content model.
  268. * @param type the type of the new content model
  269. * @return the new <code>ContentModel</code>
  270. */
  271. protected ContentModel defContentModel(int type, Object obj, ContentModel next) {
  272. return new ContentModel(type, obj, next);
  273. }
  274. /**
  275. * Returns a string representation of this DTD.
  276. * @return the string representation of this DTD
  277. */
  278. public String toString() {
  279. return name;
  280. }
  281. /**
  282. * The hashtable of DTDs.
  283. */
  284. static Hashtable dtdHash = new Hashtable();
  285. public static void putDTDHash(String name, DTD dtd) {
  286. dtdHash.put(name, dtd);
  287. }
  288. /**
  289. * Returns a DTD with the specified <code>name</code>. If
  290. * a DTD with that name doesn't exist, one is created
  291. * and returned. Any uppercase characters in the name
  292. * are converted to lowercase.
  293. *
  294. * @param name the name of the DTD
  295. * @return the DTD which corresponds to <code>name</code>
  296. */
  297. public static DTD getDTD(String name) throws IOException {
  298. name = name.toLowerCase();
  299. DTD dtd = (DTD)dtdHash.get(name);
  300. if (dtd == null)
  301. dtd = new DTD(name);
  302. return dtd;
  303. }
  304. /**
  305. * Recreates a DTD from an archived format.
  306. * @param in the <code>DataInputStream</code> to read from
  307. */
  308. public void read(DataInputStream in) throws IOException {
  309. if (in.readInt() != FILE_VERSION) {
  310. }
  311. //
  312. // Read the list of names
  313. //
  314. String[] names = new String[in.readShort()];
  315. for (int i = 0; i < names.length; i++) {
  316. names[i] = in.readUTF();
  317. }
  318. //
  319. // Read the entities
  320. //
  321. int num = in.readShort();
  322. for (int i = 0; i < num; i++) {
  323. short nameId = in.readShort();
  324. int type = in.readByte();
  325. String name = in.readUTF();
  326. defEntity(names[nameId], type | GENERAL, name);
  327. }
  328. // Read the elements
  329. //
  330. num = in.readShort();
  331. for (int i = 0; i < num; i++) {
  332. short nameId = in.readShort();
  333. int type = in.readByte();
  334. byte flags = in.readByte();
  335. ContentModel m = readContentModel(in, names);
  336. String[] exclusions = readNameArray(in, names);
  337. String[] inclusions = readNameArray(in, names);
  338. AttributeList atts = readAttributeList(in, names);
  339. defElement(names[nameId], type,
  340. ((flags & 0x01) != 0), ((flags & 0x02) != 0),
  341. m, exclusions, inclusions, atts);
  342. }
  343. }
  344. private ContentModel readContentModel(DataInputStream in, String[] names)
  345. throws IOException {
  346. byte flag = in.readByte();
  347. switch(flag) {
  348. case 0: // null
  349. return null;
  350. case 1: { // content_c
  351. int type = in.readByte();
  352. ContentModel m = readContentModel(in, names);
  353. ContentModel next = readContentModel(in, names);
  354. return defContentModel(type, m, next);
  355. }
  356. case 2: { // content_e
  357. int type = in.readByte();
  358. Element el = getElement(names[in.readShort()]);
  359. ContentModel next = readContentModel(in, names);
  360. return defContentModel(type, el, next);
  361. }
  362. default:
  363. throw new IOException("bad bdtd");
  364. }
  365. }
  366. private String[] readNameArray(DataInputStream in, String[] names)
  367. throws IOException {
  368. int num = in.readShort();
  369. if (num == 0) {
  370. return null;
  371. }
  372. String[] result = new String[num];
  373. for (int i = 0; i < num; i++) {
  374. result[i] = names[in.readShort()];
  375. }
  376. return result;
  377. }
  378. private AttributeList readAttributeList(DataInputStream in, String[] names)
  379. throws IOException {
  380. AttributeList result = null;
  381. for (int num = in.readByte(); num > 0; --num) {
  382. short nameId = in.readShort();
  383. int type = in.readByte();
  384. int modifier = in.readByte();
  385. short valueId = in.readShort();
  386. String value = (valueId == -1) ? null : names[valueId];
  387. Vector values = null;
  388. short numValues = in.readShort();
  389. if (numValues > 0) {
  390. values = new Vector(numValues);
  391. for (int i = 0; i < numValues; i++) {
  392. values.addElement(names[in.readShort()]);
  393. }
  394. }
  395. result = new AttributeList(names[nameId], type, modifier, value,
  396. values, result);
  397. // We reverse the order of the linked list by doing this, but
  398. // that order isn't important.
  399. }
  400. return result;
  401. }
  402. }