Frames | No Frames |
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: */ 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: */ 112: public boolean isClosed() 113: { 114: return _closed; 115: } 116: 117: /** 118: * Check if closing this node means closing the previous node. 119: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: */ 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: 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: */ 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: */ 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: */ 333: protected void matches() 334: { 335: visits++; 336: if (mustClose()) 337: close(); 338: } 339: }