package play.utils;
import net.sf.oval.internal.util.LinkedSet;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import java.beans.Encoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* Custom impl of java.util.properties that preserves the key-order from the file
* and that reads the properties-file in utf-8
*/
public class OrderSafeProperties extends java.util.Properties {
// set used to preserve key order
private final LinkedHashSet<Object> keys = new LinkedHashSet<Object>();
@Override
public void load(InputStream inputStream) throws IOException {
// read all lines from file as utf-8
List<String> lines = IOUtils.readLines(inputStream, "utf-8");
IOUtils.closeQuietly(inputStream);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// escape "special-chars" (to utf-16 on the format \\uxxxx) in lines and store as iso-8859-1
// see info about escaping - http://download.oracle.com/javase/1.5.0/docs/api/java/util/Properties.html - "public void load(InputStream inStream)"
for (String line : lines) {
// due to "...by the rule above, single and double quote characters preceded
// by a backslash still yield single and double quote characters, respectively."
// we must transform \" => " and \' => ' before escaping to prevent escaping the backslash
line = line.replaceAll("\\\\\"", "\"").replaceAll("(^|[^\\\\])(\\\\')", "$1'");
String escapedLine = StringEscapeUtils.escapeJava( line ) + "\n";
// remove escaped backslashes
escapedLine = escapedLine.replaceAll("\\\\\\\\","\\\\");
out.write( escapedLine.getBytes("iso-8859-1"));
}
// read properties-file with regular java.util.Properties impl
super.load( new ByteArrayInputStream( out.toByteArray()) );
}
@Override
public Enumeration<Object> keys() {
return Collections.<Object>enumeration(keys);
}
@Override
public Set<Object> keySet() {
return keys;
}
@Override
public Object put(Object key, Object value) {
keys.add(key);
return super.put(key, value);
}
@Override
public Object remove(Object o) {
keys.remove(o);
return super.remove(o);
}
@Override
public void clear() {
keys.clear();
super.clear();
}
@Override
public void putAll(Map<? extends Object, ? extends Object> map) {
keys.addAll(map.keySet());
super.putAll(map);
}
@Override
public Set<Map.Entry<Object, Object>> entrySet() {
Set<Map.Entry<Object, Object>> entrySet = new LinkedSet<Map.Entry<Object, Object>>(keys.size());
for (Object key : keys) {
entrySet.add(new Entry(key, get(key)));
}
return entrySet;
}
static class Entry implements Map.Entry<Object, Object> {
private final Object key;
private final Object value;
private Entry(Object key, Object value) {
this.key = key;
this.value = value;
}
public Object getKey() {
return key;
}
public Object getValue() {
return value;
}
public Object setValue(Object o) {
throw new IllegalStateException("not implemented");
}
}
}