0N/A/*
2362N/A * Copyright (c) 1998, 2000, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage javax.swing.text.html.parser;
0N/A
0N/A/**
0N/A * A content model state. This is basically a list of pointers to
0N/A * the BNF expression representing the model (the ContentModel).
0N/A * Each element in a DTD has a content model which describes the
0N/A * elements that may occur inside, and the order in which they can
0N/A * occur.
0N/A * <p>
0N/A * Each time a token is reduced a new state is created.
0N/A * <p>
0N/A * See Annex H on page 556 of the SGML handbook for more information.
0N/A *
0N/A * @see Parser
0N/A * @see DTD
0N/A * @see Element
0N/A * @see ContentModel
0N/A * @author Arthur van Hoff
0N/A */
0N/Aclass ContentModelState {
0N/A ContentModel model;
0N/A long value;
0N/A ContentModelState next;
0N/A
0N/A /**
0N/A * Create a content model state for a content model.
0N/A */
0N/A public ContentModelState(ContentModel model) {
0N/A this(model, null, 0);
0N/A }
0N/A
0N/A /**
0N/A * Create a content model state for a content model given the
0N/A * remaining state that needs to be reduce.
0N/A */
0N/A ContentModelState(Object content, ContentModelState next) {
0N/A this(content, next, 0);
0N/A }
0N/A
0N/A /**
0N/A * Create a content model state for a content model given the
0N/A * remaining state that needs to be reduce.
0N/A */
0N/A ContentModelState(Object content, ContentModelState next, long value) {
0N/A this.model = (ContentModel)content;
0N/A this.next = next;
0N/A this.value = value;
0N/A }
0N/A
0N/A /**
0N/A * Return the content model that is relevant to the current state.
0N/A */
0N/A public ContentModel getModel() {
0N/A ContentModel m = model;
0N/A for (int i = 0; i < value; i++) {
0N/A if (m.next != null) {
0N/A m = m.next;
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A return m;
0N/A }
0N/A
0N/A /**
0N/A * Check if the state can be terminated. That is there are no more
0N/A * tokens required in the input stream.
0N/A * @return true if the model can terminate without further input
0N/A */
0N/A public boolean terminate() {
0N/A switch (model.type) {
0N/A case '+':
0N/A if ((value == 0) && !(model).empty()) {
0N/A return false;
0N/A }
0N/A case '*':
0N/A case '?':
0N/A return (next == null) || next.terminate();
0N/A
0N/A case '|':
0N/A for (ContentModel m = (ContentModel)model.content ; m != null ; m = m.next) {
0N/A if (m.empty()) {
0N/A return (next == null) || next.terminate();
0N/A }
0N/A }
0N/A return false;
0N/A
0N/A case '&': {
0N/A ContentModel m = (ContentModel)model.content;
0N/A
0N/A for (int i = 0 ; m != null ; i++, m = m.next) {
0N/A if ((value & (1L << i)) == 0) {
0N/A if (!m.empty()) {
0N/A return false;
0N/A }
0N/A }
0N/A }
0N/A return (next == null) || next.terminate();
0N/A }
0N/A
0N/A case ',': {
0N/A ContentModel m = (ContentModel)model.content;
0N/A for (int i = 0 ; i < value ; i++, m = m.next);
0N/A
0N/A for (; (m != null) && m.empty() ; m = m.next);
0N/A if (m != null) {
0N/A return false;
0N/A }
0N/A return (next == null) || next.terminate();
0N/A }
0N/A
0N/A default:
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Check if the state can be terminated. That is there are no more
0N/A * tokens required in the input stream.
0N/A * @return the only possible element that can occur next
0N/A */
0N/A public Element first() {
0N/A switch (model.type) {
0N/A case '*':
0N/A case '?':
0N/A case '|':
0N/A case '&':
0N/A return null;
0N/A
0N/A case '+':
0N/A return model.first();
0N/A
0N/A case ',': {
0N/A ContentModel m = (ContentModel)model.content;
0N/A for (int i = 0 ; i < value ; i++, m = m.next);
0N/A return m.first();
0N/A }
0N/A
0N/A default:
0N/A return model.first();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Advance this state to a new state. An exception is thrown if the
0N/A * token is illegal at this point in the content model.
0N/A * @return next state after reducing a token
0N/A */
0N/A public ContentModelState advance(Object token) {
0N/A switch (model.type) {
0N/A case '+':
0N/A if (model.first(token)) {
0N/A return new ContentModelState(model.content,
0N/A new ContentModelState(model, next, value + 1)).advance(token);
0N/A }
0N/A if (value != 0) {
0N/A if (next != null) {
0N/A return next.advance(token);
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case '*':
0N/A if (model.first(token)) {
0N/A return new ContentModelState(model.content, this).advance(token);
0N/A }
0N/A if (next != null) {
0N/A return next.advance(token);
0N/A } else {
0N/A return null;
0N/A }
0N/A
0N/A case '?':
0N/A if (model.first(token)) {
0N/A return new ContentModelState(model.content, next).advance(token);
0N/A }
0N/A if (next != null) {
0N/A return next.advance(token);
0N/A } else {
0N/A return null;
0N/A }
0N/A
0N/A case '|':
0N/A for (ContentModel m = (ContentModel)model.content ; m != null ; m = m.next) {
0N/A if (m.first(token)) {
0N/A return new ContentModelState(m, next).advance(token);
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case ',': {
0N/A ContentModel m = (ContentModel)model.content;
0N/A for (int i = 0 ; i < value ; i++, m = m.next);
0N/A
0N/A if (m.first(token) || m.empty()) {
0N/A if (m.next == null) {
0N/A return new ContentModelState(m, next).advance(token);
0N/A } else {
0N/A return new ContentModelState(m,
0N/A new ContentModelState(model, next, value + 1)).advance(token);
0N/A }
0N/A }
0N/A break;
0N/A }
0N/A
0N/A case '&': {
0N/A ContentModel m = (ContentModel)model.content;
0N/A boolean complete = true;
0N/A
0N/A for (int i = 0 ; m != null ; i++, m = m.next) {
0N/A if ((value & (1L << i)) == 0) {
0N/A if (m.first(token)) {
0N/A return new ContentModelState(m,
0N/A new ContentModelState(model, next, value | (1L << i))).advance(token);
0N/A }
0N/A if (!m.empty()) {
0N/A complete = false;
0N/A }
0N/A }
0N/A }
0N/A if (complete) {
0N/A if (next != null) {
0N/A return next.advance(token);
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A break;
0N/A }
0N/A
0N/A default:
0N/A if (model.content == token) {
0N/A if (next == null && (token instanceof Element) &&
0N/A ((Element)token).content != null) {
0N/A return new ContentModelState(((Element)token).content);
0N/A }
0N/A return next;
0N/A }
0N/A // PENDING: Currently we don't correctly deal with optional start
0N/A // tags. This can most notably be seen with the 4.01 spec where
0N/A // TBODY's start and end tags are optional.
0N/A // Uncommenting this and the PENDING in ContentModel will
0N/A // correctly skip the omit tags, but the delegate is not notified.
0N/A // Some additional API needs to be added to track skipped tags,
0N/A // and this can then be added back.
0N/A/*
0N/A if ((model.content instanceof Element)) {
0N/A Element e = (Element)model.content;
0N/A
0N/A if (e.omitStart() && e.content != null) {
0N/A return new ContentModelState(e.content, next).advance(
0N/A token);
0N/A }
0N/A }
0N/A*/
0N/A }
0N/A
0N/A // We used to throw this exception at this point. However, it
0N/A // was determined that throwing this exception was more expensive
0N/A // than returning null, and we could not justify to ourselves why
0N/A // it was necessary to throw an exception, rather than simply
0N/A // returning null. I'm leaving it in a commented out state so
0N/A // that it can be easily restored if the situation ever arises.
0N/A //
0N/A // throw new IllegalArgumentException("invalid token: " + token);
0N/A return null;
0N/A }
0N/A}