ohair@286: /* mkos@397: * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.dtdparser; ohair@286: ohair@286: import org.xml.sax.EntityResolver; ohair@286: import org.xml.sax.InputSource; ohair@286: import org.xml.sax.Locator; ohair@286: import org.xml.sax.SAXException; ohair@286: import org.xml.sax.SAXParseException; ohair@286: ohair@286: import java.io.IOException; ohair@286: import java.util.ArrayList; ohair@286: import java.util.Enumeration; ohair@286: import java.util.Hashtable; ohair@286: import java.util.Locale; ohair@286: import java.util.Set; ohair@286: import java.util.Vector; ohair@286: ohair@286: /** ohair@286: * This implements parsing of XML 1.0 DTDs. ohair@286: *

ohair@286: * This conforms to the portion of the XML 1.0 specification related ohair@286: * to the external DTD subset. ohair@286: *

ohair@286: * For multi-language applications (such as web servers using XML ohair@286: * processing to create dynamic content), a method supports choosing ohair@286: * a locale for parser diagnostics which is both understood by the ohair@286: * message recipient and supported by the parser. ohair@286: *

ohair@286: * This parser produces a stream of parse events. It supports some ohair@286: * features (exposing comments, CDATA sections, and entity references) ohair@286: * which are not required to be reported by conformant XML processors. ohair@286: * ohair@286: * @author David Brownell ohair@286: * @author Janet Koenig ohair@286: * @author Kohsuke KAWAGUCHI mkos@397: * @version $Id: DTDParser.java,v 1.2 2009/04/16 15:25:49 snajper Exp $ ohair@286: */ ohair@286: public class DTDParser { ohair@286: public final static String TYPE_CDATA = "CDATA"; ohair@286: public final static String TYPE_ID = "ID"; ohair@286: public final static String TYPE_IDREF = "IDREF"; ohair@286: public final static String TYPE_IDREFS = "IDREFS"; ohair@286: public final static String TYPE_ENTITY = "ENTITY"; ohair@286: public final static String TYPE_ENTITIES = "ENTITIES"; ohair@286: public final static String TYPE_NMTOKEN = "NMTOKEN"; ohair@286: public final static String TYPE_NMTOKENS = "NMTOKENS"; ohair@286: public final static String TYPE_NOTATION = "NOTATION"; ohair@286: public final static String TYPE_ENUMERATION = "ENUMERATION"; ohair@286: ohair@286: ohair@286: // stack of input entities being merged ohair@286: private InputEntity in; ohair@286: ohair@286: // temporaries reused during parsing ohair@286: private StringBuffer strTmp; ohair@286: private char nameTmp []; ohair@286: private NameCache nameCache; ohair@286: private char charTmp [] = new char[2]; ohair@286: ohair@286: // temporary DTD parsing state ohair@286: private boolean doLexicalPE; ohair@286: ohair@286: // DTD state, used during parsing ohair@286: // private SimpleHashtable elements = new SimpleHashtable (47); ohair@286: protected final Set declaredElements = new java.util.HashSet(); ohair@286: private SimpleHashtable params = new SimpleHashtable(7); ohair@286: ohair@286: // exposed to package-private subclass ohair@286: Hashtable notations = new Hashtable(7); ohair@286: SimpleHashtable entities = new SimpleHashtable(17); ohair@286: ohair@286: private SimpleHashtable ids = new SimpleHashtable(); ohair@286: ohair@286: // listeners for DTD parsing events ohair@286: private DTDEventListener dtdHandler; ohair@286: ohair@286: private EntityResolver resolver; ohair@286: private Locale locale; ohair@286: ohair@286: // string constants -- use these copies so "==" works ohair@286: // package private ohair@286: static final String strANY = "ANY"; ohair@286: static final String strEMPTY = "EMPTY"; ohair@286: ohair@286: /** ohair@286: * Used by applications to request locale for diagnostics. ohair@286: * ohair@286: * @param l The locale to use, or null to use system defaults ohair@286: * (which may include only message IDs). ohair@286: */ ohair@286: public void setLocale(Locale l) throws SAXException { ohair@286: ohair@286: if (l != null && !messages.isLocaleSupported(l.toString())) { ohair@286: throw new SAXException(messages.getMessage(locale, ohair@286: "P-078", new Object[]{l})); ohair@286: } ohair@286: locale = l; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the diagnostic locale. ohair@286: */ ohair@286: public Locale getLocale() { ohair@286: return locale; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Chooses a client locale to use for diagnostics, using the first ohair@286: * language specified in the list that is supported by this parser. ohair@286: * That locale is then set using ohair@286: * setLocale(). Such a list could be provided by a variety of user ohair@286: * preference mechanisms, including the HTTP Accept-Language ohair@286: * header field. ohair@286: * ohair@286: * @param languages Array of language specifiers, ordered with the most ohair@286: * preferable one at the front. For example, "en-ca" then "fr-ca", ohair@286: * followed by "zh_CN". Both RFC 1766 and Java styles are supported. ohair@286: * @return The chosen locale, or null. ohair@286: * @see MessageCatalog ohair@286: */ ohair@286: public Locale chooseLocale(String languages []) ohair@286: throws SAXException { ohair@286: ohair@286: Locale l = messages.chooseLocale(languages); ohair@286: ohair@286: if (l != null) { ohair@286: setLocale(l); ohair@286: } ohair@286: return l; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Lets applications control entity resolution. ohair@286: */ ohair@286: public void setEntityResolver(EntityResolver r) { ohair@286: ohair@286: resolver = r; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the object used to resolve entities ohair@286: */ ohair@286: public EntityResolver getEntityResolver() { ohair@286: ohair@286: return resolver; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Used by applications to set handling of DTD parsing events. ohair@286: */ ohair@286: public void setDtdHandler(DTDEventListener handler) { ohair@286: dtdHandler = handler; ohair@286: if (handler != null) ohair@286: handler.setDocumentLocator(new Locator() { ohair@286: public String getPublicId() { ohair@286: return DTDParser.this.getPublicId(); ohair@286: } ohair@286: ohair@286: public String getSystemId() { ohair@286: return DTDParser.this.getSystemId(); ohair@286: } ohair@286: ohair@286: public int getLineNumber() { ohair@286: return DTDParser.this.getLineNumber(); ohair@286: } ohair@286: ohair@286: public int getColumnNumber() { ohair@286: return DTDParser.this.getColumnNumber(); ohair@286: } ohair@286: }); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the handler used to for DTD parsing events. ohair@286: */ ohair@286: public DTDEventListener getDtdHandler() { ohair@286: return dtdHandler; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Parse a DTD. ohair@286: */ ohair@286: public void parse(InputSource in) ohair@286: throws IOException, SAXException { ohair@286: init(); ohair@286: parseInternal(in); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Parse a DTD. ohair@286: */ ohair@286: public void parse(String uri) ohair@286: throws IOException, SAXException { mkos@397: InputSource in; ohair@286: ohair@286: init(); ohair@286: // System.out.println ("parse (\"" + uri + "\")"); mkos@397: in = resolver.resolveEntity(null, uri); ohair@286: ohair@286: // If custom resolver punts resolution to parser, handle it ... mkos@397: if (in == null) { mkos@397: in = Resolver.createInputSource(new java.net.URL(uri), false); ohair@286: ohair@286: // ... or if custom resolver doesn't correctly construct the ohair@286: // input entity, patch it up enough so relative URIs work, and ohair@286: // issue a warning to minimize later confusion. mkos@397: } else if (in.getSystemId() == null) { ohair@286: warning("P-065", null); mkos@397: in.setSystemId(uri); ohair@286: } ohair@286: mkos@397: parseInternal(in); ohair@286: } ohair@286: ohair@286: // makes sure the parser is reset to "before a document" ohair@286: private void init() { ohair@286: in = null; ohair@286: ohair@286: // alloc temporary data used in parsing ohair@286: strTmp = new StringBuffer(); ohair@286: nameTmp = new char[20]; ohair@286: nameCache = new NameCache(); ohair@286: ohair@286: // reset doc info ohair@286: // isInAttribute = false; ohair@286: ohair@286: doLexicalPE = false; ohair@286: ohair@286: entities.clear(); ohair@286: notations.clear(); ohair@286: params.clear(); ohair@286: // elements.clear (); ohair@286: declaredElements.clear(); ohair@286: ohair@286: // initialize predefined references ... re-interpreted later ohair@286: builtin("amp", "&"); ohair@286: builtin("lt", "<"); ohair@286: builtin("gt", ">"); ohair@286: builtin("quot", "\""); ohair@286: builtin("apos", "'"); ohair@286: ohair@286: if (locale == null) ohair@286: locale = Locale.getDefault(); ohair@286: if (resolver == null) ohair@286: resolver = new Resolver(); ohair@286: if (dtdHandler == null) ohair@286: dtdHandler = new DTDHandlerBase(); ohair@286: } ohair@286: ohair@286: private void builtin(String entityName, String entityValue) { ohair@286: InternalEntity entity; ohair@286: entity = new InternalEntity(entityName, entityValue.toCharArray()); ohair@286: entities.put(entityName, entity); ohair@286: } ohair@286: ohair@286: ohair@286: //////////////////////////////////////////////////////////////// ohair@286: // ohair@286: // parsing is by recursive descent, code roughly ohair@286: // following the BNF rules except tweaked for simple ohair@286: // lookahead. rules are more or less in numeric order, ohair@286: // except where code sharing suggests other structures. ohair@286: // ohair@286: // a classic benefit of recursive descent parsers: it's ohair@286: // relatively easy to get diagnostics that make sense. ohair@286: // ohair@286: //////////////////////////////////////////////////////////////// ohair@286: ohair@286: ohair@286: private void parseInternal(InputSource input) ohair@286: throws IOException, SAXException { ohair@286: ohair@286: if (input == null) ohair@286: fatal("P-000"); ohair@286: ohair@286: try { ohair@286: in = InputEntity.getInputEntity(dtdHandler, locale); ohair@286: in.init(input, null, null, false); ohair@286: ohair@286: dtdHandler.startDTD(in); ohair@286: ohair@286: // [30] extSubset ::= TextDecl? extSubsetDecl ohair@286: // [31] extSubsetDecl ::= ( markupdecl | conditionalSect ohair@286: // | PEReference | S )* ohair@286: // ... same as [79] extPE, which is where the code is ohair@286: ohair@286: ExternalEntity externalSubset = new ExternalEntity(in); ohair@286: externalParameterEntity(externalSubset); ohair@286: ohair@286: if (!in.isEOF()) { ohair@286: fatal("P-001", new Object[] ohair@286: {Integer.toHexString(((int) getc()))}); ohair@286: } ohair@286: afterRoot(); ohair@286: dtdHandler.endDTD(); ohair@286: ohair@286: } catch (EndOfInputException e) { ohair@286: if (!in.isDocument()) { ohair@286: String name = in.getName(); ohair@286: do { // force a relevant URI and line number ohair@286: in = in.pop(); ohair@286: } while (in.isInternal()); ohair@286: fatal("P-002", new Object[]{name}); ohair@286: } else { ohair@286: fatal("P-003", null); ohair@286: } ohair@286: } catch (RuntimeException e) { ohair@286: // Don't discard location that triggered the exception ohair@286: // ## Should properly wrap exception ohair@286: System.err.print("Internal DTD parser error: "); // ## ohair@286: e.printStackTrace(); ohair@286: throw new SAXParseException(e.getMessage() != null ohair@286: ? e.getMessage() : e.getClass().getName(), ohair@286: getPublicId(), getSystemId(), ohair@286: getLineNumber(), getColumnNumber()); ohair@286: ohair@286: } finally { ohair@286: // recycle temporary data used during parsing ohair@286: strTmp = null; ohair@286: nameTmp = null; ohair@286: nameCache = null; ohair@286: ohair@286: // ditto input sources etc ohair@286: if (in != null) { ohair@286: in.close(); ohair@286: in = null; ohair@286: } ohair@286: ohair@286: // get rid of all DTD info ... some of it would be ohair@286: // useful for editors etc, investigate later. ohair@286: ohair@286: params.clear(); ohair@286: entities.clear(); ohair@286: notations.clear(); ohair@286: declaredElements.clear(); ohair@286: // elements.clear(); ohair@286: ids.clear(); ohair@286: } ohair@286: } ohair@286: ohair@286: void afterRoot() throws SAXException { ohair@286: // Make sure all IDREFs match declared ID attributes. We scan ohair@286: // after the document element is parsed, since XML allows forward ohair@286: // references, and only now can we know if they're all resolved. ohair@286: ohair@286: for (Enumeration e = ids.keys(); ohair@286: e.hasMoreElements(); ohair@286: ) { ohair@286: String id = (String) e.nextElement(); ohair@286: Boolean value = (Boolean) ids.get(id); ohair@286: if (Boolean.FALSE == value) ohair@286: error("V-024", new Object[]{id}); ohair@286: } ohair@286: } ohair@286: ohair@286: ohair@286: // role is for diagnostics ohair@286: private void whitespace(String roleId) ohair@286: throws IOException, SAXException { ohair@286: ohair@286: // [3] S ::= (#x20 | #x9 | #xd | #xa)+ ohair@286: if (!maybeWhitespace()) { ohair@286: fatal("P-004", new Object[] ohair@286: {messages.getMessage(locale, roleId)}); ohair@286: } ohair@286: } ohair@286: ohair@286: // S? ohair@286: private boolean maybeWhitespace() ohair@286: throws IOException, SAXException { ohair@286: ohair@286: if (!doLexicalPE) ohair@286: return in.maybeWhitespace(); ohair@286: ohair@286: // see getc() for the PE logic -- this lets us splice ohair@286: // expansions of PEs in "anywhere". getc() has smarts, ohair@286: // so for external PEs we don't bypass it. ohair@286: ohair@286: // XXX we can marginally speed PE handling, and certainly ohair@286: // be cleaner (hence potentially more correct), by using ohair@286: // the observations that expanded PEs only start and stop ohair@286: // where whitespace is allowed. getc wouldn't need any ohair@286: // "lexical" PE expansion logic, and no other method needs ohair@286: // to handle termination of PEs. (parsing of literals would ohair@286: // still need to pop entities, but not parsing of references ohair@286: // in content.) ohair@286: ohair@286: char c = getc(); ohair@286: boolean saw = false; ohair@286: ohair@286: while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { ohair@286: saw = true; ohair@286: ohair@286: // this gracefully ends things when we stop playing ohair@286: // with internal parameters. caller should have a ohair@286: // grammar rule allowing whitespace at end of entity. ohair@286: if (in.isEOF() && !in.isInternal()) ohair@286: return saw; ohair@286: c = getc(); ohair@286: } ohair@286: ungetc(); ohair@286: return saw; ohair@286: } ohair@286: ohair@286: private String maybeGetName() ohair@286: throws IOException, SAXException { ohair@286: ohair@286: NameCacheEntry entry = maybeGetNameCacheEntry(); ohair@286: return (entry == null) ? null : entry.name; ohair@286: } ohair@286: ohair@286: private NameCacheEntry maybeGetNameCacheEntry() ohair@286: throws IOException, SAXException { ohair@286: ohair@286: // [5] Name ::= (Letter|'_'|':') (Namechar)* ohair@286: char c = getc(); ohair@286: ohair@286: if (!XmlChars.isLetter(c) && c != ':' && c != '_') { ohair@286: ungetc(); ohair@286: return null; ohair@286: } ohair@286: return nameCharString(c); ohair@286: } ohair@286: ohair@286: // Used when parsing enumerations ohair@286: private String getNmtoken() ohair@286: throws IOException, SAXException { ohair@286: ohair@286: // [7] Nmtoken ::= (Namechar)+ ohair@286: char c = getc(); ohair@286: if (!XmlChars.isNameChar(c)) ohair@286: fatal("P-006", new Object[]{new Character(c)}); ohair@286: return nameCharString(c).name; ohair@286: } ohair@286: ohair@286: // n.b. this gets used when parsing attribute values (for ohair@286: // internal references) so we can't use strTmp; it's also ohair@286: // a hotspot for CPU and memory in the parser (called at least ohair@286: // once for each element) so this has been optimized a bit. ohair@286: ohair@286: private NameCacheEntry nameCharString(char c) ohair@286: throws IOException, SAXException { ohair@286: ohair@286: int i = 1; ohair@286: ohair@286: nameTmp[0] = c; ohair@286: for (; ;) { ohair@286: if ((c = in.getNameChar()) == 0) ohair@286: break; ohair@286: if (i >= nameTmp.length) { ohair@286: char tmp [] = new char[nameTmp.length + 10]; ohair@286: System.arraycopy(nameTmp, 0, tmp, 0, nameTmp.length); ohair@286: nameTmp = tmp; ohair@286: } ohair@286: nameTmp[i++] = c; ohair@286: } ohair@286: return nameCache.lookupEntry(nameTmp, i); ohair@286: } ohair@286: ohair@286: // ohair@286: // much similarity between parsing entity values in DTD ohair@286: // and attribute values (in DTD or content) ... both follow ohair@286: // literal parsing rules, newline canonicalization, etc ohair@286: // ohair@286: // leaves value in 'strTmp' ... either a "replacement text" (4.5), ohair@286: // or else partially normalized attribute value (the first bit ohair@286: // of 3.3.3's spec, without the "if not CDATA" bits). ohair@286: // ohair@286: private void parseLiteral(boolean isEntityValue) ohair@286: throws IOException, SAXException { ohair@286: ohair@286: // [9] EntityValue ::= ohair@286: // '"' ([^"&%] | Reference | PEReference)* '"' ohair@286: // | "'" ([^'&%] | Reference | PEReference)* "'" ohair@286: // [10] AttValue ::= ohair@286: // '"' ([^"&] | Reference )* '"' ohair@286: // | "'" ([^'&] | Reference )* "'" ohair@286: char quote = getc(); ohair@286: char c; ohair@286: InputEntity source = in; ohair@286: ohair@286: if (quote != '\'' && quote != '"') { ohair@286: fatal("P-007"); ohair@286: } ohair@286: ohair@286: // don't report entity expansions within attributes, ohair@286: // they're reported "fully expanded" via SAX ohair@286: // isInAttribute = !isEntityValue; ohair@286: ohair@286: // get value into strTmp ohair@286: strTmp = new StringBuffer(); ohair@286: ohair@286: // scan, allowing entity push/pop wherever ... ohair@286: // expanded entities can't terminate the literal! ohair@286: for (; ;) { ohair@286: if (in != source && in.isEOF()) { ohair@286: // we don't report end of parsed entities ohair@286: // within attributes (no SAX hooks) ohair@286: in = in.pop(); ohair@286: continue; ohair@286: } ohair@286: if ((c = getc()) == quote && in == source) { ohair@286: break; ohair@286: } ohair@286: ohair@286: // ohair@286: // Basically the "reference in attribute value" ohair@286: // row of the chart in section 4.4 of the spec ohair@286: // ohair@286: if (c == '&') { ohair@286: String entityName = maybeGetName(); ohair@286: ohair@286: if (entityName != null) { ohair@286: nextChar(';', "F-020", entityName); ohair@286: ohair@286: // 4.4 says: bypass these here ... we'll catch ohair@286: // forbidden refs to unparsed entities on use ohair@286: if (isEntityValue) { ohair@286: strTmp.append('&'); ohair@286: strTmp.append(entityName); ohair@286: strTmp.append(';'); ohair@286: continue; ohair@286: } ohair@286: expandEntityInLiteral(entityName, entities, isEntityValue); ohair@286: ohair@286: ohair@286: // character references are always included immediately ohair@286: } else if ((c = getc()) == '#') { ohair@286: int tmp = parseCharNumber(); ohair@286: ohair@286: if (tmp > 0xffff) { ohair@286: tmp = surrogatesToCharTmp(tmp); ohair@286: strTmp.append(charTmp[0]); ohair@286: if (tmp == 2) ohair@286: strTmp.append(charTmp[1]); ohair@286: } else ohair@286: strTmp.append((char) tmp); ohair@286: } else ohair@286: fatal("P-009"); ohair@286: continue; ohair@286: ohair@286: } ohair@286: ohair@286: // expand parameter entities only within entity value literals ohair@286: if (c == '%' && isEntityValue) { ohair@286: String entityName = maybeGetName(); ohair@286: ohair@286: if (entityName != null) { ohair@286: nextChar(';', "F-021", entityName); ohair@286: expandEntityInLiteral(entityName, params, isEntityValue); ohair@286: continue; ohair@286: } else ohair@286: fatal("P-011"); ohair@286: } ohair@286: ohair@286: // For attribute values ... ohair@286: if (!isEntityValue) { ohair@286: // 3.3.3 says whitespace normalizes to space... ohair@286: if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { ohair@286: strTmp.append(' '); ohair@286: continue; ohair@286: } ohair@286: ohair@286: // "<" not legal in parsed literals ... ohair@286: if (c == '<') ohair@286: fatal("P-012"); ohair@286: } ohair@286: ohair@286: strTmp.append(c); ohair@286: } ohair@286: // isInAttribute = false; ohair@286: } ohair@286: ohair@286: // does a SINGLE expansion of the entity (often reparsed later) ohair@286: private void expandEntityInLiteral(String name, SimpleHashtable table, ohair@286: boolean isEntityValue) ohair@286: throws IOException, SAXException { ohair@286: ohair@286: Object entity = table.get(name); ohair@286: ohair@286: if (entity instanceof InternalEntity) { ohair@286: InternalEntity value = (InternalEntity) entity; ohair@286: pushReader(value.buf, name, !value.isPE); ohair@286: ohair@286: } else if (entity instanceof ExternalEntity) { ohair@286: if (!isEntityValue) // must be a PE ... ohair@286: fatal("P-013", new Object[]{name}); ohair@286: // XXX if this returns false ... ohair@286: pushReader((ExternalEntity) entity); ohair@286: ohair@286: } else if (entity == null) { ohair@286: // ohair@286: // Note: much confusion about whether spec requires such ohair@286: // errors to be fatal in many cases, but none about whether ohair@286: // it allows "normal" errors to be unrecoverable! ohair@286: // ohair@286: fatal((table == params) ? "V-022" : "P-014", ohair@286: new Object[]{name}); ohair@286: } ohair@286: } ohair@286: ohair@286: // [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") ohair@286: // for PUBLIC and SYSTEM literals, also "' ohair@286: ohair@286: // NOTE: XML spec should explicitly say that PE ref syntax is ohair@286: // ignored in PIs, comments, SystemLiterals, and Pubid Literal ohair@286: // values ... can't process the XML spec's own DTD without doing ohair@286: // that for comments. ohair@286: ohair@286: private String getQuotedString(String type, String extra) ohair@286: throws IOException, SAXException { ohair@286: ohair@286: // use in.getc to bypass PE processing ohair@286: char quote = in.getc(); ohair@286: ohair@286: if (quote != '\'' && quote != '"') ohair@286: fatal("P-015", new Object[]{ ohair@286: messages.getMessage(locale, type, new Object[]{extra}) ohair@286: }); ohair@286: ohair@286: char c; ohair@286: ohair@286: strTmp = new StringBuffer(); ohair@286: while ((c = in.getc()) != quote) ohair@286: strTmp.append((char) c); ohair@286: return strTmp.toString(); ohair@286: } ohair@286: ohair@286: ohair@286: private String parsePublicId() throws IOException, SAXException { ohair@286: ohair@286: // [12] PubidLiteral ::= ('"' PubidChar* '"') | ("'" PubidChar* "'") ohair@286: // [13] PubidChar ::= #x20|#xd|#xa|[a-zA-Z0-9]|[-'()+,./:=?;!*#@$_%] ohair@286: String retval = getQuotedString("F-033", null); ohair@286: for (int i = 0; i < retval.length(); i++) { ohair@286: char c = retval.charAt(i); ohair@286: if (" \r\n-'()+,./:=?;!*#@$_%0123456789".indexOf(c) == -1 ohair@286: && !(c >= 'A' && c <= 'Z') ohair@286: && !(c >= 'a' && c <= 'z')) ohair@286: fatal("P-016", new Object[]{new Character(c)}); ohair@286: } ohair@286: strTmp = new StringBuffer(); ohair@286: strTmp.append(retval); ohair@286: return normalize(false); ohair@286: } ohair@286: ohair@286: // [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) ohair@286: // handled by: InputEntity.parsedContent() ohair@286: ohair@286: private boolean maybeComment(boolean skipStart) ohair@286: throws IOException, SAXException { ohair@286: ohair@286: // [15] Comment ::= '' ohair@286: if (!in.peek(skipStart ? "!--" : "