| /* 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); |
| } |
| |
| } |