This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 183162
Collapse All | Expand All

(-)a/openide.util/apichanges.xml (+16 lines)
Lines 48-53 Link Here
48
    <apidef name="actions">Actions API</apidef>
48
    <apidef name="actions">Actions API</apidef>
49
</apidefs>
49
</apidefs>
50
<changes>
50
<changes>
51
    <change id="CharSequences">
52
        <api name="util"/>
53
        <summary>Added <code>CharSequences</code></summary>
54
        <version major="8" minor="3"/>
55
        <date day="2" month="4" year="2010"/>
56
        <author login="vv159170"/>
57
        <compatibility addition="yes"/>
58
        <description>
59
            <p>
60
                <code>CharSequences.create</code> now can be used
61
                to create memory efficient implementations of CharSequences for ASCII strings.
62
            </p>
63
        </description>
64
        <class package="org.openide.util" name="CharSequences"/>
65
        <issue number="183162"/>
66
    </change>
51
    <change id="ActionInvoker-ActionPresenterProvider">
67
    <change id="ActionInvoker-ActionPresenterProvider">
52
        <api name="actions"/>
68
        <api name="actions"/>
53
        <summary>Action SPI interfaces added</summary>
69
        <summary>Action SPI interfaces added</summary>
(-)a/openide.util/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.util
2
OpenIDE-Module: org.openide.util
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties
4
OpenIDE-Module-Specification-Version: 8.2
4
OpenIDE-Module-Specification-Version: 8.3
5
5
(-)a/openide.util/src/org/openide/util/CharSequences.java (+1053 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2010 Sun Microsystems, Inc.
38
 */
39
package org.openide.util;
40
41
import java.util.Arrays;
42
import java.util.Comparator;
43
44
/**
45
 * Useful static methods to provide and work with memory efficient CharSequence 
46
 * implementations for ASCII strings.
47
 *
48
 * @since 8.3
49
 * @author Alexander Simon
50
 * @author Vladimir Voskresensky
51
 */
52
public final class CharSequences {
53
54
    /**
55
     * Provides compact char sequence object like {@link String#String(char[], int, int)}
56
     */
57
    public static CharSequence create(char buf[], int start, int count) {
58
        if (start < 0) {
59
            throw new StringIndexOutOfBoundsException(start);
60
        }
61
        if (count < 0) {
62
            throw new StringIndexOutOfBoundsException(count);
63
        }
64
        // Note: offset or count might be near -1>>>1.
65
        if (start > buf.length - count) {
66
            throw new StringIndexOutOfBoundsException(start + count);
67
        }
68
        int n = count;
69
        if (n == 0) {
70
            // special constant shared among all empty char sequences
71
            return EMPTY;
72
        }
73
        byte[] b = new byte[n];
74
        boolean bytes = true;
75
        int o;
76
        // check 2 bytes vs 1 byte chars
77
        for (int i = 0; i < n; i++) {
78
            o = buf[start + i];
79
            if ((o & 0xFF) != o) {
80
                // can not compact this char sequence
81
                bytes = false;
82
                break;
83
            }
84
            b[i] = (byte) o;
85
        }
86
        if (bytes) {
87
            return createFromBytes(b, n);
88
        }
89
        char[] v = new char[count];
90
        System.arraycopy(buf, start, v, 0, count);
91
        return new CharBasedSequence(v);
92
    }
93
94
    /**
95
     * Provides compact char sequence object like {@link String#String(String)}
96
     */
97
    public static CharSequence create(CharSequence s) {
98
        if (s == null) {
99
            return null;
100
        }
101
        // already compact instance
102
        if (s instanceof CompactCharSequence) {
103
            return s;
104
        }
105
        int n = s.length();
106
        if (n == 0) {
107
            // special constant shared among all empty char sequences
108
            return EMPTY;
109
        }
110
        byte[] b = new byte[n];
111
        boolean bytes = true;
112
        int o;
113
        for (int i = 0; i < n; i++) {
114
            o = s.charAt(i);
115
            if ((o & 0xFF) != o) {
116
                bytes = false;
117
                break;
118
            }
119
            b[i] = (byte) o;
120
        }
121
        if (bytes) {
122
            return createFromBytes(b, n);
123
        }
124
        char[] v = new char[n];
125
        for (int i = 0; i < n; i++) {
126
            v[i] = s.charAt(i);
127
        }
128
        return new CharBasedSequence(v);
129
    }
130
131
    /**
132
     * Provides optimized char sequences comparator
133
     * @param ignoreCase true to get case insensitive comparator
134
     *                  false to get case sensitive comparator
135
     * @return comparator
136
     */
137
    public static Comparator<CharSequence> comparator(boolean ignoreCase) {
138
        return ignoreCase ? ComparatorIgnoreCase : Comparator;
139
    }
140
    
141
    /**
142
     * Returns object to represent empty sequence ""
143
     * @return char sequence to represent empty sequence
144
     */
145
    public static CharSequence empty() {
146
        return EMPTY;
147
    }
148
149
    /**
150
     * Predicate to check if provides char sequence is based on compact implementation
151
     * @param cs char sequence object to check
152
     * @return true if compact implementation, false otherwise
153
     */
154
    public static boolean isCompact(CharSequence cs) {
155
        return cs instanceof CompactCharSequence;
156
    }
157
    
158
    /**
159
     * Implementation of {@link String#indexOf(String)} for character sequences.
160
     */
161
    public static int indexOf(CharSequence text, CharSequence seq) {
162
        return indexOf(text, seq, 0);
163
    }
164
165
    /**
166
     * Implementation of {@link String#indexOf(String,int)} for character sequences.
167
     */
168
    public static int indexOf(CharSequence text, CharSequence seq, int fromIndex) {
169
        int textLength = text.length();
170
        int seqLength = seq.length();
171
        if (fromIndex >= textLength) {
172
            return (seqLength == 0 ? textLength : -1);
173
        }
174
        if (fromIndex < 0) {
175
            fromIndex = 0;
176
        }
177
        if (seqLength == 0) {
178
            return fromIndex;
179
        }
180
181
        char first = seq.charAt(0);
182
        int max = textLength - seqLength;
183
184
        for (int i = fromIndex; i <= max; i++) {
185
            // look for first character
186
            if (text.charAt(i) != first) {
187
                while (++i <= max && text.charAt(i) != first) {
188
                }
189
            }
190
191
            // found first character, now look at the rest of seq
192
            if (i <= max) {
193
                int j = i + 1;
194
                int end = j + seqLength - 1;
195
                for (int k = 1; j < end && text.charAt(j) == seq.charAt(k); j++, k++) {
196
                }
197
                if (j == end) {
198
                    // found whole sequence
199
                    return i;
200
                }
201
            }
202
        }
203
        return -1;
204
    }
205
206
    private static CompactCharSequence createFromBytes(byte[] b, int n) {
207
        if (n < 8) {
208
            return new Fixed_0_7(b, n);
209
        } else if (n < 16) {
210
            return new Fixed_8_15(b, n);
211
        } else if (n < 24) {
212
            return new Fixed_16_23(b, n);
213
        }
214
        return new ByteBasedSequence(b);
215
    }
216
217
    ////////////////////////////////////////////////////////////////////////////////
218
    // Memory efficient implementations of CharSequence
219
    // Comparision between Fixed and String memory consumption:
220
    // 32-bit JVM
221
    //String    String CharSequence
222
    //Length     Size    Size
223
    //1..2        40      16
224
    //3..6        48      16
225
    //7..7        56      16
226
    //8..10       56      24
227
    //11..14      64      24
228
    //15..15      72      24
229
    //16..18      72      32
230
    //19..22      80      32
231
    //23..23      88      32
232
    //24..26      88      56
233
    //27..28      96      56
234
    //29..30      96      64
235
    //31..34     104      64
236
    //35..36     112      64
237
    //37..38     112      72
238
    //39..42     120      72
239
    //......................
240
    //79..82   - 200     112
241
    //
242
    // 64-bit JVM
243
    //1           72      24
244
    //2           72      24
245
    //3           80      24
246
    //4           80      24
247
    //5           80      24
248
    //6           80      24
249
    //7           88      24
250
    //8           88      32
251
    //9           88      32
252
    //11          96      32
253
    //13          96      32
254
    //15         104      32
255
    //16         104      40
256
    //17         104      40
257
    //18         112      40
258
    //19         112      40
259
    //20         112      40
260
    //22         120      40
261
    //23         120      40
262
    //24         120      80
263
    //25         120      88
264
    //26         128      88
265
    //60         192     120
266
    //100        272     160
267
268
    //<editor-fold defaultstate="collapsed" desc="Private Classes">
269
270
    /**
271
     * compact char sequence implementation for strings in range 0-7 characters
272
     * 8 + 2*4 = 16 bytes for all strings vs String impl occupying 
273
     */
274
    private static final class Fixed_0_7 implements CompactCharSequence, Comparable<CharSequence> {
275
276
        private final int i1;
277
        private final int i2;
278
279
        @SuppressWarnings("fallthrough")
280
        private Fixed_0_7(byte[] b, int n) {
281
            int a1 = n;
282
            int a2 = 0;
283
            switch (n) {
284
                case 7:
285
                    a2 += (b[6] & 0xFF) << 24;
286
                case 6:
287
                    a2 += (b[5] & 0xFF) << 16;
288
                case 5:
289
                    a2 += (b[4] & 0xFF) << 8;
290
                case 4:
291
                    a2 += b[3] & 0xFF;
292
                case 3:
293
                    a1 += (b[2] & 0xFF) << 24;
294
                case 2:
295
                    a1 += (b[1] & 0xFF) << 16;
296
                case 1:
297
                    a1 += (b[0] & 0xFF) << 8;
298
                case 0:
299
                    break;
300
                default:
301
                    throw new IllegalArgumentException();
302
            }
303
            i1 = a1;
304
            i2 = a2;
305
        }
306
307
        @Override
308
        public int length() {
309
            return i1 & 0xFF;
310
        }
311
312
        @Override
313
        public char charAt(int index) {
314
            int r = 0;
315
            switch (index) {
316
                case 0:
317
                    r = (i1 & 0xFF00) >> 8;
318
                    break;
319
                case 1:
320
                    r = (i1 & 0xFF0000) >> 16;
321
                    break;
322
                case 2:
323
                    r = (i1 >> 24) & 0xFF;
324
                    break;
325
                case 3:
326
                    r = i2 & 0xFF;
327
                    break;
328
                case 4:
329
                    r = (i2 & 0xFF00) >> 8;
330
                    break;
331
                case 5:
332
                    r = (i2 & 0xFF0000) >> 16;
333
                    break;
334
                case 6:
335
                    r = (i2 >> 24) & 0xFF;
336
                    break;
337
            }
338
            return (char) r;
339
        }
340
341
        @Override
342
        public String toString() {
343
            int n = length();
344
            char[] r = new char[n];
345
            for (int i = 0; i < n; i++) {
346
                r[i] = charAt(i);
347
            }
348
            return new String(r);
349
        }
350
351
        @Override
352
        public boolean equals(Object object) {
353
            if (this == object) {
354
                return true;
355
            }
356
            if (object instanceof Fixed_0_7) {
357
                Fixed_0_7 otherString = (Fixed_0_7) object;
358
                return i1 == otherString.i1 && i2 == otherString.i2;
359
            }
360
            return false;
361
        }
362
363
        @Override
364
        public int hashCode() {
365
            int hash = 0;
366
            for (int i = 0; i < length(); i++) {
367
                hash = 31 * hash + charAt(i);
368
            }
369
            return hash;
370
            //            return (i1 >> 4) + (i1 >> 8) + (i2 << 5) - i2;
371
        }
372
373
        @Override
374
        public CharSequence subSequence(int start, int end) {
375
            return CharSequences.create(toString().substring(start, end));
376
        }
377
378
        @Override
379
        public int compareTo(CharSequence o) {
380
            return Comparator.compare(this, o);
381
        }
382
    }
383
384
    /**
385
     * compact char sequence implementation for strings in range 8-15 characters
386
     * size: 8 + 4*4 = 24 bytes for all strings vs String impl occupying
387
     */
388
    private static final class Fixed_8_15 implements CompactCharSequence, Comparable<CharSequence> {
389
390
        private final int i1;
391
        private final int i2;
392
        private final int i3;
393
        private final int i4;
394
395
        @SuppressWarnings("fallthrough")
396
        private Fixed_8_15(byte[] b, int n) {
397
            int a1 = n;
398
            int a2 = 0;
399
            int a3 = 0;
400
            int a4 = 0;
401
            switch (n) {
402
                case 15:
403
                    a4 += (b[14] & 0xFF) << 24;
404
                case 14:
405
                    a4 += (b[13] & 0xFF) << 16;
406
                case 13:
407
                    a4 += (b[12] & 0xFF) << 8;
408
                case 12:
409
                    a4 += b[11] & 0xFF;
410
                case 11:
411
                    a3 += (b[10] & 0xFF) << 24;
412
                case 10:
413
                    a3 += (b[9] & 0xFF) << 16;
414
                case 9:
415
                    a3 += (b[8] & 0xFF) << 8;
416
                case 8:
417
                    a3 += b[7] & 0xFF;
418
                case 7:
419
                    a2 += (b[6] & 0xFF) << 24;
420
                case 6:
421
                    a2 += (b[5] & 0xFF) << 16;
422
                case 5:
423
                    a2 += (b[4] & 0xFF) << 8;
424
                case 4:
425
                    a2 += b[3] & 0xFF;
426
                case 3:
427
                    a1 += (b[2] & 0xFF) << 24;
428
                case 2:
429
                    a1 += (b[1] & 0xFF) << 16;
430
                case 1:
431
                    a1 += (b[0] & 0xFF) << 8;
432
                case 0:
433
                    break;
434
                default:
435
                    throw new IllegalArgumentException();
436
            }
437
            i1 = a1;
438
            i2 = a2;
439
            i3 = a3;
440
            i4 = a4;
441
        }
442
443
        @Override
444
        public int length() {
445
            return i1 & 0xFF;
446
        }
447
448
        @Override
449
        public char charAt(int index) {
450
            int r = 0;
451
            switch (index) {
452
                case 0:
453
                    r = (i1 & 0xFF00) >> 8;
454
                    break;
455
                case 1:
456
                    r = (i1 & 0xFF0000) >> 16;
457
                    break;
458
                case 2:
459
                    r = (i1 >> 24) & 0xFF;
460
                    break;
461
                case 3:
462
                    r = i2 & 0xFF;
463
                    break;
464
                case 4:
465
                    r = (i2 & 0xFF00) >> 8;
466
                    break;
467
                case 5:
468
                    r = (i2 & 0xFF0000) >> 16;
469
                    break;
470
                case 6:
471
                    r = (i2 >> 24) & 0xFF;
472
                    break;
473
                case 7:
474
                    r = i3 & 0xFF;
475
                    break;
476
                case 8:
477
                    r = (i3 & 0xFF00) >> 8;
478
                    break;
479
                case 9:
480
                    r = (i3 & 0xFF0000) >> 16;
481
                    break;
482
                case 10:
483
                    r = (i3 >> 24) & 0xFF;
484
                    break;
485
                case 11:
486
                    r = i4 & 0xFF;
487
                    break;
488
                case 12:
489
                    r = (i4 & 0xFF00) >> 8;
490
                    break;
491
                case 13:
492
                    r = (i4 & 0xFF0000) >> 16;
493
                    break;
494
                case 14:
495
                    r = (i4 >> 24) & 0xFF;
496
                    break;
497
            }
498
            return (char) r;
499
        }
500
501
        @Override
502
        public String toString() {
503
            int n = length();
504
            char[] r = new char[n];
505
            for (int i = 0; i < n; i++) {
506
                r[i] = charAt(i);
507
            }
508
            return new String(r);
509
        }
510
511
        @Override
512
        public boolean equals(Object object) {
513
            if (this == object) {
514
                return true;
515
            }
516
            if (object instanceof Fixed_8_15) {
517
                Fixed_8_15 otherString = (Fixed_8_15) object;
518
                return i1 == otherString.i1 && i2 == otherString.i2 && i3 == otherString.i3 && i4 == otherString.i4;
519
            }
520
            return false;
521
        }
522
523
        @Override
524
        public int hashCode() {
525
            return i1 + 31 * (i2 + 31 * (i3 + 31 * i4));
526
        }
527
528
        @Override
529
        public CharSequence subSequence(int start, int end) {
530
            return CharSequences.create(toString().substring(start, end));
531
        }
532
533
        @Override
534
        public int compareTo(CharSequence o) {
535
            return Comparator.compare(this, o);
536
        }
537
    }
538
539
    /**
540
     * compact char sequence implementation for strings in range 16-23 characters
541
     * size: 8 + 3*8 = 32 bytes for all strings vs String impl occupying 
542
     */
543
    private static final class Fixed_16_23 implements CompactCharSequence, Comparable<CharSequence> {
544
545
        private final long i1;
546
        private final long i2;
547
        private final long i3;
548
549
        @SuppressWarnings("fallthrough")
550
        private Fixed_16_23(byte[] b, int n) {
551
            long a1 = 0;
552
            long a2 = 0;
553
            long a3 = 0;
554
            switch (n) {
555
                case 23:
556
                    a3 += (b[22] & 0xFF) << 24;
557
                case 22:
558
                    a3 += (b[21] & 0xFF) << 16;
559
                case 21:
560
                    a3 += (b[20] & 0xFF) << 8;
561
                case 20:
562
                    a3 += (b[19] & 0xFF);
563
                    a3 <<= 32;
564
                case 19:
565
                    a3 += (b[18] & 0xFF) << 24;
566
                case 18:
567
                    a3 += (b[17] & 0xFF) << 16;
568
                case 17:
569
                    a3 += (b[16] & 0xFF) << 8;
570
                case 16:
571
                    a3 += b[15] & 0xFF;
572
                case 15:
573
                    a2 += (b[14] & 0xFF) << 24;
574
                case 14:
575
                    a2 += (b[13] & 0xFF) << 16;
576
                case 13:
577
                    a2 += (b[12] & 0xFF) << 8;
578
                case 12:
579
                    a2 += (b[11] & 0xFF);
580
                    a2 <<= 32;
581
                case 11:
582
                    a2 += (b[10] & 0xFF) << 24;
583
                case 10:
584
                    a2 += (b[9] & 0xFF) << 16;
585
                case 9:
586
                    a2 += (b[8] & 0xFF) << 8;
587
                case 8:
588
                    a2 += b[7] & 0xFF;
589
                case 7:
590
                    a1 += (b[6] & 0xFF) << 24;
591
                case 6:
592
                    a1 += (b[5] & 0xFF) << 16;
593
                case 5:
594
                    a1 += (b[4] & 0xFF) << 8;
595
                case 4:
596
                    a1 += (b[3] & 0xFF);
597
                    a1 <<= 32;
598
                case 3:
599
                    a1 += (b[2] & 0xFF) << 24;
600
                case 2:
601
                    a1 += (b[1] & 0xFF) << 16;
602
                case 1:
603
                    a1 += (b[0] & 0xFF) << 8;
604
                case 0:
605
                    a1 += n;
606
                    break;
607
                default:
608
                    throw new IllegalArgumentException();
609
            }
610
            i1 = a1;
611
            i2 = a2;
612
            i3 = a3;
613
        }
614
615
        @Override
616
        public int length() {
617
            return (int) (i1 & 0xFF);
618
        }
619
620
        @Override
621
        public char charAt(int index) {
622
            int r = 0;
623
            switch (index) {
624
                case 0:
625
                    r = (int) ((i1 >> 8) & 0xFFL);
626
                    break;
627
                case 1:
628
                    r = (int) ((i1 >> 16) & 0xFFL);
629
                    break;
630
                case 2:
631
                    r = (int) ((i1 >> 24) & 0xFFL);
632
                    break;
633
                case 3:
634
                    r = (int) ((i1 >> 32) & 0xFFL);
635
                    break;
636
                case 4:
637
                    r = (int) ((i1 >> 40) & 0xFFL);
638
                    break;
639
                case 5:
640
                    r = (int) ((i1 >> 48) & 0xFFL);
641
                    break;
642
                case 6:
643
                    r = (int) ((i1 >> 56) & 0xFFL);
644
                    break;
645
                case 7:
646
                    r = (int) (i2 & 0xFFL);
647
                    break;
648
                case 8:
649
                    r = (int) ((i2 >> 8) & 0xFFL);
650
                    break;
651
                case 9:
652
                    r = (int) ((i2 >> 16) & 0xFFL);
653
                    break;
654
                case 10:
655
                    r = (int) ((i2 >> 24) & 0xFFL);
656
                    break;
657
                case 11:
658
                    r = (int) ((i2 >> 32) & 0xFFL);
659
                    break;
660
                case 12:
661
                    r = (int) ((i2 >> 40) & 0xFFL);
662
                    break;
663
                case 13:
664
                    r = (int) ((i2 >> 48) & 0xFFL);
665
                    break;
666
                case 14:
667
                    r = (int) ((i2 >> 56) & 0xFFL);
668
                    break;
669
                case 15:
670
                    r = (int) (i3 & 0xFFL);
671
                    break;
672
                case 16:
673
                    r = (int) ((i3 >> 8) & 0xFFL);
674
                    break;
675
                case 17:
676
                    r = (int) ((i3 >> 16) & 0xFFL);
677
                    break;
678
                case 18:
679
                    r = (int) ((i3 >> 24) & 0xFFL);
680
                    break;
681
                case 19:
682
                    r = (int) ((i3 >> 32) & 0xFFL);
683
                    break;
684
                case 20:
685
                    r = (int) ((i3 >> 40) & 0xFFL);
686
                    break;
687
                case 21:
688
                    r = (int) ((i3 >> 48) & 0xFFL);
689
                    break;
690
                case 22:
691
                    r = (int) ((i3 >> 56) & 0xFFL);
692
                    break;
693
            }
694
            return (char) r;
695
        }
696
697
        @Override
698
        public String toString() {
699
            int n = length();
700
            char[] r = new char[n];
701
            for (int i = 0; i < n; i++) {
702
                r[i] = charAt(i);
703
            }
704
            return new String(r);
705
        }
706
707
        @Override
708
        public boolean equals(Object object) {
709
            if (this == object) {
710
                return true;
711
            }
712
            if (object instanceof Fixed_16_23) {
713
                Fixed_16_23 otherString = (Fixed_16_23) object;
714
                return i1 == otherString.i1 && i2 == otherString.i2 && i3 == otherString.i3;
715
            }
716
            return false;
717
        }
718
719
        @Override
720
        public int hashCode() {
721
            long res = i1 + 31 * (i2 + 31 * i3);
722
            res = (res + (res >> 32)) & 0xFFFFFFFFL;
723
            return (int) res;
724
        }
725
726
        @Override
727
        public CharSequence subSequence(int start, int end) {
728
            return CharSequences.create(toString().substring(start, end));
729
        }
730
731
        @Override
732
        public int compareTo(CharSequence o) {
733
            return Comparator.compare(this, o);
734
        }
735
    }
736
737
    /**
738
     * compact char sequence implementation based on char[] array
739
     * size: 8 + 4 + 4 (= 16 bytes) + sizeof ('value')
740
     * it is still more effective than String, because string stores length in field
741
     * and it costs 20 bytes aligned into 24
742
     */
743
    private final static class CharBasedSequence implements CompactCharSequence, Comparable<CharSequence> {
744
745
        private final char[] value;
746
        private int hash;
747
748
        private CharBasedSequence(char[] v) {
749
            value = v;
750
        }
751
752
        @Override
753
        public int length() {
754
            return value.length;
755
        }
756
757
        @Override
758
        public char charAt(int index) {
759
            return value[index];
760
        }
761
762
        @Override
763
        public boolean equals(Object object) {
764
            if (this == object) {
765
                return true;
766
            }
767
            if (object instanceof CharBasedSequence) {
768
                CharBasedSequence otherString = (CharBasedSequence) object;
769
                if (hash != 0 && otherString.hash != 0) {
770
                    if (hash != otherString.hash) {
771
                        return false;
772
                    }
773
                }
774
                return Arrays.equals(value, otherString.value);
775
            }
776
            return false;
777
        }
778
779
        @Override
780
        public int hashCode() {
781
            int h = hash;
782
            if (h == 0) {
783
                int n = value.length;
784
                for (int i = 0; i < n; i++) {
785
                    h = 31 * h + value[i];
786
                }
787
                hash = h;
788
            }
789
            return h;
790
        }
791
792
        @Override
793
        public CharSequence subSequence(int beginIndex, int endIndex) {
794
            return CharSequences.create(value, beginIndex, endIndex);
795
        }
796
797
        @Override
798
        public String toString() {
799
            return new String(value);
800
        }
801
802
        @Override
803
        public int compareTo(CharSequence o) {
804
            return Comparator.compare(this, o);
805
        }
806
    }
807
808
    /**
809
     * compact char sequence implementation based on byte[]
810
     * size: 8 + 4 + 4 (= 16 bytes) + sizeof ('value')
811
     */
812
    private final static class ByteBasedSequence implements CompactCharSequence, Comparable<CharSequence> {
813
814
        private final byte[] value;
815
        private int hash;
816
817
        private ByteBasedSequence(byte[] b) {
818
            value = b;
819
        }
820
821
        @Override
822
        public int length() {
823
            return value.length;
824
        }
825
826
        @Override
827
        public char charAt(int index) {
828
            int r = value[index] & 0xFF;
829
            return (char) r;
830
        }
831
832
        @Override
833
        public boolean equals(Object object) {
834
            if (this == object) {
835
                return true;
836
            }
837
            if (object instanceof ByteBasedSequence) {
838
                ByteBasedSequence otherString = (ByteBasedSequence) object;
839
                if (hash != 0 && otherString.hash != 0) {
840
                    if (hash != otherString.hash) {
841
                        return false;
842
                    }
843
                }
844
                return Arrays.equals(value, otherString.value);
845
            }
846
            return false;
847
        }
848
849
        @Override
850
        public int hashCode() {
851
            int h = hash;
852
            if (h == 0) {
853
                int n = value.length;
854
                for (int i = 0; i < n; i++) {
855
                    h = 31 * h + value[i];
856
                }
857
                hash = h;
858
            }
859
            return h;
860
        }
861
862
        @Override
863
        public CharSequence subSequence(int beginIndex, int endIndex) {
864
            return CharSequences.create(toChars(), beginIndex, endIndex);
865
        }
866
867
        @Override
868
        public String toString() {
869
            char[] r = toChars();
870
            return new String(r);
871
        }
872
873
        private char[] toChars() {
874
            int n = value.length;
875
            char[] r = new char[n];
876
            for (int i = 0; i < n; i++) {
877
                int c = value[i] & 0xFF;
878
                r[i] = (char) c;
879
            }
880
            return r;
881
        }
882
883
        @Override
884
        public int compareTo(CharSequence o) {
885
            return Comparator.compare(this, o);
886
        }
887
    }
888
889
    private static final CompactCharSequence EMPTY = new Fixed_0_7(new byte[0], 0);
890
    private static final Comparator<CharSequence> Comparator = new CharSequenceComparator();
891
    private static final Comparator<CharSequence> ComparatorIgnoreCase = new CharSequenceComparatorIgnoreCase();
892
893
    /**
894
     * performance tuned comparator to prevent charAt calls when possible
895
     */
896
    private static class CharSequenceComparator implements Comparator<CharSequence> {
897
898
        @Override
899
        public int compare(CharSequence o1, CharSequence o2) {
900
            if (o1 instanceof ByteBasedSequence) {
901
                return compareByteBasedWithOther((ByteBasedSequence)o1, o2);
902
            } else if (o2 instanceof ByteBasedSequence) {
903
                return -compareByteBasedWithOther((ByteBasedSequence) o2, o1);
904
            } else if (o1 instanceof CharBasedSequence) {
905
                return compareCharBasedWithOther((CharBasedSequence)o1, o2);
906
            } else if (o2 instanceof CharBasedSequence) {
907
                return -compareCharBasedWithOther((CharBasedSequence)o2, o1);
908
            }
909
            int len1 = o1.length();
910
            int len2 = o2.length();
911
            int n = Math.min(len1, len2);
912
            int k = 0;
913
            while (k < n) {
914
                char c1 = o1.charAt(k);
915
                char c2 = o2.charAt(k);
916
                if (c1 != c2) {
917
                    return c1 - c2;
918
                }
919
                k++;
920
            }
921
            return len1 - len2;
922
        }
923
        
924
        //<editor-fold defaultstate="collapsed" desc="Private methods">
925
        private static int compareByteBased(ByteBasedSequence bbs1, ByteBasedSequence bbs2) {
926
            int len1 = bbs1.value.length;
927
            int len2 = bbs2.value.length;
928
            int n = Math.min(len1, len2);
929
            int k = 0;
930
            while (k < n) {
931
                if (bbs1.value[k] != bbs2.value[k]) {
932
                    return (bbs1.value[k] & 0xFF) - (bbs2.value[k] & 0xFF);
933
                }
934
                k++;
935
            }
936
            return len1 - len2;
937
        }
938
939
        private static int compareCharBased(CharBasedSequence cbs1, CharBasedSequence cbs2) {
940
            int len1 = cbs1.value.length;
941
            int len2 = cbs2.value.length;
942
            int n = Math.min(len1, len2);
943
            int k = 0;
944
            while (k < n) {
945
                if (cbs1.value[k] != cbs2.value[k]) {
946
                    return cbs1.value[k] - cbs2.value[k];
947
                }
948
                k++;
949
            }
950
            return len1 - len2;
951
        }
952
953
        private static int compareByteBasedWithCharBased(ByteBasedSequence bbs1, CharBasedSequence cbs2) {
954
            int len1 = bbs1.value.length;
955
            int len2 = cbs2.value.length;
956
            int n = Math.min(len1, len2);
957
            int k = 0;
958
            while (k < n) {
959
                int c1 = bbs1.value[k] & 0xFF;
960
                int c2 = cbs2.value[k];
961
                if (c1 != c2) {
962
                    return c1 - c2;
963
                }
964
                k++;
965
            }
966
            return len1 - len2;
967
        }
968
969
        private static int compareByteBasedWithOther(ByteBasedSequence bbs1, CharSequence o2) {
970
            if (o2 instanceof ByteBasedSequence) {
971
                return compareByteBased(bbs1, (ByteBasedSequence) o2);
972
            } else if (o2 instanceof CharBasedSequence) {
973
                return compareByteBasedWithCharBased(bbs1, (CharBasedSequence) o2);
974
            }
975
            int len1 = bbs1.value.length;
976
            int len2 = o2.length();
977
            int n = Math.min(len1, len2);
978
            int k = 0;
979
            int c1, c2;
980
            while (k < n) {
981
                c1 = bbs1.value[k] & 0xFF;
982
                c2 = o2.charAt(k);
983
                if (c1 != c2) {
984
                    return c1 - c2;
985
                }
986
                k++;
987
            }
988
            return len1 - len2;
989
        }
990
991
        private static int compareCharBasedWithOther(CharBasedSequence cbs1, CharSequence o2) {
992
            if (o2 instanceof CharBasedSequence) {
993
                return compareCharBased(cbs1, (CharBasedSequence) o2);
994
            } else if (o2 instanceof ByteBasedSequence) {
995
                return -compareByteBasedWithCharBased((ByteBasedSequence) o2, cbs1);
996
            }
997
            int len1 = cbs1.value.length;
998
            int len2 = o2.length();
999
            int n = Math.min(len1, len2);
1000
            int k = 0;
1001
            int c1, c2;
1002
            while (k < n) {
1003
                c1 = cbs1.value[k];
1004
                c2 = o2.charAt(k);
1005
                if (c1 != c2) {
1006
                    return c1 - c2;
1007
                }
1008
                k++;
1009
            }
1010
            return len1 - len2;
1011
        }
1012
        //</editor-fold>
1013
    }
1014
1015
    private static class CharSequenceComparatorIgnoreCase implements Comparator<CharSequence> {
1016
1017
        @Override
1018
        public int compare(CharSequence o1, CharSequence o2) {
1019
            int n1 = o1.length();
1020
            int n2 = o2.length();
1021
            for (int i1 = 0, i2 = 0; i1 < n1 && i2 < n2; i1++, i2++) {
1022
                char c1 = o1.charAt(i1);
1023
                char c2 = o2.charAt(i2);
1024
                if (c1 != c2) {
1025
                    c1 = Character.toUpperCase(c1);
1026
                    c2 = Character.toUpperCase(c2);
1027
                    if (c1 != c2) {
1028
                        c1 = Character.toLowerCase(c1);
1029
                        c2 = Character.toLowerCase(c2);
1030
                        if (c1 != c2) {
1031
                            return c1 - c2;
1032
                        }
1033
                    }
1034
                }
1035
            }
1036
            return n1 - n2;
1037
        }
1038
    }
1039
1040
    /**
1041
     * marker interface for compact char sequence implementations
1042
     */
1043
    private interface CompactCharSequence extends CharSequence {
1044
    }
1045
1046
    /**
1047
     * private constructor for utilities class
1048
     */
1049
    private CharSequences() {
1050
    }
1051
1052
    //</editor-fold>
1053
}
(-)a/openide.util/test/unit/src/org/openide/util/CharSequencesTest.java (+236 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2010 Sun Microsystems, Inc.
38
 */
39
40
package org.openide.util;
41
42
import org.netbeans.junit.NbTestCase;
43
import java.util.Comparator;
44
import junit.framework.AssertionFailedError;
45
import org.junit.Test;
46
47
/**
48
 *
49
 * @author Vladimir Voskresensky
50
 */
51
public class CharSequencesTest extends NbTestCase {
52
53
    public CharSequencesTest(String testName) {
54
        super(testName);
55
    }
56
57
    /**
58
     * Test of create method, of class CharSequences.
59
     */
60
    @Test
61
    public void testCreate_3args() {
62
        char[] buf = "Hello, Platform! Из Санкт Петербурга".toCharArray();
63
        for (int start = 0; start < buf.length; start++) {
64
            for (int count = 1; count < buf.length - start; count++) {
65
                String expResult = new String(buf, start, count);
66
                CharSequence result = CharSequences.create(buf, start, count);
67
                assertEquals("["+start+", " + count+"]", expResult, result.toString());
68
                assertTrue("["+start+", " + count+"]", expResult.contentEquals(result));
69
            }
70
        }
71
    }
72
73
    /**
74
     * Test of create method, of class CharSequences.
75
     */
76
    @Test
77
    public void testCreate_CharSequence() {
78
        String[] strs = new String[] { "", "1234567", "123456789012345", "12345678901234567890123", "123456789012345678901234", "Русский Текст" };
79
        for (String str : strs) {
80
            CharSequence cs = CharSequences.create(str);
81
            assertEquals(str, cs.toString());
82
            assertEquals(cs.toString(), str);
83
            assertEquals(cs.toString(), cs.toString());
84
            assertTrue(str.contentEquals(cs));
85
        }
86
    }
87
88
    /**
89
     * Test of comparator method, of class CharSequences.
90
     */
91
    @Test
92
    public void testCaseSensitiveComparator() {
93
        Comparator<CharSequence> comparator = CharSequences.comparator(false);
94
        String[] strs = new String[]{"", "1234567", "123456789012345", "12345678901234567890123", "Русский Текст", "123456789012345678901234"};
95
        for (String str : strs) {
96
            assertEquals(0, comparator.compare(str, CharSequences.create(str)));
97
            assertEquals(0, comparator.compare(CharSequences.create(str), str));
98
            assertEquals(0, comparator.compare(CharSequences.create(str), CharSequences.create(str)));
99
        }
100
    }
101
102
    /**
103
     * Test of comparator method, of class CharSequences.
104
     */
105
    @Test
106
    public void testCaseInsensitiveComparator() {
107
        Comparator<CharSequence> comparator = CharSequences.comparator(true);
108
        String[] strs = new String[]{"", "1234567", "123456789012345", "12345678901234567890123", "Русский Текст", "123456789012345678901234"};
109
        for (int i = 0; i < strs.length; i++) {
110
            String str = strs[i];
111
            String upperStr = str.toUpperCase();
112
            String lowerStr = str.toLowerCase();
113
            assertEquals(0, comparator.compare(str, CharSequences.create(str)));
114
            assertEquals(0, comparator.compare(CharSequences.create(str), str));
115
            assertEquals(0, comparator.compare(CharSequences.create(str), CharSequences.create(str)));
116
            assertEquals(0, comparator.compare(upperStr, CharSequences.create(str)));
117
            assertEquals(0, comparator.compare(CharSequences.create(str), upperStr));
118
            assertEquals(0, comparator.compare(CharSequences.create(upperStr), CharSequences.create(str)));
119
            assertEquals(0, comparator.compare(lowerStr, CharSequences.create(str)));
120
            assertEquals(0, comparator.compare(CharSequences.create(str), lowerStr));
121
            assertEquals(0, comparator.compare(CharSequences.create(str), CharSequences.create(lowerStr)));
122
        }
123
    }
124
125
    /**
126
     * Test of empty method, of class CharSequences.
127
     */
128
    @Test
129
    public void testEmpty() {
130
        assertEquals("", CharSequences.create("").toString());
131
        assertEquals(CharSequences.create("").toString(), "");
132
        assertEquals("", CharSequences.empty().toString());
133
        assertEquals(CharSequences.empty().toString(), "");
134
        assertSame(CharSequences.empty(), CharSequences.create(""));
135
    }
136
137
    /**
138
     * Test of isCompact method, of class CharSequences.
139
     */
140
    @Test
141
    public void testIsCompact() {
142
        String[] strs = new String[]{"", "1234567", "123456789012345", "12345678901234567890123", "Русский Текст", "123456789012345678901234"};
143
        for (String str : strs) {
144
            assertFalse(" string is compact but must not be", CharSequences.isCompact(str));
145
            assertTrue(" string is not compact but must be", CharSequences.isCompact(CharSequences.create(str)));
146
        }
147
        assertTrue(" empty string is not compact ", CharSequences.isCompact(CharSequences.empty()));
148
    }
149
150
    /**
151
     * Test of indexOf method, of class CharSequences.
152
     */
153
    @Test
154
    public void testIndexOf_CharSequence_CharSequence() {
155
        CharSequence text = CharSequences.create("CharSequences");
156
        CharSequence seq = CharSequences.create("Sequence");
157
        assertEquals(4, CharSequences.indexOf(text, "Sequence"));
158
        assertEquals(4, CharSequences.indexOf(text, seq));
159
        assertEquals(4, CharSequences.indexOf("CharSequences", "Sequence"));
160
        assertEquals(4, CharSequences.indexOf("CharSequences", seq));
161
        assertEquals(-1, CharSequences.indexOf(text, "Sequens"));
162
    }
163
164
    /**
165
     * Test of indexOf method, of class CharSequences.
166
     */
167
    @Test
168
    public void testIndexOf_3args() {
169
        CharSequence text = CharSequences.create("CharSequences");
170
        CharSequence seq = CharSequences.create("Sequence");
171
        assertEquals(4, CharSequences.indexOf(text, "Sequence", 2));
172
        assertEquals(4, CharSequences.indexOf(text, seq, 2));
173
        assertEquals(4, CharSequences.indexOf("CharSequences", "Sequence", 2));
174
        assertEquals(4, CharSequences.indexOf("CharSequences", seq, 2));
175
        assertEquals(-1, CharSequences.indexOf("CharSequences", seq, 5));
176
    }
177
178
    @Test
179
    public void testSizes() {
180
        // 32-bit JVM
181
        //String    String CharSequence
182
        //Length     Size    Size
183
        //1..2        40      16
184
        //3..6        48      16
185
        //7..7        56      16
186
        //8..10       56      24
187
        //11..14      64      24
188
        //15..15      72      24
189
        //16..18      72      32
190
        //19..22      80      32
191
        //23..23      88      32
192
        //24..26      88      56
193
        //27..28      96      56
194
        //29..30      96      64
195
        //31..34     104      64
196
        //35..36     112      64
197
        //37..38     112      72
198
        //39..42     120      72
199
        //......................
200
        //79..82   - 200     112
201
        char[] buf = "12345678901234567890123456789012345678901234567890".toCharArray();
202
        int curStrLen = 0;
203
        int[][] lenSize = new int[][] { { 7, 16 }, { 15, 24 }, { 23, 32 }, {28, 56 }, { 36, 64 }, {42, 72}, {50, 80}};
204
        for (int j = 0; j < lenSize.length; j++) {
205
            int strLenLimit = lenSize[j][0];
206
            int sizeLimit = lenSize[j][1];
207
            for (; curStrLen <= strLenLimit; curStrLen++) {
208
                CharSequence cs = CharSequences.create(buf, 0, curStrLen);
209
                assertSize("Size is too big " + cs, sizeLimit, cs);
210
                // check we are better than strings
211
                boolean stringIsBigger = false;
212
                String str = new String(buf, 0, curStrLen);
213
                try {
214
                    assertSize("Size is too big for " + str, sizeLimit, str);
215
                } catch (AssertionFailedError e) {
216
//                    System.err.println(e.getMessage());
217
                    stringIsBigger = true;
218
                }
219
                assertTrue("string object is smaller than our char sequence", stringIsBigger);
220
            }
221
        }
222
        // check that our Impl is better than default String for Unicode as well
223
        String rusText = "Русский Текст";
224
        CharSequence cs = CharSequences.create(rusText);
225
        assertTrue(rusText.contentEquals(cs));
226
        assertSize("Size is too big for " + cs, 56, cs);
227
        boolean stringIsBigger = false;
228
        try {
229
            assertSize("Size is too big for " + rusText, 56, rusText);
230
        } catch (AssertionFailedError e) {
231
//                    System.err.println(e.getMessage());
232
            stringIsBigger = true;
233
        }
234
        assertTrue("string object is smaller than our char sequence", stringIsBigger);
235
    }
236
}

Return to bug 183162