/* * Copyright (C) 2004 Joe Walnes. * Copyright (C) 2006, 2007, 2008 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. * * Created on 09. May 2004 by Joe Walnes */ package com.thoughtworks.xstream.core.util; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Store IDs against given object references. * <p> * Behaves similar to java.util.IdentityHashMap, but in JDK1.3 as well. Additionally the * implementation keeps track of orphaned IDs by using a WeakReference to store the reference * object. * </p> */ public class ObjectIdDictionary { private final Map map = new HashMap(); private volatile int counter; private static interface Wrapper { int hashCode(); boolean equals(Object obj); String toString(); Object get(); } private static class IdWrapper implements Wrapper { private final Object obj; private final int hashCode; public IdWrapper(Object obj) { hashCode = System.identityHashCode(obj); this.obj = obj; } public int hashCode() { return hashCode; } public boolean equals(Object other) { return obj == ((Wrapper)other).get(); } public String toString() { return obj.toString(); } public Object get() { return obj; } } private static class WeakIdWrapper implements Wrapper { private final int hashCode; private final WeakReference ref; public WeakIdWrapper(Object obj) { hashCode = System.identityHashCode(obj); ref = new WeakReference(obj); } public int hashCode() { return hashCode; } public boolean equals(Object other) { return get() == ((Wrapper)other).get(); } public String toString() { Object obj = get(); return obj == null ? "(null)" : obj.toString(); } public Object get() { Object obj = ref.get(); return obj; } } public void associateId(Object obj, Object id) { map.put(new WeakIdWrapper(obj), id); ++counter; cleanup(); } public Object lookupId(Object obj) { Object id = map.get(new IdWrapper(obj)); ++counter; return id; } public boolean containsId(Object item) { boolean b = map.containsKey(new IdWrapper(item)); ++counter; return b; } public void removeId(Object item) { map.remove(new IdWrapper(item)); ++counter; cleanup(); } public int size() { return map.size(); } private void cleanup() { if (counter > 10000) { counter = 0; // much more efficient to remove any orphaned wrappers at once for (final Iterator iterator = map.keySet().iterator(); iterator.hasNext();) { final WeakIdWrapper key = (WeakIdWrapper)iterator.next(); if (key.get() == null) { iterator.remove(); } } } } }