/** * Scans an entity value. [9] * * @param p pe reference flag * @return value * @throws IOException I/O exception */ private byte[] entityValue(final boolean p) throws IOException { final int qu = consume(); if (qu != '\'' && qu != '"') { prev(1); return null; } TokenBuilder tok = new TokenBuilder(); int ch; while ((ch = nextChar()) != qu) { if (ch == '&') tok.add(ref(false)); else if (ch == '%') { if (!p) error(INVPE); tok.add(peRef()); } else { tok.add(ch); } } final XMLInput tmp = input; input = new XMLInput(new IOContent(tok.finish())); tok = new TokenBuilder(); while ((ch = consume()) != 0) { if (ch == '&') tok.add(ref(false)); else tok.add(ch); } input = tmp; return tok.finish(); }
/** * Scans CDATA. * * @throws IOException I/O exception */ private void cDATA() throws IOException { int ch; while (true) { while ((ch = nextChar()) != ']') token.add(ch); if (consume(']')) { if (consume('>')) return; prev(1); } token.add(ch); } }
/** * Scans a reference. [67] * * @param f dissolve entities * @return entity * @throws IOException I/O exception */ private byte[] ref(final boolean f) throws IOException { // scans numeric entities if (consume('#')) { // [66] final TokenBuilder ent = new TokenBuilder(); int b = 10; int ch = nextChar(); ent.add(ch); if (ch == 'x') { b = 16; ent.add(ch = nextChar()); } int n = 0; do { final boolean m = ch >= '0' && ch <= '9'; final boolean h = b == 16 && (ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F'); if (!m && !h) { completeRef(ent); return QUESTION; } n *= b; n += ch & 15; if (!m) n += 9; ent.add(ch = nextChar()); } while (ch != ';'); if (!valid(n)) return QUESTION; ent.reset(); ent.add(n); return ent.finish(); } // scans predefined entities [68] final byte[] name = name(false); if (!consume(';')) return QUESTION; if (!f) return concat(AMPER, name, SEMI); byte[] en = ents.get(name); if (en == null) { // unknown entity: try HTML entities (lazy initialization) if (HTMLENTS.size() == 0) { for (int s = 0; s < HTMLENTITIES.length; s += 2) { HTMLENTS.add(token(HTMLENTITIES[s]), token(HTMLENTITIES[s + 1])); } } en = HTMLENTS.get(name); } return en == null ? QUESTION : en; }
/** * Consumes an Nmtoken. [7] * * @throws IOException I/O exception */ private void nmtoken() throws IOException { final TokenBuilder name = new TokenBuilder(); int c; while (isChar(c = nextChar())) name.add(c); prev(1); if (name.isEmpty()) error(INVNAME); }
/** * Adds some characters to the entity. * * @param ent token builder * @throws IOException I/O exception */ private void completeRef(final TokenBuilder ent) throws IOException { int ch = consume(); while (ent.size() < 10 && ch >= ' ' && ch != ';') { ent.add(ch); ch = consume(); } }
/** * Scans a processing instruction. * * @throws IOException I/O exception */ private void pi() throws IOException { final byte[] tok = name(true); if (eq(lc(tok), XML)) error(PIRES); token.add(tok); int ch = nextChar(); if (ch != '?' && !ws(ch)) error(PITEXT); do { while (ch != '?') { token.add(ch); ch = nextChar(); } if ((ch = consume()) == '>') return; token.add('?'); } while (true); }
/** * Scans an attribute value. [10] * * @param ch current character * @throws IOException I/O exception */ private void attValue(final int ch) throws IOException { boolean wrong = false; int c = ch; do { if (c == 0) error(ATTCLOSE, (char) c); wrong |= c == '\'' || c == '"'; if (c == '<') error(wrong ? ATTCLOSE : ATTCHAR, (char) c); if (c == 0x0A) c = ' '; if (c == '&') { // verify... final byte[] r = ref(true); if (r.length == 1) token.add(r); else if (!input.add(r, false)) error(RECENT); } else { token.add(c); } } while ((c = consume()) != quote); }
/** * Scans an external ID. * * @param f full flag * @param r root flag * @return id * @throws IOException I/O exception */ private byte[] externalID(final boolean f, final boolean r) throws IOException { byte[] cont = null; final boolean pub = consume(PUBLIC); if (pub || consume(SYSTEM)) { checkS(); if (pub) { pubidLit(); if (f) checkS(); } final int qu = consume(); // [11] if (qu == '\'' || qu == '"') { int ch; final TokenBuilder tok = new TokenBuilder(); while ((ch = nextChar()) != qu) tok.add(ch); if (!f) return null; final String name = string(tok.finish()); if (!dtd && r) return cont; final XMLInput tin = input; try { final IO file = input.io().merge(name); cont = file.read(); } catch (final IOException ex) { Util.debug(ex); // skip unknown DTDs/entities cont = new byte[] {'?'}; } input = new XMLInput(new IOContent(cont, name)); if (consume(XDECL)) { check(XML); s(); if (version()) checkS(); s(); if (encoding() == null) error(TEXTENC); ch = nextChar(); if (s(ch)) ch = nextChar(); if (ch != '?') error(WRONGCHAR, '?', ch); ch = nextChar(); if (ch != '>') error(WRONGCHAR, '>', ch); cont = Arrays.copyOfRange(cont, input.pos(), cont.length); } s(); if (r) { extSubsetDecl(); if (!consume((char) 0)) error(INVEND); } input = tin; } else { if (f) error(SCANQUOTE, (char) qu); prev(1); } } return cont; }
/** * Scans a comment. * * @throws IOException I/O exception */ private void comment() throws IOException { do { final int ch = nextChar(); if (ch == '-' && consume('-')) { check('>'); return; } token.add(ch); } while (true); }
/** * Scans XML text. * * @param ch current character * @throws IOException I/O exception */ private void content(final int ch) throws IOException { type = Type.TEXT; boolean f = true; int c = ch; while (c != 0) { if (c != '<') { if (c == '&') { // scan entity final byte[] r = ref(true); if (r.length == 1) token.add(r); else if (!input.add(r, false)) error(RECENT); } else { if (c == ']') { // ']]>' not allowed in content if (consume() == ']') { if (consume() == '>') error(CONTCDATA); prev(1); } prev(1); } // add character to cached content token.add(c); } } else { if (!f && !isCDATA()) { text = false; prev(1); if (chop) token.trim(); return; } cDATA(); } c = consume(); f = false; } // end of file if (!fragment) { if (!ws(token.finish())) error(AFTERROOT); type = Type.EOF; } }
/** * Consumes an XML name. [5] * * @param f force parsing * @return name * @throws IOException I/O exception */ private byte[] name(final boolean f) throws IOException { final TokenBuilder name = new TokenBuilder(); int c = consume(); if (!isStartChar(c)) { if (f) error(INVNAME); prev(1); return null; } do name.add(c); while (isChar(c = nextChar())); prev(1); return name.finish(); }
/** * Scans an XML tag. * * @param ch current character * @throws IOException I/O exception */ private void scanTAG(final int ch) throws IOException { int c = ch; // scan tag end... if (c == '>') { type = Type.R_BR; state = State.CONTENT; } else if (c == '=') { // scan equal sign... type = Type.EQ; } else if (c == '\'' || c == '"') { // scan quote... type = Type.QUOTE; state = State.QUOTE; quote = c; } else if (c == '/') { // scan empty tag end... type = Type.CLOSE_R_BR; if ((c = nextChar()) == '>') { state = State.CONTENT; } else { token.add(c); error(CLOSING); } } else if (s(c)) { // scan whitespace... type = Type.WS; } else if (isStartChar(c)) { // scan tag name... type = state == State.ATT ? Type.ATTNAME : Type.ELEMNAME; do token.add(c); while (isChar(c = nextChar())); prev(1); state = State.ATT; } else { // undefined character... error(CHARACTER, (char) c); } }
/** * Scans a document encoding. * * @return encoding * @throws IOException I/O exception */ private String encoding() throws IOException { if (!consume(ENCOD)) { if (fragment) error(TEXTENC); return null; } s(); check('='); s(); final TokenBuilder enc = new TokenBuilder(); final int d = qu(); int ch = nextChar(); if (letter(ch) && ch != '_') { while (letterOrDigit(ch) || ch == '.' || ch == '-') { enc.add(ch); ch = nextChar(); } prev(1); } check((char) d); if (enc.isEmpty()) error(DECLENCODE, enc); final String e = string(enc.finish()); input.encoding(e); return e; }