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 |
* utilities to provide and work with compact CharSequence implementations |
46 |
* |
47 |
* @since 8.3 |
48 |
* @author Alexander Simon |
49 |
* @author Vladiir Voskresensky |
50 |
*/ |
51 |
public class CharSequences { |
52 |
|
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 CharSequenceKey(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 CharSequenceKey(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_1_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 CharSequenceKey(b); |
215 |
} |
216 |
|
217 |
/** |
218 |
* compact char sequence implementation for strings in range 1-7 characters |
219 |
* 8 + 2*4 = 16 bytes for all strings vs String impl occupying |
220 |
*/ |
221 |
private static final class Fixed_1_7 implements CompactCharSequence, Comparable<CharSequence> { |
222 |
|
223 |
private final int i1; |
224 |
private final int i2; |
225 |
|
226 |
@SuppressWarnings("fallthrough") |
227 |
private Fixed_1_7(byte[] b, int n) { |
228 |
int a1 = n; |
229 |
int a2 = 0; |
230 |
switch (n) { |
231 |
case 7: |
232 |
a2 += (b[6] & 0xFF) << 24; |
233 |
case 6: |
234 |
a2 += (b[5] & 0xFF) << 16; |
235 |
case 5: |
236 |
a2 += (b[4] & 0xFF) << 8; |
237 |
case 4: |
238 |
a2 += b[3] & 0xFF; |
239 |
case 3: |
240 |
a1 += (b[2] & 0xFF) << 24; |
241 |
case 2: |
242 |
a1 += (b[1] & 0xFF) << 16; |
243 |
case 1: |
244 |
a1 += (b[0] & 0xFF) << 8; |
245 |
case 0: |
246 |
break; |
247 |
default: |
248 |
throw new IllegalArgumentException(); |
249 |
} |
250 |
i1 = a1; |
251 |
i2 = a2; |
252 |
} |
253 |
|
254 |
@Override |
255 |
public int length() { |
256 |
return i1 & 0xFF; |
257 |
} |
258 |
|
259 |
@Override |
260 |
public char charAt(int index) { |
261 |
int r = 0; |
262 |
switch (index) { |
263 |
case 0: |
264 |
r = (i1 & 0xFF00) >> 8; |
265 |
break; |
266 |
case 1: |
267 |
r = (i1 & 0xFF0000) >> 16; |
268 |
break; |
269 |
case 2: |
270 |
r = (i1 >> 24) & 0xFF; |
271 |
break; |
272 |
case 3: |
273 |
r = i2 & 0xFF; |
274 |
break; |
275 |
case 4: |
276 |
r = (i2 & 0xFF00) >> 8; |
277 |
break; |
278 |
case 5: |
279 |
r = (i2 & 0xFF0000) >> 16; |
280 |
break; |
281 |
case 6: |
282 |
r = (i2 >> 24) & 0xFF; |
283 |
break; |
284 |
} |
285 |
return (char) r; |
286 |
} |
287 |
|
288 |
@Override |
289 |
public String toString() { |
290 |
int n = length(); |
291 |
char[] r = new char[n]; |
292 |
for (int i = 0; i < n; i++) { |
293 |
r[i] = charAt(i); |
294 |
} |
295 |
return new String(r); |
296 |
} |
297 |
|
298 |
@Override |
299 |
public boolean equals(Object object) { |
300 |
if (this == object) { |
301 |
return true; |
302 |
} |
303 |
if (object instanceof Fixed_1_7) { |
304 |
Fixed_1_7 otherString = (Fixed_1_7) object; |
305 |
return i1 == otherString.i1 && i2 == otherString.i2; |
306 |
} |
307 |
return false; |
308 |
} |
309 |
|
310 |
@Override |
311 |
public int hashCode() { |
312 |
int hash = 0; |
313 |
for (int i = 0; i < length(); i++) { |
314 |
hash = 31 * hash + charAt(i); |
315 |
} |
316 |
return hash; |
317 |
// return (i1 >> 4) + (i1 >> 8) + (i2 << 5) - i2; |
318 |
} |
319 |
|
320 |
@Override |
321 |
public CharSequence subSequence(int start, int end) { |
322 |
return CharSequences.create(toString().substring(start, end)); |
323 |
} |
324 |
|
325 |
@Override |
326 |
public int compareTo(CharSequence o) { |
327 |
return Comparator.compare(this, o); |
328 |
} |
329 |
} |
330 |
|
331 |
/** |
332 |
* compact char sequence implementation for strings in range 8-15 characters |
333 |
* size: 8 + 4*4 = 24 bytes for all strings vs String impl occupying |
334 |
*/ |
335 |
private static final class Fixed_8_15 implements CompactCharSequence, Comparable<CharSequence> { |
336 |
|
337 |
private final int i1; |
338 |
private final int i2; |
339 |
private final int i3; |
340 |
private final int i4; |
341 |
|
342 |
@SuppressWarnings("fallthrough") |
343 |
private Fixed_8_15(byte[] b, int n) { |
344 |
int a1 = n; |
345 |
int a2 = 0; |
346 |
int a3 = 0; |
347 |
int a4 = 0; |
348 |
switch (n) { |
349 |
case 15: |
350 |
a4 += (b[14] & 0xFF) << 24; |
351 |
case 14: |
352 |
a4 += (b[13] & 0xFF) << 16; |
353 |
case 13: |
354 |
a4 += (b[12] & 0xFF) << 8; |
355 |
case 12: |
356 |
a4 += b[11] & 0xFF; |
357 |
case 11: |
358 |
a3 += (b[10] & 0xFF) << 24; |
359 |
case 10: |
360 |
a3 += (b[9] & 0xFF) << 16; |
361 |
case 9: |
362 |
a3 += (b[8] & 0xFF) << 8; |
363 |
case 8: |
364 |
a3 += b[7] & 0xFF; |
365 |
case 7: |
366 |
a2 += (b[6] & 0xFF) << 24; |
367 |
case 6: |
368 |
a2 += (b[5] & 0xFF) << 16; |
369 |
case 5: |
370 |
a2 += (b[4] & 0xFF) << 8; |
371 |
case 4: |
372 |
a2 += b[3] & 0xFF; |
373 |
case 3: |
374 |
a1 += (b[2] & 0xFF) << 24; |
375 |
case 2: |
376 |
a1 += (b[1] & 0xFF) << 16; |
377 |
case 1: |
378 |
a1 += (b[0] & 0xFF) << 8; |
379 |
case 0: |
380 |
break; |
381 |
default: |
382 |
throw new IllegalArgumentException(); |
383 |
} |
384 |
i1 = a1; |
385 |
i2 = a2; |
386 |
i3 = a3; |
387 |
i4 = a4; |
388 |
} |
389 |
|
390 |
@Override |
391 |
public int length() { |
392 |
return i1 & 0xFF; |
393 |
} |
394 |
|
395 |
@Override |
396 |
public char charAt(int index) { |
397 |
int r = 0; |
398 |
switch (index) { |
399 |
case 0: |
400 |
r = (i1 & 0xFF00) >> 8; |
401 |
break; |
402 |
case 1: |
403 |
r = (i1 & 0xFF0000) >> 16; |
404 |
break; |
405 |
case 2: |
406 |
r = (i1 >> 24) & 0xFF; |
407 |
break; |
408 |
case 3: |
409 |
r = i2 & 0xFF; |
410 |
break; |
411 |
case 4: |
412 |
r = (i2 & 0xFF00) >> 8; |
413 |
break; |
414 |
case 5: |
415 |
r = (i2 & 0xFF0000) >> 16; |
416 |
break; |
417 |
case 6: |
418 |
r = (i2 >> 24) & 0xFF; |
419 |
break; |
420 |
case 7: |
421 |
r = i3 & 0xFF; |
422 |
break; |
423 |
case 8: |
424 |
r = (i3 & 0xFF00) >> 8; |
425 |
break; |
426 |
case 9: |
427 |
r = (i3 & 0xFF0000) >> 16; |
428 |
break; |
429 |
case 10: |
430 |
r = (i3 >> 24) & 0xFF; |
431 |
break; |
432 |
case 11: |
433 |
r = i4 & 0xFF; |
434 |
break; |
435 |
case 12: |
436 |
r = (i4 & 0xFF00) >> 8; |
437 |
break; |
438 |
case 13: |
439 |
r = (i4 & 0xFF0000) >> 16; |
440 |
break; |
441 |
case 14: |
442 |
r = (i4 >> 24) & 0xFF; |
443 |
break; |
444 |
} |
445 |
return (char) r; |
446 |
} |
447 |
|
448 |
@Override |
449 |
public String toString() { |
450 |
int n = length(); |
451 |
char[] r = new char[n]; |
452 |
for (int i = 0; i < n; i++) { |
453 |
r[i] = charAt(i); |
454 |
} |
455 |
return new String(r); |
456 |
} |
457 |
|
458 |
@Override |
459 |
public boolean equals(Object object) { |
460 |
if (this == object) { |
461 |
return true; |
462 |
} |
463 |
if (object instanceof Fixed_8_15) { |
464 |
Fixed_8_15 otherString = (Fixed_8_15) object; |
465 |
return i1 == otherString.i1 && i2 == otherString.i2 && i3 == otherString.i3 && i4 == otherString.i4; |
466 |
} |
467 |
return false; |
468 |
} |
469 |
|
470 |
@Override |
471 |
public int hashCode() { |
472 |
return i1 + 31 * (i2 + 31 * (i3 + 31 * i4)); |
473 |
} |
474 |
|
475 |
@Override |
476 |
public CharSequence subSequence(int start, int end) { |
477 |
return CharSequences.create(toString().substring(start, end)); |
478 |
} |
479 |
|
480 |
@Override |
481 |
public int compareTo(CharSequence o) { |
482 |
return Comparator.compare(this, o); |
483 |
} |
484 |
} |
485 |
|
486 |
/** |
487 |
* compact char sequence implementation for strings in range 16-23 characters |
488 |
* size: 8 + 3*8 = 32 bytes for all strings vs String impl occupying |
489 |
*/ |
490 |
private static final class Fixed_16_23 implements CompactCharSequence, Comparable<CharSequence> { |
491 |
|
492 |
private final long i1; |
493 |
private final long i2; |
494 |
private final long i3; |
495 |
|
496 |
@SuppressWarnings("fallthrough") |
497 |
private Fixed_16_23(byte[] b, int n) { |
498 |
long a1 = 0; |
499 |
long a2 = 0; |
500 |
long a3 = 0; |
501 |
switch (n) { |
502 |
case 23: |
503 |
a3 += (b[22] & 0xFF) << 24; |
504 |
case 22: |
505 |
a3 += (b[21] & 0xFF) << 16; |
506 |
case 21: |
507 |
a3 += (b[20] & 0xFF) << 8; |
508 |
case 20: |
509 |
a3 += (b[19] & 0xFF); |
510 |
a3 <<= 32; |
511 |
case 19: |
512 |
a3 += (b[18] & 0xFF) << 24; |
513 |
case 18: |
514 |
a3 += (b[17] & 0xFF) << 16; |
515 |
case 17: |
516 |
a3 += (b[16] & 0xFF) << 8; |
517 |
case 16: |
518 |
a3 += b[15] & 0xFF; |
519 |
case 15: |
520 |
a2 += (b[14] & 0xFF) << 24; |
521 |
case 14: |
522 |
a2 += (b[13] & 0xFF) << 16; |
523 |
case 13: |
524 |
a2 += (b[12] & 0xFF) << 8; |
525 |
case 12: |
526 |
a2 += (b[11] & 0xFF); |
527 |
a2 <<= 32; |
528 |
case 11: |
529 |
a2 += (b[10] & 0xFF) << 24; |
530 |
case 10: |
531 |
a2 += (b[9] & 0xFF) << 16; |
532 |
case 9: |
533 |
a2 += (b[8] & 0xFF) << 8; |
534 |
case 8: |
535 |
a2 += b[7] & 0xFF; |
536 |
case 7: |
537 |
a1 += (b[6] & 0xFF) << 24; |
538 |
case 6: |
539 |
a1 += (b[5] & 0xFF) << 16; |
540 |
case 5: |
541 |
a1 += (b[4] & 0xFF) << 8; |
542 |
case 4: |
543 |
a1 += (b[3] & 0xFF); |
544 |
a1 <<= 32; |
545 |
case 3: |
546 |
a1 += (b[2] & 0xFF) << 24; |
547 |
case 2: |
548 |
a1 += (b[1] & 0xFF) << 16; |
549 |
case 1: |
550 |
a1 += (b[0] & 0xFF) << 8; |
551 |
case 0: |
552 |
a1 += n; |
553 |
break; |
554 |
default: |
555 |
throw new IllegalArgumentException(); |
556 |
} |
557 |
i1 = a1; |
558 |
i2 = a2; |
559 |
i3 = a3; |
560 |
} |
561 |
|
562 |
@Override |
563 |
public int length() { |
564 |
return (int) (i1 & 0xFF); |
565 |
} |
566 |
|
567 |
@Override |
568 |
public char charAt(int index) { |
569 |
int r = 0; |
570 |
switch (index) { |
571 |
case 0: |
572 |
r = (int) ((i1 >> 8) & 0xFFL); |
573 |
break; |
574 |
case 1: |
575 |
r = (int) ((i1 >> 16) & 0xFFL); |
576 |
break; |
577 |
case 2: |
578 |
r = (int) ((i1 >> 24) & 0xFFL); |
579 |
break; |
580 |
case 3: |
581 |
r = (int) ((i1 >> 32) & 0xFFL); |
582 |
break; |
583 |
case 4: |
584 |
r = (int) ((i1 >> 40) & 0xFFL); |
585 |
break; |
586 |
case 5: |
587 |
r = (int) ((i1 >> 48) & 0xFFL); |
588 |
break; |
589 |
case 6: |
590 |
r = (int) ((i1 >> 56) & 0xFFL); |
591 |
break; |
592 |
case 7: |
593 |
r = (int) (i2 & 0xFFL); |
594 |
break; |
595 |
case 8: |
596 |
r = (int) ((i2 >> 8) & 0xFFL); |
597 |
break; |
598 |
case 9: |
599 |
r = (int) ((i2 >> 16) & 0xFFL); |
600 |
break; |
601 |
case 10: |
602 |
r = (int) ((i2 >> 24) & 0xFFL); |
603 |
break; |
604 |
case 11: |
605 |
r = (int) ((i2 >> 32) & 0xFFL); |
606 |
break; |
607 |
case 12: |
608 |
r = (int) ((i2 >> 40) & 0xFFL); |
609 |
break; |
610 |
case 13: |
611 |
r = (int) ((i2 >> 48) & 0xFFL); |
612 |
break; |
613 |
case 14: |
614 |
r = (int) ((i2 >> 56) & 0xFFL); |
615 |
break; |
616 |
case 15: |
617 |
r = (int) (i3 & 0xFFL); |
618 |
break; |
619 |
case 16: |
620 |
r = (int) ((i3 >> 8) & 0xFFL); |
621 |
break; |
622 |
case 17: |
623 |
r = (int) ((i3 >> 16) & 0xFFL); |
624 |
break; |
625 |
case 18: |
626 |
r = (int) ((i3 >> 24) & 0xFFL); |
627 |
break; |
628 |
case 19: |
629 |
r = (int) ((i3 >> 32) & 0xFFL); |
630 |
break; |
631 |
case 20: |
632 |
r = (int) ((i3 >> 40) & 0xFFL); |
633 |
break; |
634 |
case 21: |
635 |
r = (int) ((i3 >> 48) & 0xFFL); |
636 |
break; |
637 |
case 22: |
638 |
r = (int) ((i3 >> 56) & 0xFFL); |
639 |
break; |
640 |
} |
641 |
return (char) r; |
642 |
} |
643 |
|
644 |
@Override |
645 |
public String toString() { |
646 |
int n = length(); |
647 |
char[] r = new char[n]; |
648 |
for (int i = 0; i < n; i++) { |
649 |
r[i] = charAt(i); |
650 |
} |
651 |
return new String(r); |
652 |
} |
653 |
|
654 |
@Override |
655 |
public boolean equals(Object object) { |
656 |
if (this == object) { |
657 |
return true; |
658 |
} |
659 |
if (object instanceof Fixed_16_23) { |
660 |
Fixed_16_23 otherString = (Fixed_16_23) object; |
661 |
return i1 == otherString.i1 && i2 == otherString.i2 && i3 == otherString.i3; |
662 |
} |
663 |
return false; |
664 |
} |
665 |
|
666 |
@Override |
667 |
public int hashCode() { |
668 |
long res = i1 + 31 * (i2 + 31 * i3); |
669 |
res = (res + (res >> 32)) & 0xFFFFFFFFL; |
670 |
return (int) res; |
671 |
} |
672 |
|
673 |
@Override |
674 |
public CharSequence subSequence(int start, int end) { |
675 |
return CharSequences.create(toString().substring(start, end)); |
676 |
} |
677 |
|
678 |
@Override |
679 |
public int compareTo(CharSequence o) { |
680 |
return Comparator.compare(this, o); |
681 |
} |
682 |
} |
683 |
|
684 |
/** |
685 |
* compact char sequence implementation based on byte[] or char[] array |
686 |
* size: 8 + 4 + 4 (= 16 bytes) + sizeof ('value') |
687 |
*/ |
688 |
private final static class CharSequenceKey implements CompactCharSequence, Comparable<CharSequence> { |
689 |
|
690 |
private final Object value; |
691 |
private int hash; |
692 |
|
693 |
private CharSequenceKey(byte[] b) { |
694 |
value = b; |
695 |
} |
696 |
|
697 |
private CharSequenceKey(char[] v) { |
698 |
value = v; |
699 |
} |
700 |
|
701 |
@Override |
702 |
public int length() { |
703 |
if (value instanceof byte[]) { |
704 |
return ((byte[]) value).length; |
705 |
} |
706 |
return ((char[]) value).length; |
707 |
} |
708 |
|
709 |
@Override |
710 |
public char charAt(int index) { |
711 |
if (value instanceof byte[]) { |
712 |
int r = ((byte[]) value)[index] & 0xFF; |
713 |
return (char) r; |
714 |
} |
715 |
return ((char[]) value)[index]; |
716 |
} |
717 |
|
718 |
@Override |
719 |
public boolean equals(Object object) { |
720 |
if (this == object) { |
721 |
return true; |
722 |
} |
723 |
if (object instanceof CharSequenceKey) { |
724 |
CharSequenceKey otherString = (CharSequenceKey) object; |
725 |
if (hash != 0 && otherString.hash != 0) { |
726 |
if (hash != otherString.hash) { |
727 |
return false; |
728 |
} |
729 |
} |
730 |
if ((value instanceof byte[]) && (otherString.value instanceof byte[])) { |
731 |
return Arrays.equals((byte[]) value, (byte[]) otherString.value); |
732 |
} else if ((value instanceof char[]) && (otherString.value instanceof char[])) { |
733 |
return Arrays.equals((char[]) value, (char[]) otherString.value); |
734 |
} |
735 |
} |
736 |
return false; |
737 |
} |
738 |
|
739 |
@Override |
740 |
public int hashCode() { |
741 |
int h = hash; |
742 |
if (h == 0) { |
743 |
if (value instanceof byte[]) { |
744 |
byte[] v = (byte[]) value; |
745 |
int n = v.length; |
746 |
for (int i = 0; i < n; i++) { |
747 |
h = 31 * h + v[i]; |
748 |
} |
749 |
} else { |
750 |
char[] v = (char[]) value; |
751 |
int n = v.length; |
752 |
for (int i = 0; i < n; i++) { |
753 |
h = 31 * h + v[i]; |
754 |
} |
755 |
} |
756 |
hash = h; |
757 |
} |
758 |
return h; |
759 |
} |
760 |
|
761 |
@Override |
762 |
public CharSequence subSequence(int beginIndex, int endIndex) { |
763 |
return CharSequences.create(toString().substring(beginIndex, endIndex)); |
764 |
} |
765 |
|
766 |
@Override |
767 |
public String toString() { |
768 |
if (value instanceof byte[]) { |
769 |
byte[] v = (byte[]) value; |
770 |
int n = v.length; |
771 |
char[] r = new char[n]; |
772 |
for (int i = 0; i < n; i++) { |
773 |
int c = v[i] & 0xFF; |
774 |
r[i] = (char) c; |
775 |
} |
776 |
return new String(r); |
777 |
} |
778 |
char[] v = (char[]) value; |
779 |
return new String(v); |
780 |
} |
781 |
|
782 |
@Override |
783 |
public int compareTo(CharSequence o) { |
784 |
return Comparator.compare(this, o); |
785 |
} |
786 |
} |
787 |
|
788 |
private static final CompactCharSequence EMPTY = new CharSequenceKey(new byte[]{}); |
789 |
private static final Comparator<CharSequence> Comparator = new CharSequenceComparator(); |
790 |
private static final Comparator<CharSequence> ComparatorIgnoreCase = new CharSequenceComparatorIgnoreCase(); |
791 |
|
792 |
private static class CharSequenceComparator implements Comparator<CharSequence> { |
793 |
|
794 |
@Override |
795 |
public int compare(CharSequence o1, CharSequence o2) { |
796 |
if ((o1 instanceof CharSequenceKey)) { |
797 |
if ((o2 instanceof CharSequenceKey)) { |
798 |
CharSequenceKey csk1 = (CharSequenceKey) o1; |
799 |
CharSequenceKey csk2 = (CharSequenceKey) o2; |
800 |
if ((csk1.value instanceof byte[]) |
801 |
&& (csk2.value instanceof byte[])) { |
802 |
byte[] b1 = (byte[]) csk1.value; |
803 |
byte[] b2 = (byte[]) csk2.value; |
804 |
int len1 = b1.length; |
805 |
int len2 = b2.length; |
806 |
int n = Math.min(len1, len2); |
807 |
int k = 0; |
808 |
while (k < n) { |
809 |
if (b1[k] != b2[k]) { |
810 |
return (b1[k] & 0xFF) - (b2[k] & 0xFF); |
811 |
} |
812 |
k++; |
813 |
} |
814 |
return len1 - len2; |
815 |
} |
816 |
} else { |
817 |
CharSequenceKey csk1 = (CharSequenceKey) o1; |
818 |
if ((csk1.value instanceof byte[])) { |
819 |
byte[] b1 = (byte[]) csk1.value; |
820 |
int len1 = b1.length; |
821 |
int len2 = o2.length(); |
822 |
int n = Math.min(len1, len2); |
823 |
int k = 0; |
824 |
int c1, c2; |
825 |
while (k < n) { |
826 |
c1 = b1[k] & 0xFF; |
827 |
c2 = o2.charAt(k); |
828 |
if (c1 != c2) { |
829 |
return c1 - c2; |
830 |
} |
831 |
k++; |
832 |
} |
833 |
return len1 - len2; |
834 |
} |
835 |
} |
836 |
} else if ((o2 instanceof CharSequenceKey)) { |
837 |
CharSequenceKey csk2 = (CharSequenceKey) o2; |
838 |
if ((csk2.value instanceof byte[])) { |
839 |
byte[] b2 = (byte[]) csk2.value; |
840 |
int len1 = o1.length(); |
841 |
int len2 = b2.length; |
842 |
int n = Math.min(len1, len2); |
843 |
int k = 0; |
844 |
int c1, c2; |
845 |
while (k < n) { |
846 |
c1 = o1.charAt(k); |
847 |
c2 = b2[k] & 0xFF; |
848 |
if (c1 != c2) { |
849 |
return c1 - c2; |
850 |
} |
851 |
k++; |
852 |
} |
853 |
return len1 - len2; |
854 |
} |
855 |
} |
856 |
int len1 = o1.length(); |
857 |
int len2 = o2.length(); |
858 |
int n = Math.min(len1, len2); |
859 |
int k = 0; |
860 |
while (k < n) { |
861 |
char c1 = o1.charAt(k); |
862 |
char c2 = o2.charAt(k); |
863 |
if (c1 != c2) { |
864 |
return c1 - c2; |
865 |
} |
866 |
k++; |
867 |
} |
868 |
return len1 - len2; |
869 |
} |
870 |
} |
871 |
|
872 |
private static class CharSequenceComparatorIgnoreCase implements Comparator<CharSequence> { |
873 |
|
874 |
@Override |
875 |
public int compare(CharSequence o1, CharSequence o2) { |
876 |
int n1 = o1.length(); |
877 |
int n2 = o2.length(); |
878 |
for (int i1 = 0, i2 = 0; i1 < n1 && i2 < n2; i1++, i2++) { |
879 |
char c1 = o1.charAt(i1); |
880 |
char c2 = o2.charAt(i2); |
881 |
if (c1 != c2) { |
882 |
c1 = Character.toUpperCase(c1); |
883 |
c2 = Character.toUpperCase(c2); |
884 |
if (c1 != c2) { |
885 |
c1 = Character.toLowerCase(c1); |
886 |
c2 = Character.toLowerCase(c2); |
887 |
if (c1 != c2) { |
888 |
return c1 - c2; |
889 |
} |
890 |
} |
891 |
} |
892 |
} |
893 |
return n1 - n2; |
894 |
} |
895 |
} |
896 |
|
897 |
/** |
898 |
* marker interface for compact char sequence implementations |
899 |
*/ |
900 |
private interface CompactCharSequence extends CharSequence { |
901 |
} |
902 |
|
903 |
/** |
904 |
* private constructor for utilities class |
905 |
*/ |
906 |
private CharSequences() { |
907 |
} |
908 |
} |