Source for gnu.javax.swing.text.html.parser.models.node

   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: }