/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.util;
import java.io.IOException;
import java.io.InputStream;
/**
* This class implements some search algorithms.
*/
public class Search {
/**
* Creates a fault function for a given pattern.
*
* @param pattern pattern to search as a byte array.
* @return array of offset corrections.
*/
public static int[] createFaultFunction(byte[] pattern) {
int[] faultFunction = new int[pattern.length + 1];
int pos = 0;
int preLen = -1;
faultFunction[0] = -1;
while (pos < pattern.length) {
while (preLen >= 0 && pattern[pos] != pattern[preLen]) {
preLen = faultFunction[preLen];
}
pos++;
preLen++;
faultFunction[pos] = preLen;
}
return faultFunction;
}
/**
* Encapsulates the search for the pattern on stream in an other stream.
* Using the <a
* href="http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm"
* >Knuth-Morris-Pratt algorithm</a>.
*
* @param in input stream.
* @param pattern pattern to search as a byte array.
* @return the input stream which encapsulates the search.
*/
public static InputStream getSearchPatternWrapper(InputStream in, byte[] pattern) {
return new KMPAlgoInputStream(in, pattern);
}
public static InputStream getSearchPatternWrapper(InputStream in, byte[] pattern, int[] faultFunction) {
return new KMPAlgoInputStream(in, pattern);
}
/**
* This stream encapsulates the search with the Knuth-Morris-Pratt algorithm
* for a pattern.
*/
private static class KMPAlgoInputStream extends InputStream {
private InputStream in = null;
private byte[] pattern = null;
private int[] faultFunction = null;
private int patternPos = 0;
private int virtualBufferSize = 0;
private int returnedBytesCount = 0;
private int readByte;
/**
* Creates a Knuth-Morris-Pratt algorithm input stream.
*
* @param in input stream.
* @param pattern pattern to search as a byte array.
*/
public KMPAlgoInputStream(InputStream in, byte[] pattern) {
this(in, pattern, createFaultFunction(pattern));
}
public KMPAlgoInputStream(InputStream in, byte[] pattern, int[] faultFunction) {
this.in = in;
this.pattern = pattern;
this.faultFunction = faultFunction;
}
/*
* (non-Javadoc)
* @see java.io.InputStream#available()
*/
public synchronized int available() throws IOException {
if (virtualBufferSize == -42) {
return 0;
}
int bytesToReturn = virtualBufferSize - returnedBytesCount - patternPos;
if (bytesToReturn > 0) {
return bytesToReturn;
}
return 0;
}
/*
* (non-Javadoc)
* @see java.io.InputStream#read()
*/
public synchronized int read() throws IOException {
if (virtualBufferSize == -42) {
return -1;
}
int bytesToReturn = virtualBufferSize - returnedBytesCount - patternPos;
if (bytesToReturn > 0) {
if (bytesToReturn == 1) {
int result = (patternPos == 0) ? readByte : pattern[returnedBytesCount];
virtualBufferSize = patternPos;
returnedBytesCount = 0;
return result;
}
else {
return pattern[returnedBytesCount++];
}
}
while (true) {
readByte = in.read();
virtualBufferSize++;
if (readByte != -1) {
while (patternPos >= 0 && readByte != pattern[patternPos]) {
patternPos = faultFunction[patternPos];
}
patternPos++;
}
if (patternPos == pattern.length) {
virtualBufferSize = -42;
return -1;
}
if (virtualBufferSize > patternPos) {
if (virtualBufferSize == 1) {
virtualBufferSize = 0;
return readByte;
}
if (virtualBufferSize - patternPos == 1) {
virtualBufferSize = patternPos;
}
else {
returnedBytesCount = 1;
}
return pattern[0];
}
}
}
}
}