/// <summary>
        /// Transforms the string into a series of characters that can be compared
        /// with CollationKey.compareTo. This overrides java.text.Collator.getCollationKey.
        /// It can be overriden in a subclass.
        /// </summary>
        public override CollationKey GetCollationKey(String source)
        {
            lock (this)
            {
                //
                // The basic algorithm here is to find all of the collation elements for each
                // character in the source string, convert them to a char representation,
                // and put them into the collation key.  But it's trickier than that.
                // Each collation element in a string has three components: primary (A vs B),
                // secondary (A vs A-acute), and tertiary (A' vs a); and a primary difference
                // at the end of a string takes precedence over a secondary or tertiary
                // difference earlier in the string.
                //
                // To account for this, we put all of the primary orders at the beginning of the
                // string, followed by the secondary and tertiary orders, separated by nulls.
                //
                // Here's a hypothetical example, with the collation element represented as
                // a three-digit number, one digit for primary, one for secondary, etc.
                //
                // String:              A     a     B   \u00e9 <--(e-acute)
                // Collation Elements: 101   100   201  510
                //
                // Collation Key:      1125<null>0001<null>1010
                //
                // To make things even trickier, secondary differences (accent marks) are compared
                // starting at the *end* of the string in languages with French secondary ordering.
                // But when comparing the accent marks on a single base character, they are compared
                // from the beginning.  To handle this, we reverse all of the accents that belong
                // to each base character, then we reverse the entire string of secondary orderings
                // at the end.  Taking the same example above, a French collator might return
                // this instead:
                //
                // Collation Key:      1125<null>1000<null>1010
                //
                if (source == null)
                {
                    return(null);
                }

                if (PrimResult == null)
                {
                    PrimResult = new StringBuffer();
                    SecResult  = new StringBuffer();
                    TerResult  = new StringBuffer();
                }
                else
                {
                    PrimResult.Length = 0;
                    SecResult.Length  = 0;
                    TerResult.Length  = 0;
                }
                int  order        = 0;
                bool compareSec   = (Strength >= Collator.SECONDARY);
                bool compareTer   = (Strength >= Collator.TERTIARY);
                int  secOrder     = CollationElementIterator.NULLORDER;
                int  terOrder     = CollationElementIterator.NULLORDER;
                int  preSecIgnore = 0;

                if (SourceCursor == null)
                {
                    SourceCursor = GetCollationElementIterator(source);
                }
                else
                {
                    SourceCursor.Text = source;
                }

                // walk through each character
                while ((order = SourceCursor.Next()) != CollationElementIterator.NULLORDER)
                {
                    secOrder = CollationElementIterator.SecondaryOrder(order);
                    terOrder = CollationElementIterator.TertiaryOrder(order);
                    if (!CollationElementIterator.IsIgnorable(order))
                    {
                        PrimResult.Append((char)(CollationElementIterator.PrimaryOrder(order) + COLLATIONKEYOFFSET));

                        if (compareSec)
                        {
                            //
                            // accumulate all of the ignorable/secondary characters attached
                            // to a given base character
                            //
                            if (Tables_Renamed.FrenchSec && preSecIgnore < SecResult.Length())
                            {
                                //
                                // We're doing reversed secondary ordering and we've hit a base
                                // (non-ignorable) character.  Reverse any secondary orderings
                                // that applied to the last base character.  (see block comment above.)
                                //
                                RBCollationTables.Reverse(SecResult, preSecIgnore, SecResult.Length());
                            }
                            // Remember where we are in the secondary orderings - this is how far
                            // back to go if we need to reverse them later.
                            SecResult.Append((char)(secOrder + COLLATIONKEYOFFSET));
                            preSecIgnore = SecResult.Length();
                        }
                        if (compareTer)
                        {
                            TerResult.Append((char)(terOrder + COLLATIONKEYOFFSET));
                        }
                    }
                    else
                    {
                        if (compareSec && secOrder != 0)
                        {
                            SecResult.Append((char)(secOrder + Tables_Renamed.MaxSecOrder + COLLATIONKEYOFFSET));
                        }
                        if (compareTer && terOrder != 0)
                        {
                            TerResult.Append((char)(terOrder + Tables_Renamed.MaxTerOrder + COLLATIONKEYOFFSET));
                        }
                    }
                }
                if (Tables_Renamed.FrenchSec)
                {
                    if (preSecIgnore < SecResult.Length())
                    {
                        // If we've accumulated any secondary characters after the last base character,
                        // reverse them.
                        RBCollationTables.Reverse(SecResult, preSecIgnore, SecResult.Length());
                    }
                    // And now reverse the entire secResult to get French secondary ordering.
                    RBCollationTables.Reverse(SecResult, 0, SecResult.Length());
                }
                PrimResult.Append((char)0);
                SecResult.Append((char)0);
                SecResult.Append(TerResult.ToString());
                PrimResult.Append(SecResult.ToString());

                if (Strength == IDENTICAL)
                {
                    PrimResult.Append((char)0);
                    int mode = Decomposition;
                    if (mode == CANONICAL_DECOMPOSITION)
                    {
                        PrimResult.Append(Normalizer.Normalize(source, Normalizer.Form.NFD));
                    }
                    else if (mode == FULL_DECOMPOSITION)
                    {
                        PrimResult.Append(Normalizer.Normalize(source, Normalizer.Form.NFKD));
                    }
                    else
                    {
                        PrimResult.Append(source);
                    }
                }
                return(new RuleBasedCollationKey(source, PrimResult.ToString()));
            }
        }