Lines 11-20
Link Here
|
11 |
package org.eclipse.equinox.bidi; |
11 |
package org.eclipse.equinox.bidi; |
12 |
|
12 |
|
13 |
import java.lang.ref.SoftReference; |
13 |
import java.lang.ref.SoftReference; |
14 |
import org.eclipse.equinox.bidi.custom.STextStringProcessor; |
14 |
import org.eclipse.equinox.bidi.custom.STextProcessor; |
15 |
|
15 |
|
16 |
/** |
16 |
/** |
17 |
* This class records strings which are structured text. Several static |
17 |
* This class records strings which contain structured text. Several static |
18 |
* methods in this class allow to record such strings in a pool, and to find if |
18 |
* methods in this class allow to record such strings in a pool, and to find if |
19 |
* a given string is member of the pool. |
19 |
* a given string is member of the pool. |
20 |
* <p> |
20 |
* <p> |
Lines 22-27
Link Here
|
22 |
* <p> |
22 |
* <p> |
23 |
* The pool is managed as a cyclic list. When the pool is full, |
23 |
* The pool is managed as a cyclic list. When the pool is full, |
24 |
* each new element overrides the oldest element in the list. |
24 |
* each new element overrides the oldest element in the list. |
|
|
25 |
* <p> |
26 |
* A string may be itself entirely a structured text, or it may contain |
27 |
* segments each of which is a structured text of a given type. Each such |
28 |
* segment is identified by its starting and ending offsets within the |
29 |
* string, and by the processor which is appropriate to handle it. |
25 |
*/ |
30 |
*/ |
26 |
public class STextStringRecord { |
31 |
public class STextStringRecord { |
27 |
/** |
32 |
/** |
Lines 33-192
Link Here
|
33 |
private static final int MAXINDEX = POOLSIZE - 1; |
38 |
private static final int MAXINDEX = POOLSIZE - 1; |
34 |
|
39 |
|
35 |
// index of the last entered record |
40 |
// index of the last entered record |
36 |
private static int last = MAXINDEX; |
41 |
private static int last = -1; |
|
|
42 |
|
43 |
// flag indicating that the pool has wrapped around |
44 |
private static boolean wrapAround; |
37 |
|
45 |
|
38 |
// the pool |
46 |
// the pool |
39 |
private static STextStringRecord[] records = new STextStringRecord[POOLSIZE]; |
47 |
private static SoftReference[] recordRefs = new SoftReference[POOLSIZE]; |
40 |
|
48 |
|
41 |
// structured text types |
49 |
// hash code of the recorded strings |
42 |
private static final String[] types = STextStringProcessor.getKnownTypes(); |
50 |
private static int[] hashArray = new int[POOLSIZE]; |
43 |
|
51 |
|
44 |
// maximum type index allowed |
52 |
// total number of segments in the record |
45 |
private static int MAXTYPE = types.length - 1; |
53 |
private int totalSegmentCount; |
|
|
54 |
|
55 |
// number of used segments in the record |
56 |
private int usedSegmentCount; |
46 |
|
57 |
|
47 |
// reference to the recorded string |
58 |
// reference to the recorded string |
48 |
private SoftReference strRef; |
59 |
private String string; |
49 |
|
60 |
|
50 |
// hash code of the recorded string |
61 |
// reference to the processors of the STT segments in the recorded string |
51 |
private int hash; |
62 |
private STextProcessor[] processors; |
52 |
|
63 |
|
53 |
// reference to the triplets of the recorded string |
64 |
// reference to the boundaries of the STT segments in the recorded string |
54 |
private SoftReference triRef; |
65 |
// (entries 0, 2, 4, ... are start offsets; entries 1, 3, 5, ... are |
|
|
66 |
// ending offsets) |
67 |
private short[] boundaries; |
55 |
|
68 |
|
56 |
/** |
69 |
/** |
57 |
* Constructor. |
70 |
* Constructor |
58 |
* |
|
|
59 |
* @param string the string to record |
60 |
* |
61 |
* @param triplets |
62 |
* array of short integers, the number of elements in the array |
63 |
* must be a multiple of 3, so that the array is made of one or |
64 |
* more triplets of short integers. |
65 |
* <p> |
66 |
* The first element in each triplet is the beginning offset of a |
67 |
* susbstring of <code>string</code> which is a structured text. |
68 |
* <p> |
69 |
* The second element in each triplet is the ending offset of a |
70 |
* susbstring of <code>string</code> which is a structured text. |
71 |
* This offset points to one position beyond the last |
72 |
* character of the substring. |
73 |
* <p> |
74 |
* The third element in each triplet is the numeric type of the |
75 |
* structured text.<br> |
76 |
* The type of a structured text must be one of the string |
77 |
* values listed in {@link ISTextTypes}.<br> |
78 |
* The corresponding numeric type must be obtained using the |
79 |
* method {@link #typeStringToShort typeStringToShort}. |
80 |
*/ |
71 |
*/ |
81 |
public STextStringRecord(String string, short[] triplets) { |
72 |
private STextStringRecord() { |
82 |
if (string == null || triplets == null) |
73 |
// inhibit creation of new instances by customers |
83 |
throw new IllegalArgumentException("The string and triplets argument must not be null!"); //$NON-NLS-1$ |
|
|
84 |
if ((triplets.length % 3) != 0) |
85 |
throw new IllegalArgumentException("The number of elements in triplets must be a multiple of 3!"); //$NON-NLS-1$ |
86 |
for (int i = 2; i < triplets.length; i += 3) |
87 |
if (triplets[i] < 0 || triplets[i] > MAXTYPE) |
88 |
throw new IllegalArgumentException("Illegal type value in element" + i); //$NON-NLS-1$ |
89 |
strRef = new SoftReference(string); |
90 |
triRef = new SoftReference(triplets); |
91 |
hash = string.hashCode(); |
92 |
} |
74 |
} |
93 |
|
75 |
|
94 |
/** |
76 |
/** |
95 |
* Get the numeric type of a structured text given its string type. |
77 |
* Record a string in the pool. The caller must specify the number |
|
|
78 |
* of segments in the record (at least 1), and the processor, starting |
79 |
* and ending offsets for the first segment. |
96 |
* |
80 |
* |
97 |
* @param type type of structured text as string. It must be one |
81 |
* @param string the string to record. |
98 |
* of the strings listed in {@link ISTextTypes}. |
|
|
99 |
* |
82 |
* |
100 |
* @return a value which is the corresponding numeric type. If |
83 |
* @param segmentCount number of segments allowed in this string. |
101 |
* <code>type</code> is invalid, the method returns <code>-1</code>. |
84 |
* This number must be >= 1. |
102 |
*/ |
85 |
* |
103 |
public static short typeStringToShort(String type) { |
86 |
* @param processor the processor appropriate to handle the type |
104 |
for (int i = 0; i < types.length; i++) |
87 |
* of structured text present in the first segment. |
105 |
if (types[i].equals(type)) |
88 |
* It may be one of the pre-defined processor instances |
106 |
return (short) i; |
89 |
* appearing in {@link STextEngine}, or it may be an instance |
107 |
return -1; |
90 |
* created by a plug-in or by the application. |
108 |
} |
91 |
* |
109 |
|
92 |
* @param start offset in the string of the starting character of the first |
110 |
/** |
93 |
* segment. It must be >= 0 and less than the length of the string. |
111 |
* Get the string type of a structured text given its numeric type. |
|
|
112 |
* |
94 |
* |
113 |
* @param shType |
95 |
* @param limit offset of the character following the first segment. It |
114 |
* the numeric type of a structured text. It should be a value |
96 |
* must be greater than the <code>start</code> argument and |
115 |
* obtained using {@link #typeStringToShort typeStringToShort}. |
97 |
* not greater than the length of the string. |
116 |
* |
98 |
* |
117 |
* @return the corresponding string type. If <code>shType</code> is invalid, |
99 |
* @return an instance of STextRecordString which represents this record. |
118 |
* the method returns <code>null</code>. |
100 |
* This instance may be used to specify additional segment with |
|
|
101 |
* {@link #addSegment addSegment}. |
102 |
* |
103 |
* @throws IllegalArgumentException if <code>string</code> is null or |
104 |
* if <code>segmentCount</code> is less than 1. |
105 |
* @throws also the same exceptions as {@link #addSegment addSegment}. |
119 |
*/ |
106 |
*/ |
120 |
public static String typeShortToString(short shType) { |
107 |
public static STextStringRecord addRecord(String string, int segmentCount, STextProcessor processor, int start, int limit) { |
121 |
if (shType < 0 || shType > MAXTYPE) |
108 |
if (string == null) |
122 |
return null; |
109 |
throw new IllegalArgumentException("The string argument must not be null!"); //$NON-NLS-1$ |
123 |
return types[shType]; |
110 |
if (segmentCount < 1) |
|
|
111 |
throw new IllegalArgumentException("The segment count must be at least 1!"); //$NON-NLS-1$ |
112 |
synchronized (recordRefs) { |
113 |
if (last < MAXINDEX) |
114 |
last++; |
115 |
else { |
116 |
wrapAround = true; |
117 |
last = 0; |
118 |
} |
119 |
} |
120 |
STextStringRecord record = null; |
121 |
if (recordRefs[last] != null) |
122 |
record = (STextStringRecord) recordRefs[last].get(); |
123 |
if (record == null) { |
124 |
record = new STextStringRecord(); |
125 |
recordRefs[last] = new SoftReference(record); |
126 |
} |
127 |
hashArray[last] = string.hashCode(); |
128 |
for (int i = 0; i < record.usedSegmentCount; i++) |
129 |
record.processors[i] = null; |
130 |
if (segmentCount > record.totalSegmentCount) { |
131 |
record.processors = new STextProcessor[segmentCount]; |
132 |
record.boundaries = new short[segmentCount * 2]; |
133 |
record.totalSegmentCount = segmentCount; |
134 |
} |
135 |
record.usedSegmentCount = 0; |
136 |
record.string = string; |
137 |
record.addSegment(processor, start, limit); |
138 |
return record; |
124 |
} |
139 |
} |
125 |
|
140 |
|
126 |
/** |
141 |
/** |
127 |
* Add a record to the pool. |
142 |
* Add a second or further segment to a record. |
|
|
143 |
* |
144 |
* @param processor the processor appropriate to handle the type |
145 |
* of structured text present in this segment. |
146 |
* It may be one of the pre-defined processor instances |
147 |
* appearing in {@link STextEngine}, or it may be an instance |
148 |
* created by a plug-in or by the application. |
128 |
* |
149 |
* |
129 |
* @param record a STextStringRecord instance |
150 |
* @param start offset in the string of the starting character of the |
|
|
151 |
* segment. It must be >= 0 and less than the length of the string. |
152 |
* |
153 |
* @param limit offset of the character following the segment. It must be |
154 |
* greater than the <code>start</code> argument and not greater |
155 |
* than the length of the string. |
156 |
* |
157 |
* @throws IllegalArgumentException if <code>processor</code> is null, |
158 |
* or if <code>start</code> or <code>limit</code> have invalid |
159 |
* values. |
160 |
* @throws IllegalStateException if the current segment exceeds the |
161 |
* number of segments specified by <code>segmentCount</code> |
162 |
* in the call to {@link #addRecord addRecord} which created |
163 |
* the STextStringRecord instance. |
130 |
*/ |
164 |
*/ |
131 |
public static synchronized void add(STextStringRecord record) { |
165 |
public void addSegment(STextProcessor processor, int start, int limit) { |
132 |
if (last < MAXINDEX) |
166 |
if (processor == null) |
133 |
last++; |
167 |
throw new IllegalArgumentException("The processor argument must not be null!"); //$NON-NLS-1$ |
134 |
else |
168 |
if (start < 0 || start >= string.length()) |
135 |
last = 0; |
169 |
throw new IllegalArgumentException("The start position must be at least 0 and less than the length of the string!"); //$NON-NLS-1$ |
136 |
records[last] = record; |
170 |
if (limit <= start || limit > string.length()) |
|
|
171 |
throw new IllegalArgumentException("The limit position must be greater than the start position but no greater than the length of the string!"); //$NON-NLS-1$ |
172 |
if (usedSegmentCount >= totalSegmentCount) |
173 |
throw new IllegalStateException("All segments of the record are already used!"); //$NON-NLS-1$ |
174 |
processors[usedSegmentCount] = processor; |
175 |
boundaries[usedSegmentCount * 2] = (short) start; |
176 |
boundaries[usedSegmentCount * 2 + 1] = (short) limit; |
177 |
usedSegmentCount++; |
137 |
} |
178 |
} |
138 |
|
179 |
|
139 |
/** |
180 |
/** |
140 |
* Check if a string is recorded and retrieve its triplets. |
181 |
* Check if a string is recorded and retrieve its record. |
141 |
* |
182 |
* |
142 |
* @param string the string to check |
183 |
* @param string the string to check. |
143 |
* |
184 |
* |
144 |
* @return <code>null</code> if the string is not recorded in the pool; |
185 |
* @return <code>null</code> if the string is not recorded in the pool; |
145 |
* otherwise, return the triplets associated with this string. |
186 |
* otherwise, return the STextStringRecord instance which |
|
|
187 |
* records this string.<br> |
188 |
* Once a record has been found, the number of its segments can |
189 |
* be retrieved using {@link #getSegmentCount getSegmentCount}, |
190 |
* its processor can |
191 |
* be retrieved using {@link #getProcessor getProcessor}, |
192 |
* its starting offset can |
193 |
* be retrieved using {@link #getStart getStart}, |
194 |
* its ending offset can |
195 |
* be retrieved using {@link #getLimit getLimit}, |
146 |
*/ |
196 |
*/ |
147 |
public static short[] getTriplets(String string) { |
197 |
public static STextStringRecord getRecord(String string) { |
148 |
if (records[0] == null) // no records at all |
198 |
if (last < 0) // no records at all |
149 |
return null; |
199 |
return null; |
150 |
if (string == null || string.length() < 1) |
200 |
if (string == null || string.length() < 1) |
151 |
return null; |
201 |
return null; |
152 |
STextStringRecord rec; |
202 |
STextStringRecord record; |
153 |
String str; |
|
|
154 |
short[] tri; |
155 |
int myLast = last; |
203 |
int myLast = last; |
156 |
int hash = string.hashCode(); |
204 |
int hash = string.hashCode(); |
157 |
for (int i = myLast; i >= 0; i--) { |
205 |
for (int i = myLast; i >= 0; i--) { |
158 |
rec = records[i]; |
206 |
if (hash != hashArray[i]) |
159 |
if (hash == rec.hash && (tri = (short[]) rec.triRef.get()) != null && (str = (String) rec.strRef.get()) != null && string.equals(str)) { |
207 |
continue; |
160 |
return tri; |
208 |
record = (STextStringRecord) recordRefs[i].get(); |
161 |
} |
209 |
if (record == null) |
|
|
210 |
continue; |
211 |
if (string.equals(record.string)) |
212 |
return record; |
162 |
} |
213 |
} |
163 |
if (records[MAXINDEX] == null) // never recorded past myLast |
214 |
if (!wrapAround) // never recorded past myLast |
164 |
return null; |
215 |
return null; |
165 |
for (int i = MAXINDEX; i > myLast; i--) { |
216 |
for (int i = MAXINDEX; i > myLast; i--) { |
166 |
rec = records[i]; |
217 |
if (hash != hashArray[i]) |
167 |
if (hash == rec.hash && (tri = (short[]) rec.triRef.get()) != null && (str = (String) rec.strRef.get()) != null && string.equals(str)) { |
218 |
continue; |
168 |
return tri; |
219 |
record = (STextStringRecord) recordRefs[i].get(); |
169 |
} |
220 |
if (record == null) |
|
|
221 |
continue; |
222 |
if (string.equals(record.string)) |
223 |
return record; |
170 |
} |
224 |
} |
171 |
return null; |
225 |
return null; |
172 |
} |
226 |
} |
173 |
|
227 |
|
174 |
/** |
228 |
/** |
|
|
229 |
* Retrieve the number of segments in a record. |
230 |
* |
231 |
* @return the number of segments in the current record. This number |
232 |
* is always >= 1. |
233 |
*/ |
234 |
public int getSegmentCount() { |
235 |
return usedSegmentCount; |
236 |
} |
237 |
|
238 |
private void checkSegmentNumber(int segmentNumber) { |
239 |
if (segmentNumber >= usedSegmentCount) |
240 |
throw new IllegalArgumentException("The segment number " + segmentNumber + " is greater than the total number of segments = " + usedSegmentCount + "!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
241 |
} |
242 |
|
243 |
/** |
244 |
* Retrieve the processor of a given segment. |
245 |
* |
246 |
* @param segmentNumber number of the segment about which information |
247 |
* is required. It must be >= 0 and less than the number of |
248 |
* segments specified by <code>segmentCount</code> |
249 |
* in the call to {@link #addRecord addRecord} which created |
250 |
* the STextStringRecord instance. |
251 |
* |
252 |
* @return the processor to handle the structured text in the segment |
253 |
* specified by <code>segmentNumber</code>. |
254 |
* |
255 |
* @throws IllegalArgumentException if <code>segmentNumber</code> |
256 |
* has an invalid value. |
257 |
* |
258 |
* @see #getSegmentCount getSegmentCount |
259 |
*/ |
260 |
public STextProcessor getProcessor(int segmentNumber) { |
261 |
checkSegmentNumber(segmentNumber); |
262 |
return processors[segmentNumber]; |
263 |
} |
264 |
|
265 |
/** |
266 |
* Retrieve the starting offset of a given segment. |
267 |
* |
268 |
* @param segmentNumber number of the segment about which information |
269 |
* is required. It must be >= 0 and less than the number of |
270 |
* segments specified by <code>segmentCount</code> |
271 |
* in the call to {@link #addRecord addRecord} which created |
272 |
* the STextStringRecord instance. |
273 |
* |
274 |
* @return the starting offset within the string of the segment |
275 |
* specified by <code>segmentNumber</code>. |
276 |
* |
277 |
* @throws IllegalArgumentException if <code>segmentNumber</code> |
278 |
* has an invalid value. |
279 |
* |
280 |
* @see #getSegmentCount getSegmentCount |
281 |
*/ |
282 |
public int getStart(int segmentNumber) { |
283 |
checkSegmentNumber(segmentNumber); |
284 |
return boundaries[segmentNumber * 2]; |
285 |
} |
286 |
|
287 |
/** |
288 |
* Retrieve the ending offset of a given segment. |
289 |
* |
290 |
* @param segmentNumber number of the segment about which information |
291 |
* is required. It must be >= 0 and less than the number of |
292 |
* segments specified by <code>segmentCount</code> |
293 |
* in the call to {@link #addRecord addRecord} which created |
294 |
* the STextStringRecord instance. |
295 |
* |
296 |
* @return the offset of the position following the segment |
297 |
* specified by <code>segmentNumber</code>. |
298 |
* |
299 |
* @throws IllegalArgumentException if <code>segmentNumber</code> |
300 |
* has an invalid value. |
301 |
* |
302 |
* @see #getSegmentCount getSegmentCount |
303 |
*/ |
304 |
public int getLimit(int segmentNumber) { |
305 |
checkSegmentNumber(segmentNumber); |
306 |
return boundaries[segmentNumber * 2 + 1]; |
307 |
} |
308 |
|
309 |
/** |
175 |
* Clear the pool. All elements of the pool are erased and any associated |
310 |
* Clear the pool. All elements of the pool are erased and any associated |
176 |
* memory is freed. |
311 |
* memory is freed. |
177 |
* |
312 |
* |
178 |
*/ |
313 |
*/ |
179 |
public static synchronized void clear() { |
314 |
public static synchronized void clear() { |
180 |
for (int i = 0; i <= MAXINDEX; i++) { |
315 |
for (int i = 0; i <= MAXINDEX; i++) { |
181 |
STextStringRecord sr = records[i]; |
316 |
hashArray[i] = 0; |
182 |
if (sr == null) |
317 |
SoftReference softRef = recordRefs[i]; |
183 |
break; |
318 |
if (softRef == null) |
184 |
sr.hash = 0; |
319 |
continue; |
185 |
sr.strRef.clear(); |
320 |
STextStringRecord record = (STextStringRecord) softRef.get(); |
186 |
sr.triRef.clear(); |
321 |
if (record == null) |
187 |
records[i] = null; |
322 |
continue; |
|
|
323 |
record.boundaries = null; |
324 |
record.processors = null; |
325 |
record.totalSegmentCount = 0; |
326 |
record.usedSegmentCount = 0; |
327 |
recordRefs[i].clear(); |
188 |
} |
328 |
} |
189 |
last = MAXINDEX; |
329 |
last = -1; |
|
|
330 |
wrapAround = false; |
190 |
} |
331 |
} |
191 |
|
332 |
|
192 |
} |
333 |
} |