* OrderedProperties: Vastly implify, use in i2psnark

This commit is contained in:
zzz
2009-11-24 20:15:29 +00:00
parent 94faf74aa4
commit 234c084c2a
2 changed files with 16 additions and 340 deletions

View File

@ -20,6 +20,7 @@ import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
/**
* Manage multiple snarks
@ -126,7 +127,7 @@ public class SnarkManager implements Snark.CompleteListener {
/** null to set initial defaults */
public void loadConfig(String filename) {
if (_config == null)
_config = new Properties();
_config = new OrderedProperties();
if (filename != null) {
File cfg = new File(filename);
if (!cfg.isAbsolute())

View File

@ -9,371 +9,46 @@ package net.i2p.util;
*
*/
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Comparator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import net.i2p.data.DataHelper;
/**
* Properties map that has its keySet ordered consistently (via the key's lexicographical ordering).
* This is useful in environments where maps must stay the same order (e.g. for signature verification)
* This does NOT support remove against the iterators / etc.
*
* @author zzz Rewritten
*
* Now unsorted until the keyset or entryset is requested.
* The class is unsynchronized.
* The keySet() and entrySet() methods return ordered sets.
* Others - such as the enumerations values(), keys(), propertyNames() - do not.
*/
public class OrderedProperties extends Properties {
private final static Log _log = new Log(OrderedProperties.class);
/** ordered set of keys (strings) stored in the properties */
private TreeSet _order;
/** simple key=value mapping of the actual data */
private Map _data;
/** lock this before touching _order or _data */
private final Object _lock = new Object();
public OrderedProperties() {
super();
_order = new TreeSet();
_data = new HashMap();
}
@Override
public boolean contains(Object value) {
return containsValue(value);
}
@Override
public boolean containsKey(Object key) {
synchronized (_lock) {
return _data.containsKey(key);
}
}
@Override
public boolean containsValue(Object value) {
synchronized (_lock) {
return _data.containsValue(value);
}
}
@Override
public boolean equals(Object obj) {
if ((obj != null) && (obj instanceof OrderedProperties)) {
synchronized (_lock) {
return _data.equals(obj);
}
}
return false;
}
@Override
public int hashCode() {
synchronized (_lock) {
return _data.hashCode();
}
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public String getProperty(String key) {
return getProperty((Object) key);
}
@Override
public Object get(Object key) {
return getProperty(key);
}
private String getProperty(Object key) {
if (key == null) return null;
synchronized (_lock) {
Object rv = _data.get(key);
if ((rv != null) && (rv instanceof String)) return (String) rv;
return null;
}
}
@Override
public Object setProperty(String key, String val) {
if ((key == null) || (val == null)) throw new IllegalArgumentException("Null values are not supported");
synchronized (_lock) {
_order.add(key);
Object rv = _data.put(key, val);
return rv;
}
}
@Override
public Object put(Object key, Object val) {
if ((key == null) || (val == null)) throw new NullPointerException("Null values or keys are not allowed");
if (!(key instanceof String) || !(val instanceof String))
throw new IllegalArgumentException("Key or value is not a string");
return setProperty((String) key, (String) val);
}
@Override
public void putAll(Map data) {
if (data == null) return;
for (Iterator iter = data.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry)iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
put(key, val);
}
}
@Override
public Object clone() {
synchronized (_lock) {
OrderedProperties rv = new OrderedProperties();
rv.putAll(this);
return rv;
}
}
@Override
public void clear() {
synchronized (_lock) {
_order.clear();
_data.clear();
}
}
@Override
public int size() {
synchronized (_lock) {
return _order.size();
}
}
@Override
public Object remove(Object key) {
synchronized (_lock) {
_order.remove(key);
Object rv = _data.remove(key);
return rv;
}
}
@Override
public Set keySet() {
synchronized (_lock) {
return Collections.unmodifiableSortedSet((TreeSet) _order.clone());
}
return Collections.unmodifiableSortedSet(new TreeSet(super.keySet()));
}
@Override
public Set entrySet() {
synchronized (_lock) {
return Collections.unmodifiableSet(buildEntrySet((TreeSet) _order.clone()));
}
TreeSet rv = new TreeSet(new EntryComparator());
rv.addAll(super.entrySet());
return Collections.unmodifiableSortedSet(rv);
}
@Override
public Collection values() {
synchronized (_lock) {
Collection values = new ArrayList(_data.size());
for (Iterator iter = _data.values().iterator(); iter.hasNext();) {
values.add(iter.next());
}
return values;
}
}
@Override
public Enumeration elements() {
return Collections.enumeration(values());
}
@Override
public Enumeration keys() {
return Collections.enumeration(keySet());
}
@Override
public Enumeration propertyNames() {
return Collections.enumeration(keySet());
}
@Override
public void list(PrintStream out) { // nop
}
@Override
public void list(PrintWriter out) { // nop
}
@Override
public void load(InputStream in) { // nop
}
//public void save(OutputStream out, String header) {}
@Override
public void store(OutputStream out, String header) { // nop
}
private Set buildEntrySet(Set data) {
TreeSet ts = new TreeSet();
for (Iterator iter = data.iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = getProperty(key);
ts.add(new StringMapEntry(key, val));
}
return ts;
}
private static class StringMapEntry implements Map.Entry, Comparable {
private Object _key;
private Object _value;
public StringMapEntry(String key, String val) {
_key = key;
_value = val;
}
public Object getKey() {
return _key;
}
public Object getValue() {
return _value;
}
public Object setValue(Object value) {
Object old = _value;
_value = value;
return old;
}
public int compareTo(Object o) {
if (o == null) return -1;
if (o instanceof StringMapEntry) return ((String) getKey()).compareTo((String)((StringMapEntry) o).getKey());
if (o instanceof String) return ((String) getKey()).compareTo((String)o);
return -2;
}
/* FIXME missing hashCode() method FIXME */
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof StringMapEntry)) return false;
StringMapEntry e = (StringMapEntry) o;
return DataHelper.eq(e.getKey(), getKey()) && DataHelper.eq(e.getValue(), getValue());
}
}
///
/// tests
///
public static void main(String args[]) {
test(new OrderedProperties());
_log.debug("After ordered");
//test(new Properties());
//System.out.println("After normal");
test2();
testThrash();
}
private static void test2() {
OrderedProperties p = new OrderedProperties();
p.setProperty("a", "b");
p.setProperty("c", "d");
OrderedProperties p2 = new OrderedProperties();
try {
p2.putAll(p);
} catch (Throwable t) {
t.printStackTrace();
}
_log.debug("After test2");
}
private static void test(Properties p) {
for (int i = 0; i < 10; i++)
p.setProperty(i + "asdfasdfasdf", "qwerasdfqwer");
for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = p.getProperty(key);
_log.debug("[" + key + "] = [" + val + "]");
}
p.remove(4 + "asdfasdfasdf");
_log.debug("After remove");
for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = p.getProperty(key);
_log.debug("[" + key + "] = [" + val + "]");
}
try {
p.put("nullVal", null);
_log.debug("Null put did NOT fail!");
} catch (NullPointerException npe) {
_log.debug("Null put failed correctly");
}
}
/**
* Set 100 concurrent threads trying to do some operations against a single
* OrderedProperties object a thousand times. Hopefully this will help
* flesh out any synchronization issues.
*
*/
private static void testThrash() {
OrderedProperties prop = new OrderedProperties();
for (int i = 0; i < 100; i++)
prop.setProperty(i + "", i + " value");
_log.debug("Thrash properties built");
for (int i = 0; i < 100; i++)
thrash(prop, i);
}
private static void thrash(Properties props, int i) {
I2PThread t = new I2PThread(new Thrash(props));
t.setName("Thrash" + i);
t.start();
}
private static class Thrash implements Runnable {
private Properties _props;
public Thrash(Properties props) {
_props = props;
}
public void run() {
int numRuns = 1000;
_log.debug("Begin thrashing " + numRuns + " times");
for (int i = 0; i < numRuns; i++) {
Set keys = _props.keySet();
//_log.debug("keySet fetched");
int cur = 0;
for (Iterator iter = keys.iterator(); iter.hasNext();) {
Object o = iter.next();
Object val = _props.get(o);
//_log.debug("Value " + cur + " fetched");
cur++;
}
//_log.debug("Values fetched");
int size = _props.size();
_log.debug("Size calculated");
}
_log.debug("Done thrashing " + numRuns + " times");
private static class EntryComparator implements Comparator {
public int compare(Object l, Object r) {
return ((String)((Map.Entry)l).getKey()).compareTo(((String)((Map.Entry)r).getKey()));
}
}
}