Source code: com/thermidor/xml/TokenTable.java
1 package com.thermidor.xml;
2 /*@LEGAL*/
3 import java.util.HashMap;
4 import java.util.Iterator;
5
6 /**
7 * The TokenTable class is a genric token table used to maintain an list of
8 * string identifiers to token mappings, and finds particular use in the
9 * implementation of parsers.
10 * @author James Turnock
11 */
12 public class TokenTable {
13 /**
14 * Constant used to indicate that no mapping was defined for a given
15 * identifier.
16 */
17 public static final int NO_TOKEN_DEFINED = -1;
18
19 /**
20 * Essentially the token table for storing the mapping between given
21 * identifiers and their tokens.
22 */
23 private HashMap tokenTable = new HashMap(89);
24
25 /**
26 * Flag indicating whether the TokenTable instance has been declared ready.
27 * Ready means that no more mappings will be added to the token table and
28 * it is ready to be used by clients.
29 */
30 private boolean ready = false;
31
32 /**
33 * Flag indication whether the TokenTable instance should check for the
34 * integrity of the proposed mappings.
35 */
36 private final boolean checkIntegrity;
37
38 /**
39 * Default constructor. Instantiates a token table with integrity checking.
40 */
41 public TokenTable() {
42 this(true);
43 }
44
45 /**
46 * Instantiates a token table with integrity checking.
47 * @param checkIntegrity Whether the TokenTable should ensure integrity
48 * by not allowing duplicate identifiers and tokens.
49 */
50 public TokenTable(boolean checkIntegrity) {
51 this.checkIntegrity = checkIntegrity;
52 }
53
54 /**
55 * Stores the mapping between the identifier and the token.
56 * @param identifier The identifier to be associated with the token.
57 * @param token The token to associated with the identifier.
58 */
59 public final void put(String identifier, int token) {
60 if (ready) {
61 throw new IllegalStateException("Cannot add token mappings once " +
62 " ready has been called");
63
64 } else {
65 synchronized (tokenTable) {
66 if (checkIntegrity) {
67 assertIntegrity(identifier, token);
68 }
69
70 tokenTable.put(identifier, new Integer(token));
71 }
72 }
73 }
74
75 /**
76 * Performs a series of integrity checks on the proposed identifier
77 * and token: (a) that the identifier is not NULL (b) that the token
78 * is not the reserved value for designating an unknown mapping and
79 * (c) that there is no duplication of previously held mapping information.
80 * @param identifier The proposed identifier.
81 * @param token The proposed token.
82 */
83 private void assertIntegrity(String identifier, int token) {
84 assertNotNull(identifier);
85 assertNotUndefinedToken(token);
86 assertNoDuplicates(identifier, token);
87 }
88
89 /**
90 * Asserts that the proposed identifier is not Null.
91 * @param identifier The proposed identifier.
92 */
93 private static void assertNotNull(String identifier) {
94 if (identifier == null) {
95 throw new NullPointerException("Proposed token identitifer" +
96 "is NULL");
97 }
98 }
99 /**
100 Message constant <code>A token cannot posses value
101 used to define an undefined mapping:</code>.
102 */
103 private static final String MSG_UNDEFINED_TOKEN;
104 /**
105 Message constant <code>Duplicate identifier and token added.
106 [Identifier,TOKEN]=</code>.
107 */
108 private static final String MSG_TOKEN_REASSIGNMENT;
109 /**
110 Message constant <code>Duplicate token. [Existing,Token,Identifier]
111 =</code>.
112 */
113 private static final String MSG_DUPLICATE_TOKEN;
114 /**
115 Message constant <code>Duplicate identifier [Identifier]
116 =</code>.
117 */
118 private static final String MSG_DUPLICATE_IDENTIFIER;
119 static{
120 MSG_UNDEFINED_TOKEN = "A token cannot posses value " +
121 "used to define an undefined mapping:" + NO_TOKEN_DEFINED;
122 MSG_DUPLICATE_TOKEN = "Duplicate identifier and token added." +
123 " [Identifier,TOKEN]=";
124 MSG_TOKEN_REASSIGNMENT = "Duplicate token. [Existing,Token," +
125 "Identifier]=";
126 MSG_DUPLICATE_IDENTIFIER = "Duplicate identifier [Identifier]=";
127 }
128
129 /**
130 * Asserts that the proposed token is not the same as the constant used to
131 * designate an undefined mapping.
132 * @param proposedToken The proposed token.
133 */
134 private static void assertNotUndefinedToken(int proposedToken) {
135 if (proposedToken == NO_TOKEN_DEFINED) {
136 throw new IllegalArgumentException(MSG_UNDEFINED_TOKEN);
137 }
138 }
139
140 /**
141 *Asserts that the TokenTable is in the state specified in the isReady
142 * parameter.
143 *@param isReady The test state to compare to the internal state.
144 */
145 private void assertReady(boolean isReady) {
146 if (ready != isReady) {
147 throw new IllegalStateException();
148 }
149 }
150
151 /**
152 * Asserts that there is no duplication of previously held mapping
153 * information.
154 * @param proposedIdentifier The proposed identifier.
155 * @param proposedToken The proposed token.
156 */
157 private void assertNoDuplicates(String proposedIdentifier,
158 int proposedToken) {
159 Iterator it = tokenTable.entrySet().iterator();
160
161 while (it.hasNext()) {
162
163 java.util.Map.Entry
164 entry
165 = (java.util.Map.Entry
166 )it.next();
167
168 int declaredToken = ((Integer)entry.getValue()).intValue();
169
170 String declaredIdentifier = (String)entry.getKey();
171
172 boolean tokenDuplicate = (declaredToken == proposedToken);
173
174 boolean identifierDuplicate =
175 declaredIdentifier.equals(proposedIdentifier);
176
177 if (tokenDuplicate && identifierDuplicate) {
178 throw new IllegalArgumentException(MSG_DUPLICATE_TOKEN +
179 proposedIdentifier +
180 "," + proposedToken);
181
182 } else if (tokenDuplicate) {
183 throw new IllegalArgumentException(MSG_TOKEN_REASSIGNMENT +
184 declaredIdentifier +
185 "," + proposedToken +
186 "," + proposedIdentifier);
187
188 } else if (identifierDuplicate) {
189 throw new IllegalArgumentException(MSG_DUPLICATE_IDENTIFIER +
190 proposedIdentifier);
191 }
192 }
193 }
194
195 /**
196 * @param identifier The identifier for which the mapped token is required.
197 * @return The mapped value of the identifier or -1 if no mapping was
198 * defined.
199 */
200 public final int getToken(String identifier) {
201 assertReady(true);
202 Integer token = (Integer)tokenTable.get(identifier);
203 return token == null ? (NO_TOKEN_DEFINED) : (token.intValue());
204 }
205
206 /**
207 * Signals that the population of the token table has finished. This
208 * prevents further mappings from being added to the TokenTable.
209 */
210 public void ready() {
211 ready = true;
212 }
213
214 /**
215 * Utility method for discovering whether the token table has been declared
216 * ready.
217 * @return Whether the TokenTable instance has been transitioned to the
218 * ready state.
219 */
220 public boolean isReady() {
221 return ready;
222 }
223 }
224
225
226
227