View Javadoc

1   package cz.cuni.amis.utils.token;
2   
3   import java.io.IOException;
4   import java.io.ObjectInputStream;
5   import java.io.Serializable;
6   import java.util.Map;
7   import java.util.Set;
8   
9   import cz.cuni.amis.utils.HashCode;
10  import cz.cuni.amis.utils.exception.PogamutException;
11  
12  /**
13   * Represents a String that can be used inside {@link Map} as keys or {@link Set}s as
14   * {@link Token#equals(Object)} has O(1) time complexity which is much better when
15   * compared to the O(N) time complexity of {@link String#equals(Object)}.
16   * <p><p>
17   * Note that you can't instantiate this token, instead use {@link Tokens#get(long)} or {@link Tokens#get(String)}
18   * which is THREAD-SAFE.
19   * <p><p>
20   * The object is suitable for serialization / deserialization, even sending the object between
21   * JVM won't broke anything as it has {@link Token#readObject()} implemented the way that
22   * ensures that two same {@link Token#name} won't receive different {@link Token#ids}.
23   *    
24   * @author Jimmy
25   */
26  public class Token implements IToken, Serializable {
27  	
28  	/**
29  	 * Contains the string identifier of the token.
30  	 */
31  	private String name;
32  	
33  	/**
34  	 * Contains the 'ids' that belongs to this token.
35  	 * <p><p>
36  	 * Transient - no need to serialize it as the {@link Token#readObject()} is retranslating
37  	 * the {@link Token#name} through {@link Tokens#get(String)} again.
38  	 */
39  	transient private long[] ids;
40  	
41  	/**
42  	 * 
43  	 */
44  	transient private int hashCode;
45  	transient private String nameWithIds = null;
46  
47  	/**
48  	 * Hidden constructor used by {@link Tokens} in a THREAD-SAFE way.
49  	 * @param name
50  	 * @param ids
51  	 */
52  	Token(String name, long[] ids) {
53  		this.name = name;
54  		this.ids = ids;
55  		HashCode hc = new HashCode();
56  		hc.add(ids.length);
57  		for (long id : ids) {
58  			hc.add(id);
59  		}
60  		this.hashCode = hc.getHash();
61  	}
62  	
63  	/**
64  	 * Utilized by the de-serialization, whenever we receive a token from different
65  	 * JVM, we must retranslate its 'name' to correct ids/hashcode.
66  	 * @return
67  	 */
68  	private void readObject(ObjectInputStream ois) {
69  		try {
70  			ois.defaultReadObject();
71  		} catch (IOException e) {
72  			throw new PogamutException("Could not deserialize Token", e);
73  		} catch (ClassNotFoundException e) {
74  			throw new PogamutException("Could not deserialize Token", e);
75  		}
76  		Token trueToken = Tokens.get(name);
77  		this.ids = trueToken.ids;
78  		this.hashCode = trueToken.hashCode;
79  	}
80  	
81  	@Override
82  	public int hashCode() {
83  		return hashCode;
84  	}
85  	
86  	/**
87  	 * Actually the implementation is as good as it can be, containing 
88  	 * early-success checking + NPE-proofed.
89  	 */
90  	@Override
91  	public boolean equals(Object obj) {
92  		if (this == obj) return true; // early-success
93  		if (obj == null) return false;		
94  		if (hashCode != obj.hashCode()) return false;
95  		if (!(obj instanceof IToken)) return false;
96  		IToken token = (IToken)obj;
97  		if (ids.length != token.getIds().length) return false;
98  		for (int i = 0; i < ids.length; ++i) {
99  			if (ids[i] != token.getIds()[i]) return false;
100 		}
101 		return true;
102 	}
103 
104 	/**
105 	 * Returns an underlying String identifier (might be useful when storing
106 	 * human-readable names).
107 	 */
108 	public String getToken() {
109 		return name;
110 	}
111 
112 	/**
113 	 * Returns unique ID of the token. No two different instances has the same ids.
114 	 */
115 	public long[] getIds() {
116 		return ids;
117 	}
118 	
119 	/**
120 	 * Returns the name with ids as suffix enclosed inside '[', ']' brackets.
121 	 * @return
122 	 */
123 	public String getNameWithIds() {
124 		if (nameWithIds != null) return nameWithIds ;
125 		StringBuffer sb = new StringBuffer();
126 		sb.append(name);
127 		sb.append("[");
128 		sb.append(ids[0]);
129 		for (int i = 1; i < ids.length; ++i) {
130 			sb.append(",");
131 			sb.append(ids[i]);
132 		}
133 		sb.append("]");
134 		nameWithIds = sb.toString();
135 		return nameWithIds;
136 	}
137 	
138 	/**
139 	 * Returns {@link Token#getNameWithIds()}.
140 	 */
141 	@Override
142 	public String toString() {
143 		return getNameWithIds();
144 	}
145 
146 }