mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-04 15:24:12 +01:00
627 lines
16 KiB
Java
627 lines
16 KiB
Java
/* StdXMLReader.java NanoXML/Java
|
|
*
|
|
* $Revision: 1.4 $
|
|
* $Date: 2002/01/04 21:03:28 $
|
|
* $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.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.IOException;
|
|
//import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.LineNumberReader;
|
|
import java.io.PushbackReader;
|
|
import java.io.PushbackInputStream;
|
|
import java.io.Reader;
|
|
import java.io.StringReader;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.util.Stack;
|
|
|
|
|
|
/**
|
|
* StdXMLReader reads the data to be parsed.
|
|
*
|
|
* @author Marc De Scheemaecker
|
|
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
|
|
*/
|
|
public class StdXMLReader
|
|
{
|
|
|
|
/**
|
|
* A stacked reader.
|
|
*
|
|
* @author Marc De Scheemaecker
|
|
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
|
|
*/
|
|
private class StackedReader
|
|
{
|
|
|
|
PushbackReader pbReader;
|
|
|
|
LineNumberReader lineReader;
|
|
|
|
URL systemId;
|
|
|
|
String publicId;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* The stack of readers.
|
|
*/
|
|
private Stack<StackedReader> readers;
|
|
|
|
|
|
/**
|
|
* The current push-back reader.
|
|
*/
|
|
private StackedReader currentReader;
|
|
|
|
|
|
/**
|
|
* Creates a new reader using a string as input.
|
|
*
|
|
* @param str the string containing the XML data
|
|
*/
|
|
public static StdXMLReader stringReader(String str)
|
|
{
|
|
return new StdXMLReader(new StringReader(str));
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a new reader using a file as input.
|
|
*
|
|
* @param filename the name of the file containing the XML data
|
|
*
|
|
* @throws java.io.FileNotFoundException
|
|
* if the file could not be found
|
|
* @throws java.io.IOException
|
|
* if an I/O error occurred
|
|
*/
|
|
public static StdXMLReader fileReader(String filename)
|
|
throws FileNotFoundException,
|
|
IOException
|
|
{
|
|
StdXMLReader r = new StdXMLReader(new FileInputStream(filename));
|
|
r.setSystemID(filename);
|
|
|
|
for (int i = 0; i < r.readers.size(); i++) {
|
|
StackedReader sr = (StackedReader) r.readers.elementAt(i);
|
|
sr.systemId = r.currentReader.systemId;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
/**
|
|
* Initializes the reader from a system and public ID.
|
|
*
|
|
* @param publicID the public ID which may be null.
|
|
* @param systemID the non-null system ID.
|
|
*
|
|
* @throws MalformedURLException
|
|
* if the system ID does not contain a valid URL
|
|
* @throws FileNotFoundException
|
|
* if the system ID refers to a local file which does not exist
|
|
* @throws IOException
|
|
* if an error occurred opening the stream
|
|
*/
|
|
public StdXMLReader(String publicID,
|
|
String systemID)
|
|
throws MalformedURLException,
|
|
FileNotFoundException,
|
|
IOException
|
|
{
|
|
URL systemIDasURL = null;
|
|
|
|
try {
|
|
systemIDasURL = new URL(systemID);
|
|
} catch (MalformedURLException e) {
|
|
systemID = "file:" + systemID;
|
|
|
|
try {
|
|
systemIDasURL = new URL(systemID);
|
|
} catch (MalformedURLException e2) {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
this.currentReader = new StackedReader();
|
|
this.readers = new Stack<StackedReader>();
|
|
Reader reader = this.openStream(publicID, systemIDasURL.toString());
|
|
this.currentReader.lineReader = new LineNumberReader(reader);
|
|
this.currentReader.pbReader
|
|
= new PushbackReader(this.currentReader.lineReader, 2);
|
|
}
|
|
|
|
|
|
/**
|
|
* Initializes the XML reader.
|
|
*
|
|
* @param reader the input for the XML data.
|
|
*/
|
|
public StdXMLReader(Reader reader)
|
|
{
|
|
this.currentReader = new StackedReader();
|
|
this.readers = new Stack<StackedReader>();
|
|
this.currentReader.lineReader = new LineNumberReader(reader);
|
|
this.currentReader.pbReader
|
|
= new PushbackReader(this.currentReader.lineReader, 2);
|
|
this.currentReader.publicId = "";
|
|
|
|
try {
|
|
this.currentReader.systemId = new URL("file:.");
|
|
} catch (MalformedURLException e) {
|
|
// never happens
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Cleans up the object when it's destroyed.
|
|
*/
|
|
protected void finalize()
|
|
throws Throwable
|
|
{
|
|
this.currentReader.lineReader = null;
|
|
this.currentReader.pbReader = null;
|
|
this.currentReader.systemId = null;
|
|
this.currentReader.publicId = null;
|
|
this.currentReader = null;
|
|
this.readers.clear();
|
|
super.finalize();
|
|
}
|
|
|
|
|
|
/**
|
|
* Scans the encoding from an <?xml...?> tag.
|
|
*
|
|
* @param str the first tag in the XML data.
|
|
*
|
|
* @return the encoding, or null if no encoding has been specified.
|
|
*/
|
|
protected String getEncoding(String str)
|
|
{
|
|
if (! str.startsWith("<?xml")) {
|
|
return null;
|
|
}
|
|
|
|
int index = 5;
|
|
|
|
while (index < str.length()) {
|
|
StringBuffer key = new StringBuffer();
|
|
|
|
while ((index < str.length()) && (str.charAt(index) <= ' ')) {
|
|
index++;
|
|
}
|
|
|
|
while ((index < str.length())
|
|
&& (str.charAt(index) >= 'a')
|
|
&& (str.charAt(index) <= 'z')) {
|
|
key.append(str.charAt(index));
|
|
index++;
|
|
}
|
|
|
|
while ((index < str.length()) && (str.charAt(index) <= ' ')) {
|
|
index++;
|
|
}
|
|
|
|
if ((index >= str.length()) || (str.charAt(index) != '=')) {
|
|
break;
|
|
}
|
|
|
|
while ((index < str.length()) && (str.charAt(index) != '\'')
|
|
&& (str.charAt(index) != '"')) {
|
|
index++;
|
|
}
|
|
|
|
if (index >= str.length()) {
|
|
break;
|
|
}
|
|
|
|
char delimiter = str.charAt(index);
|
|
index++;
|
|
int index2 = str.indexOf(delimiter, index);
|
|
|
|
if (index2 < 0) {
|
|
break;
|
|
}
|
|
|
|
if (key.toString().equals("encoding")) {
|
|
return str.substring(index, index2);
|
|
}
|
|
|
|
index = index2 + 1;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Converts a stream to a reader while detecting the encoding.
|
|
*
|
|
* @param stream the input for the XML data.
|
|
* @param charsRead buffer where to put characters that have been read
|
|
*
|
|
* @throws java.io.IOException
|
|
* if an I/O error occurred
|
|
*/
|
|
protected Reader stream2reader(InputStream stream,
|
|
StringBuffer charsRead)
|
|
throws IOException
|
|
{
|
|
PushbackInputStream pbstream = new PushbackInputStream(stream);
|
|
int b = pbstream.read();
|
|
|
|
switch (b) {
|
|
case 0x00:
|
|
case 0xFE:
|
|
case 0xFF:
|
|
pbstream.unread(b);
|
|
return new InputStreamReader(pbstream, "UTF-16");
|
|
|
|
case 0xEF:
|
|
for (int i = 0; i < 2; i++) {
|
|
pbstream.read();
|
|
}
|
|
|
|
return new InputStreamReader(pbstream, "UTF-8");
|
|
|
|
case 0x3C:
|
|
b = pbstream.read();
|
|
charsRead.append('<');
|
|
|
|
while ((b > 0) && (b != 0x3E)) {
|
|
charsRead.append((char) b);
|
|
b = pbstream.read();
|
|
}
|
|
|
|
if (b > 0) {
|
|
charsRead.append((char) b);
|
|
}
|
|
|
|
String encoding = this.getEncoding(charsRead.toString());
|
|
|
|
if (encoding == null) {
|
|
return new InputStreamReader(pbstream, "UTF-8");
|
|
}
|
|
|
|
charsRead.setLength(0);
|
|
|
|
try {
|
|
return new InputStreamReader(pbstream, encoding);
|
|
} catch (UnsupportedEncodingException e) {
|
|
return new InputStreamReader(pbstream, "UTF-8");
|
|
}
|
|
|
|
default:
|
|
charsRead.append((char) b);
|
|
return new InputStreamReader(pbstream, "UTF-8");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Initializes the XML reader.
|
|
*
|
|
* @param stream the input for the XML data.
|
|
*
|
|
* @throws java.io.IOException
|
|
* if an I/O error occurred
|
|
*/
|
|
public StdXMLReader(InputStream stream)
|
|
throws IOException
|
|
{
|
|
// unused?
|
|
//PushbackInputStream pbstream = new PushbackInputStream(stream);
|
|
StringBuffer charsRead = new StringBuffer();
|
|
Reader reader = this.stream2reader(stream, charsRead);
|
|
this.currentReader = new StackedReader();
|
|
this.readers = new Stack<StackedReader>();
|
|
this.currentReader.lineReader = new LineNumberReader(reader);
|
|
this.currentReader.pbReader
|
|
= new PushbackReader(this.currentReader.lineReader, 2);
|
|
this.currentReader.publicId = "";
|
|
|
|
try {
|
|
this.currentReader.systemId = new URL("file:.");
|
|
} catch (MalformedURLException e) {
|
|
// never happens
|
|
}
|
|
|
|
this.startNewStream(new StringReader(charsRead.toString()));
|
|
}
|
|
|
|
|
|
/**
|
|
* Reads a character.
|
|
*
|
|
* @return the character
|
|
*
|
|
* @throws java.io.IOException
|
|
* if no character could be read
|
|
*/
|
|
public char read()
|
|
throws IOException
|
|
{
|
|
int ch = this.currentReader.pbReader.read();
|
|
|
|
while (ch < 0) {
|
|
if (this.readers.empty()) {
|
|
throw new IOException("Unexpected EOF");
|
|
}
|
|
|
|
this.currentReader.pbReader.close();
|
|
this.currentReader = (StackedReader) this.readers.pop();
|
|
ch = this.currentReader.pbReader.read();
|
|
}
|
|
|
|
return (char) ch;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns true if the current stream has no more characters left to be
|
|
* read.
|
|
*
|
|
* @throws java.io.IOException
|
|
* if an I/O error occurred
|
|
*/
|
|
public boolean atEOFOfCurrentStream()
|
|
throws IOException
|
|
{
|
|
int ch = this.currentReader.pbReader.read();
|
|
|
|
if (ch < 0) {
|
|
return true;
|
|
} else {
|
|
this.currentReader.pbReader.unread(ch);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns true if there are no more characters left to be read.
|
|
*
|
|
* @throws java.io.IOException
|
|
* if an I/O error occurred
|
|
*/
|
|
public boolean atEOF()
|
|
throws IOException
|
|
{
|
|
int ch = this.currentReader.pbReader.read();
|
|
|
|
while (ch < 0) {
|
|
if (this.readers.empty()) {
|
|
return true;
|
|
}
|
|
|
|
this.currentReader.pbReader.close();
|
|
this.currentReader = (StackedReader) this.readers.pop();
|
|
ch = this.currentReader.pbReader.read();
|
|
}
|
|
|
|
this.currentReader.pbReader.unread(ch);
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Pushes the last character read back to the stream.
|
|
*
|
|
* @param ch the character to push back.
|
|
*
|
|
* @throws java.io.IOException
|
|
* if an I/O error occurred
|
|
*/
|
|
public void unread(char ch)
|
|
throws IOException
|
|
{
|
|
this.currentReader.pbReader.unread(ch);
|
|
}
|
|
|
|
|
|
/**
|
|
* Opens a stream from a public and system ID.
|
|
*
|
|
* @param publicID the public ID, which may be null
|
|
* @param systemID the system ID, which is never null
|
|
*
|
|
* @throws java.net.MalformedURLException
|
|
* if the system ID does not contain a valid URL
|
|
* @throws java.io.FileNotFoundException
|
|
* if the system ID refers to a local file which does not exist
|
|
* @throws java.io.IOException
|
|
* if an error occurred opening the stream
|
|
*/
|
|
public Reader openStream(String publicID,
|
|
String systemID)
|
|
throws MalformedURLException,
|
|
FileNotFoundException,
|
|
IOException
|
|
{
|
|
URL url = new URL(this.currentReader.systemId, systemID);
|
|
|
|
if (url.getRef() != null) {
|
|
String ref = url.getRef();
|
|
|
|
if (url.getFile().length() > 0) {
|
|
url = new URL(url.getProtocol(), url.getHost(), url.getPort(),
|
|
url.getFile());
|
|
url = new URL("jar:" + url + '!' + ref);
|
|
} else {
|
|
url = StdXMLReader.class.getResource(ref);
|
|
}
|
|
}
|
|
|
|
this.currentReader.publicId = publicID;
|
|
this.currentReader.systemId = url;
|
|
StringBuffer charsRead = new StringBuffer();
|
|
Reader reader = this.stream2reader(url.openStream(), charsRead);
|
|
|
|
if (charsRead.length() == 0) {
|
|
return reader;
|
|
}
|
|
|
|
String charsReadStr = charsRead.toString();
|
|
PushbackReader pbreader = new PushbackReader(reader,
|
|
charsReadStr.length());
|
|
|
|
for (int i = charsReadStr.length() - 1; i >= 0; i--) {
|
|
pbreader.unread(charsReadStr.charAt(i));
|
|
}
|
|
|
|
return pbreader;
|
|
}
|
|
|
|
|
|
/**
|
|
* Starts a new stream from a Java reader. The new stream is used
|
|
* temporary to read data from. If that stream is exhausted, control
|
|
* returns to the parent stream.
|
|
*
|
|
* @param reader the non-null reader to read the new data from
|
|
*/
|
|
public void startNewStream(Reader reader)
|
|
{
|
|
this.startNewStream(reader, false);
|
|
}
|
|
|
|
|
|
/**
|
|
* Starts a new stream from a Java reader. The new stream is used
|
|
* temporary to read data from. If that stream is exhausted, control
|
|
* returns to the parent stream.
|
|
*
|
|
* @param reader the non-null reader to read the new data from
|
|
* @param isInternalEntity true if the reader is produced by resolving
|
|
* an internal entity
|
|
*/
|
|
public void startNewStream(Reader reader,
|
|
boolean isInternalEntity)
|
|
{
|
|
StackedReader oldReader = this.currentReader;
|
|
this.readers.push(this.currentReader);
|
|
this.currentReader = new StackedReader();
|
|
|
|
if (isInternalEntity) {
|
|
this.currentReader.lineReader = null;
|
|
this.currentReader.pbReader = new PushbackReader(reader, 2);
|
|
} else {
|
|
this.currentReader.lineReader = new LineNumberReader(reader);
|
|
this.currentReader.pbReader
|
|
= new PushbackReader(this.currentReader.lineReader, 2);
|
|
}
|
|
|
|
this.currentReader.systemId = oldReader.systemId;
|
|
this.currentReader.publicId = oldReader.publicId;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the current "level" of the stream on the stack of streams.
|
|
*/
|
|
public int getStreamLevel()
|
|
{
|
|
return this.readers.size();
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the line number of the data in the current stream.
|
|
*/
|
|
public int getLineNr()
|
|
{
|
|
if (this.currentReader.lineReader == null) {
|
|
StackedReader sr = (StackedReader) this.readers.peek();
|
|
|
|
if (sr.lineReader == null) {
|
|
return 0;
|
|
} else {
|
|
return sr.lineReader.getLineNumber() + 1;
|
|
}
|
|
}
|
|
|
|
return this.currentReader.lineReader.getLineNumber() + 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the system ID of the current stream.
|
|
*
|
|
* @param systemID the system ID
|
|
*
|
|
* @throws java.net.MalformedURLException
|
|
* if the system ID does not contain a valid URL
|
|
*/
|
|
public void setSystemID(String systemID)
|
|
throws MalformedURLException
|
|
{
|
|
this.currentReader.systemId = new URL(this.currentReader.systemId,
|
|
systemID);
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the public ID of the current stream.
|
|
*
|
|
* @param publicID the public ID
|
|
*/
|
|
public void setPublicID(String publicID)
|
|
{
|
|
this.currentReader.publicId = publicID;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the current system ID.
|
|
*/
|
|
public String getSystemID()
|
|
{
|
|
return this.currentReader.systemId.toString();
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the current public ID.
|
|
*/
|
|
public String getPublicID()
|
|
{
|
|
return this.currentReader.publicId;
|
|
}
|
|
|
|
}
|