1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
/* * @(#)ContentModel.java 1.12 05/11/17 * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package javax.swing.text.html.parser; import java.util.Vector; import java.util.Enumeration; import java.io.*; /** * A representation of a content model. A content model is * basically a restricted BNF expression. It is restricted in * the sense that it must be deterministic. This means that you * don't have to represent it as a finite state automata.<p> * See Annex H on page 556 of the SGML handbook for more information. * * @author Arthur van Hoff * @version 1.12,11/17/05 * */ public final class ContentModel implements Serializable { /** * Type. Either '*', '?', '+', ',', '|', '&'. */ public int type; /** * The content. Either an Element or a ContentModel. */ public Object content; /** * The next content model (in a ',', '|' or '&' expression). */ public ContentModel next; public ContentModel() { } /** * Create a content model for an element. */ public ContentModel(Element content) { this(0, content, null); } /** * Create a content model of a particular type. */ public ContentModel(int type, ContentModel content) { this(type, content, null); } /** * Create a content model of a particular type. */ public ContentModel(int type, Object content, ContentModel next) { this.type = type; this.content = content; this.next = next; } /** * Return true if the content model could * match an empty input stream. */ public boolean empty() { switch (type) { case '*': case '?': return true; case '+': case '|': for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { if (m.empty()) { return true; } } return false; case ',': case '&': for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { if (!m.empty()) { return false; } } return true; default: return false; } } /** * Update elemVec with the list of elements that are * part of the this contentModel. */ public void getElements(Vector<Element> elemVec) { switch (type) { case '*': case '?': case '+': ((ContentModel)content).getElements(elemVec); break; case ',': case '|': case '&': for (ContentModel m=(ContentModel)content; m != null; m=m.next){ m.getElements(elemVec); } break; default: elemVec.addElement((Element)content); } } private boolean valSet[]; private boolean val[]; // A cache used by first(). This cache was found to speed parsing // by about 10% (based on measurements of the 4-12 code base after // buffering was fixed). /** * Return true if the token could potentially be the * first token in the input stream. */ public boolean first(Object token) { switch (type) { case '*': case '?': case '+': return ((ContentModel)content).first(token); case ',': for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { if (m.first(token)) { return true; } if (!m.empty()) { return false; } } return false; case '|': case '&': { Element e = (Element) token; if (valSet == null) { valSet = new boolean[Element.maxIndex + 1]; val = new boolean[Element.maxIndex + 1]; // All Element instances are created before this ever executes } if (valSet[e.index]) { return val[e.index]; } for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { if (m.first(token)) { val[e.index] = true; break; } } valSet[e.index] = true; return val[e.index]; } default: return (content == token); // PENDING: refer to comment in ContentModelState /* if (content == token) { return true; } Element e = (Element)content; if (e.omitStart() && e.content != null) { return e.content.first(token); } return false; */ } } /** * Return the element that must be next. */ public Element first() { switch (type) { case '&': case '|': case '*': case '?': return null; case '+': case ',': return ((ContentModel)content).first(); default: return (Element)content; } } /** * Convert to a string. */ public String toString() { switch (type) { case '*': return content + "*"; case '?': return content + "?"; case '+': return content + "+"; case ',': case '|': case '&': char data[] = {' ', (char)type, ' '}; String str = ""; for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { str = str + m; if (m.next != null) { str += new String(data); } } return "(" + str + ")"; default: return content.toString(); } } }