001: /*
002: * Copyright (C) 2004 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 09. May 2004 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.core.util;
013:
014: import java.lang.ref.WeakReference;
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.Map;
018:
019: /**
020: * Store IDs against given object references.
021: * <p>
022: * Behaves similar to java.util.IdentityHashMap, but in JDK1.3 as well. Additionally the implementation
023: * keeps track of orphaned IDs by using a WeakReference to store the reference object.
024: * </p>
025: */
026: public class ObjectIdDictionary {
027:
028: private final Map map = new HashMap();
029: private int invalidCounter;
030:
031: private static interface Wrapper {
032: int hashCode();
033:
034: boolean equals(Object obj);
035:
036: String toString();
037:
038: Object get();
039: }
040:
041: private static class IdWrapper implements Wrapper {
042:
043: private final Object obj;
044:
045: public IdWrapper(Object obj) {
046: this .obj = obj;
047: }
048:
049: public int hashCode() {
050: return System.identityHashCode(obj);
051: }
052:
053: public boolean equals(Object other) {
054: return obj == ((Wrapper) other).get();
055: }
056:
057: public String toString() {
058: return obj.toString();
059: }
060:
061: public Object get() {
062: return obj;
063: }
064: }
065:
066: private class WeakIdWrapper implements Wrapper {
067:
068: private final int hashCode;
069: private final WeakReference ref;
070:
071: public WeakIdWrapper(Object obj) {
072: hashCode = System.identityHashCode(obj);
073: ref = new WeakReference(obj);
074: }
075:
076: public int hashCode() {
077: return hashCode;
078: }
079:
080: public boolean equals(Object other) {
081: return get() == ((Wrapper) other).get();
082: }
083:
084: public String toString() {
085: Object obj = get();
086: return obj == null ? "(null)" : obj.toString();
087: }
088:
089: public Object get() {
090: Object obj = ref.get();
091: if (obj == null) {
092: // it was a lot faster and more efficient simply to count the number of
093: // evidences instead of keeping the Wrapper somewhere in a remove list
094: ++ObjectIdDictionary.this .invalidCounter;
095: }
096: return obj;
097: }
098: }
099:
100: public void associateId(Object obj, Object id) {
101: map.put(new WeakIdWrapper(obj), id);
102: cleanup();
103: }
104:
105: public Object lookupId(Object obj) {
106: Object id = map.get(new IdWrapper(obj));
107: cleanup();
108: return id;
109: }
110:
111: public boolean containsId(Object item) {
112: boolean b = map.containsKey(new IdWrapper(item));
113: cleanup();
114: return b;
115: }
116:
117: public void removeId(Object item) {
118: map.remove(new IdWrapper(item));
119: cleanup();
120: }
121:
122: public int size() {
123: return map.size();
124: }
125:
126: private void cleanup() {
127: if (invalidCounter > 100) {
128: // much more efficient to remove any orphaned wrappers at once
129: for (final Iterator iterator = map.keySet().iterator(); iterator
130: .hasNext();) {
131: final WeakIdWrapper key = (WeakIdWrapper) iterator
132: .next();
133: if (key.get() == null) {
134: iterator.remove();
135: }
136: }
137: invalidCounter = 0;
138: }
139: }
140: }
|