1 /* 2 * Copyright (c) 1998, 2000, 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 /** 29 * A content model state. This is basically a list of pointers to 30 * the BNF expression representing the model (the ContentModel). 31 * Each element in a DTD has a content model which describes the 32 * elements that may occur inside, and the order in which they can 33 * occur. 34 * <p> 35 * Each time a token is reduced a new state is created. 36 * <p> 37 * See Annex H on page 556 of the SGML handbook for more information. 38 * 39 * @see Parser 40 * @see DTD 41 * @see Element 42 * @see ContentModel 43 * @author Arthur van Hoff 44 */ 45 class ContentModelState { 46 ContentModel model; 47 long value; 48 ContentModelState next; 49 50 /** 51 * Create a content model state for a content model. 52 */ 53 public ContentModelState(ContentModel model) { 54 this(model, null, 0); 55 } 56 57 /** 58 * Create a content model state for a content model given the 59 * remaining state that needs to be reduce. 60 */ 61 ContentModelState(Object content, ContentModelState next) { 62 this(content, next, 0); 63 } 64 65 /** 66 * Create a content model state for a content model given the 67 * remaining state that needs to be reduce. 68 */ 69 ContentModelState(Object content, ContentModelState next, long value) { 70 this.model = (ContentModel)content; 71 this.next = next; 72 this.value = value; 73 } 74 75 /** 76 * Return the content model that is relevant to the current state. 77 */ 78 public ContentModel getModel() { 79 ContentModel m = model; 80 for (int i = 0; i < value; i++) { 81 if (m.next != null) { 82 m = m.next; 83 } else { 84 return null; 85 } 86 } 87 return m; 88 } 89 90 /** 91 * Check if the state can be terminated. That is there are no more 92 * tokens required in the input stream. 93 * @return true if the model can terminate without further input 94 */ 95 public boolean terminate() { 96 switch (model.type) { 97 case '+': 98 if ((value == 0) && !(model).empty()) { 99 return false; 100 } 101 case '*': 102 case '?': 103 return (next == null) || next.terminate(); 104 105 case '|': 106 for (ContentModel m = (ContentModel)model.content ; m != null ; m = m.next) { 107 if (m.empty()) { 108 return (next == null) || next.terminate(); 109 } 110 } 111 return false; 112 113 case '&': { 114 ContentModel m = (ContentModel)model.content; 115 116 for (int i = 0 ; m != null ; i++, m = m.next) { 117 if ((value & (1L << i)) == 0) { 118 if (!m.empty()) { 119 return false; 120 } 121 } 122 } 123 return (next == null) || next.terminate(); 124 } 125 126 case ',': { 127 ContentModel m = (ContentModel)model.content; 128 for (int i = 0 ; i < value ; i++, m = m.next); 129 130 for (; (m != null) && m.empty() ; m = m.next); 131 if (m != null) { 132 return false; 133 } 134 return (next == null) || next.terminate(); 135 } 136 137 default: 138 return false; 139 } 140 } 141 142 /** 143 * Check if the state can be terminated. That is there are no more 144 * tokens required in the input stream. 145 * @return the only possible element that can occur next 146 */ 147 public Element first() { 148 switch (model.type) { 149 case '*': 150 case '?': 151 case '|': 152 case '&': 153 return null; 154 155 case '+': 156 return model.first(); 157 158 case ',': { 159 ContentModel m = (ContentModel)model.content; 160 for (int i = 0 ; i < value ; i++, m = m.next); 161 return m.first(); 162 } 163 164 default: 165 return model.first(); 166 } 167 } 168 169 /** 170 * Advance this state to a new state. An exception is thrown if the 171 * token is illegal at this point in the content model. 172 * @return next state after reducing a token 173 */ 174 public ContentModelState advance(Object token) { 175 switch (model.type) { 176 case '+': 177 if (model.first(token)) { 178 return new ContentModelState(model.content, 179 new ContentModelState(model, next, value + 1)).advance(token); 180 } 181 if (value != 0) { 182 if (next != null) { 183 return next.advance(token); 184 } else { 185 return null; 186 } 187 } 188 break; 189 190 case '*': 191 if (model.first(token)) { 192 return new ContentModelState(model.content, this).advance(token); 193 } 194 if (next != null) { 195 return next.advance(token); 196 } else { 197 return null; 198 } 199 200 case '?': 201 if (model.first(token)) { 202 return new ContentModelState(model.content, next).advance(token); 203 } 204 if (next != null) { 205 return next.advance(token); 206 } else { 207 return null; 208 } 209 210 case '|': 211 for (ContentModel m = (ContentModel)model.content ; m != null ; m = m.next) { 212 if (m.first(token)) { 213 return new ContentModelState(m, next).advance(token); 214 } 215 } 216 break; 217 218 case ',': { 219 ContentModel m = (ContentModel)model.content; 220 for (int i = 0 ; i < value ; i++, m = m.next); 221 222 if (m.first(token) || m.empty()) { 223 if (m.next == null) { 224 return new ContentModelState(m, next).advance(token); 225 } else { 226 return new ContentModelState(m, 227 new ContentModelState(model, next, value + 1)).advance(token); 228 } 229 } 230 break; 231 } 232 233 case '&': { 234 ContentModel m = (ContentModel)model.content; 235 boolean complete = true; 236 237 for (int i = 0 ; m != null ; i++, m = m.next) { 238 if ((value & (1L << i)) == 0) { 239 if (m.first(token)) { 240 return new ContentModelState(m, 241 new ContentModelState(model, next, value | (1L << i))).advance(token); 242 } 243 if (!m.empty()) { 244 complete = false; 245 } 246 } 247 } 248 if (complete) { 249 if (next != null) { 250 return next.advance(token); 251 } else { 252 return null; 253 } 254 } 255 break; 256 } 257 258 default: 259 if (model.content == token) { 260 if (next == null && (token instanceof Element) && 261 ((Element)token).content != null) { 262 return new ContentModelState(((Element)token).content); 263 } 264 return next; 265 } 266 // PENDING: Currently we don't correctly deal with optional start 267 // tags. This can most notably be seen with the 4.01 spec where 268 // TBODY's start and end tags are optional. 269 // Uncommenting this and the PENDING in ContentModel will 270 // correctly skip the omit tags, but the delegate is not notified. 271 // Some additional API needs to be added to track skipped tags, 272 // and this can then be added back. 273 /* 274 if ((model.content instanceof Element)) { 275 Element e = (Element)model.content; 276 277 if (e.omitStart() && e.content != null) { 278 return new ContentModelState(e.content, next).advance( 279 token); 280 } 281 } 282 */ 283 } 284 285 // We used to throw this exception at this point. However, it 286 // was determined that throwing this exception was more expensive 287 // than returning null, and we could not justify to ourselves why 288 // it was necessary to throw an exception, rather than simply 289 // returning null. I'm leaving it in a commented out state so 290 // that it can be easily restored if the situation ever arises. 291 // 292 // throw new IllegalArgumentException("invalid token: " + token); 293 return null; 294 } 295 }