All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schildbach.pte.util.StringReplaceReader Maven / Gradle / Ivy

There is a newer version: 2
Show newest version
package de.schildbach.pte.util;

/* 
 * Copyright (C) 1997 Roger Whitney 
 *
 * 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 pattern, a string replacementPattern and an input stream, this class will
 * replace all occurrences of pattern with replacementPattern 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 occurrences of "cat" or "dog"
 * will be replaced with "house".
 *
 * @version 0.6 21 August 1997
 * @since version 0.5, Fixed error that occurred when input was shorter than the pattern
 * @author Roger Whitney ([email protected])
 */

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 of pattern 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 reach 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();
            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 true if end of file was reached.
     */
    public boolean eof() {
        return reachedEOF;
    }

    /**
     * Read input to see if we have found the pattern. Requires: When this is called we have already
     * have read first character in pattern.
* Side Effects: After attempt to find pattern, output buffer contains either the replacement * pattern or all characters we know 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy