1 /*
   2  * Copyright (c) 1998, 2013, 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 import java.util.Vector;
  29 import java.util.Enumeration;
  30 import java.io.*;
  31 
  32 
  33 /**
  34  * A representation of a content model. A content model is
  35  * basically a restricted BNF expression. It is restricted in
  36  * the sense that it must be deterministic. This means that you
  37  * don't have to represent it as a finite state automaton.<p>
  38  * See Annex H on page 556 of the SGML handbook for more information.
  39  *
  40  * @author   Arthur van Hoff
  41  *
  42  */
  43 public final class ContentModel implements Serializable {
  44     /**
  45      * Type. Either '*', '?', '+', ',', '|', '&amp;'.
  46      */
  47     public int type;
  48 
  49     /**
  50      * The content. Either an Element or a ContentModel.
  51      */
  52     public Object content;
  53 
  54     /**
  55      * The next content model (in a ',', '|' or '&amp;' expression).
  56      */
  57     public ContentModel next;
  58 
  59     public ContentModel() {
  60     }
  61 
  62     /**
  63      * Create a content model for an element.
  64      */
  65     public ContentModel(Element content) {
  66         this(0, content, null);
  67     }
  68 
  69     /**
  70      * Create a content model of a particular type.
  71      */
  72     public ContentModel(int type, ContentModel content) {
  73         this(type, content, null);
  74     }
  75 
  76     /**
  77      * Create a content model of a particular type.
  78      */
  79     public ContentModel(int type, Object content, ContentModel next) {
  80         this.type = type;
  81         this.content = content;
  82         this.next = next;
  83     }
  84 
  85     /**
  86      * Return true if the content model could
  87      * match an empty input stream.
  88      */
  89     public boolean empty() {
  90         switch (type) {
  91           case '*':
  92           case '?':
  93             return true;
  94 
  95           case '+':
  96           case '|':
  97             for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
  98                 if (m.empty()) {
  99                     return true;
 100                 }
 101             }
 102             return false;
 103 
 104           case ',':
 105           case '&':
 106             for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
 107                 if (!m.empty()) {
 108                     return false;
 109                 }
 110             }
 111             return true;
 112 
 113           default:
 114             return false;
 115         }
 116     }
 117 
 118     /**
 119      * Update elemVec with the list of elements that are
 120      * part of the this contentModel.
 121      */
 122      public void getElements(Vector<Element> elemVec) {
 123          switch (type) {
 124          case '*':
 125          case '?':
 126          case '+':
 127              ((ContentModel)content).getElements(elemVec);
 128              break;
 129          case ',':
 130          case '|':
 131          case '&':
 132              for (ContentModel m=(ContentModel)content; m != null; m=m.next){
 133                  m.getElements(elemVec);
 134              }
 135              break;
 136          default:
 137              elemVec.addElement((Element)content);
 138          }
 139      }
 140 
 141      private boolean valSet[];
 142      private boolean val[];
 143      // A cache used by first().  This cache was found to speed parsing
 144      // by about 10% (based on measurements of the 4-12 code base after
 145      // buffering was fixed).
 146 
 147     /**
 148      * Return true if the token could potentially be the
 149      * first token in the input stream.
 150      */
 151     public boolean first(Object token) {
 152         switch (type) {
 153           case '*':
 154           case '?':
 155           case '+':
 156             return ((ContentModel)content).first(token);
 157 
 158           case ',':
 159             for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
 160                 if (m.first(token)) {
 161                     return true;
 162                 }
 163                 if (!m.empty()) {
 164                     return false;
 165                 }
 166             }
 167             return false;
 168 
 169           case '|':
 170           case '&': {
 171             Element e = (Element) token;
 172             if (valSet == null) {
 173                 valSet = new boolean[Element.getMaxIndex() + 1];
 174                 val = new boolean[valSet.length];
 175                 // All Element instances are created before this ever executes
 176             }
 177             if (valSet[e.index]) {
 178                 return val[e.index];
 179             }
 180             for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
 181                 if (m.first(token)) {
 182                     val[e.index] = true;
 183                     break;
 184                 }
 185             }
 186             valSet[e.index] = true;
 187             return val[e.index];
 188           }
 189 
 190           default:
 191             return (content == token);
 192             // PENDING: refer to comment in ContentModelState
 193 /*
 194               if (content == token) {
 195                   return true;
 196               }
 197               Element e = (Element)content;
 198               if (e.omitStart() && e.content != null) {
 199                   return e.content.first(token);
 200               }
 201               return false;
 202 */
 203         }
 204     }
 205 
 206     /**
 207      * Return the element that must be next.
 208      */
 209     public Element first() {
 210         switch (type) {
 211           case '&':
 212           case '|':
 213           case '*':
 214           case '?':
 215             return null;
 216 
 217           case '+':
 218           case ',':
 219             return ((ContentModel)content).first();
 220 
 221           default:
 222             return (Element)content;
 223         }
 224     }
 225 
 226     /**
 227      * Convert to a string.
 228      */
 229     public String toString() {
 230         switch (type) {
 231           case '*':
 232             return content + "*";
 233           case '?':
 234             return content + "?";
 235           case '+':
 236             return content + "+";
 237 
 238           case ',':
 239           case '|':
 240           case '&':
 241             char data[] = {' ', (char)type, ' '};
 242             String str = "";
 243             for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
 244                 str = str + m;
 245                 if (m.next != null) {
 246                     str += new String(data);
 247                 }
 248             }
 249             return "(" + str + ")";
 250 
 251           default:
 252             return content.toString();
 253         }
 254     }
 255 }