/*
** 2011 June 19
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
*/
package info.ata4.bsplib.io;
import info.ata4.bsplib.entity.Entity;
import info.ata4.bsplib.entity.KeyValue;
import info.ata4.log.LogUtils;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.input.CountingInputStream;
/**
* Enity stream reading class. Converts keyvalue text into Entity objects.
*
* @author Nico Bergemann <barracuda415 at yahoo.de>
*/
public class EntityInputStream extends CountingInputStream {
private static final Logger L = LogUtils.getLogger();
private boolean allowEsc = false;
public EntityInputStream(InputStream in) {
super(in);
}
public Entity readEntity() throws IOException {
boolean section = false;
boolean string = false;
boolean esc = false;
StringBuilder sb = new StringBuilder(512);
List<KeyValue> keyValues = new ArrayList<>();
String key = null;
try {
for (int b = 0; b != -1; b = read()) {
switch (b) {
case '"':
if (!section) {
throw new ParseException("String in unopened section", getCount());
}
// ignore '"' if the previous character was '\'
if (esc) {
esc = false;
break;
}
// parse strings
if (string) {
if (key == null) {
key = sb.toString();
} else {
String value = sb.toString();
// ignore empty keys
if (key.isEmpty()) {
L.log(Level.FINE, "Skipped value \"{0}\" with empty key at {1}",
new Object[] {value, getCount()});
} else {
keyValues.add(new KeyValue(key, value));
}
key = null;
}
// empty string buffer
sb.delete(0, sb.length());
}
string = !string;
continue;
case '{':
if (section && !string) {
throw new ParseException("Opened unclosed section", getCount());
}
if (!string) {
section = true;
}
break;
case '}':
if (!section && !string) {
throw new ParseException("Closed unopened section", getCount());
}
if (!string) {
return new Entity(keyValues);
}
break;
case '\\':
if (allowEsc) {
// skip this character and add the next '"' to the string
esc = true;
}
break;
}
// append to current string if inside section
if (section && string) {
sb.append((char) b);
}
}
} catch (ParseException ex) {
L.log(Level.WARNING, "{0} at {1}", new Object[]{ex.getMessage(), ex.getErrorOffset()});
// skip rest of this section by reading until EOF or '}'
for (int b = 0; b != -1 && b != '}'; b = read());
// return what we've got so far
return new Entity(keyValues);
}
return null;
}
public boolean isAllowEscSeq() {
return allowEsc;
}
public void setAllowEscSeq(boolean allowEsc) {
this.allowEsc = allowEsc;
}
}