class 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;
	}

	public  SgmlReader(Reader reader);

}