View Javadoc

1   package cz.cuni.amis.pogamut.multi.utils.timekey;
2   
3   import java.lang.ref.WeakReference;
4   import java.util.ArrayList;
5   import java.util.Comparator;
6   import java.util.HashMap;
7   import java.util.List;
8   import java.util.Map;
9   
10  import cz.cuni.amis.utils.flag.FlagInteger;
11  import cz.cuni.amis.utils.flag.ImmutableFlag;
12  
13  /**
14   * the TimeKey class makes sure that for every integer time there is only one instance of the TimeKey object.
15   * The inner structure of active is implemented via a WeakHashMap.
16   * This means that if a process loses reference for the TimeKey object, it can be garbage collected.
17   * @author srlok
18   *
19   */
20  public class TimeKey implements Comparable {
21  	
22  	public static class TimeKeyComparator implements Comparator<TimeKey> {
23  
24  		@Override
25  		public int compare(TimeKey o1, TimeKey o2) {
26  			if (o1 == null && o2 == null) return 0;
27  			if (o1 == null) return -1;
28  			if (o2 == null) return 1;
29  			if (o1._time < o2._time) return -1;
30  			if (o1._time > o2._time) return 1;
31  			return 0;
32  		}
33  		
34  	};
35  	
36  	private static FlagInteger instances = new FlagInteger(0);
37  	
38  	public static ImmutableFlag<Integer> getInstances() {
39  		return instances.getImmutable();
40  	}
41  	
42  	@Override
43  	protected void finalize() throws Throwable {
44  		super.finalize();
45  		synchronized(keys) {
46  			WeakReference<TimeKey> ref = keys.get(_time);
47  			if (ref == null || ref.get() == null || ref.get()._time == _time) {
48  				keys.remove(_time);
49  			}
50  		}
51  		instances.decrement(1);
52  	}
53  	
54  	private long _time;
55  
56  	private TimeKey(long time) {
57  		instances.increment(1);
58  		_time = time;
59  	}
60  	
61  	// PROTECTED because of Test00_TimeKey
62  	protected static Map<Long, WeakReference<TimeKey>> keys = new HashMap<Long, WeakReference<TimeKey>>(1024);
63  	
64  	/**
65  	 * Returns the TimeKey object for the required time. <b>synchronized</b>
66  	 * 
67  	 * @param time
68  	 * @return always returns the same reference for the same time!
69  	 */
70  	public static TimeKey get(long time) {
71  		WeakReference<TimeKey> ref = keys.get(time);
72  		if (ref != null) {
73  			TimeKey key = ref.get();
74  			if (key != null) return key;
75  		}
76  		synchronized(keys) {
77  			ref = keys.get(time);
78  			if (ref == null) {
79  				TimeKey key = new TimeKey(time);
80  				keys.put(time, new WeakReference<TimeKey>(key));
81  				return key;
82  			}
83  			TimeKey key = ref.get();
84  			if (key == null) {
85  				key = new TimeKey(time);
86  				keys.put(time, new WeakReference<TimeKey>(key));
87  				return key;
88  			}
89  			return key;			
90  		}		
91  	}
92  	
93  	/**
94  	 * Wipes out case with time keys. Used by tests.
95  	 */
96  	public static void clear() {
97  		synchronized(keys) {
98  			keys.clear();
99  		}
100 	}
101 	
102 	@Override
103 	public boolean equals(Object other) {
104 		if (other == this) return true;  // early success
105 		if (other == null) return false; // early fail
106 		// We usually won't get here if comparing just TimeKey(s) as we maintain max 1 instance per _time.
107 		if (other instanceof TimeKey) {
108 			return ((TimeKey)other)._time == this._time;
109 		}
110 		return false;		
111 	}
112 	
113 	@Override
114 	public int hashCode() {
115 		return (int)(_time % Integer.MAX_VALUE);
116 	}
117 	
118 	/**
119 	 * Checks if a TimeKey instance exists for the integer time.
120 	 * @param time
121 	 * @return
122 	 */
123 	public static boolean exists(long time) {
124 		synchronized(keys) {
125 			WeakReference<TimeKey> ref = keys.get(time);
126 			if (ref == null) return false;
127 			if (ref.get() == null) {
128 				keys.remove(time);
129 				return false;
130 			}
131 			return true;
132 		}
133 	}
134 	
135 	/**
136 	 * returns the integer time for this timekey.
137 	 * @return
138 	 */
139 	public long getTime() {
140 		return _time;
141 	}
142 
143 	@Override
144 	public int compareTo(Object key) {
145 		if (this == key) return 0; // early success
146 		if (key == null) return 1; // early fail
147 		if (!(key instanceof TimeKey)) {
148 			throw new ClassCastException("TimeKey can only be compared with TimeKey objects");
149 		}
150 		if ( this._time < ((TimeKey)key)._time ) {
151 			return -1;
152 		} else {
153 			return 1;
154 		}
155 	}
156 	
157 	@Override
158 	public String toString() {
159 		return "TimeKey[" + _time + "]";
160 	}
161 
162 	public static List<WeakReference<TimeKey>> getAllKeys() {
163 		synchronized(keys) {
164 			return new ArrayList<WeakReference<TimeKey>>(keys.values());
165 		}
166 	}
167 }