/* 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 processing.xml; 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 { /** * 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(StdXMLReader reader) throws IOException, XMLParseException { if (reader.read() != '-') { XMLUtil.errorExpectedInput(reader.getSystemID(), reader.getLineNr(), "<!--"); } int dashesRead = 0; for (;;) { char ch = reader.read(); switch (ch) { case '-': dashesRead++; break; case '>': if (dashesRead == 2) { return; } dashesRead = 0; break; 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(StdXMLReader reader) throws IOException, XMLParseException { int level = 1; while (level > 0) { char ch = reader.read(); switch (ch) { case '<': ++level; break; case '>': --level; break; } } } /** * 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(StringBuffer publicID, StdXMLReader 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); } /** * 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(StdXMLReader reader) throws IOException, XMLParseException { if (! XMLUtil.checkLiteral(reader, "YSTEM")) { return null; } XMLUtil.skipWhitespace(reader, null); return XMLUtil.scanString(reader, '\0', null); } /** * Retrieves an identifier from the data. * * @param reader the reader * * @throws java.io.IOException * if an error occurred reading the data */ static String scanIdentifier(StdXMLReader reader) throws IOException, XMLParseException { StringBuffer result = new StringBuffer(); for (;;) { 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(); } /** * 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(StdXMLReader reader, char entityChar, XMLEntityResolver entityResolver) throws IOException, XMLParseException { StringBuffer result = new StringBuffer(); int startingLevel = reader.getStreamLevel(); char delim = reader.read(); if ((delim != '\'') && (delim != '"')) { XMLUtil.errorExpectedInput(reader.getSystemID(), reader.getLineNr(), "delimited string"); } for (;;) { String str = XMLUtil.read(reader, entityChar); 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(); } /** * 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, StdXMLReader reader, XMLEntityResolver entityResolver) throws IOException, XMLParseException { entity = entity.substring(1, entity.length() - 1); Reader entityReader = entityResolver.getEntity(reader, entity); if (entityReader == null) { XMLUtil.errorInvalidEntity(reader.getSystemID(), reader.getLineNr(), entity); } boolean externalEntity = entityResolver.isExternalEntity(entity); reader.startNewStream(entityReader, !externalEntity); } /** * 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); } } /** * 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(StdXMLReader reader, StringBuffer 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); } /** * 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. &lt;) * * @throws java.io.IOException * if an error occurred reading the data */ static String read(StdXMLReader reader, char entityChar) throws IOException, XMLParseException { char ch = reader.read(); StringBuffer buf = new StringBuffer(); 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(StdXMLReader reader, char entityChar) throws IOException, XMLParseException { String str = XMLUtil.read(reader, entityChar); char ch = str.charAt(0); if (ch == entityChar) { XMLUtil.errorUnexpectedEntity(reader.getSystemID(), reader.getLineNr(), str); } return ch; } /** * 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(StdXMLReader reader, 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 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(String systemID, int lineNr, String expectedString) throws XMLParseException { throw new XMLParseException(systemID, lineNr, "Expected: " + expectedString); } /** * 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(String systemID, int lineNr, String entity) throws XMLParseException { throw new XMLParseException(systemID, lineNr, "Invalid entity: `&" + entity + ";'"); } /** * 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(String systemID, int lineNr, String entity) throws XMLParseException { throw new XMLParseException(systemID, lineNr, "No entity reference is expected here (" + entity + ")"); } /** * 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(String systemID, int lineNr) throws XMLParseException { throw new XMLParseException(systemID, lineNr, "No CDATA section is expected here"); } /** * 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(String systemID, int lineNr, String unexpectedString) throws XMLParseException { throw new XMLParseException(systemID, lineNr, "Invalid input: " + unexpectedString); } /** * 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(String systemID, int lineNr, String expectedName, String wrongName) throws XMLParseException { throw new XMLParseException(systemID, lineNr, "Closing tag does not match opening tag: `" + wrongName + "' != `" + expectedName + "'"); } /** * 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(String systemID, int lineNr) throws XMLParseException { throw new XMLParseException(systemID, lineNr, "Closing tag must be empty"); } /** * 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(String systemID, int lineNr, String parentElementName, 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 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(String systemID, int lineNr, String parentElementName, String unexpectedElementName) throws XMLValidationException { throw new XMLValidationException( XMLValidationException.UNEXPECTED_ELEMENT, systemID, lineNr, unexpectedElementName, /*attributeName*/ null, /*attributeValue*/ null, "Unexpected " + unexpectedElementName + " in a " + parentElementName); } /** * 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(String systemID, int lineNr, String elementName, 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 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(String systemID, int lineNr, String elementName, 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 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(String systemID, int lineNr, String elementName, String attributeName, String attributeValue) throws XMLValidationException { throw new XMLValidationException( XMLValidationException.ATTRIBUTE_WITH_INVALID_VALUE, systemID, lineNr, elementName, attributeName, attributeValue, "Invalid value for attribute " + attributeName); } /** * 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(String systemID, int lineNr, 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 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(String systemID, int lineNr, 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 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(String systemID, int lineNr, String message, String elementName, String attributeName, String attributeValue) throws XMLValidationException { throw new XMLValidationException(XMLValidationException.MISC_ERROR, systemID, lineNr, elementName, attributeName, attributeValue, message); } }