mirror of https://github.com/roytam1/UXP
11 changed files with 0 additions and 20001 deletions
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,618 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2007 Henri Sivonen |
||||
* Copyright (c) 2008-2011 Mozilla Foundation |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a |
||||
* copy of this software and associated documentation files (the "Software"), |
||||
* to deal in the Software without restriction, including without limitation |
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
* and/or sell copies of the Software, and to permit persons to whom the |
||||
* Software is furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in |
||||
* all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
* DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
package nu.validator.htmlparser.impl; |
||||
|
||||
import nu.validator.htmlparser.annotation.Auto; |
||||
import nu.validator.htmlparser.annotation.IdType; |
||||
import nu.validator.htmlparser.annotation.Local; |
||||
import nu.validator.htmlparser.annotation.NsUri; |
||||
import nu.validator.htmlparser.annotation.Prefix; |
||||
import nu.validator.htmlparser.annotation.QName; |
||||
import nu.validator.htmlparser.common.Interner; |
||||
import nu.validator.htmlparser.common.XmlViolationPolicy; |
||||
|
||||
import org.xml.sax.Attributes; |
||||
import org.xml.sax.SAXException; |
||||
|
||||
/** |
||||
* Be careful with this class. QName is the name in from HTML tokenization. |
||||
* Otherwise, please refer to the interface doc. |
||||
* |
||||
* @version $Id: AttributesImpl.java 206 2008-03-20 14:09:29Z hsivonen $ |
||||
* @author hsivonen |
||||
*/ |
||||
public final class HtmlAttributes implements Attributes { |
||||
|
||||
// [NOCPP[
|
||||
|
||||
private static final AttributeName[] EMPTY_ATTRIBUTENAMES = new AttributeName[0]; |
||||
|
||||
private static final String[] EMPTY_STRINGS = new String[0]; |
||||
|
||||
// ]NOCPP]
|
||||
|
||||
public static final HtmlAttributes EMPTY_ATTRIBUTES = new HtmlAttributes( |
||||
AttributeName.HTML); |
||||
|
||||
private int mode; |
||||
|
||||
private int length; |
||||
|
||||
private @Auto AttributeName[] names; |
||||
|
||||
private @Auto String[] values; // XXX perhaps make this @NoLength?
|
||||
|
||||
// CPPONLY: private @Auto int[] lines; // XXX perhaps make this @NoLength?
|
||||
|
||||
// [NOCPP[
|
||||
|
||||
private String idValue; |
||||
|
||||
private int xmlnsLength; |
||||
|
||||
private AttributeName[] xmlnsNames; |
||||
|
||||
private String[] xmlnsValues; |
||||
|
||||
// ]NOCPP]
|
||||
|
||||
public HtmlAttributes(int mode) { |
||||
this.mode = mode; |
||||
this.length = 0; |
||||
/* |
||||
* The length of 5 covers covers 98.3% of elements |
||||
* according to Hixie, but lets round to the next power of two for |
||||
* jemalloc. |
||||
*/ |
||||
this.names = new AttributeName[8]; |
||||
this.values = new String[8]; |
||||
// CPPONLY: this.lines = new int[8];
|
||||
|
||||
// [NOCPP[
|
||||
|
||||
this.idValue = null; |
||||
|
||||
this.xmlnsLength = 0; |
||||
|
||||
this.xmlnsNames = HtmlAttributes.EMPTY_ATTRIBUTENAMES; |
||||
|
||||
this.xmlnsValues = HtmlAttributes.EMPTY_STRINGS; |
||||
|
||||
// ]NOCPP]
|
||||
} |
||||
/* |
||||
public HtmlAttributes(HtmlAttributes other) { |
||||
this.mode = other.mode; |
||||
this.length = other.length; |
||||
this.names = new AttributeName[other.length]; |
||||
this.values = new String[other.length]; |
||||
// [NOCPP[
|
||||
this.idValue = other.idValue; |
||||
this.xmlnsLength = other.xmlnsLength; |
||||
this.xmlnsNames = new AttributeName[other.xmlnsLength]; |
||||
this.xmlnsValues = new String[other.xmlnsLength]; |
||||
// ]NOCPP]
|
||||
} |
||||
*/ |
||||
|
||||
void destructor() { |
||||
clear(0); |
||||
} |
||||
|
||||
/** |
||||
* Only use with a static argument |
||||
* |
||||
* @param name |
||||
* @return |
||||
*/ |
||||
public int getIndex(AttributeName name) { |
||||
for (int i = 0; i < length; i++) { |
||||
if (names[i] == name) { |
||||
return i; |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
/** |
||||
* Only use with static argument. |
||||
* |
||||
* @see org.xml.sax.Attributes#getValue(java.lang.String) |
||||
*/ |
||||
public String getValue(AttributeName name) { |
||||
int index = getIndex(name); |
||||
if (index == -1) { |
||||
return null; |
||||
} else { |
||||
return getValueNoBoundsCheck(index); |
||||
} |
||||
} |
||||
|
||||
public int getLength() { |
||||
return length; |
||||
} |
||||
|
||||
/** |
||||
* Variant of <code>getLocalName(int index)</code> without bounds check. |
||||
* @param index a valid attribute index |
||||
* @return the local name at index |
||||
*/ |
||||
public @Local String getLocalNameNoBoundsCheck(int index) { |
||||
// CPPONLY: assert index < length && index >= 0: "Index out of bounds";
|
||||
return names[index].getLocal(mode); |
||||
} |
||||
|
||||
/** |
||||
* Variant of <code>getURI(int index)</code> without bounds check. |
||||
* @param index a valid attribute index |
||||
* @return the namespace URI at index |
||||
*/ |
||||
public @NsUri String getURINoBoundsCheck(int index) { |
||||
// CPPONLY: assert index < length && index >= 0: "Index out of bounds";
|
||||
return names[index].getUri(mode); |
||||
} |
||||
|
||||
/** |
||||
* Variant of <code>getPrefix(int index)</code> without bounds check. |
||||
* @param index a valid attribute index |
||||
* @return the namespace prefix at index |
||||
*/ |
||||
public @Prefix String getPrefixNoBoundsCheck(int index) { |
||||
// CPPONLY: assert index < length && index >= 0: "Index out of bounds";
|
||||
return names[index].getPrefix(mode); |
||||
} |
||||
|
||||
/** |
||||
* Variant of <code>getValue(int index)</code> without bounds check. |
||||
* @param index a valid attribute index |
||||
* @return the attribute value at index |
||||
*/ |
||||
public String getValueNoBoundsCheck(int index) { |
||||
// CPPONLY: assert index < length && index >= 0: "Index out of bounds";
|
||||
return values[index]; |
||||
} |
||||
|
||||
/** |
||||
* Variant of <code>getAttributeName(int index)</code> without bounds check. |
||||
* @param index a valid attribute index |
||||
* @return the attribute name at index |
||||
*/ |
||||
public AttributeName getAttributeNameNoBoundsCheck(int index) { |
||||
// CPPONLY: assert index < length && index >= 0: "Index out of bounds";
|
||||
return names[index]; |
||||
} |
||||
|
||||
// CPPONLY: /**
|
||||
// CPPONLY: * Obtains a line number without bounds check.
|
||||
// CPPONLY: * @param index a valid attribute index
|
||||
// CPPONLY: * @return the line number at index or -1 if unknown
|
||||
// CPPONLY: */
|
||||
// CPPONLY: public int getLineNoBoundsCheck(int index) {
|
||||
// CPPONLY: assert index < length && index >= 0: "Index out of bounds";
|
||||
// CPPONLY: return lines[index];
|
||||
// CPPONLY: }
|
||||
|
||||
// [NOCPP[
|
||||
|
||||
/** |
||||
* Variant of <code>getQName(int index)</code> without bounds check. |
||||
* @param index a valid attribute index |
||||
* @return the QName at index |
||||
*/ |
||||
public @QName String getQNameNoBoundsCheck(int index) { |
||||
return names[index].getQName(mode); |
||||
} |
||||
|
||||
/** |
||||
* Variant of <code>getType(int index)</code> without bounds check. |
||||
* @param index a valid attribute index |
||||
* @return the attribute type at index |
||||
*/ |
||||
public @IdType String getTypeNoBoundsCheck(int index) { |
||||
return (names[index] == AttributeName.ID) ? "ID" : "CDATA"; |
||||
} |
||||
|
||||
public int getIndex(String qName) { |
||||
for (int i = 0; i < length; i++) { |
||||
if (names[i].getQName(mode).equals(qName)) { |
||||
return i; |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
public int getIndex(String uri, String localName) { |
||||
for (int i = 0; i < length; i++) { |
||||
if (names[i].getLocal(mode).equals(localName) |
||||
&& names[i].getUri(mode).equals(uri)) { |
||||
return i; |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
public @IdType String getType(String qName) { |
||||
int index = getIndex(qName); |
||||
if (index == -1) { |
||||
return null; |
||||
} else { |
||||
return getType(index); |
||||
} |
||||
} |
||||
|
||||
public @IdType String getType(String uri, String localName) { |
||||
int index = getIndex(uri, localName); |
||||
if (index == -1) { |
||||
return null; |
||||
} else { |
||||
return getType(index); |
||||
} |
||||
} |
||||
|
||||
public String getValue(String qName) { |
||||
int index = getIndex(qName); |
||||
if (index == -1) { |
||||
return null; |
||||
} else { |
||||
return getValue(index); |
||||
} |
||||
} |
||||
|
||||
public String getValue(String uri, String localName) { |
||||
int index = getIndex(uri, localName); |
||||
if (index == -1) { |
||||
return null; |
||||
} else { |
||||
return getValue(index); |
||||
} |
||||
} |
||||
|
||||
public @Local String getLocalName(int index) { |
||||
if (index < length && index >= 0) { |
||||
return names[index].getLocal(mode); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public @QName String getQName(int index) { |
||||
if (index < length && index >= 0) { |
||||
return names[index].getQName(mode); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public @IdType String getType(int index) { |
||||
if (index < length && index >= 0) { |
||||
return (names[index] == AttributeName.ID) ? "ID" : "CDATA"; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public AttributeName getAttributeName(int index) { |
||||
if (index < length && index >= 0) { |
||||
return names[index]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public @NsUri String getURI(int index) { |
||||
if (index < length && index >= 0) { |
||||
return names[index].getUri(mode); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public @Prefix String getPrefix(int index) { |
||||
if (index < length && index >= 0) { |
||||
return names[index].getPrefix(mode); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public String getValue(int index) { |
||||
if (index < length && index >= 0) { |
||||
return values[index]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public String getId() { |
||||
return idValue; |
||||
} |
||||
|
||||
public int getXmlnsLength() { |
||||
return xmlnsLength; |
||||
} |
||||
|
||||
public @Local String getXmlnsLocalName(int index) { |
||||
if (index < xmlnsLength && index >= 0) { |
||||
return xmlnsNames[index].getLocal(mode); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public @NsUri String getXmlnsURI(int index) { |
||||
if (index < xmlnsLength && index >= 0) { |
||||
return xmlnsNames[index].getUri(mode); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public String getXmlnsValue(int index) { |
||||
if (index < xmlnsLength && index >= 0) { |
||||
return xmlnsValues[index]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public int getXmlnsIndex(AttributeName name) { |
||||
for (int i = 0; i < xmlnsLength; i++) { |
||||
if (xmlnsNames[i] == name) { |
||||
return i; |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
public String getXmlnsValue(AttributeName name) { |
||||
int index = getXmlnsIndex(name); |
||||
if (index == -1) { |
||||
return null; |
||||
} else { |
||||
return getXmlnsValue(index); |
||||
} |
||||
} |
||||
|
||||
public AttributeName getXmlnsAttributeName(int index) { |
||||
if (index < xmlnsLength && index >= 0) { |
||||
return xmlnsNames[index]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
// ]NOCPP]
|
||||
|
||||
void addAttribute(AttributeName name, String value |
||||
// [NOCPP[
|
||||
, XmlViolationPolicy xmlnsPolicy |
||||
// ]NOCPP]
|
||||
// CPPONLY: , int line
|
||||
) throws SAXException { |
||||
// [NOCPP[
|
||||
if (name == AttributeName.ID) { |
||||
idValue = value; |
||||
} |
||||
|
||||
if (name.isXmlns()) { |
||||
if (xmlnsNames.length == xmlnsLength) { |
||||
int newLen = xmlnsLength == 0 ? 2 : xmlnsLength << 1; |
||||
AttributeName[] newNames = new AttributeName[newLen]; |
||||
System.arraycopy(xmlnsNames, 0, newNames, 0, xmlnsNames.length); |
||||
xmlnsNames = newNames; |
||||
String[] newValues = new String[newLen]; |
||||
System.arraycopy(xmlnsValues, 0, newValues, 0, xmlnsValues.length); |
||||
xmlnsValues = newValues; |
||||
} |
||||
xmlnsNames[xmlnsLength] = name; |
||||
xmlnsValues[xmlnsLength] = value; |
||||
xmlnsLength++; |
||||
switch (xmlnsPolicy) { |
||||
case FATAL: |
||||
// this is ugly
|
||||
throw new SAXException("Saw an xmlns attribute."); |
||||
case ALTER_INFOSET: |
||||
return; |
||||
case ALLOW: |
||||
// fall through
|
||||
} |
||||
} |
||||
|
||||
// ]NOCPP]
|
||||
|
||||
if (names.length == length) { |
||||
int newLen = length << 1; // The first growth covers virtually
|
||||
// 100% of elements according to
|
||||
// Hixie
|
||||
AttributeName[] newNames = new AttributeName[newLen]; |
||||
System.arraycopy(names, 0, newNames, 0, names.length); |
||||
names = newNames; |
||||
String[] newValues = new String[newLen]; |
||||
System.arraycopy(values, 0, newValues, 0, values.length); |
||||
values = newValues; |
||||
// CPPONLY: int[] newLines = new int[newLen];
|
||||
// CPPONLY: System.arraycopy(lines, 0, newLines, 0, lines.length);
|
||||
// CPPONLY: lines = newLines;
|
||||
} |
||||
names[length] = name; |
||||
values[length] = value; |
||||
// CPPONLY: lines[length] = line;
|
||||
length++; |
||||
} |
||||
|
||||
void clear(int m) { |
||||
for (int i = 0; i < length; i++) { |
||||
names[i].release(); |
||||
names[i] = null; |
||||
Portability.releaseString(values[i]); |
||||
values[i] = null; |
||||
} |
||||
length = 0; |
||||
mode = m; |
||||
// [NOCPP[
|
||||
idValue = null; |
||||
for (int i = 0; i < xmlnsLength; i++) { |
||||
xmlnsNames[i] = null; |
||||
xmlnsValues[i] = null; |
||||
} |
||||
xmlnsLength = 0; |
||||
// ]NOCPP]
|
||||
} |
||||
|
||||
/** |
||||
* This is used in C++ to release special <code>isindex</code> |
||||
* attribute values whose ownership is not transferred. |
||||
*/ |
||||
void releaseValue(int i) { |
||||
Portability.releaseString(values[i]); |
||||
} |
||||
|
||||
/** |
||||
* This is only used for <code>AttributeName</code> ownership transfer |
||||
* in the isindex case to avoid freeing custom names twice in C++. |
||||
*/ |
||||
void clearWithoutReleasingContents() { |
||||
for (int i = 0; i < length; i++) { |
||||
names[i] = null; |
||||
values[i] = null; |
||||
} |
||||
length = 0; |
||||
} |
||||
|
||||
boolean contains(AttributeName name) { |
||||
for (int i = 0; i < length; i++) { |
||||
if (name.equalsAnother(names[i])) { |
||||
return true; |
||||
} |
||||
} |
||||
// [NOCPP[
|
||||
for (int i = 0; i < xmlnsLength; i++) { |
||||
if (name.equalsAnother(xmlnsNames[i])) { |
||||
return true; |
||||
} |
||||
} |
||||
// ]NOCPP]
|
||||
return false; |
||||
} |
||||
|
||||
public void adjustForMath() { |
||||
mode = AttributeName.MATHML; |
||||
} |
||||
|
||||
public void adjustForSvg() { |
||||
mode = AttributeName.SVG; |
||||
} |
||||
|
||||
public HtmlAttributes cloneAttributes(Interner interner) |
||||
throws SAXException { |
||||
assert (length == 0 |
||||
// [NOCPP[
|
||||
&& xmlnsLength == 0 |
||||
// ]NOCPP]
|
||||
) |
||||
|| mode == 0 || mode == 3; |
||||
HtmlAttributes clone = new HtmlAttributes(0); |
||||
for (int i = 0; i < length; i++) { |
||||
clone.addAttribute(names[i].cloneAttributeName(interner), |
||||
Portability.newStringFromString(values[i]) |
||||
// [NOCPP[
|
||||
, XmlViolationPolicy.ALLOW |
||||
// ]NOCPP]
|
||||
// CPPONLY: , lines[i]
|
||||
); |
||||
} |
||||
// [NOCPP[
|
||||
for (int i = 0; i < xmlnsLength; i++) { |
||||
clone.addAttribute(xmlnsNames[i], xmlnsValues[i], |
||||
XmlViolationPolicy.ALLOW); |
||||
} |
||||
// ]NOCPP]
|
||||
return clone; // XXX!!!
|
||||
} |
||||
|
||||
public boolean equalsAnother(HtmlAttributes other) { |
||||
assert mode == 0 || mode == 3 : "Trying to compare attributes in foreign content."; |
||||
int otherLength = other.getLength(); |
||||
if (length != otherLength) { |
||||
return false; |
||||
} |
||||
for (int i = 0; i < length; i++) { |
||||
// Work around the limitations of C++
|
||||
boolean found = false; |
||||
// The comparing just the local names is OK, since these attribute
|
||||
// holders are both supposed to belong to HTML formatting elements
|
||||
@Local String ownLocal = names[i].getLocal(AttributeName.HTML); |
||||
for (int j = 0; j < otherLength; j++) { |
||||
if (ownLocal == other.names[j].getLocal(AttributeName.HTML)) { |
||||
found = true; |
||||
if (!Portability.stringEqualsString(values[i], other.values[j])) { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
if (!found) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
// [NOCPP[
|
||||
|
||||
void processNonNcNames(TreeBuilder<?> treeBuilder, XmlViolationPolicy namePolicy) throws SAXException { |
||||
for (int i = 0; i < length; i++) { |
||||
AttributeName attName = names[i]; |
||||
if (!attName.isNcName(mode)) { |
||||
String name = attName.getLocal(mode); |
||||
switch (namePolicy) { |
||||
case ALTER_INFOSET: |
||||
names[i] = AttributeName.create(NCName.escapeName(name)); |
||||
// fall through
|
||||
case ALLOW: |
||||
if (attName != AttributeName.XML_LANG) { |
||||
treeBuilder.warn("Attribute \u201C" + name + "\u201D is not serializable as XML 1.0."); |
||||
} |
||||
break; |
||||
case FATAL: |
||||
treeBuilder.fatal("Attribute \u201C" + name + "\u201D is not serializable as XML 1.0."); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void merge(HtmlAttributes attributes) throws SAXException { |
||||
int len = attributes.getLength(); |
||||
for (int i = 0; i < len; i++) { |
||||
AttributeName name = attributes.getAttributeNameNoBoundsCheck(i); |
||||
if (!contains(name)) { |
||||
addAttribute(name, attributes.getValueNoBoundsCheck(i), XmlViolationPolicy.ALLOW); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
// ]NOCPP]
|
||||
|
||||
} |
@ -1,854 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2007 Henri Sivonen |
||||
* Copyright (c) 2008-2015 Mozilla Foundation |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a |
||||
* copy of this software and associated documentation files (the "Software"), |
||||
* to deal in the Software without restriction, including without limitation |
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
* and/or sell copies of the Software, and to permit persons to whom the |
||||
* Software is furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in |
||||
* all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
* DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
package nu.validator.htmlparser.impl; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import nu.validator.htmlparser.annotation.Auto; |
||||
import nu.validator.htmlparser.annotation.Inline; |
||||
import nu.validator.htmlparser.common.ByteReadable; |
||||
|
||||
import org.xml.sax.SAXException; |
||||
|
||||
public abstract class MetaScanner { |
||||
|
||||
/** |
||||
* Constant for "charset". |
||||
*/ |
||||
private static final char[] CHARSET = { 'h', 'a', 'r', 's', 'e', 't' }; |
||||
|
||||
/** |
||||
* Constant for "content". |
||||
*/ |
||||
private static final char[] CONTENT = { 'o', 'n', 't', 'e', 'n', 't' }; |
||||
|
||||
/** |
||||
* Constant for "http-equiv". |
||||
*/ |
||||
private static final char[] HTTP_EQUIV = { 't', 't', 'p', '-', 'e', 'q', |
||||
'u', 'i', 'v' }; |
||||
|
||||
/** |
||||
* Constant for "content-type". |
||||
*/ |
||||
private static final char[] CONTENT_TYPE = { 'c', 'o', 'n', 't', 'e', 'n', |
||||
't', '-', 't', 'y', 'p', 'e' }; |
||||
|
||||
private static final int NO = 0; |
||||
|
||||
private static final int M = 1; |
||||
|
||||
private static final int E = 2; |
||||
|
||||
private static final int T = 3; |
||||
|
||||
private static final int A = 4; |
||||
|
||||
private static final int DATA = 0; |
||||
|
||||
private static final int TAG_OPEN = 1; |
||||
|
||||
private static final int SCAN_UNTIL_GT = 2; |
||||
|
||||
private static final int TAG_NAME = 3; |
||||
|
||||
private static final int BEFORE_ATTRIBUTE_NAME = 4; |
||||
|
||||
private static final int ATTRIBUTE_NAME = 5; |
||||
|
||||
private static final int AFTER_ATTRIBUTE_NAME = 6; |
||||
|
||||
private static final int BEFORE_ATTRIBUTE_VALUE = 7; |
||||
|
||||
private static final int ATTRIBUTE_VALUE_DOUBLE_QUOTED = 8; |
||||
|
||||
private static final int ATTRIBUTE_VALUE_SINGLE_QUOTED = 9; |
||||
|
||||
private static final int ATTRIBUTE_VALUE_UNQUOTED = 10; |
||||
|
||||
private static final int AFTER_ATTRIBUTE_VALUE_QUOTED = 11; |
||||
|
||||
private static final int MARKUP_DECLARATION_OPEN = 13; |
||||
|
||||
private static final int MARKUP_DECLARATION_HYPHEN = 14; |
||||
|
||||
private static final int COMMENT_START = 15; |
||||
|
||||
private static final int COMMENT_START_DASH = 16; |
||||
|
||||
private static final int COMMENT = 17; |
||||
|
||||
private static final int COMMENT_END_DASH = 18; |
||||
|
||||
private static final int COMMENT_END = 19; |
||||
|
||||
private static final int SELF_CLOSING_START_TAG = 20; |
||||
|
||||
private static final int HTTP_EQUIV_NOT_SEEN = 0; |
||||
|
||||
private static final int HTTP_EQUIV_CONTENT_TYPE = 1; |
||||
|
||||
private static final int HTTP_EQUIV_OTHER = 2; |
||||
|
||||
/** |
||||
* The data source. |
||||
*/ |
||||
protected ByteReadable readable; |
||||
|
||||
/** |
||||
* The state of the state machine that recognizes the tag name "meta". |
||||
*/ |
||||
private int metaState = NO; |
||||
|
||||
/** |
||||
* The current position in recognizing the attribute name "content". |
||||
*/ |
||||
private int contentIndex = Integer.MAX_VALUE; |
||||
|
||||
/** |
||||
* The current position in recognizing the attribute name "charset". |
||||
*/ |
||||
private int charsetIndex = Integer.MAX_VALUE; |
||||
|
||||
/** |
||||
* The current position in recognizing the attribute name "http-equive". |
||||
*/ |
||||
private int httpEquivIndex = Integer.MAX_VALUE; |
||||
|
||||
/** |
||||
* The current position in recognizing the attribute value "content-type". |
||||
*/ |
||||
private int contentTypeIndex = Integer.MAX_VALUE; |
||||
|
||||
/** |
||||
* The tokenizer state. |
||||
*/ |
||||
protected int stateSave = DATA; |
||||
|
||||
/** |
||||
* The currently filled length of strBuf. |
||||
*/ |
||||
private int strBufLen; |
||||
|
||||
/** |
||||
* Accumulation buffer for attribute values. |
||||
*/ |
||||
private @Auto char[] strBuf; |
||||
|
||||
private String content; |
||||
|
||||
private String charset; |
||||
|
||||
private int httpEquivState; |
||||
|
||||
// CPPONLY: private TreeBuilder treeBuilder;
|
||||
|
||||
public MetaScanner( |
||||
// CPPONLY: TreeBuilder tb
|
||||
) { |
||||
this.readable = null; |
||||
this.metaState = NO; |
||||
this.contentIndex = Integer.MAX_VALUE; |
||||
this.charsetIndex = Integer.MAX_VALUE; |
||||
this.httpEquivIndex = Integer.MAX_VALUE; |
||||
this.contentTypeIndex = Integer.MAX_VALUE; |
||||
this.stateSave = DATA; |
||||
this.strBufLen = 0; |
||||
this.strBuf = new char[36]; |
||||
this.content = null; |
||||
this.charset = null; |
||||
this.httpEquivState = HTTP_EQUIV_NOT_SEEN; |
||||
// CPPONLY: this.treeBuilder = tb;
|
||||
} |
||||
|
||||
@SuppressWarnings("unused") private void destructor() { |
||||
Portability.releaseString(content); |
||||
Portability.releaseString(charset); |
||||
} |
||||
|
||||
// [NOCPP[
|
||||
|
||||
/** |
||||
* Reads a byte from the data source. |
||||
* |
||||
* -1 means end. |
||||
* @return |
||||
* @throws IOException |
||||
*/ |
||||
protected int read() throws IOException { |
||||
return readable.readByte(); |
||||
} |
||||
|
||||
// ]NOCPP]
|
||||
|
||||
// WARNING When editing this, makes sure the bytecode length shown by javap
|
||||
// stays under 8000 bytes!
|
||||
/** |
||||
* The runs the meta scanning algorithm. |
||||
*/ |
||||
protected final void stateLoop(int state) |
||||
throws SAXException, IOException { |
||||
int c = -1; |
||||
boolean reconsume = false; |
||||
stateloop: for (;;) { |
||||
switch (state) { |
||||
case DATA: |
||||
dataloop: for (;;) { |
||||
if (reconsume) { |
||||
reconsume = false; |
||||
} else { |
||||
c = read(); |
||||
} |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '<': |
||||
state = MetaScanner.TAG_OPEN; |
||||
break dataloop; // FALL THROUGH continue
|
||||
// stateloop;
|
||||
default: |
||||
continue; |
||||
} |
||||
} |
||||
// WARNING FALLTHRU CASE TRANSITION: DON'T REORDER
|
||||
case TAG_OPEN: |
||||
tagopenloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case 'm': |
||||
case 'M': |
||||
metaState = M; |
||||
state = MetaScanner.TAG_NAME; |
||||
break tagopenloop; |
||||
// continue stateloop;
|
||||
case '!': |
||||
state = MetaScanner.MARKUP_DECLARATION_OPEN; |
||||
continue stateloop; |
||||
case '?': |
||||
case '/': |
||||
state = MetaScanner.SCAN_UNTIL_GT; |
||||
continue stateloop; |
||||
case '>': |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { |
||||
metaState = NO; |
||||
state = MetaScanner.TAG_NAME; |
||||
break tagopenloop; |
||||
// continue stateloop;
|
||||
} |
||||
state = MetaScanner.DATA; |
||||
reconsume = true; |
||||
continue stateloop; |
||||
} |
||||
} |
||||
// FALL THROUGH DON'T REORDER
|
||||
case TAG_NAME: |
||||
tagnameloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
case '\u000C': |
||||
state = MetaScanner.BEFORE_ATTRIBUTE_NAME; |
||||
break tagnameloop; |
||||
// continue stateloop;
|
||||
case '/': |
||||
state = MetaScanner.SELF_CLOSING_START_TAG; |
||||
continue stateloop; |
||||
case '>': |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
case 'e': |
||||
case 'E': |
||||
if (metaState == M) { |
||||
metaState = E; |
||||
} else { |
||||
metaState = NO; |
||||
} |
||||
continue; |
||||
case 't': |
||||
case 'T': |
||||
if (metaState == E) { |
||||
metaState = T; |
||||
} else { |
||||
metaState = NO; |
||||
} |
||||
continue; |
||||
case 'a': |
||||
case 'A': |
||||
if (metaState == T) { |
||||
metaState = A; |
||||
} else { |
||||
metaState = NO; |
||||
} |
||||
continue; |
||||
default: |
||||
metaState = NO; |
||||
continue; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case BEFORE_ATTRIBUTE_NAME: |
||||
beforeattributenameloop: for (;;) { |
||||
if (reconsume) { |
||||
reconsume = false; |
||||
} else { |
||||
c = read(); |
||||
} |
||||
/* |
||||
* Consume the next input character: |
||||
*/ |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
case '\u000C': |
||||
continue; |
||||
case '/': |
||||
state = MetaScanner.SELF_CLOSING_START_TAG; |
||||
continue stateloop; |
||||
case '>': |
||||
if (handleTag()) { |
||||
break stateloop; |
||||
} |
||||
state = DATA; |
||||
continue stateloop; |
||||
case 'c': |
||||
case 'C': |
||||
contentIndex = 0; |
||||
charsetIndex = 0; |
||||
httpEquivIndex = Integer.MAX_VALUE; |
||||
contentTypeIndex = Integer.MAX_VALUE; |
||||
state = MetaScanner.ATTRIBUTE_NAME; |
||||
break beforeattributenameloop; |
||||
case 'h': |
||||
case 'H': |
||||
contentIndex = Integer.MAX_VALUE; |
||||
charsetIndex = Integer.MAX_VALUE; |
||||
httpEquivIndex = 0; |
||||
contentTypeIndex = Integer.MAX_VALUE; |
||||
state = MetaScanner.ATTRIBUTE_NAME; |
||||
break beforeattributenameloop; |
||||
default: |
||||
contentIndex = Integer.MAX_VALUE; |
||||
charsetIndex = Integer.MAX_VALUE; |
||||
httpEquivIndex = Integer.MAX_VALUE; |
||||
contentTypeIndex = Integer.MAX_VALUE; |
||||
state = MetaScanner.ATTRIBUTE_NAME; |
||||
break beforeattributenameloop; |
||||
// continue stateloop;
|
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case ATTRIBUTE_NAME: |
||||
attributenameloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
case '\u000C': |
||||
state = MetaScanner.AFTER_ATTRIBUTE_NAME; |
||||
continue stateloop; |
||||
case '/': |
||||
state = MetaScanner.SELF_CLOSING_START_TAG; |
||||
continue stateloop; |
||||
case '=': |
||||
strBufLen = 0; |
||||
contentTypeIndex = 0; |
||||
state = MetaScanner.BEFORE_ATTRIBUTE_VALUE; |
||||
break attributenameloop; |
||||
// continue stateloop;
|
||||
case '>': |
||||
if (handleTag()) { |
||||
break stateloop; |
||||
} |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
if (metaState == A) { |
||||
if (c >= 'A' && c <= 'Z') { |
||||
c += 0x20; |
||||
} |
||||
if (contentIndex < CONTENT.length && c == CONTENT[contentIndex]) { |
||||
++contentIndex; |
||||
} else { |
||||
contentIndex = Integer.MAX_VALUE; |
||||
} |
||||
if (charsetIndex < CHARSET.length && c == CHARSET[charsetIndex]) { |
||||
++charsetIndex; |
||||
} else { |
||||
charsetIndex = Integer.MAX_VALUE; |
||||
} |
||||
if (httpEquivIndex < HTTP_EQUIV.length && c == HTTP_EQUIV[httpEquivIndex]) { |
||||
++httpEquivIndex; |
||||
} else { |
||||
httpEquivIndex = Integer.MAX_VALUE; |
||||
} |
||||
} |
||||
continue; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case BEFORE_ATTRIBUTE_VALUE: |
||||
beforeattributevalueloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
case '\u000C': |
||||
continue; |
||||
case '"': |
||||
state = MetaScanner.ATTRIBUTE_VALUE_DOUBLE_QUOTED; |
||||
break beforeattributevalueloop; |
||||
// continue stateloop;
|
||||
case '\'': |
||||
state = MetaScanner.ATTRIBUTE_VALUE_SINGLE_QUOTED; |
||||
continue stateloop; |
||||
case '>': |
||||
if (handleTag()) { |
||||
break stateloop; |
||||
} |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
handleCharInAttributeValue(c); |
||||
state = MetaScanner.ATTRIBUTE_VALUE_UNQUOTED; |
||||
continue stateloop; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case ATTRIBUTE_VALUE_DOUBLE_QUOTED: |
||||
attributevaluedoublequotedloop: for (;;) { |
||||
if (reconsume) { |
||||
reconsume = false; |
||||
} else { |
||||
c = read(); |
||||
} |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '"': |
||||
handleAttributeValue(); |
||||
state = MetaScanner.AFTER_ATTRIBUTE_VALUE_QUOTED; |
||||
break attributevaluedoublequotedloop; |
||||
// continue stateloop;
|
||||
default: |
||||
handleCharInAttributeValue(c); |
||||
continue; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case AFTER_ATTRIBUTE_VALUE_QUOTED: |
||||
afterattributevaluequotedloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
case '\u000C': |
||||
state = MetaScanner.BEFORE_ATTRIBUTE_NAME; |
||||
continue stateloop; |
||||
case '/': |
||||
state = MetaScanner.SELF_CLOSING_START_TAG; |
||||
break afterattributevaluequotedloop; |
||||
// continue stateloop;
|
||||
case '>': |
||||
if (handleTag()) { |
||||
break stateloop; |
||||
} |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
state = MetaScanner.BEFORE_ATTRIBUTE_NAME; |
||||
reconsume = true; |
||||
continue stateloop; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case SELF_CLOSING_START_TAG: |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '>': |
||||
if (handleTag()) { |
||||
break stateloop; |
||||
} |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
state = MetaScanner.BEFORE_ATTRIBUTE_NAME; |
||||
reconsume = true; |
||||
continue stateloop; |
||||
} |
||||
// XXX reorder point
|
||||
case ATTRIBUTE_VALUE_UNQUOTED: |
||||
for (;;) { |
||||
if (reconsume) { |
||||
reconsume = false; |
||||
} else { |
||||
c = read(); |
||||
} |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
|
||||
case '\u000C': |
||||
handleAttributeValue(); |
||||
state = MetaScanner.BEFORE_ATTRIBUTE_NAME; |
||||
continue stateloop; |
||||
case '>': |
||||
handleAttributeValue(); |
||||
if (handleTag()) { |
||||
break stateloop; |
||||
} |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
handleCharInAttributeValue(c); |
||||
continue; |
||||
} |
||||
} |
||||
// XXX reorder point
|
||||
case AFTER_ATTRIBUTE_NAME: |
||||
for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case ' ': |
||||
case '\t': |
||||
case '\n': |
||||
case '\u000C': |
||||
continue; |
||||
case '/': |
||||
handleAttributeValue(); |
||||
state = MetaScanner.SELF_CLOSING_START_TAG; |
||||
continue stateloop; |
||||
case '=': |
||||
strBufLen = 0; |
||||
contentTypeIndex = 0; |
||||
state = MetaScanner.BEFORE_ATTRIBUTE_VALUE; |
||||
continue stateloop; |
||||
case '>': |
||||
handleAttributeValue(); |
||||
if (handleTag()) { |
||||
break stateloop; |
||||
} |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
case 'c': |
||||
case 'C': |
||||
contentIndex = 0; |
||||
charsetIndex = 0; |
||||
state = MetaScanner.ATTRIBUTE_NAME; |
||||
continue stateloop; |
||||
default: |
||||
contentIndex = Integer.MAX_VALUE; |
||||
charsetIndex = Integer.MAX_VALUE; |
||||
state = MetaScanner.ATTRIBUTE_NAME; |
||||
continue stateloop; |
||||
} |
||||
} |
||||
// XXX reorder point
|
||||
case MARKUP_DECLARATION_OPEN: |
||||
markupdeclarationopenloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '-': |
||||
state = MetaScanner.MARKUP_DECLARATION_HYPHEN; |
||||
break markupdeclarationopenloop; |
||||
// continue stateloop;
|
||||
default: |
||||
state = MetaScanner.SCAN_UNTIL_GT; |
||||
reconsume = true; |
||||
continue stateloop; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case MARKUP_DECLARATION_HYPHEN: |
||||
markupdeclarationhyphenloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '-': |
||||
state = MetaScanner.COMMENT_START; |
||||
break markupdeclarationhyphenloop; |
||||
// continue stateloop;
|
||||
default: |
||||
state = MetaScanner.SCAN_UNTIL_GT; |
||||
reconsume = true; |
||||
continue stateloop; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case COMMENT_START: |
||||
commentstartloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '-': |
||||
state = MetaScanner.COMMENT_START_DASH; |
||||
continue stateloop; |
||||
case '>': |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
state = MetaScanner.COMMENT; |
||||
break commentstartloop; |
||||
// continue stateloop;
|
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case COMMENT: |
||||
commentloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '-': |
||||
state = MetaScanner.COMMENT_END_DASH; |
||||
break commentloop; |
||||
// continue stateloop;
|
||||
default: |
||||
continue; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case COMMENT_END_DASH: |
||||
commentenddashloop: for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '-': |
||||
state = MetaScanner.COMMENT_END; |
||||
break commentenddashloop; |
||||
// continue stateloop;
|
||||
default: |
||||
state = MetaScanner.COMMENT; |
||||
continue stateloop; |
||||
} |
||||
} |
||||
// FALLTHRU DON'T REORDER
|
||||
case COMMENT_END: |
||||
for (;;) { |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '>': |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
case '-': |
||||
continue; |
||||
default: |
||||
state = MetaScanner.COMMENT; |
||||
continue stateloop; |
||||
} |
||||
} |
||||
// XXX reorder point
|
||||
case COMMENT_START_DASH: |
||||
c = read(); |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '-': |
||||
state = MetaScanner.COMMENT_END; |
||||
continue stateloop; |
||||
case '>': |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
state = MetaScanner.COMMENT; |
||||
continue stateloop; |
||||
} |
||||
// XXX reorder point
|
||||
case ATTRIBUTE_VALUE_SINGLE_QUOTED: |
||||
for (;;) { |
||||
if (reconsume) { |
||||
reconsume = false; |
||||
} else { |
||||
c = read(); |
||||
} |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '\'': |
||||
handleAttributeValue(); |
||||
state = MetaScanner.AFTER_ATTRIBUTE_VALUE_QUOTED; |
||||
continue stateloop; |
||||
default: |
||||
handleCharInAttributeValue(c); |
||||
continue; |
||||
} |
||||
} |
||||
// XXX reorder point
|
||||
case SCAN_UNTIL_GT: |
||||
for (;;) { |
||||
if (reconsume) { |
||||
reconsume = false; |
||||
} else { |
||||
c = read(); |
||||
} |
||||
switch (c) { |
||||
case -1: |
||||
break stateloop; |
||||
case '>': |
||||
state = MetaScanner.DATA; |
||||
continue stateloop; |
||||
default: |
||||
continue; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
stateSave = state; |
||||
} |
||||
|
||||
private void handleCharInAttributeValue(int c) { |
||||
if (metaState == A) { |
||||
if (contentIndex == CONTENT.length || charsetIndex == CHARSET.length) { |
||||
addToBuffer(c); |
||||
} else if (httpEquivIndex == HTTP_EQUIV.length) { |
||||
if (contentTypeIndex < CONTENT_TYPE.length && toAsciiLowerCase(c) == CONTENT_TYPE[contentTypeIndex]) { |
||||
++contentTypeIndex; |
||||
} else { |
||||
contentTypeIndex = Integer.MAX_VALUE; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Inline private int toAsciiLowerCase(int c) { |
||||
if (c >= 'A' && c <= 'Z') { |
||||
return c + 0x20; |
||||
} |
||||
return c; |
||||
} |
||||
|
||||
/** |
||||
* Adds a character to the accumulation buffer. |
||||
* @param c the character to add |
||||
*/ |
||||
private void addToBuffer(int c) { |
||||
if (strBufLen == strBuf.length) { |
||||
char[] newBuf = new char[strBuf.length + (strBuf.length << 1)]; |
||||
System.arraycopy(strBuf, 0, newBuf, 0, strBuf.length); |
||||
strBuf = newBuf; |
||||
} |
||||
strBuf[strBufLen++] = (char)c; |
||||
} |
||||
|
||||
/** |
||||
* Attempts to extract a charset name from the accumulation buffer. |
||||
* @return <code>true</code> if successful |
||||
* @throws SAXException |
||||
*/ |
||||
private void handleAttributeValue() throws SAXException { |
||||
if (metaState != A) { |
||||
return; |
||||
} |
||||