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+
37+package sun.text.normalizer;
38+
39+import java.text.CharacterIterator;
40+import 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+
144+public 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+}