/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.commons.utils;
import java.io.IOException;
import java.io.Reader;
/**
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public class PropertyResolverReader extends Reader {
/** . */
private static final int STATUS_READING = 0;
/** . */
private static final int STATUS_READ_DOLLAR = 1;
/** . */
private static final int STATUS_READING_PROPERTY = 2;
/** . */
private static final int STATUS_WRITING = 3;
/** . */
private static final int STATUS_TERMINATED = 4;
/** . */
private Reader delegate;
/** . */
private int status = STATUS_READING;
/** . */
private char[] buffer;
/** . */
private int bufferLen;
/** . */
private int bufferOff;
/** . */
private int mark;
public PropertyResolverReader(Reader delegate) {
this(delegate, 64);
}
public PropertyResolverReader(Reader delegate, int bufferSize) {
if (delegate == null) {
throw new NullPointerException();
}
if (bufferSize < 0) {
throw new IllegalArgumentException();
}
//
this.delegate = delegate;
this.buffer = new char[bufferSize];
this.bufferLen = 0;
this.bufferOff = 0;
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
final int olen = len;
while (true) {
switch (status) {
case STATUS_READING: {
if (len > 0) {
if (bufferOff == bufferLen) {
resetBuffer();
fillBuffer(Math.min(len, buffer.length));
} else {
char c = buffer[bufferOff++];
if (c == '$') {
mark = bufferOff - 1;
status = STATUS_READ_DOLLAR;
} else {
cbuf[off++] = c;
len--;
}
}
} else {
return olen - len;
}
break;
}
case STATUS_TERMINATED: {
if (mark != -1 && mark < bufferOff) {
if (len > 0) {
cbuf[off++] = buffer[mark++];
len--;
} else {
return olen - len;
}
} else {
if (olen > len) {
return olen - len;
} else {
return -1;
}
}
break;
}
case STATUS_READ_DOLLAR: {
if (bufferOff == bufferLen) {
// For now let's read 1 ?
fillBuffer(1);
} else {
char c = buffer[bufferOff++];
if (c == '{') {
status = STATUS_READING_PROPERTY;
} else {
status = STATUS_WRITING;
}
}
break;
}
case STATUS_WRITING: {
if (mark < bufferOff) {
if (len > 0) {
cbuf[off++] = buffer[mark++];
len--;
} else {
return olen - len;
}
} else {
mark = -1;
status = STATUS_READING;
}
break;
}
case STATUS_READING_PROPERTY: {
if (bufferOff == bufferLen) {
// For now let's read 1 ?
fillBuffer(1);
} else {
char c = buffer[bufferOff++];
if (c == '}') {
String name = new String(buffer, mark + 2, bufferOff - mark - 2 - 1);
String value = resolve(name);
if (value == null) {
status = STATUS_WRITING;
} else {
mark = bufferOff - value.length();
if (mark < 0) {
int nextBufferLen = bufferLen - mark;
if (nextBufferLen > buffer.length) {
char[] tmp = new char[nextBufferLen];
System.arraycopy(buffer, bufferOff, tmp, bufferOff - mark, bufferLen - bufferOff);
buffer = tmp;
} else {
System.arraycopy(buffer, bufferOff, buffer, bufferOff - mark, bufferLen - bufferOff);
}
bufferOff -= mark;
bufferLen = nextBufferLen;
mark = 0;
}
value.getChars(0, value.length(), buffer, mark);
status = STATUS_WRITING;
}
} else {
// We do nothing until we get end of stream of }
}
}
break;
}
default:
throw new UnsupportedOperationException();
}
}
}
/**
* Resolves a property value, this method is called during the stream analysis. When the returned value is null, the
* property declaration will be read by the client (i.e ${a} will be read as ${a}).
*
* @param name the property name
* @return the property value
* @throws IOException any IOException
*/
protected String resolve(String name) {
return name;
}
private void resetBuffer() {
if (bufferLen != bufferOff) {
throw new AssertionError();
}
bufferOff = 0;
bufferLen = 0;
}
private void fillBuffer(int amount) throws IOException {
if (bufferLen > bufferOff) {
throw new AssertionError();
}
if (amount < 0) {
throw new IllegalArgumentException();
}
int space = bufferLen + amount - buffer.length;
if (space > 0) {
// We allocate more space
char[] tmp = new char[buffer.length + space];
System.arraycopy(buffer, 0, tmp, 0, buffer.length);
buffer = tmp;
}
int ret = delegate.read(buffer, bufferLen, amount);
if (ret != -1) {
bufferLen += ret;
} else {
status = STATUS_TERMINATED;
}
}
@Override
public void close() throws IOException {
delegate.close();
}
}