/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* A simple DTD-driven HTML parser. The parser reads an
* HTML file from an InputStream and calls various methods
* (which should be overridden in a subclass) when tags and
* data are encountered.
* <p>
* Unfortunately there are many badly implemented HTML parsers
* out there, and as a result there are many badly formatted
* HTML files. This parser attempts to parse most HTML files.
* This means that the implementation sometimes deviates from
* the SGML specification in favor of HTML.
* <p>
* The parser treats \r and \r\n as \n. Newlines after starttags
* specification.
* <p>
* The html spec does not specify how spaces are to be coalesced very well.
* Specifically, the following scenarios are not discussed (note that a
* space should be used here, but I am using &nbsp to force the space to
* be displayed):
* <p>
* '<b>blah <i> <strike> foo' which can be treated as:
* '<b>blah <i><strike>foo'
* <p>as well as:
* '<p><a href="xx"> <em>Using</em></a></p>'
* which appears to be treated as:
* '<p><a href="xx"><em>Using</em></a></p>'
* <p>
* If <code>strict</code> is false, when a tag that breaks flow,
* (<code>TagElement.breaksFlows</code>) or trailing whitespace is
* encountered, all whitespace will be ignored until a non whitespace
* character is encountered. This appears to give behavior closer to
* the popular browsers.
*
* @see DTD
* @see TagElement
* @see SimpleAttributeSet
* @author Arthur van Hoff
* @author Sunita Mani
*/
public
private boolean space;
private int ch;
private int ln;
private boolean skipTag = false;
// State for <html>, <head> and <body>. Since people like to slap
// together HTML documents without thinking, occasionally they
// have multiple instances of these tags. These booleans track
// the first sightings of these tags so they can be safely ignored
// by the parser if repeated.
private boolean seenHtml = false;
private boolean seenHead = false;
private boolean seenBody = false;
/**
* The html spec does not specify how spaces are coalesced very well.
* If strict == false, ignoreSpace is used to try and mimic the behavior
* of the popular browsers.
* <p>
* The problematic scenarios are:
* '<b>blah <i> <strike> foo' which can be treated as:
* '<b>blah <i><strike>foo'
* as well as:
* '<p><a href="xx"> <em>Using</em></a></p>'
* which appears to be treated as:
* '<p><a href="xx"><em>Using</em></a></p>'
* <p>
* When a tag that breaks flow, or trailing whitespace is encountered
* ignoreSpace is set to true. From then on, all whitespace will be
* ignored.
* ignoreSpace will be set back to false the first time a
* non whitespace character is encountered. This appears to give
* behavior closer to the popular browsers.
*/
private boolean ignoreSpace;
/**
* This flag determines whether or not the Parser will be strict
* in enforcing SGML compatibility. If false, it will be lenient
* with certain common classes of erroneous HTML constructs.
* Strict or not, in either case an error will be recorded.
*
*/
protected boolean strict = false;
/** Number of \r\n's encountered. */
private int crlfCount;
/** Number of \r's encountered. A \r\n will not increment this. */
private int crCount;
/** Number of \n's encountered. A \r\n will not increment this. */
private int lfCount;
//
// ivars. Two are needed as handleText isn't invoked until the tag
// after the text has been parsed, that is the parser parses the text,
// then a tag, then invokes handleText followed by handleStart.
//
/** The start position of the current block. Block is overloaded here,
* it really means the current start position for the current comment,
* tag, text. Use getBlockStartPosition to access this. */
private int currentBlockStartPos;
/** Start position of the last block. */
private int lastBlockStartPos;
/**
* array for mapping numeric references in range
* 130-159 to displayable Unicode characters.
*/
private static final char[] cp1252Map = {
8218, // ‚
402, // ƒ
8222, // „
8230, // …
8224, // †
8225, // ‡
710, // ˆ
8240, // ‰
352, // Š
8249, // ‹
338, // Œ
141, // 
142, // Ž
143, // 
144, // 
8216, // ‘
8217, // ’
8220, // “
8221, // ”
8226, // •
8211, // –
8212, // —
732, // ˜
8482, // ™
353, // š
8250, // ›
339, // œ
157, // 
158, // ž
376 // Ÿ
};
}
/**
* @return the line number of the line currently being parsed
*/
protected int getCurrentLine() {
return ln;
}
/**
* Returns the start position of the current block. Block is
* overloaded here, it really means the current start position for
* the current comment tag, text, block.... This is provided for
* subclassers that wish to know the start of the current block when
* called with one of the handleXXX methods.
*/
int getBlockStartPosition() {
}
/**
* Makes a TagElement.
*/
}
}
return attributes;
}
protected void flushAttributes() {
}
/**
* Called when PCDATA is encountered.
*/
}
/**
* Called when an HTML title tag is encountered.
*/
// default behavior is to call handleText. Subclasses
// can override if necessary.
}
/**
* Called when an HTML comment is encountered.
*/
}
protected void handleEOFInComment() {
// We've reached EOF. Our recovery strategy is to
// see if we have more than one line in the comment;
// if so, we pretend that the comment was an unterminated
// single line comment, and reparse the lines after the
// first line as normal HTML content.
if (commentEndPos >= 0) {
try {
ch = '>';
} catch (IOException e) {
error("ioexception");
}
} else {
// no newline, so signal an error
error("eof.comment");
}
}
/**
* Called when an empty tag is encountered.
*/
}
/**
* Called when a start tag is encountered.
*/
}
/**
* Called when an end tag is encountered.
*/
}
/**
* An error has occurred.
*/
/*
Thread.dumpStack();
System.out.println("**** " + stack);
System.out.println("line " + ln + ": error: " + msg);
System.out.println();
*/
}
/**
* Output text.
*/
if (tag.breaksFlow()) {
space = false;
if (!strict) {
ignoreSpace = true;
}
}
if (textpos == 0) {
space = false;
return;
}
}
if (space) {
if (!ignoreSpace) {
// enlarge buffer if needed
}
// output pending space
ignoreSpace = true;
}
}
space = false;
}
// Handles cases of bad html where the title tag
// was getting lost when we did error recovery.
} else {
}
textpos = 0;
space = false;
}
/**
* Invoke the error handler.
*/
}
}
}
}
/**
* Handle a start tag. The new tag is pushed
* onto the tag stack. The attribute list is
* checked for required attributes.
*/
// If the tag is an empty tag and texpos != 0
// this implies that there is text before the
// start tag that needs to be processed before
// handling the tag.
//
(textpos != 0)) {
} else {
// this variable gets updated in handleText().
// Since in this case we do not call handleText()
// we need to update it here.
//
// Note that we should really check last.breakFlows before
// assuming this should be false.
space = false;
}
// check required attributes
((attributes.isEmpty()) ||
}
}
/*
} else if (elem.getName().equals("form")) {
handleStartTag(tag);
*/
} else {
}
}
/**
* Handle an end tag. The end tag is popped
* from the tag stack.
*/
}
// handle the tag
}
/* We ignore all elements that are not valid in the context of
a table except <td>, <th> (these we handle in
legalElementContext()) and #pcdata. We also ignore the
<font> tag in the context of <ul> and <ol> We additonally
ignore the <meta> and the <style> tag if the body tag has
been seen. **/
return true;
}
s = s.next;
}
if (s == null) {
return true;
}
}
return true;
}
return false;
}
/**
* Marks the first time a tag has been seen in a document
*/
seenHtml = true;
seenHead = true;
// Refer to note in definition of buf for details on this.
char[] newBuf = new char[256];
}
seenBody = true;
}
}
/**
* Create a legal content for an element.
*/
// System.out.println("-- legalContext -- " + elem);
// Deal with the empty stack
// System.out.println("-- stack is empty");
// System.out.println("-- pushing html");
return legalElementContext(elem);
}
return true;
}
// Is it allowed in the current context
// System.out.println("-- legal context");
return true;
}
boolean insertTag = false;
// The use of all error recovery strategies are contingent
// on the value of the strict property.
//
// These are commonly occuring errors. if insertTag is true,
// then we want to adopt an error recovery strategy that
// involves attempting to insert an additional tag to
// legalize the context. The two errors addressed here
// are:
// 1) when a <td> or <th> is seen soon after a <table> tag.
// In this case we insert a <tr>.
// 2) when any other tag apart from a <tr> is seen
// in the context of a <tr>. In this case we would
// like to add a <td>. If a <tr> is seen within a
// <tr> context, then we will close out the current
// <tr>.
//
// This insertion strategy is handled later in the method.
// The reason for checking this now, is that in other cases
// we would like to apply other error recovery strategies for example
// ignoring tags.
//
// In certain cases it is better to ignore a tag than try to
// fix the situation. So the first test is to see if this
// is what we need to do.
//
if (!strict &&
insertTag = true;
}
return skipTag;
}
}
// Check for anything after the start of the table besides tr, td, th
// or caption, and if those aren't there, insert the <tr> and call
// legalElementContext again.
TagElement t = makeTag(e, true);
legalTagContext(t);
startTag(t);
return legalElementContext(elem);
}
// They try to find a legal context by checking if the current
// tag is valid in an enclosing context. If so
// close out the tags by outputing end tags and then
// insert the curent tag. If the tags that are
// being closed out do not have an optional end tag
// specification in the DTD then an html error is
// reported.
//
while (stack != s) {
endTag(true);
}
return true;
}
break;
}
}
}
// Check if we know what tag is expected next.
// If so insert the tag. Report an error if the
// tag does not have its start tag spec in the DTD as optional.
//
// System.out.println("-- omitting start tag: " + next);
legalTagContext(t);
startTag(t);
}
return legalElementContext(elem);
}
// Traverse the list of expected elements and determine if adding
// any of these elements would make for a legal context.
//
if (!strict) {
// Ensure that this element has not been included as
// part of the exclusions in the DTD.
//
continue;
}
boolean reqAtts = false;
reqAtts = true;
break;
}
}
// Ensure that no tag that has required attributes
// gets inserted.
//
if (reqAtts) {
continue;
}
ContentModel m = e.getContent();
// System.out.println("-- adding a legal tag: " + e);
TagElement t = makeTag(e, true);
legalTagContext(t);
startTag(t);
return legalElementContext(elem);
}
}
}
}
// Check if the stack can be terminated. If so add the appropriate
// end tag. Report an error if the tag being ended does not have its
// end tag spec in the DTD as optional.
//
// System.out.println("-- omitting end tag: " + stack.elem);
}
endTag(true);
return legalElementContext(elem);
}
// At this point we know that something is screwed up.
return false;
}
/**
* Create a legal context for a tag.
*/
return;
}
// Avoid putting a block tag in a flow tag.
endTag(true);
return;
}
// Avoid putting something wierd in the head of the document.
while (stack != s) {
endTag(true);
}
endTag(true);
return;
}
}
// Everything failed
}
/**
* Error context. Something went wrong, make sure we are in
* the document's body context
*/
}
}
}
/**
* Add a char to the string buffer.
*/
void addString(int c) {
}
}
/**
* Get the string that's been accumulated.
*/
}
return newStr;
}
// REMIND: it's not clear whether this version should set strpos or not
// strpos = pos;
return newStr;
}
void resetStrBuffer() {
strpos = 0;
}
for (int i = 0; i < strpos; i++) {
return i;
}
}
return -1;
}
/**
* Skip space.
* [5] 297:5
*/
while (true) {
switch (ch) {
case '\n':
ln++;
lfCount++;
break;
case '\r':
ln++;
crlfCount++;
}
else {
crCount++;
}
break;
case ' ':
case '\t':
break;
default:
return;
}
}
}
/**
* Parse identifier. Uppercase characters are folded
* to lowercase when lower is true. Returns falsed if
* no identifier is found. [55] 346:17
*/
switch (ch) {
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
case 'Y': case 'Z':
if (lower) {
}
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
case 's': case 't': case 'u': case 'v': case 'w': case 'x':
case 'y': case 'z':
break;
default:
return false;
}
while (true) {
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
case 'Y': case 'Z':
if (lower) {
}
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
case 's': case 't': case 'u': case 'v': case 'w': case 'x':
case 'y': case 'z':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.': case '-':
case '_': // not officially allowed
break;
default:
return true;
}
}
}
/**
* Parse an entity reference. [59] 350:17
*/
int n = 0;
// parse decimal reference
}
} else {
// parse hexadecimal reference
} else {
}
}
}
switch (ch) {
case '\n':
ln++;
lfCount++;
break;
case '\r':
ln++;
crlfCount++;
}
else {
crCount++;
}
break;
case ';':
break;
}
char data[] = mapNumericReference(n);
return data;
}
addString('#');
if (!parseIdentifier(false)) {
error("ident.expected");
return data;
}
} else if (!parseIdentifier(false)) {
char data[] = {'&'};
return data;
}
boolean semicolon = false;
switch (ch) {
case '\n':
ln++;
lfCount++;
break;
case '\r':
ln++;
crlfCount++;
}
else {
crCount++;
}
break;
case ';':
semicolon = true;
break;
}
// entities are case sensitive - however if strict
// is false then we will try to make a match by
// converting the string to all lowercase.
//
}
return new char[0];
}
/* given that there is not a match restore the entity reference */
return b;
}
}
/**
* Converts numeric character reference to char array.
*
* Normally the code in a reference should be always converted
* to the Unicode character with the same code, but due to
* wide usage of Cp1252 charset most browsers map numeric references
* in the range 130-159 (which are control chars in Unicode set)
* to displayable characters with other codes.
*
* @param c the code of numeric character reference.
* @return a char array corresponding to the reference code.
*/
private char[] mapNumericReference(int c) {
char[] data;
if (c >= 0xffff) { // outside unicode BMP.
try {
} catch (IllegalArgumentException e) {
data = new char[0];
}
} else {
data = new char[1];
}
return data;
}
/**
* Parse a comment. [92] 391:7
*/
while (true) {
int c = ch;
switch (c) {
case '-':
/** Presuming that the start string of a comment "<!--" has
already been parsed, the '-' character is valid only as
part of a comment termination and further more it must
be present in even numbers. Hence if strict is true, we
presume the comment has been terminated and return.
However if strict is false, then there is no even number
requirement and this character can appear anywhere in the
comment. The parser reads on until it sees the following
pattern: "-->" or "--!>".
**/
return;
}
if (ch == '!') {
return;
} else {
/* to account for extra read()'s that happened */
addString('-');
addString('!');
continue;
}
}
break;
}
return;
}
if (ch == '!') {
return;
} else {
/* to account for extra read()'s that happened */
addString('-');
addString('!');
continue;
}
}
/* to account for the extra read() */
addString('-');
}
break;
case -1:
return;
case '\n':
ln++;
lfCount++;
break;
case '>':
break;
case '\r':
ln++;
crlfCount++;
}
else {
crCount++;
}
c = '\n';
break;
default:
break;
}
addString(c);
}
}
/**
* Parse literal content. [46] 343:1 and [47] 344:1
*/
while (true) {
int c = ch;
switch (c) {
case -1:
endTag(true);
return;
case '>':
// match end tag
while ((++i < textpos) &&
if (i == textpos) {
textpos--;
}
endTag(false);
return;
}
}
break;
case '&':
char data[] = parseEntityReference();
}
continue;
case '\n':
ln++;
lfCount++;
break;
case '\r':
ln++;
crlfCount++;
}
else {
crCount++;
}
c = '\n';
break;
default:
break;
}
// output character
}
}
}
/**
* Parse attribute value. [33] 331:1
*/
int delim = -1;
// Check for a delimiter
switch(ch) {
case '\'':
case '"':
break;
}
// Parse the rest of the value
while (true) {
int c = ch;
switch (c) {
case '\n':
ln++;
lfCount++;
if (delim < 0) {
return getString(0);
}
break;
case '\r':
ln++;
crlfCount++;
}
else {
crCount++;
}
if (delim < 0) {
return getString(0);
}
break;
case '\t':
if (delim < 0)
c = ' ';
case ' ':
if (delim < 0) {
return getString(0);
}
break;
case '>':
case '<':
if (delim < 0) {
return getString(0);
}
break;
case '\'':
case '"':
if (c == delim) {
return getString(0);
} else if (delim == -1) {
error("attvalerr");
return getString(0);
} else {
continue;
}
}
break;
case '=':
if (delim < 0) {
is considered invalid since an = sign can only be contained
in an attributes value if the string is quoted.
*/
error("attvalerr");
/* If strict is true then we return with the string we have thus far.
Otherwise we accept the = sign as part of the attribute's value and
process the rest of the img tag. */
if (strict) {
return getString(0);
}
}
break;
case '&':
break;
}
char data[] = parseEntityReference();
c = data[i];
}
continue;
case -1:
return getString(0);
default:
c = 'a' + c - 'A';
}
break;
}
addString(c);
}
}
/**
* Parse attribute specification List. [31] 327:17
*/
while (true) {
skipSpace();
switch (ch) {
case '/':
case '>':
case '<':
case -1:
return;
case '-':
parseComment();
strpos = 0;
} else {
}
continue;
}
if (parseIdentifier(true)) {
skipSpace();
if (ch == '=') {
skipSpace();
// Bug ID 4102750
// Load the NAME of an Attribute Case Sensitive
// The case of the NAME must be intact
// MG 021898
attvalue = parseAttributeValue((att != null) && (att.type != CDATA) && (att.type != NOTATION) && (att.type != NAME));
// attvalue = parseAttributeValue((att != null) && (att.type != CDATA) && (att.type != NOTATION));
} else {
}
else {
// Make it null so that NULL_ATTRIBUTE_VALUE is
// used
}
}
}
continue;
skipSpace();
if (parseIdentifier(true)) {
if (ch == '"') {
}
skipSpace();
if (ch == '=') {
skipSpace();
} else {
}
}
}
} else {
continue;
}
skipSpace();
skipSpace();
attvalue = parseAttributeValue(true);
error("attvalerr");
return;
} else {
if (!strict) {
continue;
} else {
return;
}
}
} else {
}
// Check out the value
}
}
} else {
}
}
}
/**
* Parses th Document Declaration Type markup declaration.
* Currently ignores it.
*/
while(true) {
switch (ch) {
case '>':
case -1:
error("invalid.markup");
case '\n':
ln++;
lfCount++;
break;
case '"':
break;
case '\r':
ln++;
crlfCount++;
}
else {
crCount++;
}
break;
default:
break;
}
}
}
/**
* Parse markup declarations.
* Currently only handles the Document Type Declaration markup.
* Returns true if it is a markup declaration false otherwise.
*/
/* Currently handles only the DOCTYPE */
return true;
}
return false;
}
/**
* Parse an invalid tag.
*/
// ignore all data upto the close bracket '>'
while (true) {
skipSpace();
switch (ch) {
case '>':
case -1:
return;
case '<':
return;
default:
}
}
}
/**
* Parse a start or end tag.
*/
boolean net = false;
boolean warned = false;
boolean unknown = false;
case '!':
case '-':
// Parse comment. [92] 391:7
while (true) {
if (ch == '-') {
}
// send over any text you might see
// before parsing and sending the
// comment
if (textpos != 0) {
textpos = 0;
}
parseComment();
continue;
} else if (!warned) {
warned = true;
}
}
skipSpace();
switch (ch) {
case '-':
continue;
case '>':
case -1:
return;
default:
if (!warned) {
warned = true;
error("invalid.commentchar",
}
break;
}
}
default:
// deal with marked sections
while (true) {
if (parseMarkupDeclarations(strBuff)) {
return;
}
switch(ch) {
case '>':
case -1:
error("invalid.markup");
return;
case '\n':
ln++;
lfCount++;
break;
case '\r':
ln++;
crlfCount++;
}
else {
crCount++;
}
break;
default:
break;
}
}
}
case '/':
// parse end tag [19] 317:4
case '>':
case '<':
// empty end tag. either </> or </<
error("invalid.shortend");
return;
}
break;
default:
if (!parseIdentifier(true)) {
error("expected.endtagname");
return;
}
skipSpace();
switch (ch) {
case '>':
case '<':
break;
default:
}
if (ch == '>') {
}
break;
}
// Ignore RE before end tag
textpos--;
}
unknown = true;
} else {
}
break;
}
// If the stack is null, we're seeing end tags without any begin
// tags. Ignore them.
return;
}
// Ignore RE before end tag
// In a pre tag, if there are blank lines
// we do not want to remove the newline
// before the end tag. Hence this code.
//
textpos--;
}
} else {
textpos--;
}
}
// If the end tag is a form, since we did not put it
// on the tag stack, there is no corresponding start
// start tag to find. Hence do not touch the tag stack.
//
/*
if (!strict && elem.getName().equals("form")) {
if (lastFormSent != null) {
handleEndTag(lastFormSent);
return;
} else {
// do nothing.
return;
}
}
*/
if (unknown) {
// we will not see a corresponding start tag
// on the the stack. If we are seeing an
// end tag, lets send this on as an empty
// tag with the end tag attribute set to
// true.
handleText(t);
unknown = false;
return;
}
// find the corresponding start tag
// A commonly occuring error appears to be the insertion
// of extra end tags in a table. The intent here is ignore
// such extra end tags.
//
if (!strict) {
// If it isnt a valid end tag ignore it and return
//
return;
}
}
return;
}
}
}
}
return;
}
// People put font ending tags in the darndest places.
// Don't close other contexts based on them being between
// a font tag and the corresponding end tag. Instead,
// ignore the end tag like it doesn't exist and allow the end
// of the document to close us out.
// Since closing out a center tag can have real wierd
// effects on the formatting, make sure that tags
// for which omitting an end tag is legimitate
// get closed out.
//
endTag(true);
}
endTag(false);
}
}
return;
}
// People do the same thing with center tags. In this
// case we would like to close off the center tag but
// not necessarily all enclosing tags.
// end tags
endTag(true);
}
endTag(false);
return;
case -1:
error("eof");
return;
}
// start tag [14] 314:1
if (!parseIdentifier(true)) {
error("expected.tagname");
return;
}
} else {
elemStr = "img";
}
/* determine if this element is part of the dtd. */
// parseInvalidTag();
unknown = true;
} else {
}
}
// Parse attributes
switch (ch) {
case '/':
net = true;
case '>':
}
case '<':
break;
default:
break;
}
if (!strict) {
error("javascript.unsupported");
}
}
// ignore RE after start tag
//
if (ch == '\n') {
ln++;
lfCount++;
} else if (ch == '\r') {
ln++;
crlfCount++;
}
else {
crCount++;
}
}
}
// ensure a legal context for the tag
/** In dealing with forms, we have decided to treat
them as legal in any context. Also, even though
they do have a start and an end tag, we will
not put this tag on the stack. This is to deal
several pages in the web oasis that choose to
start and end forms in any possible location. **/
/*
if (!strict && elem.getName().equals("form")) {
if (lastFormSent == null) {
lastFormSent = tag;
} else {
handleEndTag(lastFormSent);
lastFormSent = tag;
}
} else {
*/
// Smlly, if a tag is unknown, we will apply
// no legalTagContext logic to it.
//
if (!unknown) {
// If skip tag is true, this implies that
// the tag was illegal and that the error
// recovery strategy adopted is to ignore
// the tag.
skipTag = false;
return;
}
}
/*
}
*/
case CDATA:
parseLiteral(false);
break;
case RCDATA:
parseLiteral(true);
break;
default:
}
break;
}
}
}
private static final char[] SCRIPT_END_TAG_UPPER_CASE =
"</SCRIPT>".toCharArray();
/* Here, ch should be the first character after <script> */
while (true) {
int i = 0;
while (i < SCRIPT_END_TAG.length
&& (SCRIPT_END_TAG[i] == ch
|| SCRIPT_END_TAG_UPPER_CASE[i] == ch)) {
charsToAdd[i] = (char) ch;
i++;
}
if (i == SCRIPT_END_TAG.length) {
/* '</script>' tag detected */
/* Here, ch == the first character after </script> */
return;
} else {
/* To account for extra read()'s that happened */
for (int j = 0; j < i; j++) {
addString(charsToAdd[j]);
}
switch (ch) {
case -1:
error("eof.script");
return;
case '\n':
ln++;
lfCount++;
addString('\n');
break;
case '\r':
ln++;
crlfCount++;
} else {
crCount++;
}
addString('\n');
break;
default:
break;
} // switch
}
} // while
}
/**
* Parse Content. [24] 320:1
*/
for (;;) {
if (curThread.isInterrupted()) {
break;
}
int c = ch;
/* Here, ch has to be the first character after <script> */
parseScript();
/* Remove leading and trailing HTML comment declarations */
}
/* Handle resulting chars as comment */
endTag(false);
continue;
} else {
switch (c) {
case '<':
parseTag();
continue;
case '/':
// null end tag.
endTag(false);
continue;
}
break;
case -1:
return;
case '&':
if (textpos == 0) {
error("unexpected.pcdata");
}
if (last.breaksFlow()) {
space = false;
}
}
char data[] = parseEntityReference();
}
if (space) {
space = false;
}
ignoreSpace = false;
continue;
case '\n':
ln++;
lfCount++;
break;
}
if (textpos == 0) {
}
if (!ignoreSpace) {
space = true;
}
continue;
case '\r':
ln++;
c = '\n';
crlfCount++;
}
else {
crCount++;
}
break;
}
if (textpos == 0) {
}
if (!ignoreSpace) {
space = true;
}
continue;
case '\t':
case ' ':
break;
}
if (textpos == 0) {
}
if (!ignoreSpace) {
space = true;
}
continue;
default:
if (textpos == 0) {
error("unexpected.pcdata");
}
if (last.breaksFlow()) {
space = false;
}
}
break;
}
}
// enlarge buffer if needed
}
// output pending space
if (space) {
if (textpos == 0) {
}
space = false;
}
ignoreSpace = false;
}
}
/**
* Returns the end of line string. This will return the end of line
* string that has been encountered the most, one of \r, \n or \r\n.
*/
return "\n";
}
else {
return "\r\n";
}
}
else {
return "\r";
}
else {
return "\n";
}
}
}
/**
* Parse an HTML stream, given a DTD.
*/
this.ln = 1;
seenHtml = false;
seenHead = false;
seenBody = false;
try {
text = new char[1024];
str = new char[128];
parseContent();
// NOTE: interruption may have occurred. Control flows out
// of here normally.
endTag(true);
}
} catch (IOException e) {
errorContext();
error("ioexception");
throw e;
} catch (Exception e) {
errorContext();
e.printStackTrace();
} catch (ThreadDeath e) {
errorContext();
error("terminated");
e.printStackTrace();
throw e;
} finally {
}
}
}
/*
* Input cache. This is much faster than calling down to a synchronized
* method of BufferedReader for each byte. Measurements done 5/30/97
* show that there's no point in having a bigger buffer: Increasing
* the buffer to 8192 had no measurable impact for a program discarding
* one character at a time (reading from an http URL to a local machine).
* NOTE: If the current encoding is bogus, and we read too much
* (past the content-type) we may suffer a MalformedInputException. For
* this reason the initial size is 1 and when the body is encountered the
* size is adjusted to 256.
*/
private int pos;
private int len;
/*
tracks position relative to the beginning of the
document.
*/
private int currentPosition;
// This loop allows us to ignore interrupts if the flag
// says so
for (;;) {
try {
break;
} catch (InterruptedIOException ex) {
throw ex;
}
}
if (len <= 0) {
return -1; // eof
}
pos = 0;
}
}
protected int getCurrentPos() {
return currentPosition;
}
}