* OrderedProperties: Vastly implify, use in i2psnark
This commit is contained in:
@ -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())
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user