History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/text/normalizer/NormalizerBase.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/text/normalizer/NormalizerBase.java+1680−0
@@ -0,0 +1,1680 @@
1/*
2 * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 *******************************************************************************
27 * (C) Copyright IBM Corp. and others, 1996-2009 - All Rights Reserved *
28 * *
29 * The original version of this source code and documentation is copyrighted *
30 * and owned by IBM, These materials are provided under terms of a License *
31 * Agreement between IBM and Sun. This technology is protected by multiple *
32 * US and International patents. This notice and attribution to IBM may not *
33 * to removed. *
34 *******************************************************************************
35 */
36
37package sun.text.normalizer;
38
39import java.text.CharacterIterator;
40import java.text.Normalizer;
41
42/**
43 * Unicode Normalization
44 *
45 * <h2>Unicode normalization API</h2>
46 *
47 * <code>normalize</code> transforms Unicode text into an equivalent composed or
48 * decomposed form, allowing for easier sorting and searching of text.
49 * <code>normalize</code> supports the standard normalization forms described in
50 * <a href="http://www.unicode.org/unicode/reports/tr15/" target="unicode">
51 * Unicode Standard Annex #15 &mdash; Unicode Normalization Forms</a>.
52 *
53 * Characters with accents or other adornments can be encoded in
54 * several different ways in Unicode. For example, take the character A-acute.
55 * In Unicode, this can be encoded as a single character (the
56 * "composed" form):
57 *
58 * <p>
59 * 00C1 LATIN CAPITAL LETTER A WITH ACUTE
60 * </p>
61 *
62 * or as two separate characters (the "decomposed" form):
63 *
64 * <p>
65 * 0041 LATIN CAPITAL LETTER A
66 * 0301 COMBINING ACUTE ACCENT
67 * </p>
68 *
69 * To a user of your program, however, both of these sequences should be
70 * treated as the same "user-level" character "A with acute accent". When you
71 * are searching or comparing text, you must ensure that these two sequences are
72 * treated equivalently. In addition, you must handle characters with more than
73 * one accent. Sometimes the order of a character's combining accents is
74 * significant, while in other cases accent sequences in different orders are
75 * really equivalent.
76 *
77 * Similarly, the string "ffi" can be encoded as three separate letters:
78 *
79 * <p>
80 * 0066 LATIN SMALL LETTER F
81 * 0066 LATIN SMALL LETTER F
82 * 0069 LATIN SMALL LETTER I
83 * </p>
84 *
85 * or as the single character
86 *
87 * <p>
88 * FB03 LATIN SMALL LIGATURE FFI
89 * </p>
90 *
91 * The ffi ligature is not a distinct semantic character, and strictly speaking
92 * it shouldn't be in Unicode at all, but it was included for compatibility
93 * with existing character sets that already provided it. The Unicode standard
94 * identifies such characters by giving them "compatibility" decompositions
95 * into the corresponding semantic characters. When sorting and searching, you
96 * will often want to use these mappings.
97 *
98 * <code>normalize</code> helps solve these problems by transforming text into
99 * the canonical composed and decomposed forms as shown in the first example
100 * above. In addition, you can have it perform compatibility decompositions so
101 * that you can treat compatibility characters the same as their equivalents.
102 * Finally, <code>normalize</code> rearranges accents into the proper canonical
103 * order, so that you do not have to worry about accent rearrangement on your
104 * own.
105 *
106 * Form FCD, "Fast C or D", is also designed for collation.
107 * It allows to work on strings that are not necessarily normalized
108 * with an algorithm (like in collation) that works under "canonical closure",
109 * i.e., it treats precomposed characters and their decomposed equivalents the
110 * same.
111 *
112 * It is not a normalization form because it does not provide for uniqueness of
113 * representation. Multiple strings may be canonically equivalent (their NFDs
114 * are identical) and may all conform to FCD without being identical themselves.
115 *
116 * The form is defined such that the "raw decomposition", the recursive
117 * canonical decomposition of each character, results in a string that is
118 * canonically ordered. This means that precomposed characters are allowed for
119 * as long as their decompositions do not need canonical reordering.
120 *
121 * Its advantage for a process like collation is that all NFD and most NFC texts
122 * - and many unnormalized texts - already conform to FCD and do not need to be
123 * normalized (NFD) for such a process. The FCD quick check will return YES for
124 * most strings in practice.
125 *
126 * normalize(FCD) may be implemented with NFD.
127 *
128 * For more details on FCD see the collation design document:
129 * http://source.icu-project.org/repos/icu/icuhtml/trunk/design/collation/ICU_collation_design.htm
130 *
131 * ICU collation performs either NFD or FCD normalization automatically if
132 * normalization is turned on for the collator object. Beyond collation and
133 * string search, normalized strings may be useful for string equivalence
134 * comparisons, transliteration/transcription, unique representations, etc.
135 *
136 * The W3C generally recommends to exchange texts in NFC.
137 * Note also that most legacy character encodings use only precomposed forms and
138 * often do not encode any combining marks by themselves. For conversion to such
139 * character encodings the Unicode text needs to be normalized to NFC.
140 * For more usage examples, see the Unicode Standard Annex.
141 * @stable ICU 2.8
142 */
143
144public final class NormalizerBase implements Cloneable {
145
146 //-------------------------------------------------------------------------
147 // Private data
148 //-------------------------------------------------------------------------
149 private char[] buffer = new char[100];
150 private int bufferStart = 0;
151 private int bufferPos = 0;
152 private int bufferLimit = 0;
153
154 // The input text and our position in it
155 private UCharacterIterator text;
156 private Mode mode = NFC;
157 private int options = 0;
158 private int currentIndex;
159 private int nextIndex;
160
161 /**
162 * Options bit set value to select Unicode 3.2 normalization
163 * (except NormalizationCorrections).
164 * At most one Unicode version can be selected at a time.
165 * @stable ICU 2.6
166 */
167 public static final int UNICODE_3_2=0x20;
168
169 /**
170 * Constant indicating that the end of the iteration has been reached.
171 * This is guaranteed to have the same value as {@link UCharacterIterator#DONE}.
172 * @stable ICU 2.8
173 */
174 public static final int DONE = UCharacterIterator.DONE;
175
176 /**
177 * Constants for normalization modes.
178 * @stable ICU 2.8
179 */
180 public static class Mode {
181 private int modeValue;
182 private Mode(int value) {
183 modeValue = value;
184 }
185
186 /**
187 * This method is used for method dispatch
188 * @stable ICU 2.6
189 */
190 protected int normalize(char[] src, int srcStart, int srcLimit,
191 char[] dest,int destStart,int destLimit,
192 UnicodeSet nx) {
193 int srcLen = (srcLimit - srcStart);
194 int destLen = (destLimit - destStart);
195 if( srcLen > destLen ) {
196 return srcLen;
197 }
198 System.arraycopy(src,srcStart,dest,destStart,srcLen);
199 return srcLen;
200 }
201
202 /**
203 * This method is used for method dispatch
204 * @stable ICU 2.6
205 */
206 protected int normalize(char[] src, int srcStart, int srcLimit,
207 char[] dest,int destStart,int destLimit,
208 int options) {
209 return normalize( src, srcStart, srcLimit,
210 dest,destStart,destLimit,
211 NormalizerImpl.getNX(options)
212 );
213 }
214
215 /**
216 * This method is used for method dispatch
217 * @stable ICU 2.6
218 */
219 protected String normalize(String src, int options) {
220 return src;
221 }
222
223 /**
224 * This method is used for method dispatch
225 * @stable ICU 2.8
226 */
227 protected int getMinC() {
228 return -1;
229 }
230
231 /**
232 * This method is used for method dispatch
233 * @stable ICU 2.8
234 */
235 protected int getMask() {
236 return -1;
237 }
238
239 /**
240 * This method is used for method dispatch
241 * @stable ICU 2.8
242 */
243 protected IsPrevBoundary getPrevBoundary() {
244 return null;
245 }
246
247 /**
248 * This method is used for method dispatch
249 * @stable ICU 2.8
250 */
251 protected IsNextBoundary getNextBoundary() {
252 return null;
253 }
254
255 /**
256 * This method is used for method dispatch
257 * @stable ICU 2.6
258 */
259 protected QuickCheckResult quickCheck(char[] src,int start, int limit,
260 boolean allowMaybe,UnicodeSet nx) {
261 if(allowMaybe) {
262 return MAYBE;
263 }
264 return NO;
265 }
266
267 /**
268 * This method is used for method dispatch
269 * @stable ICU 2.8
270 */
271 protected boolean isNFSkippable(int c) {
272 return true;
273 }
274 }
275
276 /**
277 * No decomposition/composition.
278 * @stable ICU 2.8
279 */
280 public static final Mode NONE = new Mode(1);
281
282 /**
283 * Canonical decomposition.
284 * @stable ICU 2.8
285 */
286 public static final Mode NFD = new NFDMode(2);
287
288 private static final class NFDMode extends Mode {
289 private NFDMode(int value) {
290 super(value);
291 }
292
293 protected int normalize(char[] src, int srcStart, int srcLimit,
294 char[] dest,int destStart,int destLimit,
295 UnicodeSet nx) {
296 int[] trailCC = new int[1];
297 return NormalizerImpl.decompose(src, srcStart,srcLimit,
298 dest, destStart,destLimit,
299 false, trailCC,nx);
300 }
301
302 protected String normalize( String src, int options) {
303 return decompose(src,false,options);
304 }
305
306 protected int getMinC() {
307 return NormalizerImpl.MIN_WITH_LEAD_CC;
308 }
309
310 protected IsPrevBoundary getPrevBoundary() {
311 return new IsPrevNFDSafe();
312 }
313
314 protected IsNextBoundary getNextBoundary() {
315 return new IsNextNFDSafe();
316 }
317
318 protected int getMask() {
319 return (NormalizerImpl.CC_MASK|NormalizerImpl.QC_NFD);
320 }
321
322 protected QuickCheckResult quickCheck(char[] src,int start,
323 int limit,boolean allowMaybe,
324 UnicodeSet nx) {
325 return NormalizerImpl.quickCheck(
326 src, start,limit,
327 NormalizerImpl.getFromIndexesArr(
328 NormalizerImpl.INDEX_MIN_NFD_NO_MAYBE
329 ),
330 NormalizerImpl.QC_NFD,
331 0,
332 allowMaybe,
333 nx
334 );
335 }
336
337 protected boolean isNFSkippable(int c) {
338 return NormalizerImpl.isNFSkippable(c,this,
339 (NormalizerImpl.CC_MASK|NormalizerImpl.QC_NFD)
340 );
341 }
342 }
343
344 /**
345 * Compatibility decomposition.
346 * @stable ICU 2.8
347 */
348 public static final Mode NFKD = new NFKDMode(3);
349
350 private static final class NFKDMode extends Mode {
351 private NFKDMode(int value) {
352 super(value);
353 }
354
355 protected int normalize(char[] src, int srcStart, int srcLimit,
356 char[] dest,int destStart,int destLimit,
357 UnicodeSet nx) {
358 int[] trailCC = new int[1];
359 return NormalizerImpl.decompose(src, srcStart,srcLimit,
360 dest, destStart,destLimit,
361 true, trailCC, nx);
362 }
363
364 protected String normalize( String src, int options) {
365 return decompose(src,true,options);
366 }
367
368 protected int getMinC() {
369 return NormalizerImpl.MIN_WITH_LEAD_CC;
370 }
371
372 protected IsPrevBoundary getPrevBoundary() {
373 return new IsPrevNFDSafe();
374 }
375
376 protected IsNextBoundary getNextBoundary() {
377 return new IsNextNFDSafe();
378 }
379
380 protected int getMask() {
381 return (NormalizerImpl.CC_MASK|NormalizerImpl.QC_NFKD);
382 }
383
384 protected QuickCheckResult quickCheck(char[] src,int start,
385 int limit,boolean allowMaybe,
386 UnicodeSet nx) {
387 return NormalizerImpl.quickCheck(
388 src,start,limit,
389 NormalizerImpl.getFromIndexesArr(
390 NormalizerImpl.INDEX_MIN_NFKD_NO_MAYBE
391 ),
392 NormalizerImpl.QC_NFKD,
393 NormalizerImpl.OPTIONS_COMPAT,
394 allowMaybe,
395 nx
396 );
397 }
398
399 protected boolean isNFSkippable(int c) {
400 return NormalizerImpl.isNFSkippable(c, this,
401 (NormalizerImpl.CC_MASK|NormalizerImpl.QC_NFKD)
402 );
403 }
404 }
405
406 /**
407 * Canonical decomposition followed by canonical composition.
408 * @stable ICU 2.8
409 */
410 public static final Mode NFC = new NFCMode(4);
411
412 private static final class NFCMode extends Mode{
413 private NFCMode(int value) {
414 super(value);
415 }
416 protected int normalize(char[] src, int srcStart, int srcLimit,
417 char[] dest,int destStart,int destLimit,
418 UnicodeSet nx) {
419 return NormalizerImpl.compose( src, srcStart, srcLimit,
420 dest,destStart,destLimit,
421 0, nx);
422 }
423
424 protected String normalize( String src, int options) {
425 return compose(src, false, options);
426 }
427
428 protected int getMinC() {
429 return NormalizerImpl.getFromIndexesArr(
430 NormalizerImpl.INDEX_MIN_NFC_NO_MAYBE
431 );
432 }
433 protected IsPrevBoundary getPrevBoundary() {
434 return new IsPrevTrueStarter();
435 }
436 protected IsNextBoundary getNextBoundary() {
437 return new IsNextTrueStarter();
438 }
439 protected int getMask() {
440 return (NormalizerImpl.CC_MASK|NormalizerImpl.QC_NFC);
441 }
442 protected QuickCheckResult quickCheck(char[] src,int start,
443 int limit,boolean allowMaybe,
444 UnicodeSet nx) {
445 return NormalizerImpl.quickCheck(
446 src,start,limit,
447 NormalizerImpl.getFromIndexesArr(
448 NormalizerImpl.INDEX_MIN_NFC_NO_MAYBE
449 ),
450 NormalizerImpl.QC_NFC,
451 0,
452 allowMaybe,
453 nx
454 );
455 }
456 protected boolean isNFSkippable(int c) {
457 return NormalizerImpl.isNFSkippable(c,this,
458 ( NormalizerImpl.CC_MASK|NormalizerImpl.COMBINES_ANY|
459 (NormalizerImpl.QC_NFC & NormalizerImpl.QC_ANY_NO)
460 )
461 );
462 }
463 };
464
465 /**
466 * Compatibility decomposition followed by canonical composition.
467 * @stable ICU 2.8
468 */
469 public static final Mode NFKC =new NFKCMode(5);
470
471 private static final class NFKCMode extends Mode{
472 private NFKCMode(int value) {
473 super(value);
474 }
475 protected int normalize(char[] src, int srcStart, int srcLimit,
476 char[] dest,int destStart,int destLimit,
477 UnicodeSet nx) {
478 return NormalizerImpl.compose(src, srcStart,srcLimit,
479 dest, destStart,destLimit,
480 NormalizerImpl.OPTIONS_COMPAT, nx);
481 }
482
483 protected String normalize( String src, int options) {
484 return compose(src, true, options);
485 }
486 protected int getMinC() {
487 return NormalizerImpl.getFromIndexesArr(
488 NormalizerImpl.INDEX_MIN_NFKC_NO_MAYBE
489 );
490 }
491 protected IsPrevBoundary getPrevBoundary() {
492 return new IsPrevTrueStarter();
493 }
494 protected IsNextBoundary getNextBoundary() {
495 return new IsNextTrueStarter();
496 }
497 protected int getMask() {
498 return (NormalizerImpl.CC_MASK|NormalizerImpl.QC_NFKC);
499 }
500 protected QuickCheckResult quickCheck(char[] src,int start,
501 int limit,boolean allowMaybe,
502 UnicodeSet nx) {
503 return NormalizerImpl.quickCheck(
504 src,start,limit,
505 NormalizerImpl.getFromIndexesArr(
506 NormalizerImpl.INDEX_MIN_NFKC_NO_MAYBE
507 ),
508 NormalizerImpl.QC_NFKC,
509 NormalizerImpl.OPTIONS_COMPAT,
510 allowMaybe,
511 nx
512 );
513 }
514 protected boolean isNFSkippable(int c) {
515 return NormalizerImpl.isNFSkippable(c, this,
516 ( NormalizerImpl.CC_MASK|NormalizerImpl.COMBINES_ANY|
517 (NormalizerImpl.QC_NFKC & NormalizerImpl.QC_ANY_NO)
518 )
519 );
520 }
521 };
522
523 /**
524 * Result values for quickCheck().
525 * For details see Unicode Technical Report 15.
526 * @stable ICU 2.8
527 */
528 public static final class QuickCheckResult{
529 private int resultValue;
530 private QuickCheckResult(int value) {
531 resultValue=value;
532 }
533 }
534 /**
535 * Indicates that string is not in the normalized format
536 * @stable ICU 2.8
537 */
538 public static final QuickCheckResult NO = new QuickCheckResult(0);
539
540 /**
541 * Indicates that string is in the normalized format
542 * @stable ICU 2.8
543 */
544 public static final QuickCheckResult YES = new QuickCheckResult(1);
545
546 /**
547 * Indicates it cannot be determined if string is in the normalized
548 * format without further thorough checks.
549 * @stable ICU 2.8
550 */
551 public static final QuickCheckResult MAYBE = new QuickCheckResult(2);
552
553 //-------------------------------------------------------------------------
554 // Constructors
555 //-------------------------------------------------------------------------
556
557 /**
558 * Creates a new <tt>Normalizer</tt> object for iterating over the
559 * normalized form of a given string.
560 * <p>
561 * The <tt>options</tt> parameter specifies which optional
562 * <tt>Normalizer</tt> features are to be enabled for this object.
563 * <p>
564 * @param str The string to be normalized. The normalization
565 * will start at the beginning of the string.
566 *
567 * @param mode The normalization mode.
568 *
569 * @param opt Any optional features to be enabled.
570 * Currently the only available option is {@link #UNICODE_3_2}.
571 * If you want the default behavior corresponding to one of the
572 * standard Unicode Normalization Forms, use 0 for this argument.
573 * @stable ICU 2.6
574 */
575 public NormalizerBase(String str, Mode mode, int opt) {
576 this.text = UCharacterIterator.getInstance(str);
577 this.mode = mode;
578 this.options=opt;
579 }
580
581 /**
582 * Creates a new <tt>Normalizer</tt> object for iterating over the
583 * normalized form of the given text.
584 * <p>
585 * @param iter The input text to be normalized. The normalization
586 * will start at the beginning of the string.
587 *
588 * @param mode The normalization mode.
589 */
590 public NormalizerBase(CharacterIterator iter, Mode mode) {
591 this(iter, mode, UNICODE_LATEST);
592 }
593
594 /**
595 * Creates a new <tt>Normalizer</tt> object for iterating over the
596 * normalized form of the given text.
597 * <p>
598 * @param iter The input text to be normalized. The normalization
599 * will start at the beginning of the string.
600 *
601 * @param mode The normalization mode.
602 *
603 * @param opt Any optional features to be enabled.
604 * Currently the only available option is {@link #UNICODE_3_2}.
605 * If you want the default behavior corresponding to one of the
606 * standard Unicode Normalization Forms, use 0 for this argument.
607 * @stable ICU 2.6
608 */
609 public NormalizerBase(CharacterIterator iter, Mode mode, int opt) {
610 this.text = UCharacterIterator.getInstance(
611 (CharacterIterator)iter.clone()
612 );
613 this.mode = mode;
614 this.options = opt;
615 }
616
617 /**
618 * Clones this <tt>Normalizer</tt> object. All properties of this
619 * object are duplicated in the new object, including the cloning of any
620 * {@link CharacterIterator} that was passed in to the constructor
621 * or to {@link #setText(CharacterIterator) setText}.
622 * However, the text storage underlying
623 * the <tt>CharacterIterator</tt> is not duplicated unless the
624 * iterator's <tt>clone</tt> method does so.
625 * @stable ICU 2.8
626 */
627 public Object clone() {
628 try {
629 NormalizerBase copy = (NormalizerBase) super.clone();
630 copy.text = (UCharacterIterator) text.clone();
631 //clone the internal buffer
632 if (buffer != null) {
633 copy.buffer = new char[buffer.length];
634 System.arraycopy(buffer,0,copy.buffer,0,buffer.length);
635 }
636 return copy;
637 }
638 catch (CloneNotSupportedException e) {
639 throw new InternalError(e.toString());
640 }
641 }
642
643 //--------------------------------------------------------------------------
644 // Static Utility methods
645 //--------------------------------------------------------------------------
646
647 /**
648 * Compose a string.
649 * The string will be composed to according the the specified mode.
650 * @param str The string to compose.
651 * @param compat If true the string will be composed accoding to
652 * NFKC rules and if false will be composed according to
653 * NFC rules.
654 * @param options The only recognized option is UNICODE_3_2
655 * @return String The composed string
656 * @stable ICU 2.6
657 */
658 public static String compose(String str, boolean compat, int options) {
659
660 char[] dest, src;
661 if (options == UNICODE_3_2_0_ORIGINAL) {
662 String mappedStr = NormalizerImpl.convert(str);
663 dest = new char[mappedStr.length()*MAX_BUF_SIZE_COMPOSE];
664 src = mappedStr.toCharArray();
665 } else {
666 dest = new char[str.length()*MAX_BUF_SIZE_COMPOSE];
667 src = str.toCharArray();
668 }
669 int destSize=0;
670
671 UnicodeSet nx = NormalizerImpl.getNX(options);
672
673 /* reset options bits that should only be set here or inside compose() */
674 options&=~(NormalizerImpl.OPTIONS_SETS_MASK|NormalizerImpl.OPTIONS_COMPAT|NormalizerImpl.OPTIONS_COMPOSE_CONTIGUOUS);
675
676 if(compat) {
677 options|=NormalizerImpl.OPTIONS_COMPAT;
678 }
679
680 for(;;) {
681 destSize=NormalizerImpl.compose(src,0,src.length,
682 dest,0,dest.length,options,
683 nx);
684 if(destSize<=dest.length) {
685 return new String(dest,0,destSize);
686 } else {
687 dest = new char[destSize];
688 }
689 }
690 }
691
692 private static final int MAX_BUF_SIZE_COMPOSE = 2;
693 private static final int MAX_BUF_SIZE_DECOMPOSE = 3;
694
695 /**
696 * Decompose a string.
697 * The string will be decomposed to according the the specified mode.
698 * @param str The string to decompose.
699 * @param compat If true the string will be decomposed accoding to NFKD
700 * rules and if false will be decomposed according to NFD
701 * rules.
702 * @return String The decomposed string
703 * @stable ICU 2.8
704 */
705 public static String decompose(String str, boolean compat) {
706 return decompose(str,compat,UNICODE_LATEST);
707 }
708
709 /**
710 * Decompose a string.
711 * The string will be decomposed to according the the specified mode.
712 * @param str The string to decompose.
713 * @param compat If true the string will be decomposed accoding to NFKD
714 * rules and if false will be decomposed according to NFD
715 * rules.
716 * @param options The normalization options, ORed together (0 for no options).
717 * @return String The decomposed string
718 * @stable ICU 2.6
719 */
720 public static String decompose(String str, boolean compat, int options) {
721
722 int[] trailCC = new int[1];
723 int destSize=0;
724 UnicodeSet nx = NormalizerImpl.getNX(options);
725 char[] dest;
726
727 if (options == UNICODE_3_2_0_ORIGINAL) {
728 String mappedStr = NormalizerImpl.convert(str);
729 dest = new char[mappedStr.length()*MAX_BUF_SIZE_DECOMPOSE];
730
731 for(;;) {
732 destSize=NormalizerImpl.decompose(mappedStr.toCharArray(),0,mappedStr.length(),
733 dest,0,dest.length,
734 compat,trailCC, nx);
735 if(destSize<=dest.length) {
736 return new String(dest,0,destSize);
737 } else {
738 dest = new char[destSize];
739 }
740 }
741 } else {
742 dest = new char[str.length()*MAX_BUF_SIZE_DECOMPOSE];
743
744 for(;;) {
745 destSize=NormalizerImpl.decompose(str.toCharArray(),0,str.length(),
746 dest,0,dest.length,
747 compat,trailCC, nx);
748 if(destSize<=dest.length) {
749 return new String(dest,0,destSize);
750 } else {
751 dest = new char[destSize];
752 }
753 }
754 }
755 }
756
757 /**
758 * Normalize a string.
759 * The string will be normalized according the the specified normalization
760 * mode and options.
761 * @param src The char array to compose.
762 * @param srcStart Start index of the source
763 * @param srcLimit Limit index of the source
764 * @param dest The char buffer to fill in
765 * @param destStart Start index of the destination buffer
766 * @param destLimit End index of the destination buffer
767 * @param mode The normalization mode; one of Normalizer.NONE,
768 * Normalizer.NFD, Normalizer.NFC, Normalizer.NFKC,
769 * Normalizer.NFKD, Normalizer.DEFAULT
770 * @param options The normalization options, ORed together (0 for no options).
771 * @return int The total buffer size needed;if greater than length of
772 * result, the output was truncated.
773 * @exception IndexOutOfBoundsException if the target capacity is
774 * less than the required length
775 * @stable ICU 2.6
776 */
777 public static int normalize(char[] src,int srcStart, int srcLimit,
778 char[] dest,int destStart, int destLimit,
779 Mode mode, int options) {
780 int length = mode.normalize(src,srcStart,srcLimit,dest,destStart,destLimit, options);
781
782 if(length<=(destLimit-destStart)) {
783 return length;
784 } else {
785 throw new IndexOutOfBoundsException(Integer.toString(length));
786 }
787 }
788
789 //-------------------------------------------------------------------------
790 // Iteration API
791 //-------------------------------------------------------------------------
792
793 /**
794 * Return the current character in the normalized text->
795 * @return The codepoint as an int
796 * @stable ICU 2.8
797 */
798 public int current() {
799 if(bufferPos<bufferLimit || nextNormalize()) {
800 return getCodePointAt(bufferPos);
801 } else {
802 return DONE;
803 }
804 }
805
806 /**
807 * Return the next character in the normalized text and advance
808 * the iteration position by one. If the end
809 * of the text has already been reached, {@link #DONE} is returned.
810 * @return The codepoint as an int
811 * @stable ICU 2.8
812 */
813 public int next() {
814 if(bufferPos<bufferLimit || nextNormalize()) {
815 int c=getCodePointAt(bufferPos);
816 bufferPos+=(c>0xFFFF) ? 2 : 1;
817 return c;
818 } else {
819 return DONE;
820 }
821 }
822
823
824 /**
825 * Return the previous character in the normalized text and decrement
826 * the iteration position by one. If the beginning
827 * of the text has already been reached, {@link #DONE} is returned.
828 * @return The codepoint as an int
829 * @stable ICU 2.8
830 */
831 public int previous() {
832 if(bufferPos>0 || previousNormalize()) {
833 int c=getCodePointAt(bufferPos-1);
834 bufferPos-=(c>0xFFFF) ? 2 : 1;
835 return c;
836 } else {
837 return DONE;
838 }
839 }
840
841 /**
842 * Reset the index to the beginning of the text.
843 * This is equivalent to setIndexOnly(startIndex)).
844 * @stable ICU 2.8
845 */
846 public void reset() {
847 text.setIndex(0);
848 currentIndex=nextIndex=0;
849 clearBuffer();
850 }
851
852 /**
853 * Set the iteration position in the input text that is being normalized,
854 * without any immediate normalization.
855 * After setIndexOnly(), getIndex() will return the same index that is
856 * specified here.
857 *
858 * @param index the desired index in the input text.
859 * @stable ICU 2.8
860 */
861 public void setIndexOnly(int index) {
862 text.setIndex(index);
863 currentIndex=nextIndex=index; // validates index
864 clearBuffer();
865 }
866
867 /**
868 * Set the iteration position in the input text that is being normalized
869 * and return the first normalized character at that position.
870 * <p>
871 * <b>Note:</b> This method sets the position in the <em>input</em> text,
872 * while {@link #next} and {@link #previous} iterate through characters
873 * in the normalized <em>output</em>. This means that there is not
874 * necessarily a one-to-one correspondence between characters returned
875 * by <tt>next</tt> and <tt>previous</tt> and the indices passed to and
876 * returned from <tt>setIndex</tt> and {@link #getIndex}.
877 * <p>
878 * @param index the desired index in the input text->
879 *
880 * @return the first normalized character that is the result of iterating
881 * forward starting at the given index.
882 *
883 * @throws IllegalArgumentException if the given index is less than
884 * {@link #getBeginIndex} or greater than {@link #getEndIndex}.
885 * @return The codepoint as an int
886 * @deprecated ICU 3.2
887 * @obsolete ICU 3.2
888 */
889 public int setIndex(int index) {
890 setIndexOnly(index);
891 return current();
892 }
893
894 /**
895 * Retrieve the index of the start of the input text. This is the begin
896 * index of the <tt>CharacterIterator</tt> or the start (i.e. 0) of the
897 * <tt>String</tt> over which this <tt>Normalizer</tt> is iterating
898 * @deprecated ICU 2.2. Use startIndex() instead.
899 * @return The codepoint as an int
900 * @see #startIndex
901 */
902 public int getBeginIndex() {
903 return 0;
904 }
905
906 /**
907 * Retrieve the index of the end of the input text. This is the end index
908 * of the <tt>CharacterIterator</tt> or the length of the <tt>String</tt>
909 * over which this <tt>Normalizer</tt> is iterating
910 * @deprecated ICU 2.2. Use endIndex() instead.
911 * @return The codepoint as an int
912 * @see #endIndex
913 */
914 public int getEndIndex() {
915 return endIndex();
916 }
917
918 /**
919 * Retrieve the current iteration position in the input text that is
920 * being normalized. This method is useful in applications such as
921 * searching, where you need to be able to determine the position in
922 * the input text that corresponds to a given normalized output character.
923 * <p>
924 * <b>Note:</b> This method sets the position in the <em>input</em>, while
925 * {@link #next} and {@link #previous} iterate through characters in the
926 * <em>output</em>. This means that there is not necessarily a one-to-one
927 * correspondence between characters returned by <tt>next</tt> and
928 * <tt>previous</tt> and the indices passed to and returned from
929 * <tt>setIndex</tt> and {@link #getIndex}.
930 * @return The current iteration position
931 * @stable ICU 2.8
932 */
933 public int getIndex() {
934 if(bufferPos<bufferLimit) {
935 return currentIndex;
936 } else {
937 return nextIndex;
938 }
939 }
940
941 /**
942 * Retrieve the index of the end of the input text-> This is the end index
943 * of the <tt>CharacterIterator</tt> or the length of the <tt>String</tt>
944 * over which this <tt>Normalizer</tt> is iterating
945 * @return The current iteration position
946 * @stable ICU 2.8
947 */
948 public int endIndex() {
949 return text.getLength();
950 }
951
952 //-------------------------------------------------------------------------
953 // Property access methods
954 //-------------------------------------------------------------------------
955 /**
956 * Set the normalization mode for this object.
957 * <p>
958 * <b>Note:</b>If the normalization mode is changed while iterating
959 * over a string, calls to {@link #next} and {@link #previous} may
960 * return previously buffers characters in the old normalization mode
961 * until the iteration is able to re-sync at the next base character.
962 * It is safest to call {@link #setText setText()}, {@link #first},
963 * {@link #last}, etc. after calling <tt>setMode</tt>.
964 * <p>
965 * @param newMode the new mode for this <tt>Normalizer</tt>.
966 * The supported modes are:
967 * <ul>
968 * <li>{@link #COMPOSE} - Unicode canonical decompositiion
969 * followed by canonical composition.
970 * <li>{@link #COMPOSE_COMPAT} - Unicode compatibility decompositiion
971 * follwed by canonical composition.
972 * <li>{@link #DECOMP} - Unicode canonical decomposition
973 * <li>{@link #DECOMP_COMPAT} - Unicode compatibility decomposition.
974 * <li>{@link #NO_OP} - Do nothing but return characters
975 * from the underlying input text.
976 * </ul>
977 *
978 * @see #getMode
979 * @stable ICU 2.8
980 */
981 public void setMode(Mode newMode) {
982 mode = newMode;
983 }
984 /**
985 * Return the basic operation performed by this <tt>Normalizer</tt>
986 *
987 * @see #setMode
988 * @stable ICU 2.8
989 */
990 public Mode getMode() {
991 return mode;
992 }
993
994 /**
995 * Set the input text over which this <tt>Normalizer</tt> will iterate.
996 * The iteration position is set to the beginning of the input text->
997 * @param newText The new string to be normalized.
998 * @stable ICU 2.8
999 */
1000 public void setText(String newText) {
1001
1002 UCharacterIterator newIter = UCharacterIterator.getInstance(newText);
1003 if (newIter == null) {
1004 throw new InternalError("Could not create a new UCharacterIterator");
1005 }
1006 text = newIter;
1007 reset();
1008 }
1009
1010 /**
1011 * Set the input text over which this <tt>Normalizer</tt> will iterate.
1012 * The iteration position is set to the beginning of the input text->
1013 * @param newText The new string to be normalized.
1014 * @stable ICU 2.8
1015 */
1016 public void setText(CharacterIterator newText) {
1017
1018 UCharacterIterator newIter = UCharacterIterator.getInstance(newText);
1019 if (newIter == null) {
1020 throw new InternalError("Could not create a new UCharacterIterator");
1021 }
1022 text = newIter;
1023 currentIndex=nextIndex=0;
1024 clearBuffer();
1025 }
1026
1027 //-------------------------------------------------------------------------
1028 // Private utility methods
1029 //-------------------------------------------------------------------------
1030
1031
1032 /* backward iteration --------------------------------------------------- */
1033
1034 /*
1035 * read backwards and get norm32
1036 * return 0 if the character is <minC
1037 * if c2!=0 then (c2, c) is a surrogate pair (reversed - c2 is first
1038 * surrogate but read second!)
1039 */
1040
1041 private static long getPrevNorm32(UCharacterIterator src,
1042 int/*unsigned*/ minC,
1043 int/*unsigned*/ mask,
1044 char[] chars) {
1045 long norm32;
1046 int ch=0;
1047 /* need src.hasPrevious() */
1048 if((ch=src.previous()) == UCharacterIterator.DONE) {
1049 return 0;
1050 }
1051 chars[0]=(char)ch;
1052 chars[1]=0;
1053
1054 /* check for a surrogate before getting norm32 to see if we need to
1055 * predecrement further */
1056 if(chars[0]<minC) {
1057 return 0;
1058 } else if(!UTF16.isSurrogate(chars[0])) {
1059 return NormalizerImpl.getNorm32(chars[0]);
1060 } else if(UTF16.isLeadSurrogate(chars[0]) || (src.getIndex()==0)) {
1061 /* unpaired surrogate */
1062 chars[1]=(char)src.current();
1063 return 0;
1064 } else if(UTF16.isLeadSurrogate(chars[1]=(char)src.previous())) {
1065 norm32=NormalizerImpl.getNorm32(chars[1]);
1066 if((norm32&mask)==0) {
1067 /* all surrogate pairs with this lead surrogate have irrelevant
1068 * data */
1069 return 0;
1070 } else {
1071 /* norm32 must be a surrogate special */
1072 return NormalizerImpl.getNorm32FromSurrogatePair(norm32,chars[0]);
1073 }
1074 } else {
1075 /* unpaired second surrogate, undo the c2=src.previous() movement */
1076 src.moveIndex( 1);
1077 return 0;
1078 }
1079 }
1080
1081 private interface IsPrevBoundary{
1082 public boolean isPrevBoundary(UCharacterIterator src,
1083 int/*unsigned*/ minC,
1084 int/*unsigned*/ mask,
1085 char[] chars);
1086 }
1087 private static final class IsPrevNFDSafe implements IsPrevBoundary{
1088 /*
1089 * for NF*D:
1090 * read backwards and check if the lead combining class is 0
1091 * if c2!=0 then (c2, c) is a surrogate pair (reversed - c2 is first
1092 * surrogate but read second!)
1093 */
1094 public boolean isPrevBoundary(UCharacterIterator src,
1095 int/*unsigned*/ minC,
1096 int/*unsigned*/ ccOrQCMask,
1097 char[] chars) {
1098
1099 return NormalizerImpl.isNFDSafe(getPrevNorm32(src, minC,
1100 ccOrQCMask, chars),
1101 ccOrQCMask,
1102 ccOrQCMask& NormalizerImpl.QC_MASK);
1103 }
1104 }
1105
1106 private static final class IsPrevTrueStarter implements IsPrevBoundary{
1107 /*
1108 * read backwards and check if the character is (or its decomposition
1109 * begins with) a "true starter" (cc==0 and NF*C_YES)
1110 * if c2!=0 then (c2, c) is a surrogate pair (reversed - c2 is first
1111 * surrogate but read second!)
1112 */
1113 public boolean isPrevBoundary(UCharacterIterator src,
1114 int/*unsigned*/ minC,
1115 int/*unsigned*/ ccOrQCMask,
1116 char[] chars) {
1117 long norm32;
1118 int/*unsigned*/ decompQCMask;
1119
1120 decompQCMask=(ccOrQCMask<<2)&0xf; /*decomposition quick check mask*/
1121 norm32=getPrevNorm32(src, minC, ccOrQCMask|decompQCMask, chars);
1122 return NormalizerImpl.isTrueStarter(norm32,ccOrQCMask,decompQCMask);
1123 }
1124 }
1125
1126 private static int findPreviousIterationBoundary(UCharacterIterator src,
1127 IsPrevBoundary obj,
1128 int/*unsigned*/ minC,
1129 int/*mask*/ mask,
1130 char[] buffer,
1131 int[] startIndex) {
1132 char[] chars=new char[2];
1133 boolean isBoundary;
1134
1135 /* fill the buffer from the end backwards */
1136 startIndex[0] = buffer.length;
1137 chars[0]=0;
1138 while(src.getIndex()>0 && chars[0]!=UCharacterIterator.DONE) {
1139 isBoundary=obj.isPrevBoundary(src, minC, mask, chars);
1140
1141 /* always write this character to the front of the buffer */
1142 /* make sure there is enough space in the buffer */
1143 if(startIndex[0] < (chars[1]==0 ? 1 : 2)) {
1144
1145 // grow the buffer
1146 char[] newBuf = new char[buffer.length*2];
1147 /* move the current buffer contents up */
1148 System.arraycopy(buffer,startIndex[0],newBuf,
1149 newBuf.length-(buffer.length-startIndex[0]),
1150 buffer.length-startIndex[0]);
1151 //adjust the startIndex
1152 startIndex[0]+=newBuf.length-buffer.length;
1153
1154 buffer=newBuf;
1155 newBuf=null;
1156
1157 }
1158
1159 buffer[--startIndex[0]]=chars[0];
1160 if(chars[1]!=0) {
1161 buffer[--startIndex[0]]=chars[1];
1162 }
1163
1164 /* stop if this just-copied character is a boundary */
1165 if(isBoundary) {
1166 break;
1167 }
1168 }
1169
1170 /* return the length of the buffer contents */
1171 return buffer.length-startIndex[0];
1172 }
1173
1174 private static int previous(UCharacterIterator src,
1175 char[] dest, int destStart, int destLimit,
1176 Mode mode,
1177 boolean doNormalize,
1178 boolean[] pNeededToNormalize,
1179 int options) {
1180
1181 IsPrevBoundary isPreviousBoundary;
1182 int destLength, bufferLength;
1183 int/*unsigned*/ mask;
1184 int c,c2;
1185
1186 char minC;
1187 int destCapacity = destLimit-destStart;
1188 destLength=0;
1189
1190 if(pNeededToNormalize!=null) {
1191 pNeededToNormalize[0]=false;
1192 }
1193 minC = (char)mode.getMinC();
1194 mask = mode.getMask();
1195 isPreviousBoundary = mode.getPrevBoundary();
1196
1197 if(isPreviousBoundary==null) {
1198 destLength=0;
1199 if((c=src.previous())>=0) {
1200 destLength=1;
1201 if(UTF16.isTrailSurrogate((char)c)) {
1202 c2= src.previous();
1203 if(c2!= UCharacterIterator.DONE) {
1204 if(UTF16.isLeadSurrogate((char)c2)) {
1205 if(destCapacity>=2) {
1206 dest[1]=(char)c; // trail surrogate
1207 destLength=2;
1208 }
1209 // lead surrogate to be written below
1210 c=c2;
1211 } else {
1212 src.moveIndex(1);
1213 }
1214 }
1215 }
1216
1217 if(destCapacity>0) {
1218 dest[0]=(char)c;
1219 }
1220 }
1221 return destLength;
1222 }
1223
1224 char[] buffer = new char[100];
1225 int[] startIndex= new int[1];
1226 bufferLength=findPreviousIterationBoundary(src,
1227 isPreviousBoundary,
1228 minC, mask,buffer,
1229 startIndex);
1230 if(bufferLength>0) {
1231 if(doNormalize) {
1232 destLength=NormalizerBase.normalize(buffer,startIndex[0],
1233 startIndex[0]+bufferLength,
1234 dest, destStart,destLimit,
1235 mode, options);
1236
1237 if(pNeededToNormalize!=null) {
1238 pNeededToNormalize[0]=(boolean)(destLength!=bufferLength ||
1239 Utility.arrayRegionMatches(
1240 buffer,0,dest,
1241 destStart,destLimit
1242 ));
1243 }
1244 } else {
1245 /* just copy the source characters */
1246 if(destCapacity>0) {
1247 System.arraycopy(buffer,startIndex[0],dest,0,
1248 (bufferLength<destCapacity) ?
1249 bufferLength : destCapacity
1250 );
1251 }
1252 }
1253 }
1254
1255
1256 return destLength;
1257 }
1258
1259
1260
1261 /* forward iteration ---------------------------------------------------- */
1262 /*
1263 * read forward and check if the character is a next-iteration boundary
1264 * if c2!=0 then (c, c2) is a surrogate pair
1265 */
1266 private interface IsNextBoundary{
1267 boolean isNextBoundary(UCharacterIterator src,
1268 int/*unsigned*/ minC,
1269 int/*unsigned*/ mask,
1270 int[] chars);
1271 }
1272 /*
1273 * read forward and get norm32
1274 * return 0 if the character is <minC
1275 * if c2!=0 then (c2, c) is a surrogate pair
1276 * always reads complete characters
1277 */
1278 private static long /*unsigned*/ getNextNorm32(UCharacterIterator src,
1279 int/*unsigned*/ minC,
1280 int/*unsigned*/ mask,
1281 int[] chars) {
1282 long norm32;
1283
1284 /* need src.hasNext() to be true */
1285 chars[0]=src.next();
1286 chars[1]=0;
1287
1288 if(chars[0]<minC) {
1289 return 0;
1290 }
1291
1292 norm32=NormalizerImpl.getNorm32((char)chars[0]);
1293 if(UTF16.isLeadSurrogate((char)chars[0])) {
1294 if(src.current()!=UCharacterIterator.DONE &&
1295 UTF16.isTrailSurrogate((char)(chars[1]=src.current()))) {
1296 src.moveIndex(1); /* skip the c2 surrogate */
1297 if((norm32&mask)==0) {
1298 /* irrelevant data */
1299 return 0;
1300 } else {
1301 /* norm32 must be a surrogate special */
1302 return NormalizerImpl.getNorm32FromSurrogatePair(norm32,(char)chars[1]);
1303 }
1304 } else {
1305 /* unmatched surrogate */
1306 return 0;
1307 }
1308 }
1309 return norm32;
1310 }
1311
1312
1313 /*
1314 * for NF*D:
1315 * read forward and check if the lead combining class is 0
1316 * if c2!=0 then (c, c2) is a surrogate pair
1317 */
1318 private static final class IsNextNFDSafe implements IsNextBoundary{
1319 public boolean isNextBoundary(UCharacterIterator src,
1320 int/*unsigned*/ minC,
1321 int/*unsigned*/ ccOrQCMask,
1322 int[] chars) {
1323 return NormalizerImpl.isNFDSafe(getNextNorm32(src,minC,ccOrQCMask,chars),
1324 ccOrQCMask, ccOrQCMask&NormalizerImpl.QC_MASK);
1325 }
1326 }
1327
1328 /*
1329 * for NF*C:
1330 * read forward and check if the character is (or its decomposition begins
1331 * with) a "true starter" (cc==0 and NF*C_YES)
1332 * if c2!=0 then (c, c2) is a surrogate pair
1333 */
1334 private static final class IsNextTrueStarter implements IsNextBoundary{
1335 public boolean isNextBoundary(UCharacterIterator src,
1336 int/*unsigned*/ minC,
1337 int/*unsigned*/ ccOrQCMask,
1338 int[] chars) {
1339 long norm32;
1340 int/*unsigned*/ decompQCMask;
1341
1342 decompQCMask=(ccOrQCMask<<2)&0xf; /*decomposition quick check mask*/
1343 norm32=getNextNorm32(src, minC, ccOrQCMask|decompQCMask, chars);
1344 return NormalizerImpl.isTrueStarter(norm32, ccOrQCMask, decompQCMask);
1345 }
1346 }
1347
1348 private static int findNextIterationBoundary(UCharacterIterator src,
1349 IsNextBoundary obj,
1350 int/*unsigned*/ minC,
1351 int/*unsigned*/ mask,
1352 char[] buffer) {
1353 if(src.current()==UCharacterIterator.DONE) {
1354 return 0;
1355 }
1356
1357 /* get one character and ignore its properties */
1358 int[] chars = new int[2];
1359 chars[0]=src.next();
1360 buffer[0]=(char)chars[0];
1361 int bufferIndex = 1;
1362
1363 if(UTF16.isLeadSurrogate((char)chars[0])&&
1364 src.current()!=UCharacterIterator.DONE) {
1365 if(UTF16.isTrailSurrogate((char)(chars[1]=src.next()))) {
1366 buffer[bufferIndex++]=(char)chars[1];
1367 } else {
1368 src.moveIndex(-1); /* back out the non-trail-surrogate */
1369 }
1370 }
1371
1372 /* get all following characters until we see a boundary */
1373 /* checking hasNext() instead of c!=DONE on the off-chance that U+ffff
1374 * is part of the string */
1375 while( src.current()!=UCharacterIterator.DONE) {
1376 if(obj.isNextBoundary(src, minC, mask, chars)) {
1377 /* back out the latest movement to stop at the boundary */
1378 src.moveIndex(chars[1]==0 ? -1 : -2);
1379 break;
1380 } else {
1381 if(bufferIndex+(chars[1]==0 ? 1 : 2)<=buffer.length) {
1382 buffer[bufferIndex++]=(char)chars[0];
1383 if(chars[1]!=0) {
1384 buffer[bufferIndex++]=(char)chars[1];
1385 }
1386 } else {
1387 char[] newBuf = new char[buffer.length*2];
1388 System.arraycopy(buffer,0,newBuf,0,bufferIndex);
1389 buffer = newBuf;
1390 buffer[bufferIndex++]=(char)chars[0];
1391 if(chars[1]!=0) {
1392 buffer[bufferIndex++]=(char)chars[1];
1393 }
1394 }
1395 }
1396 }
1397
1398 /* return the length of the buffer contents */
1399 return bufferIndex;
1400 }
1401
1402 private static int next(UCharacterIterator src,
1403 char[] dest, int destStart, int destLimit,
1404 NormalizerBase.Mode mode,
1405 boolean doNormalize,
1406 boolean[] pNeededToNormalize,
1407 int options) {
1408
1409 IsNextBoundary isNextBoundary;
1410 int /*unsigned*/ mask;
1411 int /*unsigned*/ bufferLength;
1412 int c,c2;
1413 char minC;
1414 int destCapacity = destLimit - destStart;
1415 int destLength = 0;
1416 if(pNeededToNormalize!=null) {
1417 pNeededToNormalize[0]=false;
1418 }
1419
1420 minC = (char)mode.getMinC();
1421 mask = mode.getMask();
1422 isNextBoundary = mode.getNextBoundary();
1423
1424 if(isNextBoundary==null) {
1425 destLength=0;
1426 c=src.next();
1427 if(c!=UCharacterIterator.DONE) {
1428 destLength=1;
1429 if(UTF16.isLeadSurrogate((char)c)) {
1430 c2= src.next();
1431 if(c2!= UCharacterIterator.DONE) {
1432 if(UTF16.isTrailSurrogate((char)c2)) {
1433 if(destCapacity>=2) {
1434 dest[1]=(char)c2; // trail surrogate
1435 destLength=2;
1436 }
1437 // lead surrogate to be written below
1438 } else {
1439 src.moveIndex(-1);
1440 }
1441 }
1442 }
1443
1444 if(destCapacity>0) {
1445 dest[0]=(char)c;
1446 }
1447 }
1448 return destLength;
1449 }
1450
1451 char[] buffer=new char[100];
1452 int[] startIndex = new int[1];
1453 bufferLength=findNextIterationBoundary(src,isNextBoundary, minC, mask,
1454 buffer);
1455 if(bufferLength>0) {
1456 if(doNormalize) {
1457 destLength=mode.normalize(buffer,startIndex[0],bufferLength,
1458 dest,destStart,destLimit, options);
1459
1460 if(pNeededToNormalize!=null) {
1461 pNeededToNormalize[0]=(boolean)(destLength!=bufferLength ||
1462 Utility.arrayRegionMatches(buffer,startIndex[0],
1463 dest,destStart,
1464 destLength));
1465 }
1466 } else {
1467 /* just copy the source characters */
1468 if(destCapacity>0) {
1469 System.arraycopy(buffer,0,dest,destStart,
1470 Math.min(bufferLength,destCapacity)
1471 );
1472 }
1473
1474
1475 }
1476 }
1477 return destLength;
1478 }
1479
1480 private void clearBuffer() {
1481 bufferLimit=bufferStart=bufferPos=0;
1482 }
1483
1484 private boolean nextNormalize() {
1485
1486 clearBuffer();
1487 currentIndex=nextIndex;
1488 text.setIndex(nextIndex);
1489
1490 bufferLimit=next(text,buffer,bufferStart,buffer.length,mode,true,null,options);
1491
1492 nextIndex=text.getIndex();
1493 return (bufferLimit>0);
1494 }
1495
1496 private boolean previousNormalize() {
1497
1498 clearBuffer();
1499 nextIndex=currentIndex;
1500 text.setIndex(currentIndex);
1501 bufferLimit=previous(text,buffer,bufferStart,buffer.length,mode,true,null,options);
1502
1503 currentIndex=text.getIndex();
1504 bufferPos = bufferLimit;
1505 return bufferLimit>0;
1506 }
1507
1508 private int getCodePointAt(int index) {
1509 if( UTF16.isSurrogate(buffer[index])) {
1510 if(UTF16.isLeadSurrogate(buffer[index])) {
1511 if((index+1)<bufferLimit &&
1512 UTF16.isTrailSurrogate(buffer[index+1])) {
1513 return UCharacterProperty.getRawSupplementary(
1514 buffer[index],
1515 buffer[index+1]
1516 );
1517 }
1518 }else if(UTF16.isTrailSurrogate(buffer[index])) {
1519 if(index>0 && UTF16.isLeadSurrogate(buffer[index-1])) {
1520 return UCharacterProperty.getRawSupplementary(
1521 buffer[index-1],
1522 buffer[index]
1523 );
1524 }
1525 }
1526 }
1527 return buffer[index];
1528
1529 }
1530
1531 /**
1532 * Internal API
1533 * @internal
1534 */
1535 public static boolean isNFSkippable(int c, Mode mode) {
1536 return mode.isNFSkippable(c);
1537 }
1538
1539 //
1540 // Options
1541 //
1542
1543 /*
1544 * Default option for Unicode 3.2.0 normalization.
1545 * Corrigendum 4 was fixed in Unicode 3.2.0 but isn't supported in
1546 * IDNA/StringPrep.
1547 * The public review issue #29 was fixed in Unicode 4.1.0. Corrigendum 5
1548 * allowed Unicode 3.2 to 4.0.1 to apply the fix for PRI #29, but it isn't
1549 * supported by IDNA/StringPrep as well as Corrigendum 4.
1550 */
1551 public static final int UNICODE_3_2_0_ORIGINAL =
1552 UNICODE_3_2 |
1553 NormalizerImpl.WITHOUT_CORRIGENDUM4_CORRECTIONS |
1554 NormalizerImpl.BEFORE_PRI_29;
1555
1556 /*
1557 * Default option for the latest Unicode normalization. This option is
1558 * provided mainly for testing.
1559 * The value zero means that normalization is done with the fixes for
1560 * - Corrigendum 4 (Five CJK Canonical Mapping Errors)
1561 * - Corrigendum 5 (Normalization Idempotency)
1562 */
1563 public static final int UNICODE_LATEST = 0x00;
1564
1565 //
1566 // public constructor and methods for java.text.Normalizer and
1567 // sun.text.Normalizer
1568 //
1569
1570 /**
1571 * Creates a new <tt>Normalizer</tt> object for iterating over the
1572 * normalized form of a given string.
1573 *
1574 * @param str The string to be normalized. The normalization
1575 * will start at the beginning of the string.
1576 *
1577 * @param mode The normalization mode.
1578 */
1579 public NormalizerBase(String str, Mode mode) {
1580 this(str, mode, UNICODE_LATEST);
1581 }
1582
1583 /**
1584 * Normalizes a <code>String</code> using the given normalization form.
1585 *
1586 * @param str the input string to be normalized.
1587 * @param form the normalization form
1588 */
1589 public static String normalize(String str, Normalizer.Form form) {
1590 return normalize(str, form, UNICODE_LATEST);
1591 }
1592
1593 /**
1594 * Normalizes a <code>String</code> using the given normalization form.
1595 *
1596 * @param str the input string to be normalized.
1597 * @param form the normalization form
1598 * @param options the optional features to be enabled.
1599 */
1600 public static String normalize(String str, Normalizer.Form form, int options) {
1601 int len = str.length();
1602 boolean asciiOnly = true;
1603 if (len < 80) {
1604 for (int i = 0; i < len; i++) {
1605 if (str.charAt(i) > 127) {
1606 asciiOnly = false;
1607 break;
1608 }
1609 }
1610 } else {
1611 char[] a = str.toCharArray();
1612 for (int i = 0; i < len; i++) {
1613 if (a[i] > 127) {
1614 asciiOnly = false;
1615 break;
1616 }
1617 }
1618 }
1619
1620 switch (form) {
1621 case NFC :
1622 return asciiOnly ? str : NFC.normalize(str, options);
1623 case NFD :
1624 return asciiOnly ? str : NFD.normalize(str, options);
1625 case NFKC :
1626 return asciiOnly ? str : NFKC.normalize(str, options);
1627 case NFKD :
1628 return asciiOnly ? str : NFKD.normalize(str, options);
1629 }
1630
1631 throw new IllegalArgumentException("Unexpected normalization form: " +
1632 form);
1633 }
1634
1635 /**
1636 * Test if a string is in a given normalization form.
1637 * This is semantically equivalent to source.equals(normalize(source, mode)).
1638 *
1639 * Unlike quickCheck(), this function returns a definitive result,
1640 * never a "maybe".
1641 * For NFD, NFKD, and FCD, both functions work exactly the same.
1642 * For NFC and NFKC where quickCheck may return "maybe", this function will
1643 * perform further tests to arrive at a true/false result.
1644 * @param str the input string to be checked to see if it is normalized
1645 * @param form the normalization form
1646 * @param options the optional features to be enabled.
1647 */
1648 public static boolean isNormalized(String str, Normalizer.Form form) {
1649 return isNormalized(str, form, UNICODE_LATEST);
1650 }
1651
1652 /**
1653 * Test if a string is in a given normalization form.
1654 * This is semantically equivalent to source.equals(normalize(source, mode)).
1655 *
1656 * Unlike quickCheck(), this function returns a definitive result,
1657 * never a "maybe".
1658 * For NFD, NFKD, and FCD, both functions work exactly the same.
1659 * For NFC and NFKC where quickCheck may return "maybe", this function will
1660 * perform further tests to arrive at a true/false result.
1661 * @param str the input string to be checked to see if it is normalized
1662 * @param form the normalization form
1663 * @param options the optional features to be enabled.
1664 */
1665 public static boolean isNormalized(String str, Normalizer.Form form, int options) {
1666 switch (form) {
1667 case NFC:
1668 return (NFC.quickCheck(str.toCharArray(),0,str.length(),false,NormalizerImpl.getNX(options))==YES);
1669 case NFD:
1670 return (NFD.quickCheck(str.toCharArray(),0,str.length(),false,NormalizerImpl.getNX(options))==YES);
1671 case NFKC:
1672 return (NFKC.quickCheck(str.toCharArray(),0,str.length(),false,NormalizerImpl.getNX(options))==YES);
1673 case NFKD:
1674 return (NFKD.quickCheck(str.toCharArray(),0,str.length(),false,NormalizerImpl.getNX(options))==YES);
1675 }
1676
1677 throw new IllegalArgumentException("Unexpected normalization form: " +
1678 form);
1679 }
1680}