home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / common / normlzr.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-19  |  36.7 KB  |  1,188 lines

  1. /*
  2. *******************************************************************************
  3. *                                                                             *
  4. * COPYRIGHT:                                                                  *
  5. *   IBM Open Class Library                                                    *
  6. *   (C) Copyright Taligent, Inc.,  1996                                       *
  7. *   (C) Copyright International Business Machines Corporation,  1996-1998     *
  8. *   Licensed Material - Program-Property of IBM - All Rights Reserved.        *
  9. *   US Government Users Restricted Rights - Use, duplication, or disclosure   *
  10. *   restricted by GSA ADP Schedule Contract with IBM Corp.                    *
  11. *                                                                             *
  12. *******************************************************************************
  13. */
  14.  
  15.  
  16. #include "ucmp16.h"
  17. #include "dcmpdata.h"
  18. #include "compdata.h"
  19.  
  20. #include "normlzr.h"
  21. #include "utypes.h"
  22. #include "unistr.h"
  23. #include "chariter.h"
  24. #include "schriter.h"
  25. #include "unicode.h"
  26. #include "mutex.h"
  27.  
  28.  
  29. #define ARRAY_LENGTH(array) (sizeof (array) / sizeof (*array))
  30.  
  31. inline static void insert(UnicodeString& dest, 
  32.               UTextOffset pos, 
  33.               UChar ch)
  34. {
  35.   dest.replace(pos, 0, &ch, 1);
  36. }
  37.  
  38. const UChar     Normalizer::DONE     = 0xFFFF;
  39. const UChar    Normalizer::HANGUL_BASE = 0xac00;
  40. const UChar    Normalizer::HANGUL_LIMIT= 0xd7a4;
  41. const UChar    Normalizer::JAMO_LBASE  = 0x1100;
  42. const UChar    Normalizer::JAMO_VBASE  = 0x1161;
  43. const UChar    Normalizer::JAMO_TBASE  = 0x11a7;
  44. const int16_t    Normalizer::JAMO_LCOUNT = 19;
  45. const int16_t    Normalizer::JAMO_VCOUNT = 21;
  46. const int16_t    Normalizer::JAMO_TCOUNT = 28;
  47. const int16_t    Normalizer::JAMO_NCOUNT = JAMO_VCOUNT * JAMO_TCOUNT;
  48.  
  49.  
  50.  
  51. //-------------------------------------------------------------------------
  52. // Constructors and other boilerplate
  53. //-------------------------------------------------------------------------
  54.  
  55. Normalizer::Normalizer(const UnicodeString& str, 
  56.                EMode mode)
  57. {
  58.   init(new StringCharacterIterator(str), mode, 0);
  59. }
  60.  
  61. Normalizer::Normalizer(const UnicodeString& str, 
  62.                EMode mode, 
  63.                int32_t opt)
  64. {
  65.   init(new StringCharacterIterator(str), mode, opt);
  66. }
  67.  
  68. Normalizer::Normalizer(const CharacterIterator& iter, 
  69.                EMode mode)
  70. {
  71.   init(iter.clone(), mode, 0);
  72. }
  73.  
  74. Normalizer::Normalizer(const CharacterIterator& iter, 
  75.                EMode mode, 
  76.                int32_t opt)
  77. {
  78.   init(iter.clone(), mode, opt);
  79. }
  80.  
  81. void Normalizer::init(CharacterIterator* adoptIter, 
  82.               EMode mode, 
  83.               int32_t options)
  84. {
  85.   bufferPos = 0;
  86.   bufferLimit = 0;
  87.   fOptions = options;
  88.   currentChar = DONE;
  89.   fMode = mode;
  90.   text = adoptIter;
  91.   
  92.   minDecomp = (fMode & COMPAT_BIT) ? 0 : DecompData::MAX_COMPAT;
  93. }
  94.  
  95. Normalizer::Normalizer(const Normalizer& copy)
  96. {
  97.   init(copy.text->clone(), copy.fMode, copy.fOptions);
  98.   
  99.   buffer      = copy.buffer;
  100.   bufferPos   = copy.bufferPos;
  101.   bufferLimit = copy.bufferLimit;
  102.   explodeBuf  = copy.explodeBuf;
  103.   currentChar = copy.currentChar;
  104. }
  105.  
  106. Normalizer::~Normalizer()
  107. {
  108.   delete text;
  109. }
  110.  
  111. Normalizer*    
  112. Normalizer::clone() const
  113. {
  114.   return new Normalizer(*this);
  115. }
  116.  
  117. /**
  118.  * Generates a hash code for this iterator.
  119.  */
  120. int32_t Normalizer::hashCode() const
  121. {
  122.   return text->hashCode() + fMode + fOptions + bufferPos + bufferLimit;
  123. }
  124.     
  125. bool_t Normalizer::operator==(const Normalizer& that) const
  126. {
  127.   return *text == *(that.text)
  128.   && currentChar == that.currentChar
  129.   && buffer == that.buffer
  130.   && explodeBuf == that.explodeBuf
  131.   && bufferPos == that.bufferPos
  132.   && bufferLimit == that.bufferLimit;
  133. }
  134.  
  135. //-------------------------------------------------------------------------
  136. // Static utility methods
  137. //-------------------------------------------------------------------------
  138.  
  139. void 
  140. Normalizer::normalize(const UnicodeString& source, 
  141.               EMode mode, 
  142.               int32_t options,
  143.               UnicodeString& result, 
  144.               UErrorCode &status)
  145. {
  146.   switch (mode) {
  147.   case NO_OP:
  148.     result = source;
  149.     break;
  150.   case COMPOSE:
  151.   case COMPOSE_COMPAT:
  152.     compose(source, mode & COMPAT_BIT, options, result, status);
  153.     break;
  154.   case DECOMP:
  155.   case DECOMP_COMPAT:
  156.     decompose(source, mode & COMPAT_BIT, options, result, status);
  157.     break;
  158.   }
  159. }
  160.  
  161. //-------------------------------------------------------------------------
  162. // Compose methods
  163. //-------------------------------------------------------------------------
  164.  
  165. void
  166. Normalizer::compose(const UnicodeString& source, 
  167.             bool_t compat,
  168.             int32_t options,
  169.             UnicodeString& result, 
  170.             UErrorCode &status)
  171. {
  172.   if (U_FAILURE(status)) {
  173.     return;
  174.   }
  175.   result.truncate(0);
  176.   UnicodeString explodeBuf;
  177.   
  178.   UTextOffset  explodePos = EMPTY;         // Position in input buffer
  179.   UTextOffset  basePos = 0;                // Position of last base in output string
  180.   uint16_t    baseIndex = 0;              // Index of last base in "actions" array
  181.   uint32_t    classesSeen = 0;            // Combining classes seen since last base
  182.   uint16_t    action;
  183.     
  184.   // Compatibility explosions have lower indices; skip them if necessary
  185.   uint16_t minExplode = compat ? 0 : ComposeData::MAX_COMPAT;
  186.   uint16_t minDecomp = compat ? 0 : DecompData::MAX_COMPAT;
  187.   
  188.     UTextOffset i = 0;
  189.     while (i < source.size() || explodePos != EMPTY) {
  190.         // Get the next char from either the buffer or the source
  191.       UChar ch;
  192.       if (explodePos == EMPTY) {
  193.     ch = source[i++];
  194.       } else {
  195.     ch = explodeBuf[explodePos++];
  196.     if (explodePos >= explodeBuf.size()) {
  197.       explodePos = EMPTY;
  198.       explodeBuf.truncate(0);
  199.     }
  200.       }
  201.       
  202.       // Get the basic info for the character
  203.       uint16_t charInfo = composeLookup(ch);
  204.       uint16_t type = charInfo & ComposeData::TYPE_MASK;
  205.       uint16_t index = charInfo >> ComposeData::INDEX_SHIFT;
  206.       
  207.       if (type == ComposeData::BASE) {
  208.     classesSeen = 0;
  209.     baseIndex = index;
  210.     basePos = result.size();
  211.     result += ch;
  212.       }
  213.       else if (type == ComposeData::COMBINING || type == ComposeData::NON_COMPOSING_COMBINING)
  214.         {
  215.       uint32_t cclass = ComposeData::typeMask[index];
  216.       
  217.       // We can only combine a character with the base if we haven't
  218.       // already seen a combining character with the same canonical class.
  219.       if (type == ComposeData::COMBINING && (classesSeen & cclass) == 0
  220.           && (action = composeAction(baseIndex, index)) > 0)
  221.             {
  222.           if (action > ComposeData::MAX_COMPOSED) {
  223.         // Pairwise explosion.  Actions above this value are really
  224.         // indices into an array that in turn contains indices
  225.         // into the exploding string table
  226.         // TODO: What if there are unprocessed chars in the explode buffer?
  227.         UChar newBase = pairExplode(explodeBuf, action);
  228.         explodePos = 0;
  229.         result[basePos] = newBase;
  230.         
  231.         baseIndex = composeLookup(newBase) >> ComposeData::INDEX_SHIFT;
  232.           } else {
  233.         // Normal pairwise combination.  Replace the base char
  234.         UChar newBase = (UChar) action;
  235.         result[basePos] = newBase;
  236.         
  237.         baseIndex = composeLookup(newBase) >> ComposeData::INDEX_SHIFT;
  238.           }
  239.           //
  240.           // Since there are Unicode characters that cannot be combined in arbitrary
  241.           // order, we have to re-process any combining marks that go with this
  242.           // base character.  There are only four characters in Unicode that have
  243.           // this problem.  If they are fixed in Unicode 3.0, this code can go away.
  244.           //
  245.           UTextOffset len = result.size();
  246.           if (len - basePos > 1) {
  247.         for (UTextOffset j = basePos+1; j < len; j++) {
  248.           explodeBuf += result[j];
  249.         }
  250.         result.truncate(basePos+1);
  251.         classesSeen = 0;
  252.         if (explodePos == EMPTY) explodePos = 0;
  253.           }
  254.             } else {
  255.           // No combination with this character
  256.           bubbleAppend(result, ch, cclass);
  257.           classesSeen |= cclass;
  258.             }
  259.         }
  260.       else if (index > minExplode) {
  261.     // Single exploding character
  262.     explode(explodeBuf, index);
  263.     explodePos = 0;
  264.       }
  265.       else if (type == ComposeData::HANGUL && minExplode == 0) {
  266.     // If we're in compatibility mode we need to decompose Hangul to Jamo,
  267.     // because some of the Jamo might have compatibility decompositions.
  268.     hangulToJamo(ch, explodeBuf, minDecomp);
  269.     explodePos = 0;
  270.       }
  271.       else if (type == ComposeData::INITIAL_JAMO) {
  272.     classesSeen = 0;
  273.     baseIndex = ComposeData::INITIAL_JAMO_INDEX;
  274.     basePos = result.size();
  275.     result += ch;
  276.       }
  277.       else if (type == ComposeData::MEDIAL_JAMO && classesSeen == 0
  278.            && baseIndex == ComposeData::INITIAL_JAMO_INDEX) {
  279.     // If the last character was an initial jamo, we can combine it with this
  280.     // one to create a Hangul character.
  281.     uint16_t l = result[basePos] - JAMO_LBASE;
  282.     uint16_t v = ch - JAMO_VBASE;
  283.     result[basePos] = (UChar)(HANGUL_BASE + (l*JAMO_VCOUNT + v) * JAMO_TCOUNT);
  284.     
  285.     baseIndex = ComposeData::MEDIAL_JAMO_INDEX;
  286.       }
  287.       else if (type == ComposeData::FINAL_JAMO && classesSeen == 0
  288.            && baseIndex == ComposeData::MEDIAL_JAMO_INDEX) {
  289.     // If the last character was a medial jamo that we turned into Hangul,
  290.     // we can add this character too.
  291.     result[basePos] = (UChar)(result[basePos] + (ch - JAMO_TBASE));
  292.     
  293.     baseIndex = 0;
  294.     basePos = -1;
  295.     classesSeen = 0;
  296.       } else {
  297.     baseIndex = 0;
  298.     basePos = -1;
  299.     classesSeen = 0;
  300.     result += ch;
  301.       }
  302.     }
  303. }
  304.  
  305. /**
  306.  * Compose starting with current input character and continuing
  307.  * until just before the next base char.
  308.  * <p>
  309.  * <b>Input</b>:
  310.  * <ul>
  311.  *  <li>underlying char iter points to first character to decompose
  312.  * </ul>
  313.  * <p>
  314.  * <b>Output:</b>
  315.  * <ul>
  316.  *  <li>returns first char of decomposition or DONE if at end
  317.  *  <li>Underlying char iter is pointing at next base char or past end
  318.  * </ul>
  319.  */
  320. UChar Normalizer::nextCompose() 
  321. {
  322.     UTextOffset  explodePos = EMPTY;         // Position in input buffer
  323.     UTextOffset  basePos = 0;                // Position of last base in output string
  324.     uint16_t    baseIndex = 0;              // Index of last base in "actions" array
  325.     uint32_t    classesSeen = 0;            // Combining classes seen since last base
  326.     uint16_t    action;
  327.     UChar        lastBase = 0;
  328.     bool_t        chFromText = TRUE;
  329.     
  330.     // Compatibility explosions have lower indices; skip them if necessary
  331.     uint16_t minExplode = (fMode & COMPAT_BIT) ? 0 : ComposeData::MAX_COMPAT;
  332.     uint16_t minDecomp = (fMode & COMPAT_BIT) ? 0 : DecompData::MAX_COMPAT;
  333.     
  334.     initBuffer();
  335.     explodeBuf.truncate(0);
  336.     
  337.     UChar ch = curForward();
  338.  
  339.     while (ch != DONE) {
  340.         // Get the basic info for the character
  341.         uint16_t charInfo = composeLookup(ch);
  342.         uint16_t type = charInfo & ComposeData::TYPE_MASK;
  343.         uint16_t index = charInfo >> ComposeData::INDEX_SHIFT;
  344.         
  345.         if (type == ComposeData::BASE) {
  346.             if (buffer.size() > 0 && chFromText && explodePos == EMPTY) {
  347.                 // When we hit a base char in the source text, we can return the text
  348.                 // that's been composed so far.  We'll re-process this char next time through.
  349.                 break;
  350.             }
  351.             classesSeen = 0;
  352.             baseIndex = index;
  353.             basePos = buffer.size();
  354.             buffer += ch;
  355.             lastBase = ch;
  356.         }
  357.         else if (type == ComposeData::COMBINING || type == ComposeData::NON_COMPOSING_COMBINING)
  358.         {
  359.             uint32_t cclass = ComposeData::typeMask[index];
  360.             
  361.             // We can only combine a character with the base if we haven't
  362.             // already seen a combining character with the same canonical class.
  363.             if (type == ComposeData::COMBINING && (classesSeen & cclass) == 0
  364.                 && (action = composeAction(baseIndex, index)) > 0)
  365.             {
  366.                 if (action > ComposeData::MAX_COMPOSED) {
  367.                     // Pairwise explosion.  Actions above this value are really
  368.                     // indices into an array that in turn contains indices
  369.                     // into the exploding string table
  370.                     // TODO: What if there are unprocessed chars in the explode buffer?
  371.                     UChar newBase = pairExplode(explodeBuf, action);
  372.                     explodePos = 0;
  373.                     buffer[basePos] = newBase;
  374.  
  375.                     baseIndex = composeLookup(newBase) >> ComposeData::INDEX_SHIFT;
  376.                     lastBase = newBase;
  377.                 } else {
  378.                     // Normal pairwise combination.  Replace the base char
  379.                     UChar newBase = (UChar) action;
  380.                     buffer[basePos] = newBase;
  381.                                             
  382.                     baseIndex = composeLookup(newBase) >> ComposeData::INDEX_SHIFT;
  383.                     lastBase = newBase;
  384.                 }
  385.                 //
  386.                 // Since there are Unicode characters that cannot be combined in arbitrary
  387.                 // order, we have to re-process any combining marks that go with this
  388.                 // base character.  There are only four characters in Unicode that have
  389.                 // this problem.  If they are fixed in Unicode 3.0, this code can go away.
  390.                 //
  391.                 UTextOffset len = buffer.size();
  392.                 if (len - basePos > 1) {
  393.                     for (UTextOffset j = basePos+1; j < len; j++) {
  394.                         explodeBuf += buffer[j];
  395.                     }
  396.                     buffer.truncate(basePos+1);
  397.                     classesSeen = 0;
  398.                     if (explodePos == EMPTY) explodePos = 0;
  399.                 }
  400.             } else {
  401.                 // No combination with this character
  402.                 bubbleAppend(buffer, ch, cclass);
  403.                 classesSeen |= cclass;
  404.             }
  405.         }
  406.         else if (index > minExplode) {
  407.             // Single exploding character
  408.             explode(explodeBuf, index);
  409.             explodePos = 0;
  410.         }
  411.         else if (type == ComposeData::HANGUL && minExplode == 0) {
  412.             // If we're in compatibility mode we need to decompose Hangul to Jamo,
  413.             // because some of the Jamo might have compatibility decompositions.
  414.             hangulToJamo(ch, explodeBuf, minDecomp);
  415.             explodePos = 0;
  416.         }
  417.         else if (type == ComposeData::INITIAL_JAMO) {
  418.             if (buffer.size() > 0 && chFromText && explodePos == EMPTY) {
  419.                 // When we hit a base char in the source text, we can return the text
  420.                 // that's been composed so far.  We'll re-process this char next time through.
  421.                 break;
  422.             }
  423.             classesSeen = 0;
  424.             baseIndex = ComposeData::INITIAL_JAMO_INDEX;
  425.             basePos = buffer.size();
  426.             buffer += ch;
  427.         }
  428.         else if (type == ComposeData::MEDIAL_JAMO && classesSeen == 0
  429.                     && baseIndex == ComposeData::INITIAL_JAMO_INDEX) {
  430.             // If the last character was an initial jamo, we can combine it with this
  431.             // one to create a Hangul character.
  432.             uint16_t l = buffer[basePos] - JAMO_LBASE;
  433.             uint16_t v = ch - JAMO_VBASE;
  434.             UChar newCh = (UChar)(HANGUL_BASE + (l*JAMO_VCOUNT + v) * JAMO_TCOUNT);
  435.             buffer[basePos] = newCh;
  436.             
  437.             baseIndex = ComposeData::MEDIAL_JAMO_INDEX;
  438.         }
  439.         else if (type == ComposeData::FINAL_JAMO && classesSeen == 0
  440.                     && baseIndex == ComposeData::MEDIAL_JAMO_INDEX) {
  441.             // If the last character was a medial jamo that we turned into Hangul,
  442.             // we can add this character too.
  443.             UChar newCh = (UChar)(buffer[basePos] + (ch - JAMO_TBASE));
  444.             buffer[basePos] = newCh;
  445.  
  446.             baseIndex = 0;
  447.             basePos = -1;
  448.             classesSeen = 0;
  449.         } else {
  450.             // TODO: deal with JAMO character types
  451.             baseIndex = 0;
  452.             basePos = -1;
  453.             classesSeen = 0;
  454.             buffer += ch;
  455.         }
  456.         
  457.         if (explodePos == EMPTY) {
  458.             ch = text->next();
  459.             chFromText = TRUE;
  460.         } else {
  461.             ch = explodeBuf[explodePos++];
  462.             if (explodePos >= explodeBuf.size()) {
  463.                 explodePos = EMPTY;
  464.                 explodeBuf.truncate(0);
  465.             }
  466.             chFromText = FALSE;
  467.         }
  468.     }
  469.     if (buffer.size() > 0) {
  470.         bufferLimit = buffer.size() - 1;
  471.         ch = buffer[0];
  472.     } else {
  473.         ch = DONE;
  474.         bufferLimit = 0;
  475.     }
  476.     return ch;
  477. }
  478.  
  479. /**
  480.  * Compose starting with the input UChar just before the current position
  481.  * and continuing backward until (and including) the previous base char.
  482.  * <p>
  483.  * <b>Input</b>:
  484.  * <ul>
  485.  *  <li>underlying char iter points just after last char to decompose
  486.  * </ul>
  487.  * <p>
  488.  * <b>Output:</b>
  489.  * <ul>
  490.  *  <li>returns last char of resulting decomposition sequence
  491.  *  <li>underlying iter points to lowest-index char we decomposed, i.e. the base char
  492.  * </ul>
  493.  */
  494. UChar Normalizer::prevCompose()
  495. {
  496.     UErrorCode status = U_ZERO_ERROR;
  497.     initBuffer();
  498.     
  499.     // Slurp up characters until we hit a base char or an initial Jamo
  500.     UChar ch;
  501.     while ((ch = curBackward()) != DONE) {
  502.         insert(buffer, 0, ch);
  503.         
  504.         // Get the basic info for the character
  505.         uint16_t charInfo = composeLookup(ch);
  506.         uint16_t type = charInfo & ComposeData::TYPE_MASK;
  507.         
  508.         if (type == ComposeData::BASE || type == ComposeData::HANGUL 
  509.             || type == ComposeData::INITIAL_JAMO || type == ComposeData::IGNORE)
  510.         {
  511.             break;
  512.         }
  513.     }
  514.     // If there's more than one character in the buffer, compose it all at once....
  515.     if (buffer.size() > 0) {
  516.         // TODO: The performance of this is awful; add a way to compose
  517.         // a UnicodeString& in place.
  518.       UnicodeString composed;
  519.       compose(buffer, (fMode & COMPAT_BIT), fOptions, composed, status);
  520.       buffer.truncate(0);
  521.       buffer += composed;
  522.         
  523.         if (buffer.size() > 1) {
  524.             bufferLimit = bufferPos = buffer.size() - 1;
  525.             ch = buffer[bufferPos];
  526.         } else {
  527.             ch = buffer[0];
  528.         }
  529.     }
  530.     else {
  531.         ch = DONE;
  532.     }
  533.     
  534.     return ch;
  535. }
  536.  
  537. void Normalizer::bubbleAppend(UnicodeString& target, UChar ch, uint32_t cclass) {
  538.     UTextOffset i;
  539.     for (i = target.size() - 1; i > 0; --i) {
  540.         uint32_t iClass = getComposeClass(target[i]);
  541.  
  542.         if (iClass == 1 || iClass <= cclass) {      // 1 means combining class 0
  543.             // We've hit something we can't bubble this character past, so insert here
  544.             break;
  545.         }
  546.     }
  547.     // We need to insert just after character "i"
  548.     insert(target, i+1, ch);
  549. }
  550.     
  551.  
  552. uint32_t Normalizer::getComposeClass(UChar ch) {
  553.     uint32_t cclass = 0;
  554.     uint16_t charInfo = composeLookup(ch);
  555.     uint16_t type = charInfo & ComposeData::TYPE_MASK;
  556.     if (type == ComposeData::COMBINING || type == ComposeData::NON_COMPOSING_COMBINING) {
  557.         cclass = ComposeData::typeMask[charInfo >> ComposeData::INDEX_SHIFT];
  558.     }
  559.     return cclass;
  560. }
  561.  
  562. uint16_t Normalizer::composeLookup(UChar ch) {
  563.   return ucmp16_getu(ComposeData::lookup, ch);
  564. }
  565.  
  566. uint16_t Normalizer::composeAction(uint16_t baseIndex, uint16_t comIndex) 
  567. {
  568.   return ucmp16_getu(ComposeData::actions,
  569.              ((UChar)(baseIndex + ComposeData::MAX_BASES*comIndex)));
  570. }
  571.  
  572. void Normalizer::explode(UnicodeString& target, uint16_t index) {
  573.     UChar ch;
  574.     while ((ch = ComposeData::replace[index++]) != 0)
  575.     target += ch;
  576. }
  577.  
  578. UChar Normalizer::pairExplode(UnicodeString& target, uint16_t action) {
  579.     uint16_t index = ComposeData::actionIndex[action - ComposeData::MAX_COMPOSED];
  580.     explode(target, index + 1);
  581.     return ComposeData::replace[index];   // New base char
  582. }
  583.  
  584. //-------------------------------------------------------------------------
  585. // Decompose methods
  586. //-------------------------------------------------------------------------
  587.  
  588. void
  589. Normalizer::decompose(const UnicodeString& source, 
  590.               bool_t compat,
  591.               int32_t options,
  592.               UnicodeString& result, 
  593.               UErrorCode &status)
  594. {
  595.   if (U_FAILURE(status)) {
  596.     return;
  597.   }
  598.   bool_t     hangul = (options & IGNORE_HANGUL) == 0;
  599.   uint16_t     limit  = compat ? 0 : DecompData::MAX_COMPAT;
  600.   
  601.   result.truncate(0);
  602.   
  603.   for (UTextOffset i = 0; i < source.size(); ++i) {
  604.     UChar ch = source[i];
  605.     
  606.     uint16_t offset = ucmp16_getu(DecompData::offsets, ch);
  607.     
  608.     
  609.     if (offset > limit) {
  610.       doAppend(DecompData::contents, offset, result);
  611.     } else if (ch >= HANGUL_BASE && ch < HANGUL_LIMIT && hangul) {
  612.       hangulToJamo(ch, result, limit);
  613.     } else {
  614.       result += ch;
  615.     }
  616.   }
  617.   fixCanonical(result);
  618. }
  619.  
  620. /**
  621.  * Decompose starting with current input character and continuing
  622.  * until just before the next base char.
  623.  * <p>
  624.  * <b>Input</b>:
  625.  * <ul>
  626.  *  <li>underlying char iter points to first character to decompose
  627.  * </ul>
  628.  * <p>
  629.  * <b>Output:</b>
  630.  * <ul>
  631.  *  <li>returns first char of decomposition or DONE if at end
  632.  *  <li>Underlying char iter is pointing at next base char or past end
  633.  * </ul>
  634.  */
  635. UChar Normalizer::nextDecomp()
  636. {
  637.   bool_t hangul = ((fOptions & IGNORE_HANGUL) == 0);
  638.   UChar ch = curForward();
  639.   
  640.   uint16_t offset = ucmp16_getu(DecompData::offsets, ch);
  641.   
  642.   if (offset > minDecomp || ucmp8_get(DecompData::canonClass, ch) != DecompData::BASE)
  643.     {
  644.       initBuffer();
  645.       
  646.       if (offset > minDecomp) {
  647.     doAppend(DecompData::contents, offset, buffer);
  648.       } else {
  649.     buffer += ch;
  650.       }
  651.       bool_t needToReorder = FALSE;
  652.       
  653.       // Any other combining chacters that immediately follow the decomposed
  654.       // character must be included in the buffer too, because they're
  655.       // conceptually part of the same logical character.
  656.       //
  657.       // TODO: Might these need to be decomposed too?
  658.       // (i.e. are there non-BASE characters with decompositions?
  659.       //
  660.       while ((ch = text->next()) != DONE
  661.          && ucmp8_get(DecompData::canonClass, ch) != DecompData::BASE)
  662.         {
  663.       needToReorder = TRUE;
  664.       buffer += ch;
  665.         }
  666.       
  667.       if (buffer.size() > 1 && needToReorder) {
  668.     // If there is more than one combining character in the buffer,
  669.     // put them into the canonical order.
  670.     // But we don't need to sort if only characters are the ones that
  671.     // resulted from decomosing the base character.
  672.     fixCanonical(buffer);
  673.       }
  674.       bufferLimit = buffer.size() - 1;
  675.       ch = buffer[0];
  676.     } else {
  677.       // Just use this character, but first advance to the next one
  678.       text->next();
  679.       
  680.       // Do Hangul -> Jamo decomposition if necessary
  681.       if (hangul && ch >= HANGUL_BASE && ch < HANGUL_LIMIT) {
  682.     initBuffer();
  683.     hangulToJamo(ch, buffer, minDecomp);
  684.     bufferLimit = buffer.size() - 1;
  685.     ch = buffer[0];
  686.       }
  687.     }
  688.   return ch;
  689. }
  690.  
  691.  
  692. /**
  693.  * Decompose starting with the input char just before the current position
  694.  * and continuing backward until (and including) the previous base char.
  695.  * <p>
  696.  * <b>Input</b>:
  697.  * <ul>
  698.  *  <li>underlying char iter points just after last char to decompose
  699.  * </ul>
  700.  * <p>
  701.  * <b>Output:</b>
  702.  * <ul>
  703.  *  <li>returns last char of resulting decomposition sequence
  704.  *  <li>underlying iter points to lowest-index char we decomposed, i.e. the base char
  705.  * </ul>
  706.  */
  707. UChar Normalizer::prevDecomp() {
  708.     bool_t hangul = (fOptions & IGNORE_HANGUL) == 0;
  709.  
  710.     UChar ch = curBackward();
  711.  
  712.     uint16_t offset = ucmp16_getu(DecompData::offsets, ch);
  713.  
  714.     if (offset > minDecomp || ucmp8_get(DecompData::canonClass, ch) != DecompData::BASE)
  715.     {
  716.         initBuffer();
  717.  
  718.         // Slurp up any combining characters till we get to a base char.
  719.         while (ch != DONE && ucmp8_get(DecompData::canonClass, ch) != DecompData::BASE) {
  720.             insert(buffer, 0, ch);
  721.             ch = text->previous();
  722.         }
  723.  
  724.         // Now decompose this base character
  725.         offset = ucmp16_getu(DecompData::offsets, ch);
  726.         if (offset > minDecomp) {
  727.             doInsert(DecompData::contents, offset, buffer, 0);
  728.         } else {
  729.             // This is a base character that doesn't decompose
  730.             // and isn't involved in reordering, so throw it back
  731.             text->next();
  732.         }
  733.  
  734.         if (buffer.size() > 1) {
  735.             // If there is more than one combining character in the buffer,
  736.             // put them into the canonical order.
  737.             fixCanonical(buffer);
  738.         }
  739.         bufferLimit = bufferPos = buffer.size() - 1;
  740.         ch = buffer[bufferPos];
  741.     }
  742.     else if (hangul && ch >= HANGUL_BASE && ch < HANGUL_LIMIT) {
  743.         initBuffer();
  744.         hangulToJamo(ch, buffer, minDecomp);
  745.         bufferLimit = bufferPos = buffer.size() - 1;
  746.         ch = buffer[bufferPos];
  747.     }
  748.     return ch;
  749. }
  750.  
  751. uint8_t Normalizer::getClass(UChar ch) {
  752.     return  ucmp8_get(DecompData::canonClass, ch);
  753. }
  754.  
  755. /**
  756.  * Fixes the sorting sequence of non-spacing characters according to
  757.  * their combining class.  The algorithm is listed on p.3-11 in the
  758.  * Unicode Standard 2.0.  The table of combining classes is on p.4-2
  759.  * in the Unicode Standard 2.0.
  760.  * @param result the string to fix.
  761.  */
  762. void Normalizer::fixCanonical(UnicodeString& result) {
  763.     UTextOffset i = result.size() - 1;
  764.     uint8_t currentType = getClass(result[i]);
  765.     uint8_t lastType;
  766.     
  767.     for (--i; i >= 0; --i) {
  768.         lastType = currentType;
  769.         currentType = getClass(result[i]);
  770.         
  771.         //
  772.         // a swap is presumed to be rare (and a double-swap very rare),
  773.         // so don't worry about efficiency here.
  774.         //
  775.         if (currentType > lastType && lastType != DecompData::BASE) {
  776.             // swap characters
  777.             UChar temp = result[i];
  778.             result[i] = result[i+1];
  779.             result[i+1] = temp;
  780.  
  781.             // if not at end, backup (one further, to compensate for for-loop)
  782.             if (i < result.size() - 2) {
  783.                 i += 2;
  784.             }
  785.             // reset type, since we swapped.
  786.             currentType = getClass(result[i]);
  787.         }
  788.     }
  789. }
  790.  
  791.     
  792. //-------------------------------------------------------------------------
  793. // CharacterIterator overrides
  794. //-------------------------------------------------------------------------
  795.  
  796. /**
  797.  * Return the current character in the normalized text.
  798.  */
  799. UChar Normalizer:: current() const
  800. {
  801.   // TODO: make this method const and guarantee that currentChar is always set?
  802.   Normalizer *nonConst = (Normalizer*)this;
  803.   
  804.   if (currentChar == DONE) {
  805.     switch (fMode) {
  806.     case NO_OP:
  807.       nonConst->currentChar = text->current();            
  808.       break;
  809.     case COMPOSE:
  810.     case COMPOSE_COMPAT:
  811.       nonConst->currentChar = nonConst->nextCompose();    
  812.       break;
  813.     case DECOMP:    
  814.     case DECOMP_COMPAT:
  815.       nonConst->currentChar = nonConst->nextDecomp();        
  816.       break;
  817.     }
  818.   }
  819.   return currentChar;
  820. }
  821.  
  822. /**
  823.  * Return the first character in the normalized text.  This resets
  824.  * the <tt>Normalizer's</tt> position to the beginning of the text.
  825.  */
  826. UChar Normalizer::first() {
  827.     return setIndex(text->startIndex());
  828. }
  829.  
  830. /**
  831.  * Return the last character in the normalized text.  This resets
  832.  * the <tt>Normalizer's</tt> position to be just before the
  833.  * the input text corresponding to that normalized character.
  834.  */
  835. UChar Normalizer::last() {
  836.   text->setIndex(text->endIndex());
  837.   
  838.   currentChar = DONE;                     // The current char hasn't been processed
  839.   clearBuffer();                          // The buffer is empty too
  840.   return previous();
  841. }
  842.  
  843. /**
  844.  * Return the next character in the normalized text and advance
  845.  * the iteration position by one.  If the end
  846.  * of the text has already been reached, {@link #DONE} is returned.
  847.  */
  848. UChar Normalizer::next() {
  849.   if (bufferPos < bufferLimit) {
  850.     // There are output characters left in the buffer
  851.     currentChar = buffer[++bufferPos];
  852.   }
  853.   else {
  854.     bufferLimit = bufferPos = 0;    // Buffer is now out of date
  855.     switch (fMode) {
  856.     case NO_OP:
  857.       currentChar = text->next();        
  858.       break;
  859.     case COMPOSE:        
  860.     case COMPOSE_COMPAT:
  861.       currentChar = nextCompose();    
  862.       break;
  863.     case DECOMP:    
  864.     case DECOMP_COMPAT:
  865.       currentChar = nextDecomp();        
  866.       break;
  867.     }
  868.   }
  869.   return currentChar;
  870. }
  871.  
  872. /**
  873.  * Return the previous character in the normalized text and decrement
  874.  * the iteration position by one.  If the beginning
  875.  * of the text has already been reached, {@link #DONE} is returned.
  876.  */
  877. UChar Normalizer::previous()
  878. {
  879.   if (bufferPos > 0) {
  880.     // There are output characters left in the buffer
  881.     currentChar = buffer[--bufferPos];
  882.   }
  883.   else {
  884.     bufferLimit = bufferPos = 0;    // Buffer is now out of date
  885.     switch (fMode) {
  886.     case NO_OP:        
  887.       currentChar = text->previous();    
  888.       break;
  889.     case COMPOSE:        
  890.     case COMPOSE_COMPAT:
  891.       currentChar = prevCompose();    
  892.       break;
  893.     case DECOMP:    
  894.     case DECOMP_COMPAT:
  895.       currentChar = prevDecomp();        
  896.       break;
  897.     }
  898.   }
  899.   return currentChar;
  900. }
  901.  
  902. void Normalizer::reset() 
  903. {
  904.     text->setIndex(text->startIndex());
  905.     currentChar = DONE;     // The current char hasn't been processed
  906.     clearBuffer();          // The buffer is empty too
  907. }
  908.  
  909. /**
  910.  * Set the iteration position in the input text that is being normalized
  911.  * and return the first normalized character at that position.
  912.  * <p>
  913.  * <b>Note:</b> This method sets the position in the <em>input</em> text,
  914.  * while {@link #next} and {@link #previous} iterate through characters
  915.  * in the normalized <em>output</em>.  This means that there is not
  916.  * necessarily a one-to-one correspondence between characters returned
  917.  * by <tt>next</tt> and <tt>previous</tt> and the indices passed to and
  918.  * returned from <tt>setIndex</tt> and {@link #getIndex}.
  919.  * <p>
  920.  * @param index the desired index in the input text.
  921.  *
  922.  * @return      the first normalized character that is the result of iterating
  923.  *              forward starting at the given index.
  924.  *
  925.  * @throws IllegalArgumentException if the given index is less than
  926.  *          {@link #getBeginIndex} or greater than {@link #getEndIndex}.
  927.  */
  928. UChar Normalizer::setIndex(UTextOffset index)
  929. {
  930.     text->setIndex(index);   // Checks range
  931.     currentChar = DONE;     // The current char hasn't been processed
  932.     clearBuffer();          // The buffer is empty too
  933.  
  934.     return current();
  935. }
  936.  
  937. /**
  938.  * Retrieve the current iteration position in the input text that is
  939.  * being normalized.  This method is useful in applications such as
  940.  * searching, where you need to be able to determine the position in
  941.  * the input text that corresponds to a given normalized output character.
  942.  * <p>
  943.  * <b>Note:</b> This method sets the position in the <em>input</em>, while
  944.  * {@link #next} and {@link #previous} iterate through characters in the
  945.  * <em>output</em>.  This means that there is not necessarily a one-to-one
  946.  * correspondence between characters returned by <tt>next</tt> and
  947.  * <tt>previous</tt> and the indices passed to and returned from
  948.  * <tt>setIndex</tt> and {@link #getIndex}.
  949.  *
  950.  */
  951. UTextOffset Normalizer::getIndex() const {
  952.     return text->getIndex();
  953. }
  954.  
  955. /**
  956.  * Retrieve the index of the start of the input text.  This is the begin index
  957.  * of the <tt>CharacterIterator</tt> or the start (i.e. 0) of the <tt>String</tt>
  958.  * over which this <tt>Normalizer</tt> is iterating
  959.  */
  960. UTextOffset Normalizer::startIndex() const {
  961.     return text->startIndex();
  962. }
  963.  
  964. /**
  965.  * Retrieve the index of the end of the input text.  This is the end index
  966.  * of the <tt>CharacterIterator</tt> or the length of the <tt>String</tt>
  967.  * over which this <tt>Normalizer</tt> is iterating
  968.  */
  969. UTextOffset Normalizer::endIndex() const {
  970.     return text->endIndex();
  971. }
  972.  
  973. //-------------------------------------------------------------------------
  974. // Property access methods
  975. //-------------------------------------------------------------------------
  976.  
  977. void
  978. Normalizer::setMode(EMode newMode) 
  979. {
  980.   fMode     = newMode;
  981.   minDecomp     = ((fMode & COMPAT_BIT) != 0) ? 0 : DecompData::MAX_COMPAT;
  982. }
  983.  
  984. Normalizer::EMode 
  985. Normalizer::getMode() const
  986. {
  987.     return fMode;
  988. }
  989.  
  990. void
  991. Normalizer::setOption(int32_t option, 
  992.               bool_t value) 
  993. {
  994.   if (value) {
  995.     fOptions |= option;
  996.   } else {
  997.     fOptions &= (~option);
  998.   }
  999. }
  1000.  
  1001. bool_t
  1002. Normalizer::getOption(int32_t option) const
  1003. {
  1004.     return (fOptions & option) != 0;
  1005. }
  1006.  
  1007. /**
  1008.  * Set the input text over which this <tt>Normalizer</tt> will iterate.
  1009.  * The iteration position is set to the beginning of the input text.
  1010.  */
  1011. void
  1012. Normalizer::setText(const UnicodeString& newText, 
  1013.             UErrorCode &status)
  1014. {
  1015.   if (U_FAILURE(status)) {
  1016.     return;
  1017.   }
  1018.   CharacterIterator *newIter = new StringCharacterIterator(newText);
  1019.   if (newIter == NULL) {
  1020.     status = U_MEMORY_ALLOCATION_ERROR;
  1021.     return;
  1022.   }
  1023.   delete text;
  1024.   text = newIter;
  1025.   reset();
  1026. }
  1027.  
  1028. /**
  1029.  * Set the input text over which this <tt>Normalizer</tt> will iterate.
  1030.  * The iteration position is set to the beginning of the string.
  1031.  */
  1032. void
  1033. Normalizer::setText(const CharacterIterator& newText, 
  1034.             UErrorCode &status) 
  1035. {
  1036.   if (U_FAILURE(status)) {
  1037.     return;
  1038.   }
  1039.   CharacterIterator *newIter = newText.clone();
  1040.   if (newIter == NULL) {
  1041.     status = U_MEMORY_ALLOCATION_ERROR;
  1042.     return;
  1043.   }
  1044.   delete text;
  1045.   text = newIter;
  1046.   reset();
  1047. }
  1048.  
  1049.  
  1050. /**
  1051.  * Copies the text under iteration into the UnicodeString referred to by "result".
  1052.  * @param result Receives a copy of the text under iteration.
  1053.  */
  1054. void
  1055. Normalizer::getText(UnicodeString&  result) 
  1056. {
  1057.     text->getText(result);
  1058. }
  1059.  
  1060.  
  1061. //-------------------------------------------------------------------------
  1062. // Private utility methods
  1063. //-------------------------------------------------------------------------
  1064.  
  1065.  
  1066. UChar Normalizer::curForward() {
  1067.     UChar ch = text->current();
  1068.     return ch;
  1069. }
  1070.  
  1071. UChar Normalizer::curBackward() {
  1072.     UChar ch = text->previous();
  1073.     return ch;
  1074. }
  1075.  
  1076. void Normalizer::doAppend(const UChar source[], uint16_t offset, UnicodeString& dest) {
  1077.     uint16_t index = offset >> STR_INDEX_SHIFT;
  1078.     uint16_t length = offset & STR_LENGTH_MASK;
  1079.  
  1080.     if (length == 0) {
  1081.         UChar ch;
  1082.         while ((ch = source[index++]) != 0x0000) {
  1083.             dest += ch;
  1084.         }
  1085.     } else {
  1086.         while (length-- > 0) {
  1087.             dest += source[index++];
  1088.         }
  1089.     }
  1090. }
  1091.  
  1092. void Normalizer::doInsert(const UChar source[], uint16_t offset, UnicodeString& dest, UTextOffset pos)
  1093. {
  1094.     uint16_t index = offset >> STR_INDEX_SHIFT;
  1095.     uint16_t length = offset & STR_LENGTH_MASK;
  1096.  
  1097.     if (length == 0) {
  1098.         UChar ch;
  1099.         while ((ch = source[index++]) != 0x0000) {
  1100.             insert(dest, pos++, ch);
  1101.         }
  1102.     } else {
  1103.         while (length-- > 0) {
  1104.             insert(dest, pos++, source[index++]);
  1105.         }
  1106.     }
  1107. }
  1108.  
  1109. void Normalizer::initBuffer() {
  1110.     buffer.truncate(0);
  1111.     clearBuffer();
  1112. }
  1113.  
  1114. void Normalizer::clearBuffer() {
  1115.     bufferLimit = bufferPos = 0;
  1116. }
  1117.  
  1118. //-----------------------------------------------------------------------------
  1119. // Hangul / Jamo conversion utilities for internal use
  1120. // See section 3.10 of The Unicode Standard, v 2.0.
  1121. //
  1122. /**
  1123.  * Convert a single Hangul syllable into one or more Jamo characters.
  1124.  * 
  1125.  * @param conjoin If TRUE, decompose Jamo into conjoining Jamo.
  1126.  */
  1127. void Normalizer::hangulToJamo(UChar ch, UnicodeString& result, uint16_t decompLimit)
  1128. {
  1129.     UChar sIndex  = (UChar)(ch - HANGUL_BASE);
  1130.     UChar leading = (UChar)(JAMO_LBASE + sIndex / JAMO_NCOUNT);
  1131.     UChar vowel   = (UChar)(JAMO_VBASE +
  1132.                           (sIndex % JAMO_NCOUNT) / JAMO_TCOUNT);
  1133.     UChar trailing= (UChar)(JAMO_TBASE + (sIndex % JAMO_TCOUNT));
  1134.  
  1135.     jamoAppend(leading, decompLimit, result);
  1136.     jamoAppend(vowel, decompLimit, result);
  1137.     if (trailing != JAMO_TBASE) {
  1138.         jamoAppend(trailing, decompLimit, result);
  1139.     }
  1140. }
  1141.  
  1142. void Normalizer::jamoAppend(UChar ch, uint16_t decompLimit, UnicodeString& dest) {
  1143.   uint16_t offset = ucmp16_getu(DecompData::offsets, ch);
  1144.     if (offset > decompLimit) {
  1145.         doAppend(DecompData::contents, offset, dest);
  1146.     } else {
  1147.         dest += ch;
  1148.     }
  1149. }
  1150.  
  1151. void Normalizer::jamoToHangul(UnicodeString& buffer, UTextOffset start) {
  1152.     UTextOffset out = start;
  1153.     UTextOffset limit = buffer.size() - 1;
  1154.  
  1155.     UTextOffset in;
  1156.     uint16_t l, v, t;
  1157.  
  1158.     for (in = start; in < limit; in++) {
  1159.         UChar ch = buffer[in];
  1160.  
  1161.         if ((l = ch - JAMO_LBASE) >= 0 && l < JAMO_LCOUNT
  1162.                 && (v = buffer[in+1] - JAMO_VBASE) >= 0 && v < JAMO_VCOUNT) {
  1163.             //
  1164.             // We've found a pair of Jamo characters to compose.
  1165.             // Snarf the Jamo vowel and see if there's also a trailing char
  1166.             //
  1167.             in++;   // Snarf the Jamo vowel too.
  1168.  
  1169.             t = (in < limit) ? buffer.charAt(in+1) : 0;
  1170.             t -= JAMO_TBASE;
  1171.  
  1172.             if (t >= 0 && t < JAMO_TCOUNT) {
  1173.                 in++;   // Snarf the trailing consonant too
  1174.             } else {
  1175.                 t = 0;  // No trailing consonant
  1176.             }
  1177.             buffer[out++] = (UChar)((l*JAMO_VCOUNT + v) * JAMO_TCOUNT + t + HANGUL_BASE);
  1178.         } else {
  1179.             buffer[out++] = ch;
  1180.         }
  1181.     }
  1182.     while (in < buffer.size()) {
  1183.         buffer[out++] = buffer[in++];
  1184.     }
  1185.  
  1186.     buffer.truncate(out);
  1187. }
  1188.