/*
* XMLUtil.java NanoXML/Java $Revision: 1.5 $ $Date: 2002/02/03 21:19:38 $
* $Name: RELEASE_2_2_1 $ This file is part of NanoXML 2 for Java. Copyright (C)
* 2000-2002 Marc De Scheemaecker, All Rights Reserved. This software is
* provided 'as-is', without any express or implied warranty. In no event will
* the authors be held liable for any damages arising from the use of this
* software. Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and redistribute
* it freely, subject to the following restrictions: 1. The origin of this
* software must not be misrepresented; you must not claim that you wrote the
* original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required. 2.
* Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software. 3. This notice may not be
* removed or altered from any source distribution.
*/
package org.freeplane.n3.nanoxml;
import java.io.IOException;
import java.io.Reader;
/**
* Utility methods for NanoXML.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
*/
class XMLUtil {
/**
* Returns true if the data starts with <I>literal</I>. Enough chars are
* read to determine this result.
*
* @param reader
* the reader
* @param literal
* the literal to check
* @throws java.io.IOException
* if an error occurred reading the data
*/
static boolean checkLiteral(final IXMLReader reader, final String literal) throws IOException, XMLParseException {
for (int i = 0; i < literal.length(); i++) {
if (reader.read() != literal.charAt(i)) {
return false;
}
}
return true;
}
/**
* Throws an XMLParseException to indicate that extra data is encountered in
* a closing tag.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
*/
static void errorClosingTagNotEmpty(final String systemID, final int lineNr) throws XMLParseException {
throw new XMLParseException(systemID, lineNr, "Closing tag must be empty");
}
/**
* Throws an XMLParseException to indicate that an expected string is not
* encountered.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param expectedString
* the string that is expected
*/
static void errorExpectedInput(final String systemID, final int lineNr, final String expectedString)
throws XMLParseException {
throw new XMLParseException(systemID, lineNr, "Expected: " + expectedString);
}
/**
* Throws an XMLValidationException to indicate that an attribute has an
* invalid value.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param elementName
* the name of the element
* @param attributeName
* the name of the attribute
* @param attributeValue
* the value of that attribute
*/
static void errorInvalidAttributeValue(final String systemID, final int lineNr, final String elementName,
final String attributeName, final String attributeValue)
throws XMLValidationException {
throw new XMLValidationException(XMLValidationException.ATTRIBUTE_WITH_INVALID_VALUE, systemID, lineNr,
elementName, attributeName, attributeValue, "Invalid value for attribute " + attributeName);
}
/**
* Throws an XMLParseException to indicate that an entity could not be
* resolved.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param entity
* the name of the entity
*/
static void errorInvalidEntity(final String systemID, final int lineNr, final String entity)
throws XMLParseException {
throw new XMLParseException(systemID, lineNr, "Invalid entity: `&" + entity + ";'");
}
/**
* Throws an XMLParseException to indicate that a string is not expected at
* this point.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param unexpectedString
* the string that is unexpected
*/
static void errorInvalidInput(final String systemID, final int lineNr, final String unexpectedString)
throws XMLParseException {
throw new XMLParseException(systemID, lineNr, "Invalid input: " + unexpectedString);
}
/**
* Throws an XMLValidationException to indicate that an attribute is
* missing.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param elementName
* the name of the element
* @param attributeName
* the name of the missing attribute
*/
static void errorMissingAttribute(final String systemID, final int lineNr, final String elementName,
final String attributeName) throws XMLValidationException {
throw new XMLValidationException(XMLValidationException.MISSING_ATTRIBUTE, systemID, lineNr, elementName,
attributeName,
/* attributeValue */null, "Element " + elementName + " expects an attribute named " + attributeName);
}
/**
* Throws an XMLValidationException to indicate that an element is missing.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param parentElementName
* the name of the parent element
* @param missingElementName
* the name of the missing element
*/
static void errorMissingElement(final String systemID, final int lineNr, final String parentElementName,
final String missingElementName) throws XMLValidationException {
throw new XMLValidationException(XMLValidationException.MISSING_ELEMENT, systemID, lineNr, missingElementName,
/* attributeName */null,
/* attributeValue */null, "Element " + parentElementName + " expects to have a " + missingElementName);
}
/**
* Throws an XMLValidationException to indicate that a #PCDATA element was
* missing.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param parentElementName
* the name of the parent element
*/
static void errorMissingPCData(final String systemID, final int lineNr, final String parentElementName)
throws XMLValidationException {
throw new XMLValidationException(XMLValidationException.MISSING_PCDATA, systemID, lineNr,
/* elementName */null,
/* attributeName */null,
/* attributeValue */null, "Missing #PCDATA in element " + parentElementName);
}
/**
* Throws an XMLValidationException to indicate that an attribute is
* unexpected.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param elementName
* the name of the element
* @param attributeName
* the name of the unexpected attribute
*/
static void errorUnexpectedAttribute(final String systemID, final int lineNr, final String elementName,
final String attributeName) throws XMLValidationException {
throw new XMLValidationException(XMLValidationException.UNEXPECTED_ATTRIBUTE, systemID, lineNr, elementName,
attributeName,
/* attributeValue */null, "Element " + elementName + " did not expect an attribute " + "named "
+ attributeName);
}
/**
* Throws an XMLParseException to indicate that a CDATA section is
* unexpected at this point.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
*/
static void errorUnexpectedCDATA(final String systemID, final int lineNr) throws XMLParseException {
throw new XMLParseException(systemID, lineNr, "No CDATA section is expected here");
}
/**
* Throws an XMLValidationException to indicate that an element is
* unexpected.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param parentElementName
* the name of the parent element
* @param unexpectedElementName
* the name of the unexpected element
*/
static void errorUnexpectedElement(final String systemID, final int lineNr, final String parentElementName,
final String unexpectedElementName) throws XMLValidationException {
throw new XMLValidationException(XMLValidationException.UNEXPECTED_ELEMENT, systemID, lineNr,
unexpectedElementName,
/* attributeName */null,
/* attributeValue */null, "Unexpected " + unexpectedElementName + " in a " + parentElementName);
}
/**
* Throws an XMLParseException to indicate that an entity reference is
* unexpected at this point.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param entity
* the name of the entity
*/
static void errorUnexpectedEntity(final String systemID, final int lineNr, final String entity)
throws XMLParseException {
throw new XMLParseException(systemID, lineNr, "No entity reference is expected here (" + entity + ")");
}
/**
* Throws an XMLValidationException to indicate that a #PCDATA element was
* unexpected.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param parentElementName
* the name of the parent element
*/
static void errorUnexpectedPCData(final String systemID, final int lineNr, final String parentElementName)
throws XMLValidationException {
throw new XMLValidationException(XMLValidationException.UNEXPECTED_PCDATA, systemID, lineNr,
/* elementName */null,
/* attributeName */null,
/* attributeValue */null, "Unexpected #PCDATA in element " + parentElementName);
}
/**
* Throws an XMLParseException to indicate that the closing tag of an
* element does not match the opening tag.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param expectedName
* the name of the opening tag
* @param wrongName
* the name of the closing tag
*/
static void errorWrongClosingTag(final String systemID, final int lineNr, final String expectedName,
final String wrongName) throws XMLParseException {
throw new XMLParseException(systemID, lineNr, "Closing tag does not match opening tag: `" + wrongName
+ "' != `" + expectedName + "'");
}
/**
* Processes a character literal.
*
* @param entity
* the entity
* @throws java.io.IOException
* if an error occurred reading the data
*/
static char processCharLiteral(String entity) throws IOException, XMLParseException {
if (entity.charAt(2) == 'x') {
entity = entity.substring(3, entity.length() - 1);
return (char) Integer.parseInt(entity, 16);
}
else {
entity = entity.substring(2, entity.length() - 1);
return (char) Integer.parseInt(entity, 10);
}
}
/**
* Processes an entity.
*
* @param entity
* the entity
* @param reader
* the reader
* @param entityResolver
* the entity resolver
* @throws java.io.IOException
* if an error occurred reading the data
*/
static void processEntity(String entity, final IXMLReader reader, final IXMLEntityResolver entityResolver)
throws IOException, XMLParseException {
entity = entity.substring(1, entity.length() - 1);
final Reader entityReader = entityResolver.getEntity(reader, entity);
if (entityReader == null) {
XMLUtil.errorInvalidEntity(reader.getSystemID(), reader.getLineNr(), entity);
}
final boolean externalEntity = entityResolver.isExternalEntity(entity);
reader.startNewStream(entityReader, !externalEntity);
}
/**
* Reads a character from the reader.
*
* @param reader
* the reader
* @param entityChar
* the escape character (& or %) used to indicate an entity
* @return the character, or an entity expression (like e.g. <)
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String read(final IXMLReader reader, final char entityChar) throws IOException, XMLParseException {
char ch = reader.read();
final StringBuilder buf = new StringBuilder();
buf.append(ch);
if (ch == entityChar) {
while (ch != ';') {
ch = reader.read();
buf.append(ch);
}
}
return buf.toString();
}
/**
* Reads a character from the reader disallowing entities.
*
* @param reader
* the reader
* @param entityChar
* the escape character (& or %) used to indicate an entity
*/
static char readChar(final IXMLReader reader, final char entityChar) throws IOException, XMLParseException {
final String str = XMLUtil.read(reader, entityChar);
final char ch = str.charAt(0);
if (ch == entityChar) {
XMLUtil.errorUnexpectedEntity(reader.getSystemID(), reader.getLineNr(), str);
}
return ch;
}
/**
* Retrieves an identifier from the data.
*
* @param reader
* the reader
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String scanIdentifier(final IXMLReader reader) throws IOException, XMLParseException {
final StringBuilder result = new StringBuilder();
for (;;) {
final char ch = reader.read();
if ((ch == '_') || (ch == ':') || (ch == '-') || (ch == '.') || ((ch >= 'a') && (ch <= 'z'))
|| ((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) || (ch > '\u007E')) {
result.append(ch);
}
else {
reader.unread(ch);
break;
}
}
return result.toString();
}
/**
* Scans a public ID.
*
* @param publicID
* will contain the public ID
* @param reader
* the reader
* @return the system ID
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String scanPublicID(final StringBuilder publicID, final IXMLReader reader) throws IOException,
XMLParseException {
if (!XMLUtil.checkLiteral(reader, "UBLIC")) {
return null;
}
XMLUtil.skipWhitespace(reader, null);
publicID.append(XMLUtil.scanString(reader, '\0', null));
XMLUtil.skipWhitespace(reader, null);
return XMLUtil.scanString(reader, '\0', null);
}
/**
* Retrieves a delimited string from the data.
*
* @param reader
* the reader
* @param entityChar
* the escape character (& or %)
* @param entityResolver
* the entity resolver
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String scanString(final IXMLReader reader, final char entityChar, final IXMLEntityResolver entityResolver)
throws IOException, XMLParseException {
final StringBuilder result = new StringBuilder();
final int startingLevel = reader.getStreamLevel();
final char delim = reader.read();
if ((delim != '\'') && (delim != '"')) {
XMLUtil.errorExpectedInput(reader.getSystemID(), reader.getLineNr(), "delimited string");
}
for (;;) {
String str = XMLUtil.read(reader, entityChar);
final char ch = str.charAt(0);
if (ch == entityChar) {
if (str.charAt(1) == '#') {
result.append(XMLUtil.processCharLiteral(str));
}
else {
XMLUtil.processEntity(str, reader, entityResolver);
}
}
else if (ch == '&') {
reader.unread(ch);
str = XMLUtil.read(reader, '&');
if (str.charAt(1) == '#') {
result.append(XMLUtil.processCharLiteral(str));
}
else {
result.append(str);
}
}
else if (reader.getStreamLevel() == startingLevel) {
if (ch == delim) {
break;
}
else if ((ch == 9) || (ch == 10) || (ch == 13)) {
result.append(' ');
}
else {
result.append(ch);
}
}
else {
result.append(ch);
}
}
return result.toString();
}
/**
* Scans a system ID.
*
* @param reader
* the reader
* @return the system ID
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String scanSystemID(final IXMLReader reader) throws IOException, XMLParseException {
if (!XMLUtil.checkLiteral(reader, "YSTEM")) {
return null;
}
XMLUtil.skipWhitespace(reader, null);
return XMLUtil.scanString(reader, '\0', null);
}
/**
* Skips the remainder of a comment. It is assumed that <!- is already
* read.
*
* @param reader
* the reader
* @throws java.io.IOException
* if an error occurred reading the data
*/
static void skipComment(final IXMLReader reader) throws IOException, XMLParseException {
if (reader.read() != '-') {
XMLUtil.errorExpectedInput(reader.getSystemID(), reader.getLineNr(), "<!--");
}
int dashesRead = 0;
for (;;) {
final char ch = reader.read();
switch (ch) {
case '-':
dashesRead++;
break;
case '>':
if (dashesRead == 2) {
return;
}
default:
dashesRead = 0;
}
}
}
/**
* Skips the remainder of the current XML tag.
*
* @param reader
* the reader
* @throws java.io.IOException
* if an error occurred reading the data
*/
static void skipTag(final IXMLReader reader) throws IOException, XMLParseException {
int level = 1;
while (level > 0) {
final char ch = reader.read();
switch (ch) {
case '<':
++level;
break;
case '>':
--level;
break;
}
}
}
/**
* Skips whitespace from the reader.
*
* @param reader
* the reader
* @param buffer
* where to put the whitespace; null if the whitespace does not
* have to be stored.
* @throws java.io.IOException
* if an error occurred reading the data
*/
static void skipWhitespace(final IXMLReader reader, final StringBuilder buffer) throws IOException {
char ch;
if (buffer == null) {
do {
ch = reader.read();
} while ((ch == ' ') || (ch == '\t') || (ch == '\n'));
}
else {
for (;;) {
ch = reader.read();
if ((ch != ' ') && (ch != '\t') && (ch != '\n')) {
break;
}
if (ch == '\n') {
buffer.append('\n');
}
else {
buffer.append(' ');
}
}
}
reader.unread(ch);
}
/**
* Throws an XMLValidationException.
*
* @param systemID
* the system ID of the data source
* @param lineNr
* the line number in the data source
* @param message
* the error message
* @param elementName
* the name of the element
* @param attributeName
* the name of the attribute
* @param attributeValue
* the value of that attribute
*/
static void validationError(final String systemID, final int lineNr, final String message,
final String elementName, final String attributeName, final String attributeValue)
throws XMLValidationException {
throw new XMLValidationException(XMLValidationException.MISC_ERROR, systemID, lineNr, elementName,
attributeName, attributeValue, message);
}
}