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

   1: /* list.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 list
  50:   extends node
  51:   implements Serializable
  52: {
  53:   private static final long serialVersionUID = 1;
  54: 
  55:   /**
  56:    * Setting to true means that the list nodes must always be connected
  57:    * by the same operation. This is far safer and clearer, but not
  58:    * required by default standard.
  59:    */
  60:   public static boolean CLEAR;
  61: 
  62:   /**
  63:    * A list of nodes.
  64:    */
  65:   public final node[] nodes;
  66: 
  67:   /**
  68:    * Creates a new model list that is a member of some enclosing list.
  69:    * @param binary_operator An operator with that this list is connected
  70:    * with other members of the enclosing list.
  71:    * @param unary_operator The unary operator for this list.
  72:    * @param a_nodes The nodes inside this list.
  73:    */
  74:   public list(char binary_operator, char unary_operator, node[] a_nodes)
  75:   {
  76:     super(binary_operator, unary_operator, a_nodes);
  77:     nodes = a_nodes;
  78:   }
  79: 
  80:   /**
  81:    * Creates a new model list. Assigns the previous field.
  82:    * @param a_nodes The nodes for this list.
  83:    * @throws an error if the node elements are connected by the
  84:    * different operations. This is not supported, use grouping.
  85:    */
  86:   public list(node[] a_nodes)
  87:        throws Error
  88:   {
  89:     this(',', (char) 0, a_nodes);
  90: 
  91:     int operation = nodes [ 0 ].binary;
  92: 
  93:     for (int i = 0; i < nodes.length; i++)
  94:       {
  95:         if (CLEAR && nodes [ i ].binary != operation)
  96:           throw new Error("List members can only be connected by " +
  97:                           "the same operation, use grouping"
  98:                          );
  99: 
 100:         if (i > 0)
 101:           nodes [ i ].previous = nodes [ i - 1 ];
 102:       }
 103:   }
 104: 
 105:   /**
 106:    * Returns true if all members in the list are closed.
 107:    */
 108:   public boolean isClosed()
 109:   {
 110:     if (super.isClosed())
 111:       return true;
 112:     for (int i = 0; i < nodes.length; i++)
 113:       {
 114:         if (!nodes [ i ].isClosed())
 115:           return false;
 116:       }
 117:     return true;
 118:   }
 119: 
 120:   /**
 121:    * Find the token that could match as the next token in
 122:    * the token list.
 123:    *
 124:    * @return Such token object or null if none is found.
 125:    */
 126:   public Object findFreeNode()
 127:   {
 128:     Object fn;
 129:     for (int j = 0; j < nodes.length; j++)
 130:       {
 131:         if (!nodes [ j ].isClosed())
 132:           {
 133:             fn = nodes [ j ].findFreeNode();
 134:             if (fn != null)
 135:               return fn;
 136:           }
 137:       }
 138:     return null;
 139:   }
 140: 
 141:   /**
 142:    * Tries to match this list agains the given token sequence.
 143:    * @param tokens the sequence of the tokens to match.
 144:    * @return true if the valid match is found.
 145:    */
 146:   public boolean matches(Object[] tokens)
 147:   {
 148:     reset();
 149: 
 150:     Object x;
 151:     boolean m;
 152:     boolean matched = false;
 153: 
 154:     for (int i = 0; i < tokens.length; i++)
 155:       {
 156:         matched = false;
 157:         x = tokens [ i ];
 158: 
 159:         nodescan:
 160:         for (int j = 0; j < nodes.length; j++)
 161:           {
 162:             if (!nodes [ j ].isClosed())
 163:               {
 164:                 m = nodes [ j ].performMatch(x);
 165: 
 166:                 if (m)
 167:                   {
 168:                     matched = true;
 169:                     break nodescan;
 170:                   }
 171:               }
 172:           }
 173:         if (!matched)
 174:           return false;
 175:       }
 176: 
 177:     boolean valid = true;
 178: 
 179:     for (int i = 0; i < nodes.length; i++)
 180:       {
 181:         if (!nodes [ i ].valid())
 182:           valid = false;
 183:       }
 184: 
 185:     return valid;
 186:   }
 187: 
 188:   /**
 189:    * The list never closes, despite it is trated as closed
 190:    * if all members in the list are closed.
 191:    * @return false.
 192:    */
 193:   public boolean mustClose()
 194:   {
 195:     return false;
 196:   }
 197: 
 198:   /**
 199:    * Perform a match operation for the single token
 200:    * against this list.
 201:    * @param token a token to match.
 202:    * @return true if the match is found.
 203:    */
 204:   public boolean performMatch(Object token)
 205:   {
 206:     boolean ok = false;
 207:     Matching:
 208:     for (int i = 0; i < nodes.length; i++)
 209:       {
 210:         ok = nodes [ i ].performMatch(token);
 211: 
 212:         if (ok)
 213:           break Matching;
 214:       }
 215: 
 216:     if (ok)
 217:       matches();
 218: 
 219:     return ok;
 220:   }
 221: 
 222:   /**
 223:    * Prepeares the list for the next matching operation.
 224:    */
 225:   public void reset()
 226:   {
 227:     super.reset();
 228:     for (int i = 0; i < nodes.length; i++)
 229:       nodes [ i ].reset();
 230:   }
 231: 
 232:   /**
 233:    * Check if the provided token can match as a next token in the
 234:    * list. In the case of match, the list state changes, moving
 235:    * current position after the matched token. However if this method
 236:    * returns a suggested new token to insert before the provided one,
 237:    * the state of the list does not change.
 238:    * @return Boolean.TRUE if the match is found,
 239:    * Boolean.FALSE if the match is not possible and no token can be
 240:    * inserted to make the match valid. Otherwise, returns the
 241:    * token object that can be inserted before the last token in the
 242:    * list, probably (not for sure) making the match valid.
 243:    * If the object is an instance of Element or TagElement,
 244:    * it is first ensured that the object flag "omit start" is set.
 245:    */
 246:   public Object show(Object x)
 247:   {
 248:     boolean m;
 249:     boolean matched = false;
 250: 
 251:     nodescan:
 252:     for (int j = 0; j < nodes.length; j++)
 253:       {
 254:         if (!nodes [ j ].isClosed())
 255:           {
 256:             m = nodes [ j ].performMatch(x);
 257: 
 258:             if (m)
 259:               {
 260:                 matched = true;
 261:                 break nodescan;
 262:               }
 263:             else
 264:               {
 265:                 // For comma operation, only first not closed
 266:                 // node must be tested for a match.
 267:                 // unless it allows matching zero times.
 268:                 if (binary == ',' &&
 269:                     !(nodes [ j ].unary == '?' || nodes [ j ].unary == '*')
 270:                    )
 271:                   break nodescan;
 272:               }
 273:           }
 274:       }
 275: 
 276:     if (!matched)
 277:       {
 278:         // Find and return that would be matched.
 279:         Object freeNode = findFreeNode();
 280:         if (freeNode == null)
 281:           return Boolean.FALSE;
 282:         else
 283:           return freeNode;
 284:       }
 285: 
 286:     for (int i = 0; i < nodes.length; i++)
 287:       if (!nodes [ i ].validPreliminary())
 288:         {
 289:           return Boolean.FALSE;
 290:         }
 291: 
 292:     return Boolean.TRUE;
 293:   }
 294: 
 295:   /**
 296:    * Returns a string representation of the list.
 297:    * @return String representation, similar to BNF expression.
 298:    */
 299:   public String toString()
 300:   {
 301:     CPStringBuilder b = new CPStringBuilder();
 302:     b.append(" ( ");
 303:     for (int i = 0; i < nodes.length; i++)
 304:       {
 305:         if (i > 0)
 306:           b.append(" " + (char) nodes [ i ].binary + " ");
 307:         b.append(nodes [ i ]);
 308:       }
 309: 
 310:     b.append(" )");
 311:     if (unary != 0)
 312:       b.append((char) unary);
 313:     else
 314:       b.append(' ');
 315:     return b.toString();
 316:   }
 317: 
 318:   /**
 319:    * Returns true if all memebers in the list are valid.
 320:    */
 321:   public boolean valid()
 322:   {
 323:     for (int i = 0; i < nodes.length; i++)
 324:       {
 325:         if (!nodes [ i ].valid())
 326:           return false;
 327:       }
 328:     return true;
 329:   }
 330: 
 331:   /**
 332:    * Returns true if all memebers in the list are either valid
 333:    * or unvisited. The unvisited members can become valid after
 334:    * more tokens will be shown.
 335:    */
 336:   public boolean validPreliminary()
 337:   {
 338:     if (silenceAllowed())
 339:       {
 340:         boolean everVisited = false;
 341:         for (int i = 0; i < nodes.length; i++)
 342:           {
 343:             if (nodes [ i ].visits > 0)
 344:               {
 345:                 everVisited = true;
 346:                 break;
 347:               }
 348:           }
 349:         if (!everVisited)
 350:           return true;
 351:       }
 352: 
 353:     for (int i = 0; i < nodes.length; i++)
 354:       {
 355:         if (!nodes [ i ].validPreliminary())
 356:           return false;
 357:       }
 358:     return true;
 359:   }
 360: 
 361:   /**
 362:    * Closes all members in the list.
 363:    */
 364:   protected void close()
 365:   {
 366:     super.close();
 367:     for (int i = 0; i < nodes.length; i++)
 368:       {
 369:         nodes [ i ].close();
 370:       }
 371:   }
 372: 
 373:   /**
 374:    * Compare given token with the token of this node.
 375:    * If the token represents a <code>list</code>, the call may be
 376:    * delegeted to the child subnodes.
 377:    * @param a_token A token to compare.
 378:    * @return True if the token matches the token of this node.
 379:    */
 380:   protected boolean compare(Object a_token)
 381:   {
 382:     return performMatch(a_token);
 383:   }
 384: }