1 /* 2 * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.text.html.parser; 27 28 import java.util.Vector; 29 import java.util.Enumeration; 30 import java.io.*; 31 32 33 /** 34 * A representation of a content model. A content model is 35 * basically a restricted BNF expression. It is restricted in 36 * the sense that it must be deterministic. This means that you 37 * don't have to represent it as a finite state automaton.<p> 38 * See Annex H on page 556 of the SGML handbook for more information. 39 * 40 * @author Arthur van Hoff 41 * 42 */ 43 @SuppressWarnings("serial") // Same-version serialization only 44 public final class ContentModel implements Serializable { 45 /** 46 * Type. Either '*', '?', '+', ',', '|', '&'. 47 */ 48 public int type; 49 50 /** 51 * The content. Either an Element or a ContentModel. 52 */ 53 public Object content; 54 55 /** 56 * The next content model (in a ',', '|' or '&' expression). 57 */ 58 public ContentModel next; 59 60 /** 61 * Creates {@code ContentModel} 62 */ 63 public ContentModel() { 64 } 65 66 /** 67 * Create a content model for an element. 68 * 69 * @param content the element 70 */ 71 public ContentModel(Element content) { 72 this(0, content, null); 73 } 74 75 /** 76 * Create a content model of a particular type. 77 * 78 * @param type the type 79 * @param content the content 80 */ 81 public ContentModel(int type, ContentModel content) { 82 this(type, content, null); 83 } 84 85 /** 86 * Create a content model of a particular type. 87 * 88 * @param type the type 89 * @param content the content 90 * @param next the next content model 91 */ 92 public ContentModel(int type, Object content, ContentModel next) { 93 this.type = type; 94 this.content = content; 95 this.next = next; 96 } 97 98 /** 99 * Return true if the content model could 100 * match an empty input stream. 101 * 102 * @return {@code true} if the content model could 103 * match an empty input stream 104 */ 105 public boolean empty() { 106 switch (type) { 107 case '*': 108 case '?': 109 return true; 110 111 case '+': 112 case '|': 113 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { 114 if (m.empty()) { 115 return true; 116 } 117 } 118 return false; 119 120 case ',': 121 case '&': 122 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { 123 if (!m.empty()) { 124 return false; 125 } 126 } 127 return true; 128 129 default: 130 return false; 131 } 132 } 133 134 /** 135 * Update elemVec with the list of elements that are 136 * part of the this contentModel. 137 * 138 * @param elemVec the list of elements 139 */ 140 public void getElements(Vector<Element> elemVec) { 141 switch (type) { 142 case '*': 143 case '?': 144 case '+': 145 ((ContentModel)content).getElements(elemVec); 146 break; 147 case ',': 148 case '|': 149 case '&': 150 for (ContentModel m=(ContentModel)content; m != null; m=m.next){ 151 m.getElements(elemVec); 152 } 153 break; 154 default: 155 elemVec.addElement((Element)content); 156 } 157 } 158 159 private boolean valSet[]; 160 private boolean val[]; 161 // A cache used by first(). This cache was found to speed parsing 162 // by about 10% (based on measurements of the 4-12 code base after 163 // buffering was fixed). 164 165 /** 166 * Return true if the token could potentially be the 167 * first token in the input stream. 168 * 169 * @param token the token 170 * 171 * @return {@code true} if the token could potentially be the first token 172 * in the input stream 173 */ 174 public boolean first(Object token) { 175 switch (type) { 176 case '*': 177 case '?': 178 case '+': 179 return ((ContentModel)content).first(token); 180 181 case ',': 182 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { 183 if (m.first(token)) { 184 return true; 185 } 186 if (!m.empty()) { 187 return false; 188 } 189 } 190 return false; 191 192 case '|': 193 case '&': { 194 Element e = (Element) token; 195 if (valSet == null || valSet.length <= Element.getMaxIndex()) { 196 valSet = new boolean[Element.getMaxIndex() + 1]; 197 val = new boolean[valSet.length]; 198 } 199 if (valSet[e.index]) { 200 return val[e.index]; 201 } 202 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { 203 if (m.first(token)) { 204 val[e.index] = true; 205 break; 206 } 207 } 208 valSet[e.index] = true; 209 return val[e.index]; 210 } 211 212 default: 213 return (content == token); 214 // PENDING: refer to comment in ContentModelState 215 /* 216 if (content == token) { 217 return true; 218 } 219 Element e = (Element)content; 220 if (e.omitStart() && e.content != null) { 221 return e.content.first(token); 222 } 223 return false; 224 */ 225 } 226 } 227 228 /** 229 * Return the element that must be next. 230 * 231 * @return the element that must be next 232 */ 233 public Element first() { 234 switch (type) { 235 case '&': 236 case '|': 237 case '*': 238 case '?': 239 return null; 240 241 case '+': 242 case ',': 243 return ((ContentModel)content).first(); 244 245 default: 246 return (Element)content; 247 } 248 } 249 250 /** 251 * Convert to a string. 252 * 253 * @return the string representation of this {@code ContentModel} 254 */ 255 public String toString() { 256 switch (type) { 257 case '*': 258 return content + "*"; 259 case '?': 260 return content + "?"; 261 case '+': 262 return content + "+"; 263 264 case ',': 265 case '|': 266 case '&': 267 char data[] = {' ', (char)type, ' '}; 268 String str = ""; 269 for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) { 270 str = str + m; 271 if (m.next != null) { 272 str += new String(data); 273 } 274 } 275 return "(" + str + ")"; 276 277 default: 278 return content.toString(); 279 } 280 } 281 }