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