View | Details | Raw Unified | Return to bug 183164 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/equinox/bidi/STextStringRecord.java (-104 / +245 lines)
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
}

Return to bug 183164