~ubuntu-branches/ubuntu/vivid/mkgmap/vivid

« back to all changes in this revision

Viewing changes to src/uk/me/parabola/imgfmt/app/srt/Sort.java

  • Committer: Package Import Robot
  • Author(s): Andreas Tille
  • Date: 2014-08-13 22:13:41 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20140813221341-i9dzzjuto2o7hfh6
Tags: 0.0.0+svn3333-1
* New upstream version
  Closes: #745097
* add debian/classpath (thanks for the patch to Manfred Stock
  <manfred.stock+debian@gmail.com>)
  Closes: #741596
* d/copyright: DEP5

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import java.util.Map;
28
28
 
29
29
import uk.me.parabola.imgfmt.ExitException;
 
30
import uk.me.parabola.imgfmt.app.Label;
30
31
 
31
32
/**
32
33
 * Represents the sorting positions for all the characters in a codepage.
 
34
 *
 
35
 * A map contains a file that determines how the characters are to be sorted. So we
 
36
 * have to have to be able to create such a file and sort with exactly the same rules
 
37
 * as is contained in it.
 
38
 *
 
39
 * What about the java {@link java.text.RuleBasedCollator}? It turns out that it is possible to
 
40
 * make it work in the way we need it to, although it doesn't help with creating the srt file.
 
41
 * Also it is significantly slower than this implementation, so this one is staying. I also
 
42
 * found that sorting with the sort keys and the collator gave different results in some
 
43
 * cases. This implementation does not.
 
44
 *
 
45
 * Be careful when benchmarking. With small lists (< 10000 entries) repeated runs cause some
 
46
 * pretty aggressive optimisation to kick in. This tends to favour this implementation which has
 
47
 * much tighter loops that the java7 or ICU implementations, but this may not be realised with
 
48
 * real workloads.
 
49
 *
33
50
 * @author Steve Ratcliffe
34
51
 */
35
52
public class Sort {
36
 
 
37
 
        private static final byte[] ZERO_KEY = new byte[3];
 
53
        private static final byte[] ZERO_KEY = new byte[4];
 
54
        private static final Integer NO_ORDER = 0;
38
55
 
39
56
        private int codepage;
40
57
        private int id1; // Unknown - identifies the sort
43
60
        private String description;
44
61
        private Charset charset;
45
62
 
46
 
        private final byte[] primary = new byte[256];
47
 
        private final byte[] secondary = new byte[256];
48
 
        private final byte[] tertiary = new byte[256];
49
 
        private final byte[] flags = new byte[256];
 
63
        private final Page[] pages = new Page[256];
50
64
 
51
 
        private final List<CodePosition> expansions = new ArrayList<CodePosition>();
 
65
        private final List<CodePosition> expansions = new ArrayList<>();
52
66
        private int maxExpSize = 1;
53
67
 
54
68
        private CharsetEncoder encoder;
 
69
        private boolean multi;
 
70
        private int maxPage;
 
71
 
 
72
        public Sort() {
 
73
                pages[0] = new Page();
 
74
        }
55
75
 
56
76
        public void add(int ch, int primary, int secondary, int tertiary, int flags) {
57
 
                if (this.primary[ch & 0xff] != 0)
 
77
                ensurePage(ch >>> 8);
 
78
                if (getPrimary(ch) != 0)
58
79
                        throw new ExitException(String.format("Repeated primary index 0x%x", ch & 0xff));
59
 
                this.primary[ch & 0xff] = (byte) primary;
60
 
                this.secondary[ch & 0xff] = (byte) secondary;
61
 
                this.tertiary[ch & 0xff] = (byte) tertiary;
62
 
 
63
 
                this.flags[ch & 0xff] = (byte) flags;
 
80
                setPrimary (ch, primary);
 
81
                setSecondary(ch, secondary);
 
82
                setTertiary( ch, tertiary);
 
83
 
 
84
                setFlags(ch, flags);
 
85
        }
 
86
 
 
87
        /**
 
88
         * Run after all sorting order points have been added.
 
89
         *
 
90
         * Make sure that all tertiary values of secondary ignorable are greater
 
91
         * than any normal tertiary value.
 
92
         *
 
93
         * And the same for secondaries on primary ignorable.
 
94
         */
 
95
        public void finish() {
 
96
                int maxSecondary = 0;
 
97
                int maxTertiary = 0;
 
98
                for (Page p : pages) {
 
99
                        if (p == null)
 
100
                                continue;
 
101
 
 
102
                        for (int i = 0; i < 256; i++) {
 
103
                                if (((p.flags[i] >>> 4) & 0x3) == 0) {
 
104
                                        if (p.getPrimary(i) != 0) {
 
105
                                                byte second = p.getSecondary(i);
 
106
                                                maxSecondary = Math.max(maxSecondary, second);
 
107
                                                if (second != 0) {
 
108
                                                        maxTertiary = Math.max(maxTertiary, p.getTertiary(i));
 
109
                                                }
 
110
                                        }
 
111
                                }
 
112
                        }
 
113
                }
 
114
 
 
115
                for (Page p : pages) {
 
116
                        if (p == null)
 
117
                                continue;
 
118
 
 
119
                        for (int i = 0; i < 256; i++) {
 
120
                                if (((p.flags[i] >>> 4) & 0x3) != 0) continue;
 
121
 
 
122
                                if (p.getPrimary(i) == 0) {
 
123
                                        if (p.getSecondary(i) == 0) {
 
124
                                                if (p.getTertiary(i) != 0) {
 
125
                                                        p.setTertiary(i, p.getTertiary(i) + maxTertiary);
 
126
                                                }
 
127
                                        } else {
 
128
                                                p.setSecondary(i, p.getSecondary(i) + maxSecondary);
 
129
                                        }
 
130
                                }
 
131
                        }
 
132
                }
64
133
        }
65
134
 
66
135
        /**
67
136
         * Return a table indexed by a character value in the target codepage, that gives the complete sort
68
137
         * position of the character.
 
138
         *
 
139
         * This is only used for testing.
 
140
         *
69
141
         * @return A table of sort positions.
70
142
         */
71
143
        public char[] getSortPositions() {
72
144
                char[] tab = new char[256];
73
145
 
74
146
                for (int i = 1; i < 256; i++) {
75
 
                        tab[i] = (char) (((primary[i] << 8) & 0xff00) | ((secondary[i] << 4) & 0xf0) | (tertiary[i] & 0xf));
 
147
                        tab[i] = (char) (((getPrimary(i) << 8) & 0xff00) | ((getSecondary(i) << 4) & 0xf0) | (getTertiary(i) & 0xf));
76
148
                }
77
149
 
78
150
                return tab;
99
171
                if (cache != null) {
100
172
                        key = cache.get(s);
101
173
                        if (key != null)
102
 
                                return new SrtSortKey<T>(object, key, second);
 
174
                                return new SrtSortKey<>(object, key, second);
103
175
                }
104
176
 
105
 
                CharBuffer inb = CharBuffer.wrap(s);
106
177
                try {
107
 
                        ByteBuffer out = encoder.encode(inb);
108
 
                        byte[] bval = out.array();
 
178
                        char[] chars;
 
179
                        if (isMulti()) {
 
180
                                chars = s.toCharArray();
 
181
                        } else {
 
182
                                ByteBuffer out = encoder.encode(CharBuffer.wrap(s));
 
183
                                byte[] bval = out.array();
 
184
                                chars = new char[bval.length];
 
185
                                for (int i = 0; i < bval.length; i++)
 
186
                                        chars[i] = (char) (bval[i] & 0xff);
 
187
                        }
109
188
 
110
189
                        // In theory you could have a string where every character expands into maxExpSize separate characters
111
190
                        // in the key.  However if we allocate enough space to deal with the worst case, then we waste a
114
193
                        //
115
194
                        // We need +1 for the null bytes, we also +2 for a couple of expanded characters. For a complete
116
195
                        // german map this was always enough in tests.
117
 
                        key = new byte[(bval.length + 1 + 2) * 3];
 
196
                        key = new byte[(chars.length + 1 + 2) * 4];
118
197
                        try {
119
 
                                fillCompleteKey(bval, key);
 
198
                                fillCompleteKey(chars, key);
120
199
                        } catch (ArrayIndexOutOfBoundsException e) {
121
200
                                // Ok try again with the max possible key size allocated.
122
 
                                key = new byte[bval.length * 3 * maxExpSize + 3];
 
201
                                key = new byte[(chars.length+1) * 4 * maxExpSize];
 
202
                                fillCompleteKey(chars, key);
123
203
                        }
124
204
 
125
205
                        if (cache != null)
126
206
                                cache.put(s, key);
127
207
 
128
 
                        return new SrtSortKey<T>(object, key, second);
 
208
                        return new SrtSortKey<>(object, key, second);
129
209
                } catch (CharacterCodingException e) {
130
 
                        return new SrtSortKey<T>(object, ZERO_KEY);
131
 
                }
132
 
        }
133
 
 
 
210
                        return new SrtSortKey<>(object, ZERO_KEY);
 
211
                }
 
212
        }
 
213
 
 
214
        /**
 
215
         * Create a sort key based on a Label.
 
216
         *
 
217
         * The label will contain the actual characters (after transliteration for example)
 
218
         * @param object This is saved in the sort key for later retrieval and plays no part in the sorting.
 
219
         * @param label The label, the actual written bytes/chars will be used as input to the sort.
 
220
         * @param second Secondary sort key.
 
221
         * @param cache A cache for the created keys. This is for saving memory so it is essential that this
 
222
         * is managed by the caller.
 
223
         * @return A sort key.
 
224
         */
 
225
        public <T> SortKey<T> createSortKey(T object, Label label, int second, Map<Label, byte[]> cache) {
 
226
                byte[] key;
 
227
                if (cache != null) {
 
228
                        key = cache.get(label);
 
229
                        if (key != null)
 
230
                                return new SrtSortKey<>(object, key, second);
 
231
                }
 
232
 
 
233
                char[] encText = label.getEncText();
 
234
 
 
235
                // In theory you could have a string where every character expands into maxExpSize separate characters
 
236
                // in the key.  However if we allocate enough space to deal with the worst case, then we waste a
 
237
                // vast amount of memory. So allocate a minimal amount of space, try it and if it fails reallocate the
 
238
                // maximum amount.
 
239
                //
 
240
                // We need +1 for the null bytes, we also +2 for a couple of expanded characters. For a complete
 
241
                // german map this was always enough in tests.
 
242
                key = new byte[(encText.length + 1 + 2) * 4];
 
243
                try {
 
244
                        fillCompleteKey(encText, key);
 
245
                } catch (ArrayIndexOutOfBoundsException e) {
 
246
                        // Ok try again with the max possible key size allocated.
 
247
                        key = new byte[encText.length * 4 * maxExpSize + 4];
 
248
                        fillCompleteKey(encText, key);
 
249
                }
 
250
 
 
251
                if (cache != null)
 
252
                        cache.put(label, key);
 
253
 
 
254
                return new SrtSortKey<>(object, key, second);
 
255
        }
 
256
 
 
257
        /**
 
258
         * Convenient version of create sort key method.
 
259
         * @see #createSortKey(Object, String, int, Map)
 
260
         */
134
261
        public <T> SortKey<T> createSortKey(T object, String s, int second) {
135
262
                return createSortKey(object, s, second, null);
136
263
        }
137
264
 
 
265
        /**
 
266
         * Convenient version of create sort key method.
 
267
         *
 
268
         * @see #createSortKey(Object, String, int, Map)
 
269
         */
138
270
        public <T> SortKey<T> createSortKey(T object, String s) {
139
271
                return createSortKey(object, s, 0, null);
140
272
        }
141
273
 
 
274
        public <T> SortKey<T> createSortKey(T object, Label label) {
 
275
                return createSortKey(object, label, 0, null);
 
276
        }
 
277
 
 
278
        public <T> SortKey<T> createSortKey(T object, Label label, int second) {
 
279
                return createSortKey(object, label, second, null);
 
280
        }
 
281
 
142
282
        /**
143
283
         * Fill in the key from the given byte string.
144
284
         *
145
 
         * @param bval The string for which we are creating the sort key.
 
285
         * @param bVal The string for which we are creating the sort key.
146
286
         * @param key The sort key. This will be filled in.
147
287
         */
148
 
        private void fillCompleteKey(byte[] bval, byte[] key) {
149
 
                int start = fillKey(Collator.PRIMARY, primary, bval, key, 0);
150
 
                start = fillKey(Collator.SECONDARY, secondary, bval, key, start);
151
 
                fillKey(Collator.TERTIARY, tertiary, bval, key, start);
 
288
        private void fillCompleteKey(char[] bVal, byte[] key) {
 
289
                int start = fillKey(Collator.PRIMARY, bVal, key, 0);
 
290
                start = fillKey(Collator.SECONDARY, bVal, key, start);
 
291
                fillKey(Collator.TERTIARY, bVal, key, start);
152
292
        }
153
293
 
154
294
        /**
155
295
         * Fill in the output key for a given strength.
156
296
         *
157
 
         * @param sortPositions An array giving the sort position for each of the 256 characters.
158
297
         * @param input The input string in a particular 8 bit codepage.
159
298
         * @param outKey The output sort key.
160
299
         * @param start The index into the output key to start at.
161
300
         * @return The next position in the output key.
162
301
         */
163
 
        private int fillKey(int type, byte[] sortPositions, byte[] input, byte[] outKey, int start) {
 
302
        private int fillKey(int type, char[] input, byte[] outKey, int start) {
164
303
                int index = start;
165
 
                for (byte inb : input) {
166
 
                        int b = inb & 0xff;
167
 
 
168
 
                        int exp = (flags[b] >> 4) & 0x3;
 
304
                for (char c : input) {
 
305
 
 
306
                        if (!hasPage(c >>> 8))
 
307
                                continue;
 
308
 
 
309
                        int exp = (getFlags(c) >> 4) & 0x3;
169
310
                        if (exp == 0) {
170
 
                                // I am guessing that a sort position of 0 means that the character is ignorable at this
171
 
                                // strength. In other words it is as if it is not present in the string.  This appears to
172
 
                                // be true for shield symbols, but perhaps not for other kinds of control characters.
173
 
                                byte pos = sortPositions[b];
174
 
                                if (pos != 0)
175
 
                                        outKey[index++] = pos;
 
311
                                index = writePos(type, c, outKey, index);
176
312
                        } else {
177
313
                                // now have to redirect to a list of input chars, get the list via the primary value always.
178
 
                                byte idx = primary[b];
179
 
                                //List<CodePosition> list = expansions.get(idx-1);
180
 
 
 
314
                                int idx = getPrimary(c);
181
315
                                for (int i = idx - 1; i < idx + exp; i++) {
182
 
                                        byte pos = expansions.get(i).getPosition(type);
183
 
                                        if (pos != 0)
184
 
                                                outKey[index++] = pos;
 
316
                                        int pos = expansions.get(i).getPosition(type);
 
317
                                        if (pos != 0) {
 
318
                                                if (type == Collator.PRIMARY)
 
319
                                                        outKey[index++] = (byte) ((pos >>> 8) & 0xff);
 
320
                                                outKey[index++] = (byte) pos;
 
321
                                        }
185
322
                                }
186
323
                        }
187
324
                }
188
325
 
 
326
                if (type == Collator.PRIMARY)
 
327
                        outKey[index++] = '\0';
189
328
                outKey[index++] = '\0';
190
329
                return index;
191
330
        }
192
331
 
193
 
        public byte getPrimary(int ch) {
194
 
                return primary[ch];
195
 
        }
196
 
 
197
 
        public byte getSecondary(int ch) {
198
 
                return secondary[ch];
199
 
        }
200
 
 
201
 
        public byte getTertiary(int ch) {
202
 
                return tertiary[ch];
 
332
        public int getPrimary(int ch) {
 
333
                return this.pages[ch >>> 8].getPrimary(ch);
 
334
        }
 
335
 
 
336
        public int getSecondary(int ch) {
 
337
                return this.pages[ch >>> 8].getSecondary(ch);
 
338
        }
 
339
 
 
340
        public int getTertiary(int ch) {
 
341
                return this.pages[ch >>> 8].getTertiary(ch);
203
342
        }
204
343
 
205
344
        public byte getFlags(int ch) {
206
 
                return flags[ch];
 
345
                assert ch >= 0;
 
346
                return this.pages[ch >>> 8].flags[ch & 0xff];
207
347
        }
208
348
 
209
349
        public int getCodepage() {
251
391
 
252
392
        public void setCodepage(int codepage) {
253
393
                this.codepage = codepage;
254
 
                if (codepage == 0)
255
 
                        charset = Charset.forName("cp1252");
256
 
                else if (codepage == 65001)
257
 
                        charset = Charset.forName("UTF-8");
258
 
                else if (codepage == 932)
259
 
                        // Java uses "ms932" for code page 932
260
 
                        // (Windows-31J, Shift-JIS + MS extensions)
261
 
                        charset = Charset.forName("ms932");
262
 
                else
263
 
                        charset = Charset.forName("cp" + codepage);
 
394
                charset = charsetFromCodepage(codepage);
 
395
 
264
396
                encoder = charset.newEncoder();
265
397
                encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
266
398
        }
280
412
         * The case were two letters sort as if the were just one (and more complex cases) are
281
413
         * not supported or are unknown to us.
282
414
         *
283
 
         * @param bval The code point of this letter in the code page.
 
415
         * @param ch The code point of this letter in the code page.
284
416
         * @param inFlags The initial flags, eg if it is a letter or not.
285
417
         * @param expansionList The letters that this letter sorts as, as code points in the codepage.
286
418
         */
287
 
        public void addExpansion(byte bval, int inFlags, List<Byte> expansionList) {
288
 
                int idx = bval & 0xff;
289
 
                flags[idx] = (byte) ((inFlags & 0xf) | (((expansionList.size()-1) << 4) & 0x30));
 
419
        public void addExpansion(int ch, int inFlags, List<Integer> expansionList) {
 
420
                ensurePage(ch >>> 8);
 
421
                setFlags(ch, (byte) ((inFlags & 0xf) | (((expansionList.size()-1) << 4) & 0xf0)));
290
422
 
291
423
                // Check for repeated definitions
292
 
                if (primary[idx] != 0)
293
 
                        throw new ExitException(String.format("repeated code point %x", idx));
 
424
                if (getPrimary(ch) != 0)
 
425
                        throw new ExitException(String.format("repeated code point %x", ch));
294
426
 
295
 
                primary[idx] = (byte) (expansions.size() + 1);
296
 
                secondary[idx] = 0;
297
 
                tertiary[idx] = 0;
 
427
                setPrimary(ch, (expansions.size() + 1));
 
428
                setSecondary(ch,  0);
 
429
                setTertiary(ch, 0);
298
430
                maxExpSize = Math.max(maxExpSize, expansionList.size());
299
431
 
300
 
                for (Byte b : expansionList) {
 
432
                for (Integer b : expansionList) {
301
433
                        CodePosition cp = new CodePosition();
302
 
                        cp.setPrimary(primary[b & 0xff]);
303
 
                        cp.setSecondary(secondary[b & 0xff]);
304
 
                        cp.setTertiary((byte) (tertiary[b & 0xff] + 2));
 
434
                        cp.setPrimary((char) getPrimary(b & 0xff));
 
435
 
 
436
                        // Currently sort without secondary or tertiary differences to the base letters.
 
437
                        cp.setSecondary((byte) getSecondary(b & 0xff));
 
438
                        cp.setTertiary((byte) getTertiary(b & 0xff));
305
439
                        expansions.add(cp);
306
440
                }
307
441
        }
318
452
                return new SrtCollator(codepage);
319
453
        }
320
454
 
321
 
        /**
322
 
         * Create a default sort that simply sorts by the values of the characters.
323
 
         * It has to pretend to be associated with a particular code page, otherwise
324
 
         * it will not be recognised at all.
325
 
         *
326
 
         * This is not likely to be very useful. You need to create a sort description for your language
327
 
         * to make things work properly.
328
 
         *
329
 
         * @return A default sort.
330
 
         * @param codepage The code page that we are pretending to be.
331
 
         */
332
 
        public static Sort defaultSort(int codepage) {
333
 
                Sort sort = new Sort();
334
 
                for (int i = 1; i < 256; i++) {
335
 
                        sort.add(i, i, 0, 0, 0);
336
 
                }
337
 
                sort.charset = Charset.forName("ascii");
338
 
                sort.encoder = sort.charset.newEncoder();
339
 
                sort.setDescription("Default sort");
340
 
                sort.setCodepage(codepage);
341
 
                return sort;
342
 
        }
343
 
 
344
455
        public int getExpansionSize() {
345
456
                return expansions.size();
346
457
        }
349
460
                return String.format("sort cp=%d order=%08x", codepage, getSortOrderId());
350
461
        }
351
462
 
 
463
        private void setPrimary(int ch, int val) {
 
464
                this.pages[ch >>> 8].setPrimary(ch, val);
 
465
        }
 
466
 
 
467
        private void setSecondary(int ch, int val) {
 
468
                this.pages[ch >>> 8].setSecondary(ch, val);
 
469
        }
 
470
 
 
471
        private void setTertiary(int ch, int val) {
 
472
                this.pages[ch >>> 8].setTertiary(ch, val);
 
473
        }
 
474
 
 
475
        private void setFlags(int ch, int val) {
 
476
                this.pages[ch >>> 8].flags[ch & 0xff] = (byte) val;
 
477
        }
 
478
 
 
479
        public static Charset charsetFromCodepage(int codepage) {
 
480
                Charset charset;
 
481
                switch (codepage) {
 
482
                case 0:
 
483
                        charset = Charset.forName("ascii");
 
484
                        break;
 
485
                case 65001:
 
486
                        charset = Charset.forName("UTF-8");
 
487
                        break;
 
488
                case 932:
 
489
                        // Java uses "ms932" for code page 932
 
490
                        // (Windows-31J, Shift-JIS + MS extensions)
 
491
                        charset = Charset.forName("ms932");
 
492
                        break;
 
493
                default:
 
494
                        charset = Charset.forName("cp" + codepage);
 
495
                        break;
 
496
                }
 
497
                return charset;
 
498
        }
 
499
 
 
500
        public void setMulti(boolean multi) {
 
501
                this.multi = multi;
 
502
        }
 
503
 
 
504
        public boolean isMulti() {
 
505
                return multi;
 
506
        }
 
507
 
 
508
        public int getPos(int type, int ch) {
 
509
                return pages[ch >>> 8].getPos(type, ch);
 
510
        }
 
511
 
 
512
        public int writePos(int type, int ch, byte[] outkey, int start) {
 
513
                return pages[ch >>> 8].writePos(type, ch, outkey, start);
 
514
        }
 
515
 
 
516
        /**
 
517
         * Ensure that the given page exists in the page array.
 
518
         *
 
519
         * @param n The page index.
 
520
         */
 
521
        private void ensurePage(int n) {
 
522
                assert n == 0 || isMulti();
 
523
                if (this.pages[n] == null) {
 
524
                        this.pages[n] = new Page();
 
525
                        if (n > maxPage)
 
526
                                maxPage = n;
 
527
                }
 
528
        }
 
529
 
 
530
        /**
 
531
         * The max page, top 8+ bits of the character that we have information on.
 
532
         */
 
533
        public int getMaxPage() {
 
534
                return maxPage;
 
535
        }
 
536
 
 
537
        /**
 
538
         * @return True if there is at least one character with the given page/block number.
 
539
         */
 
540
        public boolean hasPage(int p) {
 
541
                return pages[p] != null;
 
542
        }
 
543
 
 
544
        /**
 
545
         * Holds the sort positions of a 256 character block.
 
546
         */
 
547
        private static class Page {
 
548
                private final char[] primary = new char[256];
 
549
                private final byte[] secondary = new byte[256];
 
550
                private final byte[] tertiary = new byte[256];
 
551
                private final byte[] flags = new byte[256];
 
552
 
 
553
                char getPrimary(int ch) {
 
554
                        return primary[ch & 0xff];
 
555
                }
 
556
 
 
557
                void setPrimary(int ch, int val) {
 
558
                        primary[ch & 0xff] = (char) val;
 
559
                }
 
560
 
 
561
                byte getSecondary(int ch) {
 
562
                        return secondary[ch & 0xff];
 
563
                }
 
564
 
 
565
                void setSecondary(int ch, int val) {
 
566
                        secondary[ch & 0xff] = (byte) val;
 
567
                }
 
568
 
 
569
                byte getTertiary(int ch) {
 
570
                        return tertiary[ch & 0xff];
 
571
                }
 
572
 
 
573
                void setTertiary(int ch, int val) {
 
574
                        tertiary[ch & 0xff] = (byte) val;
 
575
                }
 
576
 
 
577
                /**
 
578
                 * Get the sort position data for a given strength for a character.
 
579
                 * @param type The collation strength PRIMARY, SECONDARY etc.
 
580
                 * @param ch The character.
 
581
                 * @return The sorting weight for the given character.
 
582
                 */
 
583
                public int getPos(int type, int ch) {
 
584
                        switch (type) {
 
585
                        case Collator.PRIMARY:
 
586
                                return getPrimary(ch) & 0xffff;
 
587
                        case Collator.SECONDARY:
 
588
                                return getSecondary(ch) & 0xff;
 
589
                        case Collator.TERTIARY:
 
590
                                return getTertiary(ch) & 0xff;
 
591
                        default:
 
592
                                assert false : "bad collation type passed";
 
593
                                return 0;
 
594
                        }
 
595
                }
 
596
 
 
597
                /**
 
598
                 * Write a sort position for a given character to a sort key.
 
599
                 * @param strength The sort strength type.
 
600
                 * @param ch The character.
 
601
                 * @param outKey The output key.
 
602
                 * @param start The offset into outKey, the new position is written here.
 
603
                 * @return The new start offset, after the key information has been written.
 
604
                 */
 
605
                public int writePos(int strength, int ch, byte[] outKey, int start) {
 
606
                        int pos = getPos(strength, ch);
 
607
                        if (pos != 0) {
 
608
                                if (strength == Collator.PRIMARY)
 
609
                                        outKey[start++] = (byte) ((pos >> 8) & 0xff); // for 2 byte charsets
 
610
                                outKey[start++] = (byte) (pos & 0xff);
 
611
                        }
 
612
                        return start;
 
613
                }
 
614
        }
 
615
 
352
616
        /**
353
617
         * A collator that works with this sort. This should be used if you just need to compare two
354
618
         * strings against each other once.
355
619
         *
356
620
         * The sort key is better when the comparison must be done several times as in a sort operation.
 
621
         *
 
622
         * This implementation has the same effect when used for sorting as the sort keys.
357
623
         */
358
624
        private class SrtCollator extends Collator {
359
625
                private final int codepage;
363
629
                }
364
630
 
365
631
                public int compare(String source, String target) {
366
 
                        CharBuffer in1 = CharBuffer.wrap(source);
367
 
                        CharBuffer in2 = CharBuffer.wrap(target);
368
 
                        byte[] bytes1;
369
 
                        byte[] bytes2;
370
 
                        try {
371
 
                                bytes1 = encoder.encode(in1).array();
372
 
                                bytes2 = encoder.encode(in2).array();
373
 
                        } catch (CharacterCodingException e) {
374
 
                                throw new ExitException("character encoding failed unexpectedly", e);
 
632
                        char[] chars1;
 
633
                        char[] chars2;
 
634
                        if (isMulti()) {
 
635
                                chars1 = source.toCharArray();
 
636
                                chars2 = target.toCharArray();
 
637
                        } else {
 
638
                                CharBuffer in1 = CharBuffer.wrap(source);
 
639
                                CharBuffer in2 = CharBuffer.wrap(target);
 
640
                                try {
 
641
                                        byte[] bytes1 = encoder.encode(in1).array();
 
642
                                        byte[] bytes2 = encoder.encode(in2).array();
 
643
                                        chars1 = new char[bytes1.length];
 
644
                                        for (int i = 0; i < bytes1.length; i++)
 
645
                                                chars1[i] = (char) (bytes1[i] & 0xff);
 
646
                                        chars2 = new char[bytes2.length];
 
647
                                        for (int i = 0; i < bytes2.length; i++)
 
648
                                                chars2[i] = (char) (bytes2[i] & 0xff);
 
649
                                } catch (CharacterCodingException e) {
 
650
                                        throw new ExitException("character encoding failed unexpectedly", e);
 
651
                                }
375
652
                        }
376
653
 
377
654
                        int strength = getStrength();
378
 
                        int res = compareOneStrength(bytes1, bytes2, primary, Collator.PRIMARY);
 
655
                        int res = compareOneStrength(chars1, chars2, Collator.PRIMARY);
379
656
 
380
657
                        if (res == 0 && strength != PRIMARY) {
381
 
                                res = compareOneStrength(bytes1, bytes2, secondary, Collator.SECONDARY);
 
658
                                res = compareOneStrength(chars1, chars2, Collator.SECONDARY);
382
659
                                if (res == 0 && strength != SECONDARY) {
383
 
                                        res = compareOneStrength(bytes1, bytes2, tertiary, Collator.TERTIARY);
 
660
                                        res = compareOneStrength(chars1, chars2, Collator.TERTIARY);
384
661
                                }
385
662
                        }
386
663
 
387
 
                        if (res == 0) {
388
 
                                if (source.length() < target.length())
389
 
                                        res = -1;
390
 
                                else if (source.length() > target.length())
391
 
                                        res = 1;
392
 
                        }
393
664
                        return res;
394
665
                }
395
666
 
396
667
                /**
397
668
                 * Compare the bytes against primary, secondary or tertiary arrays.
398
 
                 * @param bytes1 Bytes for the first string in the codepage encoding.
399
 
                 * @param bytes2 Bytes for the second string in the codepage encoding.
400
 
                 * @param typePositions The strength array to use in the comparison.
 
669
                 * @param char1 Bytes for the first string in the codepage encoding.
 
670
                 * @param char2 Bytes for the second string in the codepage encoding.
401
671
                 * @return Comparison result -1, 0 or 1.
402
672
                 */
403
 
                @SuppressWarnings({"AssignmentToForLoopParameter"})
404
 
                private int compareOneStrength(byte[] bytes1, byte[] bytes2, byte[] typePositions, int type) {
 
673
                private int compareOneStrength(char[] char1, char[] char2, int type) {
405
674
                        int res = 0;
406
675
 
407
 
                        PositionIterator it1 = new PositionIterator(bytes1, typePositions, type);
408
 
                        PositionIterator it2 = new PositionIterator(bytes2, typePositions, type);
 
676
                        PositionIterator it1 = new PositionIterator(char1, type);
 
677
                        PositionIterator it2 = new PositionIterator(char2, type);
409
678
 
410
 
                        while (it1.hasNext() && it2.hasNext()) {
 
679
                        while (it1.hasNext() || it2.hasNext()) {
411
680
                                int p1 = it1.next();
412
681
                                int p2 = it2.next();
413
 
                                
 
682
 
414
683
                                if (p1 < p2) {
415
684
                                        res = -1;
416
685
                                        break;
419
688
                                        break;
420
689
                                }
421
690
                        }
 
691
 
422
692
                        return res;
423
693
                }
424
694
 
441
711
                }
442
712
 
443
713
                class PositionIterator implements Iterator<Integer> {
444
 
                        private final byte[] bytes;
445
 
                        private final byte[] sortPositions;
 
714
                        private final char[] chars;
446
715
                        private final int len;
447
716
                        private final int type;
448
717
 
452
721
                        private int expEnd;
453
722
                        private int expPos;
454
723
 
455
 
                        PositionIterator(byte[] bytes, byte[] sortPositions, int type) {
456
 
                                this.bytes = bytes;
457
 
                                this.sortPositions = sortPositions;
458
 
                                this.len = bytes.length;
 
724
                        PositionIterator(char[] chars, int type) {
 
725
                                this.chars = chars;
 
726
                                this.len = chars.length;
459
727
                                this.type = type;
460
728
                        }
461
729
 
463
731
                                return pos < len || expPos != 0;
464
732
                        }
465
733
 
 
734
                        /**
 
735
                         * Get the next sort order value for the input string. Does not ever return values
 
736
                         * that are ignorable. Returns NO_ORDER at (and beyond) the end of the string, this
 
737
                         * value sorts less than any other and so makes shorter strings sort first.
 
738
                         * @return The next non-ignored sort position. At the end of the string it returns
 
739
                         * NO_ORDER.
 
740
                         */
466
741
                        public Integer next() {
467
742
                                int next;
468
743
                                if (expPos == 0) {
469
 
                                        int in = pos++ & 0xff;
470
 
                                        byte b = bytes[in];
471
 
                                        int n = (flags[b & 0xff] >> 4) & 0x3;
472
 
                                        if (n > 0) {
473
 
                                                expStart = primary[b & 0xff] - 1;
474
 
                                                expEnd = expStart + n;
475
 
                                                expPos = expStart;
476
 
                                                next = expansions.get(expPos).getPosition(type);
477
 
 
478
 
                                                if (++expPos > expEnd)
479
 
                                                        expPos = 0;
480
 
 
481
 
                                        } else {
482
 
                                                for (next = sortPositions[bytes[in] & 0xff]; next == 0 && pos < len; ) {
483
 
                                                        next = sortPositions[bytes[pos++ & 0xff] & 0xff];
484
 
                                                }
485
 
                                        }
 
744
 
 
745
                                        do {
 
746
                                                if (pos >= len) {
 
747
                                                        next = NO_ORDER;
 
748
                                                        break;
 
749
                                                }
 
750
 
 
751
                                                // Get the first non-ignorable at this level
 
752
                                                int c = chars[(pos++ & 0xff)];
 
753
                                                if (!hasPage(c >>> 8)) {
 
754
                                                        next = 0;
 
755
                                                        continue;
 
756
                                                }
 
757
 
 
758
                                                int nExpand = (getFlags(c) >> 4) & 0x3;
 
759
                                                // Check if this is an expansion.
 
760
                                                if (nExpand > 0) {
 
761
                                                        expStart = getPrimary(c) - 1;
 
762
                                                        expEnd = expStart + nExpand;
 
763
                                                        expPos = expStart;
 
764
                                                        next = expansions.get(expPos).getPosition(type);
 
765
 
 
766
                                                        if (++expPos > expEnd)
 
767
                                                                expPos = 0;
 
768
                                                } else {
 
769
                                                        next = getPos(type, c);
 
770
                                                }
 
771
 
 
772
                                        } while (next == 0);
486
773
                                } else {
487
774
                                        next = expansions.get(expPos).getPosition(type);
488
775
                                        if (++expPos > expEnd)
489
776
                                                expPos = 0;
 
777
                                }
490
778
 
491
 
                                }
492
779
                                return next;
493
780
                        }
494
781