1 /* node.java -- 2 Copyright (C) 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package gnu.javax.swing.text.html.parser.models; 40 41 import gnu.java.lang.CPStringBuilder; 42 43 import java.io.Serializable; 44 45 /** 46 * Part of the internal representation of the content model. 47 * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) 48 */ 49 public class node 50 implements Serializable 51 { 52 private static final long serialVersionUID = 1; 53 54 /** 55 * The token to match (can be instance of list). 56 */ 57 public Object token; 58 59 /** 60 * True for the node that cannot be visited again. 61 */ 62 public boolean _closed; 63 64 /** 65 * The binary operation for this node. 66 */ 67 public char binary; 68 69 /** 70 * The unary opeation for this node. 71 */ 72 public char unary; 73 74 /** 75 * The number of times the node already was visited. 76 */ 77 public int visits; 78 79 /** 80 * The previous node in content model (used for closing nodes). 81 */ 82 public node previous; 83 84 /** 85 * Creates a new node. 86 * @param binary_operator The operator, connecting all nodes in the list. 87 * The nodes, connected by the different operators, must be arranged into 88 * the different lists. 89 * @param unary_operator The unary operator for this node or zero if 90 * no such was specified. 91 * @param token The token to match. This can be either a string or 92 * the new instance of the list. 93 * @param a_previous The previous node in the list, null for the first 94 * node. This is used for propagating the closing operation for the 95 * comma delimited list. 96 */ node(char binary_operator, char unary_operator, Object a_token)97 public node(char binary_operator, char unary_operator, Object a_token) 98 { 99 if (a_token != null) 100 if (a_token.getClass().equals(node.class)) 101 throw new Error("Creating node in node is redundant and ineffective."); 102 103 binary = binary_operator; 104 unary = unary_operator; 105 token = a_token; 106 } 107 108 /** 109 * Checks if this node is in the closed state. 110 * @return True if the node is closed. 111 */ isClosed()112 public boolean isClosed() 113 { 114 return _closed; 115 } 116 117 /** 118 * Check if closing this node means closing the previous node. 119 */ closePrevious()120 public boolean closePrevious() 121 { 122 return binary == ','; 123 } 124 125 /** 126 * Return the token object if it could match as a next token in 127 * a token list of null if it could not. 128 * @return 129 */ findFreeNode()130 public Object findFreeNode() 131 { 132 boolean ok; 133 if (isClosed() || silenceAllowed()) 134 return null; 135 136 // Try if the node would stay valid after a one more visit. 137 visits++; 138 ok = valid(); 139 visits--; 140 141 if (ok) 142 { 143 if (token instanceof node) 144 return ((node) token).findFreeNode(); 145 else 146 return token; 147 } 148 else 149 return null; 150 } 151 152 /** 153 * Check if the current situation is such that the node must be closed 154 * now. 155 */ mustClose()156 public boolean mustClose() 157 { 158 switch (unary) 159 { 160 case 0 : 161 return true; 162 163 case '*' : 164 return false; 165 166 case '+' : 167 return false; 168 169 case '?' : 170 return visits <= 1; 171 172 default : 173 throw new Error("Invalid unary operation " + unary + " ( '" + 174 (char) unary + "' )" 175 ); 176 } 177 } 178 179 /** 180 * Do the match operation with the given token. This sets various 181 * flags. 182 * @param a_token The token to match. 183 * @return true if the the token matches node, false if it does not match 184 * or if the node is closed. 185 */ performMatch(Object a_token)186 public boolean performMatch(Object a_token) 187 { 188 if (isClosed()) 189 return false; 190 191 boolean matches = compare(a_token); 192 if (matches) 193 matches(); 194 195 return matches; 196 } 197 198 /** 199 * Prepares the node for matching against a new list of tokens. 200 */ reset()201 public void reset() 202 { 203 _closed = false; 204 visits = 0; 205 } 206 207 /** 208 * Check if the provided token can match this node. 209 * In the case of match, the node state changes, moving 210 * current position after the matched token. However if this method 211 * returns a suggested new token to insert before the provided one, 212 * the state of the list does not change. 213 * @return Boolean.TRUE if the match is found, 214 * Boolean.FALSE if the match is not possible and no token can be 215 * inserted to make the match valid. Otherwise, returns the 216 * token object that can be inserted before the last token in the 217 * list, probably (not for sure) making the match valid. 218 */ show(Object x)219 public Object show(Object x) 220 { 221 if (compare(x)) 222 return performMatch(x) ? Boolean.TRUE : Boolean.FALSE; 223 224 Object recommended = findFreeNode(); 225 return recommended != null ? recommended : Boolean.FALSE; 226 } 227 228 /** 229 * Check if it would be a valid case if this node is visited zero times. 230 * Nodes with unary operator * or ? need not be matched to make a 231 * model valid. 232 */ silenceAllowed()233 public boolean silenceAllowed() 234 { 235 return unary == '?' || unary == '*'; 236 } 237 238 /** 239 * Returns a string representation of the list. 240 * @return String representation, similar to BNF expression. 241 */ toString()242 public String toString() 243 { 244 CPStringBuilder b = new CPStringBuilder(); 245 246 b.append(token); 247 if (unary != 0) 248 b.append((char) unary); 249 else 250 b.append('\''); 251 252 return b.toString(); 253 } 254 255 /** 256 * Check if the node state is valid. 257 */ valid()258 public boolean valid() 259 { 260 switch (unary) 261 { 262 case 0 : 263 if (binary == '|') 264 return true; 265 else 266 return visits == 1; 267 268 case '*' : 269 return true; 270 271 case '+' : 272 return visits > 0; 273 274 case '?' : 275 return visits <= 1; 276 277 default : 278 throw new Error("Invalid unary operation " + unary + " ( '" + 279 (char) unary + "' )" 280 ); 281 } 282 } 283 validPreliminary()284 public boolean validPreliminary() 285 { 286 return visits == 0 || valid(); 287 } 288 289 /** 290 * Closes this node and, if closePrevious() returs true, calls close() for 291 * the previous node. 292 */ close()293 protected void close() 294 { 295 _closed = true; 296 if (previous != null && closePrevious()) 297 previous.close(); 298 } 299 300 /** 301 * Compare the provided token object with the token object of this node. 302 */ compare(Object a_token)303 protected boolean compare(Object a_token) 304 { 305 if (token instanceof Object[]) 306 throw new Error("Invalid token object, probably the 'list' " + 307 "should be used. " 308 ); 309 310 if (token instanceof node[]) 311 throw new Error("Do not use 'node' for the array of nodes, use 'list'. "); 312 313 if (token instanceof node) 314 { 315 return ((node) token).performMatch(a_token); 316 } 317 318 boolean rt = false; 319 320 if (token == a_token) 321 rt = true; 322 if (token.equals(a_token)) 323 rt = true; 324 if (token.toString().equalsIgnoreCase(a_token.toString())) 325 rt = true; 326 327 return rt; 328 } 329 330 /** 331 * Fire the changes that must happen then the token matches this node. 332 */ matches()333 protected void matches() 334 { 335 visits++; 336 if (mustClose()) 337 close(); 338 } 339 } 340