workaround for < and > in XML attributes

git-svn-id: https://public-transport-enabler.googlecode.com/svn/trunk@704 0924bc21-9374-b0fa-ee44-9ff1593b38f0
This commit is contained in:
andreas.schildbach@gmail.com 2011-06-16 20:48:29 +00:00
parent 330f1d5dca
commit c99e99b0f7
4 changed files with 690 additions and 7 deletions

View file

@ -19,6 +19,7 @@ package de.schildbach.pte;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@ -53,6 +54,7 @@ import de.schildbach.pte.dto.StationDepartures;
import de.schildbach.pte.dto.Stop;
import de.schildbach.pte.util.Color;
import de.schildbach.pte.util.ParserUtils;
import de.schildbach.pte.util.StringReplaceReader;
import de.schildbach.pte.util.XmlPullUtil;
/**
@ -426,17 +428,20 @@ public abstract class AbstractHafasProvider implements NetworkProvider
protected QueryDeparturesResult xmlQueryDepartures(final String uri, final int stationId) throws IOException
{
// System.out.println(uri);
InputStream is = null;
StringReplaceReader reader = null;
try
{
is = ParserUtils.scrapeInputStream(uri);
reader = new StringReplaceReader(new InputStreamReader(ParserUtils.scrapeInputStream(uri), DEFAULT_ENCODING), "Ringbahn ->",
"Ringbahn -&gt;");
reader.replace("Ringbahn <-", "Ringbahn &lt;-");
// System.out.println(uri);
// ParserUtils.printFromReader(reader);
final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null);
final XmlPullParser pp = factory.newPullParser();
pp.setInput(is, DEFAULT_ENCODING);
pp.setInput(reader);
pp.nextTag();
@ -589,8 +594,8 @@ public abstract class AbstractHafasProvider implements NetworkProvider
}
finally
{
if (is != null)
is.close();
if (reader != null)
reader.close();
}
}

View file

@ -0,0 +1,299 @@
package de.schildbach.pte.util;
/*
* Copyright (C) 1997 Roger Whitney <whitney@cs.sdsu.edu>
*
* This file is part of the San Diego State University Java Library.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/**
* This class implements a characater queue. Yes the JKD does contain a general queue. However that queue operates on
* objects. This queue just handles char elements. Use in IO operations where converting chars to objects will be too
* expensive.
*
* @version 1.0 21 August 1997
* @author Roger Whitney (<a href=mailto:whitney@cs.sdsu.edu>whitney@cs.sdsu.edu</a>)
*/
final public class CharQueue
{
/*
* Class invariant, queueRear is the location the next queue item should be placed If the queue is not empty,
* queueFront is the location of the first item in the queue
*/
private char[] queueElements;
private int queueFront;
private int queueRear;
private int elementCount; // number of elements in the queue
public static final int DEFAULT_QUEUE_SIZE = 256;
public CharQueue(int Size)
{
queueElements = new char[Size];
queueFront = 0;
queueRear = 0;
elementCount = 0;
}
public CharQueue()
{
this(DEFAULT_QUEUE_SIZE);
}
/**
* Returns the current number of locations for chars in queue
*/
public int capacity()
{
return queueElements.length;
}
/**
* Returns true if the queue is empty
*/
public boolean isEmpty()
{
if (elementCount == 0)
return true;
else
return false;
}
/**
* Returns true if the queue is full
*/
public boolean isFull()
{
if (elementCount >= capacity())
return true;
else
return false;
}
/**
* Returns the number of chars in the queue
*/
public int size()
{
return elementCount;
}
/**
* Returns string representation of the queue
*/
@Override
public String toString()
{
StringBuffer queueString = new StringBuffer(elementCount);
if (queueFront < queueRear)
{
queueString.append(queueElements, queueFront, elementCount);
}
else
{
int elementsFromFrontToEnd = capacity() - queueFront;
queueString.append(queueElements, queueFront, elementsFromFrontToEnd);
queueString.append(queueElements, 0, queueRear);
}
return queueString.toString();
}
/**
* Returns the current number of unused locations in the queue
*/
public int unusedCapacity()
{
return capacity() - size();
}
/**
* Removes front char from the queue and returns the char
*/
public char dequeue()
{
char itemRemoved = queueElements[queueFront];
queueFront = (queueFront + 1) % capacity();
elementCount--;
return itemRemoved;
}
/**
* Fills charsRemoved with chars removed from the queue. If charsRemoved is larger than queue then charsRemoved is
* not completely filled
*
* @return actual number of chars put in charsRemoved
*/
public int dequeue(char[] charsRemoved)
{
return dequeue(charsRemoved, 0, charsRemoved.length);
}
/**
* Places chars from queue in charsRemoved starting at charsRemoved[offset]. Will place numCharsRequested into
* charsRemoved if queue has enougth chars.
*
* @return actual number of chars put in charsRemoved
*/
public int dequeue(char[] charsRemoved, int offset, int numCharsRequested)
{
// Don't return more chars than are in the queue
int numCharsToReturn = Math.min(numCharsRequested, elementCount);
int numCharsAtEnd = capacity() - queueFront;
// Are there enough characters after front pointer?
if (numCharsAtEnd >= numCharsToReturn)
{
// arraycopy is about 20 times faster than coping element by element
System.arraycopy(queueElements, queueFront, charsRemoved, offset, numCharsToReturn);
}
else
{
// Handle wrap around
System.arraycopy(queueElements, queueFront, charsRemoved, offset, numCharsAtEnd);
System.arraycopy(queueElements, 0, charsRemoved, offset + numCharsAtEnd, numCharsToReturn - numCharsAtEnd);
}
queueFront = (queueFront + numCharsToReturn) % capacity();
elementCount = elementCount - numCharsToReturn;
return numCharsToReturn;
}
/**
* Returns an array containing all chars in the queue. Afterwards queue is empty.
*/
public char[] dequeueAll()
{
char[] contents = new char[elementCount];
dequeue(contents);
return contents;
}
/**
* Returns the front char from the queue without removing it
*/
public char peek()
{
return queueElements[queueFront];
}
/**
* Adds charToAdd to the end of the queue
*/
public void enqueue(char charToAdd)
{
if (isFull())
grow();
queueElements[queueRear] = charToAdd;
queueRear = (queueRear + 1) % capacity();
elementCount++;
}
/**
* Adds charsToAdd to the end of the queue
*/
public void enqueue(String charsToAdd)
{
enqueue(charsToAdd.toCharArray());
}
/**
* Adds all elements of charsToAdd to the end of the queue
*/
public void enqueue(char[] charsToAdd)
{
enqueue(charsToAdd, 0, charsToAdd.length);
}
/**
* Adds numCharsToAdd elements of charsToAdd, starting with charsToAdd[offset] to the end of the queue
*/
public void enqueue(char[] charsToAdd, int offset, int numCharsToAdd)
{
if (numCharsToAdd > unusedCapacity())
grow(Math.max(numCharsToAdd + 32, capacity() * 2));
// 32 to insure some spare capacity after growing
int numSpacesAtEnd = capacity() - queueRear;
// Are there enough spaces after rear pointer?
if (numSpacesAtEnd >= numCharsToAdd)
{
System.arraycopy(charsToAdd, offset, queueElements, queueRear, numCharsToAdd);
}
else
// Handle wrap around
{
System.arraycopy(charsToAdd, offset, queueElements, queueRear, numSpacesAtEnd);
System.arraycopy(charsToAdd, offset + numSpacesAtEnd, queueElements, 0, numCharsToAdd - numSpacesAtEnd);
}
queueRear = (queueRear + numCharsToAdd) % capacity();
elementCount = elementCount + numCharsToAdd;
}
/**
* Clears the queue so it has no more elements in it
*/
public void clear()
{
queueFront = 0;
queueRear = 0;
elementCount = 0;
}
/**
* Grows the queue. Growth policy insures amortized cost per insert is O(1)
*/
private void grow()
{
// Doubling queue insures that amortized cost per insert is O(1)
if (capacity() <= 16)
grow(32);
else if (capacity() <= 1024)
grow(capacity() * 2);
else
grow((int) (capacity() * 1.5));
}
/**
* Grows the queue to the given new size
*/
private void grow(int newSize)
{
char[] newQueue = new char[newSize];
if (queueFront < queueRear)
{
System.arraycopy(queueElements, queueFront, newQueue, 0, elementCount);
}
else
{
int elementsFromFrontToEnd = capacity() - queueFront;
System.arraycopy(queueElements, queueFront, newQueue, 0, elementsFromFrontToEnd);
System.arraycopy(queueElements, 0, newQueue, elementsFromFrontToEnd, queueRear);
}
queueElements = newQueue;
queueFront = 0;
queueRear = elementCount;
}
}

View file

@ -386,6 +386,18 @@ public final class ParserUtils
System.out.println(m.group(1));
}
public static void printFromReader(final Reader reader) throws IOException
{
while (true)
{
final int c = reader.read();
if (c == -1)
return;
System.out.print((char) c);
}
}
public static String urlEncode(final String str)
{
try

View file

@ -0,0 +1,367 @@
package de.schildbach.pte.util;
/*
* Copyright (C) 1997 Roger Whitney <whitney@cs.sdsu.edu>
*
* This file is part of the San Diego State University Java Library.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import java.io.BufferedReader;
import java.io.FilterReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
/**
* Given a string <b>pattern</b>, a string <b>replacementPattern</b> and an input stream, this class will replace all
* occurances of <b>pattern</b> with <b>replacementPattern</b> in the inputstream. You can give multiple
* pattern-replacementPattern pairs. Multiple pairs are done in order they are given. If first pair is "cat"-"dog" and
* second pair is "dog"-"house", then the result will be all occurences of "cat" or "dog" will be replaced with "house".
*
* @version 0.6 21 August 1997
* @since version 0.5, Fixed error that occured when input was shorter than the pattern
* @author Roger Whitney (<a href=mailto:whitney@cs.sdsu.edu>whitney@cs.sdsu.edu</a>)
*/
public class StringReplaceReader extends FilterReader implements Cloneable
{
protected CharQueue outputBuffer; // holds filtered data
protected char[] inputBuffer;
protected int[] shiftTable; // quick search shift table
protected int inputBufferCharCount; // number of chars in inputBuffer
protected char[] patternToFind = null;
protected char[] replacementPattern = null;
protected boolean reachedEOF = false;
protected static int EOFIndicator = -1;
protected static int DEFAULT_BUFFER_SIZE = 1024;
/**
* Create an StringReplaceReader object that will replace all occurrences ofpattern with replacementPattern in the
* Reader in.
*/
public StringReplaceReader(Reader in, String pattern, String replacementPattern)
{
super(in);
patternToFind = pattern.toCharArray();
this.replacementPattern = replacementPattern.toCharArray();
allocateBuffers();
}
/**
* Create an StringReplaceReader object that will replace all occurrences of pattern with replacementPattern in the
* inputstream in.
*/
public StringReplaceReader(InputStream in, String pattern, String replacementPattern)
{
this(new BufferedReader(new InputStreamReader(in)), pattern, replacementPattern);
}
/**
* Create an StringReplaceReader object that will replace all occurrences of pattern with replacementPattern in the
* string input.
*/
public StringReplaceReader(String input, String pattern, String replacementPattern)
{
this(new StringReader(input), pattern, replacementPattern);
}
/**
* Returns the entire contents of the input stream.
*/
public String contents() throws IOException
{
StringBuffer contents = new StringBuffer(1024);
int readSize = 512;
char[] filteredChars = new char[readSize];
int charsRead = read(filteredChars, 0, readSize);
while (charsRead != EOFIndicator)
{
contents.append(filteredChars, 0, charsRead);
charsRead = read(filteredChars, 0, readSize);
}
return contents.toString();
}
/**
* Adds another pattern-replacementPattern pair. All occurrences of pattern will be replaced with
* replacementPattern.
*
* @exception OutOfMemoryError
* if there is not enough memory to add new pattern-replacementPattern pair
*/
public void replace(String pattern, String replacementPattern) throws OutOfMemoryError
{
// Chain StringReplaceReader objects. Clone current object
// add clone to input stream to insure it filters before this
// object, which gets the new pattern-replacement pair
if (patternToFind != null)
{
// Replace this with clone
try
{
StringReplaceReader currentReplace = (StringReplaceReader) this.clone();
in = currentReplace;
}
catch (CloneNotSupportedException x)
{
}
}
patternToFind = pattern.toCharArray();
this.replacementPattern = replacementPattern.toCharArray();
allocateBuffers();
reachedEOF = false;
}
/**
* Read characters into a portion of an array. This method will block until some input is available, an I/O error
* occurs, or the end of the stream is reached.
*
* @parm buffer Destination buffer
* @parm offset location in buffer to start storing characters
* @parm charsToRead maximum characters to read
* @return number of characters actually read, -1 if reah EOF on reading first character
* @exception IOException
* if an I/O error occurs
*/
@Override
public int read(char[] buffer, int offset, int charsToRead) throws IOException
{
int charsRead = 0;
while ((charsRead < charsToRead) && (!eof()))
{
if (outputBuffer.isEmpty())
{
fillInputWindow();
filterInput();
}
charsRead += outputBuffer.dequeue(buffer, offset + charsRead, charsToRead - charsRead);
}
if (charsRead > 0)
return charsRead;
else if (outputBuffer.size() > 0)
{
charsRead = outputBuffer.dequeue(buffer, offset, charsToRead);
return charsRead;
}
else if ((eof()) && (inputBufferCharCount > 0) && (inputBufferCharCount < patternToFind.length))
{
// remaining input is less than length of pattern
transferRemainingInputToOutputBuffer();
System.out.println(">> End << " + outputBuffer);
charsRead = outputBuffer.dequeue(buffer, offset, charsToRead);
return charsRead;
}
else if (eof())
return EOFIndicator;
else
// this should never happen
throw new IOException("Read attempted. Did not reach EOF and " + " no chars were read");
}
/**
* Call when remaining input is less than the pattern size, so pattern can not exist in remaining input. Just shift
* all input to output. Assumes that have reached EOF and inputBufferCharCount < patternToFind.length
*/
private void transferRemainingInputToOutputBuffer()
{
outputBuffer.enqueue(inputBuffer, 0, inputBufferCharCount);
inputBufferCharCount = 0;
}
/**
* Returns the next character in the inputstream with string replacement done.
*
* @exception IOException
* if error occurs reading io stream
*/
@Override
public int read() throws IOException
{
char[] output = new char[1];
int charsRead = read(output, 0, 1);
if (charsRead == EOFIndicator)
return EOFIndicator;
else if (charsRead == 1)
return output[0];
else
throw new IOException("Single Read attempted. Did not reach EOF and " + " no chars were read");
}
/**
* Determines if a previous ASCII I/O operation caught End Of File.
*
* @return <i>true</i> if end of file was reached.
*/
public boolean eof()
{
return reachedEOF;
}
/**
* Read inpout to see if we have found the pattern. <B>Requires:</B> When this is called we have already have read
* first character in pattern.<BR>
* <B>Side Effects: </B> After attempt to find pattern, output buffer contains either the replacement pattern or all
* characters we konw are not part of pattern.
*/
protected void filterInput() throws IOException
{
// Use quick-search to find pattern. Fill inputBuffer with text.
// Process all text in inputBuffer. Place processed text in
// outputBuffer.
int searchStart = 0;
int windowStart = 0;
int patternLength = patternToFind.length;
// Search until pattern extends past end of inputBuffer
while (searchStart < inputBufferCharCount - patternLength + 1)
{
boolean foundPattern = true;
// The search
for (int index = 0; index < patternLength; index++)
if (patternToFind[index] != inputBuffer[index + searchStart])
{
foundPattern = false;
break; // for loop
}
if (foundPattern)
{
// move text before pattern
outputBuffer.enqueue(inputBuffer, windowStart, searchStart - windowStart);
replacementPatternToBuffer();
windowStart = searchStart + patternLength;
searchStart = windowStart;
}
else
{
// look farther along in inputBuffer
int charLocationAfterPattern = searchStart + patternLength;
if (charLocationAfterPattern >= inputBufferCharCount)
searchStart += 1;
else
searchStart += getShift(inputBuffer[charLocationAfterPattern]);
}
}
if (searchStart > inputBufferCharCount)
searchStart = inputBufferCharCount;
// move chars already searched
if (reachedEOF)
{
outputBuffer.enqueue(inputBuffer, windowStart, inputBufferCharCount - windowStart);
inputBufferCharCount = 0;
}
else
{
outputBuffer.enqueue(inputBuffer, windowStart, searchStart - windowStart);
System.arraycopy(inputBuffer, searchStart, inputBuffer, 0, inputBufferCharCount - searchStart);
inputBufferCharCount = inputBufferCharCount - searchStart;
}
}
/**
* Fill sliding input window with chars from input Read until window is full or reach EOF
*/
final protected void fillInputWindow() throws IOException
{
int charsToRead = inputBuffer.length - inputBufferCharCount;
int firstEmptySlotInWindow = inputBufferCharCount;
int charsRead = in.read(inputBuffer, firstEmptySlotInWindow, charsToRead);
if (charsRead == charsToRead) // full read
{
inputBufferCharCount = inputBufferCharCount + charsRead;
charsToRead = 0;
}
else if (charsRead > 0) // parial read
{
inputBufferCharCount = inputBufferCharCount + charsRead;
charsToRead = charsToRead - charsRead;
}
else if (charsRead == EOFIndicator)
{
reachedEOF = true;
}
else
throw new IOException("Read attempted. Did not reach EOF and " + " no chars were read");
}
/**
* Return the number of positions we can shift pattern when findMyShift is character in inputBuffer after the
* pattern
*/
protected int getShift(char findMyShift)
{
if (findMyShift >= shiftTable.length)
return 1;
else
return shiftTable[findMyShift];
}
/**
* Put replacement pattern in output buffer. Subclass overrides for more complex replacement
*/
protected void replacementPatternToBuffer()
{
outputBuffer.enqueue(replacementPattern);
}
private void allocateBuffers()
{
outputBuffer = new CharQueue(DEFAULT_BUFFER_SIZE);
inputBuffer = new char[Math.max(patternToFind.length + 1, DEFAULT_BUFFER_SIZE)];
inputBufferCharCount = 0;
// allocate for most ascii characters
shiftTable = new int[126];
// build shiftTable for quick search
// Entry for character X contains how far to shift
// pattern when pattern does not match text and
// character X is the character in text after end of
// pattern
// Default for characters not in pattern
for (int k = 0; k < shiftTable.length; k++)
shiftTable[k] = patternToFind.length + 1;
for (int k = 0; k < patternToFind.length; k++)
if (patternToFind[k] < shiftTable.length)
shiftTable[patternToFind[k]] = patternToFind.length - k;
}
}