/* ********************************************************************** **
** Copyright notice **
** **
** (c) 2005-2009 RSSOwl Development Team **
** http://www.rssowl.org/ **
** **
** 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.rssowl.org/legal/epl-v10.html **
** **
** A copy is found in the file epl-v10.html and important notices to the **
** license from the team is found in the textfile LICENSE.txt distributed **
** in this package. **
** **
** This copyright notice MUST APPEAR in all copies of the file! **
** **
** Contributors: **
** RSSOwl Development Team - initial API and implementation **
** **
** ********************************************************************** */
package org.rssowl.core.util;
import org.eclipse.core.runtime.Assert;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
/**
* An implementation of {@link Reader} that allows to surround certain words
* with a pre- and a post-value.
*
* @author bpasero
*/
public class ExpandingReader extends Reader {
/* Pushback Buffer */
private final StringBuilder fPushed = new StringBuilder();
/* Temp Buffer */
private final StringBuilder fBuf = new StringBuilder();
/* Constructor Values */
private final Reader fIn;
private final Collection<StringMatcher> fStringMatcher;
private final String fPreExpand;
private final String fPostExpand;
private final boolean fSkipTags;
/* Some State */
private int fPushbackOffset;
private boolean fInTags;
/**
* @param in The reader to wrap around.
* @param words A List of words to expand.
* @param preValue The value to put in front of the expanded word.
* @param postValue The value to put after the expanded word.
* @param skipTags If <code>true</code>, do not expand when inside Tags.
*/
public ExpandingReader(Reader in, Collection<String> words, String preValue, String postValue, boolean skipTags) {
Assert.isNotNull(in);
Assert.isNotNull(words);
Assert.isNotNull(preValue);
Assert.isNotNull(postValue);
fIn = in;
fPreExpand = preValue;
fPostExpand = postValue;
fSkipTags = skipTags;
fStringMatcher= new ArrayList<StringMatcher>(words.size());
for (String word : words) {
fStringMatcher.add(new StringMatcher(word, true, false));
}
}
/*
* @see java.io.Reader#read()
*/
@Override
public int read() throws IOException {
/* Read next char (either from Reader or Pushback Buffer) */
int ch = next();
/* Still reading from Pushback Buffer, return */
if (fPushbackOffset != 0)
return ch;
/* Skip Tags if required */
if (fSkipTags && ch == '<')
fInTags = true;
else if (fSkipTags && ch == '>')
fInTags = false;
if (fSkipTags && (fInTags || ch == '>'))
return ch;
/* Read up to the next Word to check for expansion */
while (true) {
/* EOF or End of Word */
if (ch == -1 || isWordTerminator(ch)) {
String result = fBuf.toString();
/* Expand if required */
if (result.length() > 0 && shouldExpand(result))
result = expand(result);
/* Fill to Pushback Buffer */
fPushed.append(result);
/* Also add current ch, if its not EOF */
if (ch != -1) {
fPushed.append((char) ch);
/* Skip Tags if required */
if (fSkipTags && ch == '<')
fInTags = true;
else if (fSkipTags && ch == '>')
fInTags = false;
}
/* Reset Buffer */
fBuf.setLength(0);
/* Read next from Pushback Buffer */
return next();
}
/* Append to Bufffer */
fBuf.append((char) ch);
/* Read Next from Reader */
ch = fIn.read();
}
}
private boolean isWordTerminator(int c) {
return !Character.isLetterOrDigit(c);
}
private boolean shouldExpand(String word) {
for (StringMatcher matcher : fStringMatcher) {
if (matcher.match(word))
return true;
}
return false;
}
private String expand(String word) {
return fPreExpand + word + fPostExpand;
}
private int next() throws IOException {
int len = fPushed.length();
/* Read from Pushback Buffer */
if (fPushbackOffset < len) {
int ch = fPushed.charAt(fPushbackOffset);
fPushbackOffset++;
return ch;
}
/* Reset Pushbackbuffer */
else if (fPushbackOffset != 0) {
fPushbackOffset = 0;
fPushed.setLength(0);
}
/* Read from Reader */
return fIn.read();
}
/*
* @see java.io.Reader#close()
*/
@Override
public void close() throws IOException {
fIn.close();
}
/*
* @see java.io.Reader#read(char[], int, int)
*/
@Override
public int read(char cbuf[], int off, int len) throws IOException {
int i = 0;
for (i = 0; i < len; i++) {
int ch = read();
if (ch == -1)
break;
cbuf[off++] = (char) ch;
}
if (i == 0) {
if (len == 0)
return 0;
return -1;
}
return i;
}
}