SgmlReader implements Closeable { public Token readToken() throws IOException { buffer.delete(0, buffer.length()); TokenType tokenType = null; int c; while ((c = reader.read()) != -1) { switch (c) { case '<': if (tokenType == null) { tokenType = TokenType.OPEN_TAG; } else if (tokenType == TokenType.DATA) { reader.unread(c); return new Token(tokenType, buffer.toString().trim()); } else { throw new InvalidFileFormatException("Unexpected character '<'"); } break; case '/': if (tokenType == TokenType.OPEN_TAG && buffer.toString().trim().length() == 0) { tokenType = TokenType.CLOSE_TAG; } else if (tokenType == TokenType.DATA) { buffer.append((char) c); } else { throw new InvalidFileFormatException("Unexpected character '/'"); } break; case '>': if (tokenType == TokenType.OPEN_TAG || tokenType == TokenType.CLOSE_TAG) { String tagName = buffer.toString(); if (tagName.length() == 0) { throw new InvalidFileFormatException(format("Invalid tag \"{0}\"", tagName)); } return new Token(tokenType, tagName); } else { throw new InvalidFileFormatException("Unexpected character '>'"); } case '&': String entity = ""; while ((c = reader.read()) != -1 && c != ';') { entity += (char) c; } if (tokenType == null) { tokenType = TokenType.DATA; } buffer.append(EntityReferences.instance().namedEntityToString(entity)); break; default: boolean isWhitespace = Character.isWhitespace((char) c); if (tokenType == null) { if (!isWhitespace) { tokenType = TokenType.DATA; buffer.append((char) c); } } else if ((tokenType == TokenType.OPEN_TAG || tokenType == TokenType.CLOSE_TAG) && isWhitespace) { throw new InvalidFileFormatException("Unexpected whitespace"); } else { buffer.append((char) c); } } } if (tokenType == TokenType.DATA) { return new Token(tokenType, buffer.toString().trim()); } else if (tokenType != null) { throw new InvalidFileFormatException("Unexpected end of data"); } return null; } SgmlReader(Reader reader); Token readToken(); @Override void close();  }
@Test public void none() throws IOException { SgmlReader reader = newReader(""); assertNull(reader.readToken()); assertNull(reader.readToken()); }
@Test public void singleTag() throws IOException { SgmlReader reader = newReader("<SDF>"); Token token = reader.readToken(); assertToken(Token.openTag("SDF"), token); assertNull(reader.readToken()); }
@Test public void openCloseTag() throws IOException { SgmlReader reader = newReader("<SDF>\n</SDF>"); assertToken(Token.openTag("SDF"), reader.readToken()); assertToken(Token.closeTag("SDF"), reader.readToken()); assertNull(reader.readToken()); }
@Test public void tagsAndData() throws IOException { SgmlReader reader = newReader("<SDF>\n<QRS>SOME DATA 1.22\n</SDF>"); assertToken(Token.openTag("SDF"), reader.readToken()); assertToken(Token.openTag("QRS"), reader.readToken()); assertToken(Token.data("SOME DATA 1.22"), reader.readToken()); assertToken(Token.closeTag("SDF"), reader.readToken()); assertNull(reader.readToken()); }
@Test public void dataWithSlash() throws IOException { SgmlReader reader = newReader("ab/c"); assertToken(Token.data("ab/c"), reader.readToken()); assertNull(reader.readToken()); }
@Test public void malformedUnexpectedOpen() throws IOException { SgmlReader reader = newReader("<<"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedUnexpectedWhitespaceInOpenTag() throws IOException { SgmlReader reader = newReader("< A"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedUnexpectedWhitespaceInOpenTag2() throws IOException { SgmlReader reader = newReader("<A B>"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedUnexpectedWhitespaceInOpenTag3() throws IOException { SgmlReader reader = newReader("<A\nB>"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedUnexpectedEndOfData() throws IOException { SgmlReader reader = newReader("<A"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedUnexpectedWhitespaceInCloseTag() throws IOException { SgmlReader reader = newReader("</ A"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedEmptyOpenTag() throws IOException { SgmlReader reader = newReader("<>"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedOpenTagWithSlash() throws IOException { SgmlReader reader = newReader("<AB/C>"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedEmptyCloseTag() throws IOException { SgmlReader reader = newReader("</>"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void malformedDataWithGt() throws IOException { SgmlReader reader = newReader("A>"); thrown.expect(InvalidFileFormatException.class); reader.readToken(); }
@Test public void entities() throws IOException { SgmlReader reader = newReader("<SDF>\n<QRS>One &amp; Two &#195;</SDF>"); assertToken(Token.openTag("SDF"), reader.readToken()); assertToken(Token.openTag("QRS"), reader.readToken()); assertToken(Token.data("One & Two Ã"), reader.readToken()); assertToken(Token.closeTag("SDF"), reader.readToken()); assertNull(reader.readToken()); }