You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
6.0 KiB
255 lines
6.0 KiB
/*
|
|
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.swing.text.html.parser;
|
|
|
|
import java.util.Vector;
|
|
import java.util.Enumeration;
|
|
import java.io.*;
|
|
|
|
|
|
/**
|
|
* A representation of a content model. A content model is
|
|
* basically a restricted BNF expression. It is restricted in
|
|
* the sense that it must be deterministic. This means that you
|
|
* don't have to represent it as a finite state automaton.<p>
|
|
* See Annex H on page 556 of the SGML handbook for more information.
|
|
*
|
|
* @author Arthur van Hoff
|
|
*
|
|
*/
|
|
public final class ContentModel implements Serializable {
|
|
/**
|
|
* Type. Either '*', '?', '+', ',', '|', '&'.
|
|
*/
|
|
public int type;
|
|
|
|
/**
|
|
* The content. Either an Element or a ContentModel.
|
|
*/
|
|
public Object content;
|
|
|
|
/**
|
|
* The next content model (in a ',', '|' or '&' expression).
|
|
*/
|
|
public ContentModel next;
|
|
|
|
public ContentModel() {
|
|
}
|
|
|
|
/**
|
|
* Create a content model for an element.
|
|
*/
|
|
public ContentModel(Element content) {
|
|
this(0, content, null);
|
|
}
|
|
|
|
/**
|
|
* Create a content model of a particular type.
|
|
*/
|
|
public ContentModel(int type, ContentModel content) {
|
|
this(type, content, null);
|
|
}
|
|
|
|
/**
|
|
* Create a content model of a particular type.
|
|
*/
|
|
public ContentModel(int type, Object content, ContentModel next) {
|
|
this.type = type;
|
|
this.content = content;
|
|
this.next = next;
|
|
}
|
|
|
|
/**
|
|
* Return true if the content model could
|
|
* match an empty input stream.
|
|
*/
|
|
public boolean empty() {
|
|
switch (type) {
|
|
case '*':
|
|
case '?':
|
|
return true;
|
|
|
|
case '+':
|
|
case '|':
|
|
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
|
|
if (m.empty()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
case ',':
|
|
case '&':
|
|
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
|
|
if (!m.empty()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update elemVec with the list of elements that are
|
|
* part of the this contentModel.
|
|
*/
|
|
public void getElements(Vector<Element> elemVec) {
|
|
switch (type) {
|
|
case '*':
|
|
case '?':
|
|
case '+':
|
|
((ContentModel)content).getElements(elemVec);
|
|
break;
|
|
case ',':
|
|
case '|':
|
|
case '&':
|
|
for (ContentModel m=(ContentModel)content; m != null; m=m.next){
|
|
m.getElements(elemVec);
|
|
}
|
|
break;
|
|
default:
|
|
elemVec.addElement((Element)content);
|
|
}
|
|
}
|
|
|
|
private boolean valSet[];
|
|
private boolean val[];
|
|
// A cache used by first(). This cache was found to speed parsing
|
|
// by about 10% (based on measurements of the 4-12 code base after
|
|
// buffering was fixed).
|
|
|
|
/**
|
|
* Return true if the token could potentially be the
|
|
* first token in the input stream.
|
|
*/
|
|
public boolean first(Object token) {
|
|
switch (type) {
|
|
case '*':
|
|
case '?':
|
|
case '+':
|
|
return ((ContentModel)content).first(token);
|
|
|
|
case ',':
|
|
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
|
|
if (m.first(token)) {
|
|
return true;
|
|
}
|
|
if (!m.empty()) {
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
case '|':
|
|
case '&': {
|
|
Element e = (Element) token;
|
|
if (valSet == null || valSet.length <= Element.getMaxIndex()) {
|
|
valSet = new boolean[Element.getMaxIndex() + 1];
|
|
val = new boolean[valSet.length];
|
|
}
|
|
if (valSet[e.index]) {
|
|
return val[e.index];
|
|
}
|
|
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
|
|
if (m.first(token)) {
|
|
val[e.index] = true;
|
|
break;
|
|
}
|
|
}
|
|
valSet[e.index] = true;
|
|
return val[e.index];
|
|
}
|
|
|
|
default:
|
|
return (content == token);
|
|
// PENDING: refer to comment in ContentModelState
|
|
/*
|
|
if (content == token) {
|
|
return true;
|
|
}
|
|
Element e = (Element)content;
|
|
if (e.omitStart() && e.content != null) {
|
|
return e.content.first(token);
|
|
}
|
|
return false;
|
|
*/
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the element that must be next.
|
|
*/
|
|
public Element first() {
|
|
switch (type) {
|
|
case '&':
|
|
case '|':
|
|
case '*':
|
|
case '?':
|
|
return null;
|
|
|
|
case '+':
|
|
case ',':
|
|
return ((ContentModel)content).first();
|
|
|
|
default:
|
|
return (Element)content;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert to a string.
|
|
*/
|
|
public String toString() {
|
|
switch (type) {
|
|
case '*':
|
|
return content + "*";
|
|
case '?':
|
|
return content + "?";
|
|
case '+':
|
|
return content + "+";
|
|
|
|
case ',':
|
|
case '|':
|
|
case '&':
|
|
char data[] = {' ', (char)type, ' '};
|
|
String str = "";
|
|
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
|
|
str = str + m;
|
|
if (m.next != null) {
|
|
str += new String(data);
|
|
}
|
|
}
|
|
return "(" + str + ")";
|
|
|
|
default:
|
|
return content.toString();
|
|
}
|
|
}
|
|
}
|