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

  1. /*
  2. *******************************************************************************
  3. *                                                                             *
  4. * COPYRIGHT:                                                                  *
  5. *   (C) Copyright Taligent, Inc.,  1996                                       *
  6. *   (C) Copyright International Business Machines Corporation,  1996-1999     *
  7. *   Licensed Material - Program-Property of IBM - All Rights Reserved.        *
  8. *   US Government Users Restricted Rights - Use, duplication, or disclosure   *
  9. *   restricted by GSA ADP Schedule Contract with IBM Corp.                    *
  10. *                                                                             *
  11. *******************************************************************************
  12. *
  13. * File tblcoll.cpp
  14. *
  15. * Created by: Helena Shih
  16. *
  17. * Modification History:
  18. *
  19. *  Date        Name        Description
  20. *  2/5/97      aliu        Added streamIn and streamOut methods.  Added
  21. *                          constructor which reads RuleBasedCollator object from
  22. *                          a binary file.  Added writeToFile method which streams
  23. *                          RuleBasedCollator out to a binary file.  The streamIn
  24. *                          and streamOut methods use istream and ostream objects
  25. *                          in binary mode.
  26. *  2/11/97     aliu        Moved declarations out of for loop initializer.
  27. *                          Added Mac compatibility #ifdef for ios::nocreate.
  28. *  2/12/97     aliu        Modified to use TableCollationData sub-object to
  29. *                          hold invariant data.
  30. *  2/13/97     aliu        Moved several methods into this class from Collation.
  31. *                          Added a private RuleBasedCollator(Locale&) constructor,
  32. *                          to be used by Collator::getInstance().  General
  33. *                          clean up.  Made use of UErrorCode variables consistent.
  34. *  2/20/97     helena      Added clone, operator==, operator!=, operator=, and copy
  35. *                          constructor and getDynamicClassID.
  36. *  3/5/97      aliu        Changed compaction cycle to improve performance.  We
  37. *                          use the maximum allowable value which is kBlockCount.
  38. *                          Modified getRules() to load rules dynamically.  Changed
  39. *                          constructFromFile() call to accomodate this (added
  40. *                          parameter to specify whether binary loading is to
  41. *                          take place).
  42. * 05/06/97     helena      Added memory allocation error check.
  43. *  6/20/97     helena      Java class name change.
  44. *  6/23/97     helena      Adding comments to make code more readable.
  45. * 09/03/97     helena      Added createCollationKeyValues().
  46. * 06/26/98     erm         Changes for CollationKeys using byte arrays.
  47. * 08/10/98     erm         Synched with 1.2 version of RuleBasedCollator.java
  48. * 04/23/99     stephen     Removed EDecompositionMode, merged with
  49. *                          Normalizer::EMode
  50. * 06/14/99     stephen     Removed kResourceBundleSuffix
  51. * 06/22/99     stephen     Fixed logic in constructFromFile() since .ctx
  52. *                           files are no longer used.
  53. *******************************************************************************
  54. */
  55.  
  56. #include "ucmp32.h"
  57. #include "tcoldata.h"
  58.  
  59. #include "tblcoll.h"
  60.  
  61. #include "coleitr.h"
  62. #include "locid.h"
  63. #include "unicode.h"
  64. #include "tables.h"
  65. #include "normlzr.h"
  66. #include "mergecol.h"
  67. #include "resbund.h"
  68. #include "filestrm.h"
  69.  
  70. #ifdef _DEBUG
  71. #include "unistrm.h"
  72. #endif
  73.  
  74. #include "compitr.h"
  75.  
  76. #include <string.h>
  77.  
  78.  
  79. class RuleBasedCollatorStreamer
  80. {
  81. public:
  82.     static void streamIn(RuleBasedCollator* collator, FileStream* is);
  83.     static void streamOut(const RuleBasedCollator* collator, FileStream* os);
  84. };
  85.  
  86. //===========================================================================================
  87. //  The following diagram shows the data structure of the RuleBasedCollator object.
  88. //  Suppose we have the rule, where 'o-umlaut' is the unicode char 0x00F6.
  89. //  "a, A < b, B < c, C, ch, cH, Ch, CH < d, D ... < o, O; 'o-umlaut'/E, 'O-umlaut'/E ...".
  90. //  What the rule says is, sorts 'ch'ligatures and 'c' only with tertiary difference and
  91. //  sorts 'o-umlaut' as if it's always expanded with 'e'.
  92. //
  93. // mapping table                       contracting list                  expanding list
  94. // (contains all unicode char
  95. //  entries)                         ___     _____________         _________________________
  96. //   ________                   |==>|_*_|-->|'c'  |v('c') |   |==>|v('o')|v('umlaut')|v('e')|
  97. //  |_\u0001_|--> v('\u0001')   |   |_:_|   |-------------|   |   |-------------------------|
  98. //  |_\u0002_|--> v('\u0002')   |   |_:_|   |'ch' |v('ch')|   |   |             :           |
  99. //  |____:___|                  |   |_:_|   |-------------|   |   |-------------------------|
  100. //  |____:___|                  |           |'cH' |v('cH')|   |   |             :           |
  101. //  |__'a'___|--> v('a')        |           |-------------|   |   |-------------------------|
  102. //  |__'b'___|--> v('b')        |           |'Ch' |v('Ch')|   |   |             :           |
  103. //  |____:___|                  |           |-------------|   |   |-------------------------|
  104. //  |____:___|                  |           |'CH' |v('CH')|   |   |             :           |
  105. //  |___'c'__|-------------------            -------------    |   |-------------------------|
  106. //  |____:___|                                                |   |             :           |
  107. //  |o-umlaut|------------------------------------------------    |_________________________|
  108. //  |____:___|
  109. //
  110. //
  111. // Noted by Helena Shih on 6/23/97 with pending design changes (slimming collation).
  112. //============================================================================================
  113.  
  114. const int32_t RuleBasedCollator::CHARINDEX = 0x70000000;             // need look up in .commit()
  115. const int32_t RuleBasedCollator::EXPANDCHARINDEX = 0x7E000000;       // Expand index follows
  116. const int32_t RuleBasedCollator::CONTRACTCHARINDEX = 0x7F000000;     // contract indexes follows
  117. const int32_t RuleBasedCollator::UNMAPPED = 0xFFFFFFFF;              // unmapped character values
  118. const int32_t RuleBasedCollator::PRIMARYORDERINCREMENT = 0x00010000; // primary strength increment
  119. const int32_t RuleBasedCollator::SECONDARYORDERINCREMENT = 0x00000100; // secondary strength increment
  120. const int32_t RuleBasedCollator::TERTIARYORDERINCREMENT = 0x00000001; // tertiary strength increment
  121. const int32_t RuleBasedCollator::MAXIGNORABLE = 0x00010000;          // maximum ignorable char order value
  122. const int32_t RuleBasedCollator::PRIMARYORDERMASK = 0xffff0000;      // mask off anything but primary order
  123. const int32_t RuleBasedCollator::SECONDARYORDERMASK = 0x0000ff00;    // mask off anything but secondary order
  124. const int32_t RuleBasedCollator::TERTIARYORDERMASK = 0x000000ff;     // mask off anything but tertiary order
  125. const int32_t RuleBasedCollator::SECONDARYRESETMASK = 0x0000ffff;    // mask off secondary and tertiary order
  126. const int32_t RuleBasedCollator::IGNORABLEMASK = 0x0000ffff;         // mask off ignorable char order
  127. const int32_t RuleBasedCollator::PRIMARYDIFFERENCEONLY = 0xffff0000; // use only the primary difference
  128. const int32_t RuleBasedCollator::SECONDARYDIFFERENCEONLY = 0xffffff00;  // use only the primary and secondary difference
  129. const int32_t RuleBasedCollator::PRIMARYORDERSHIFT = 16;             // primary order shift
  130. const int32_t RuleBasedCollator::SECONDARYORDERSHIFT = 8;            // secondary order shift
  131. const int32_t RuleBasedCollator::SORTKEYOFFSET = 1;                  // minimum sort key offset
  132. const int32_t RuleBasedCollator::CONTRACTCHAROVERFLOW = 0x7FFFFFFF;  // Indicates the char is a contract char
  133.  
  134. const int16_t RuleBasedCollator::FILEID = 0x5443;                    // unique file id for parity check
  135. const char* RuleBasedCollator::kFilenameSuffix = ".col";             // binary collation file extension
  136. char  RuleBasedCollator::fgClassID = 0; // Value is irrelevant       // class id
  137.  
  138. //===============================================================================
  139.  
  140. RuleBasedCollator::RuleBasedCollator()
  141.     : Collator(),
  142.       isOverIgnore(FALSE),
  143.       mPattern(0),
  144.       sourceCursor(0),
  145.       targetCursor(0),
  146.       data(0),
  147.       dataIsOwned(FALSE)
  148. {
  149. }
  150.  
  151. RuleBasedCollator::RuleBasedCollator(const  RuleBasedCollator&  that)
  152.     : Collator(that),
  153.       isOverIgnore(that.isOverIgnore),
  154.       mPattern(0),
  155.       sourceCursor(0),
  156.       targetCursor(0),
  157.       dataIsOwned(FALSE),
  158.       data(that.data) // Alias the data pointer
  159. {
  160. }
  161.  
  162. bool_t
  163. RuleBasedCollator::operator==(const Collator& that) const
  164. {
  165.     if (this == &that)
  166.     {
  167.         return TRUE;
  168.     }
  169.  
  170.     if (this->getDynamicClassID() != that.getDynamicClassID())
  171.     {
  172.         return FALSE;  // not the same class
  173.     }
  174.  
  175.     if (!Collator::operator==(that))
  176.     {
  177.         return FALSE;
  178.     }
  179.  
  180.     RuleBasedCollator& thatAlias = (RuleBasedCollator&)that;
  181.  
  182.     if (isOverIgnore != thatAlias.isOverIgnore)
  183.     {
  184.         return FALSE;
  185.     }
  186.  
  187.     if (data != thatAlias.data)
  188.     {
  189.         return FALSE;
  190.     }
  191.  
  192.     return TRUE;
  193. }
  194.  
  195. RuleBasedCollator&
  196. RuleBasedCollator::operator=(const  RuleBasedCollator& that)
  197. {
  198.     if (this != &that)
  199.     {
  200.         Collator::operator=(that);
  201.         isOverIgnore = that.isOverIgnore;
  202.  
  203.         if (dataIsOwned)
  204.         {
  205.             delete data;
  206.         }
  207.  
  208.         data = 0;
  209.         delete mPattern;
  210.         mPattern = 0;
  211.         dataIsOwned = FALSE;
  212.         data = that.data;
  213.     }
  214.  
  215.     return *this;
  216. }
  217.  
  218. RuleBasedCollator::RuleBasedCollator(const  UnicodeString&  rules,
  219.                                         UErrorCode&      status)
  220.     : Collator(),
  221.       isOverIgnore(FALSE),
  222.       mPattern(0),
  223.       sourceCursor(0),
  224.       targetCursor(0),
  225.       data(0),
  226.       dataIsOwned(FALSE)
  227. {
  228.     if (U_FAILURE(status))
  229.     {
  230.         return;
  231.     }
  232.  
  233.     constructFromRules(rules, status);
  234. }
  235.  
  236. RuleBasedCollator::RuleBasedCollator(const  UnicodeString&  rules,
  237.                      ECollationStrength collationStrength,
  238.                      UErrorCode&      status)
  239.   : Collator(collationStrength, Normalizer::NO_OP),
  240.     isOverIgnore(FALSE),
  241.     mPattern(0),
  242.     sourceCursor(0),
  243.     targetCursor(0),
  244.     data(0),
  245.     dataIsOwned(FALSE)
  246. {
  247.     if (U_FAILURE(status))
  248.     {
  249.         return;
  250.     }
  251.  
  252.     constructFromRules(rules, status);
  253. }
  254.  
  255. RuleBasedCollator::RuleBasedCollator(const  UnicodeString&  rules,
  256.                      Normalizer::EMode decompositionMode,
  257.                      UErrorCode&      status)
  258.   : Collator(TERTIARY, decompositionMode),
  259.     isOverIgnore(FALSE),
  260.     mPattern(0),
  261.     sourceCursor(0),
  262.     targetCursor(0),
  263.     data(0),
  264.     dataIsOwned(FALSE)
  265. {
  266.   if (U_FAILURE(status))
  267.     {
  268.       return;
  269.     }
  270.   
  271.   constructFromRules(rules, status);
  272. }
  273.  
  274. RuleBasedCollator::RuleBasedCollator(const  UnicodeString&  rules,
  275.                      ECollationStrength collationStrength,
  276.                      Normalizer::EMode decompositionMode,
  277.                      UErrorCode&      status)
  278.   : Collator(collationStrength, decompositionMode),
  279.       isOverIgnore(FALSE),
  280.       mPattern(0),
  281.       sourceCursor(0),
  282.       targetCursor(0),
  283.       data(0),
  284.       dataIsOwned(FALSE)
  285. {
  286.     if (U_FAILURE(status))
  287.     {
  288.         return;
  289.     }
  290.  
  291.     constructFromRules(rules, status);
  292. }
  293.  
  294. void RuleBasedCollator::constructFromRules(const UnicodeString& rules,
  295.                                         UErrorCode& status)
  296. {
  297.     // Construct this collator's ruleset from its string representation
  298.     if (U_FAILURE(status))
  299.     {
  300.         return;
  301.     }
  302.  
  303.     if (rules.isBogus())
  304.     {
  305.         status = U_MEMORY_ALLOCATION_ERROR;
  306.         return;
  307.     }
  308.  
  309.     if (dataIsOwned)
  310.     {
  311.         delete data;
  312.         data = 0;
  313.     }
  314.  
  315.     isOverIgnore = FALSE;
  316.     setStrength(Collator::TERTIARY);
  317.  
  318.     data = new TableCollationData;
  319.     if (data->isBogus())
  320.     {
  321.         status = U_MEMORY_ALLOCATION_ERROR;
  322.         delete data;
  323.         data = 0;
  324.         return;
  325.     }
  326.  
  327.     // We constructed the data using the build method, so we own it.
  328.     dataIsOwned = TRUE;
  329.  
  330.     // Now that we've got all the buffers allocated, do the actual work
  331.     mPattern = 0;
  332.     build(rules, status);
  333. }
  334.  
  335. void
  336. RuleBasedCollator::constructFromFile(const char* fileName,
  337.                                   UErrorCode& status)
  338. {
  339.     // This method tries to read in a flattened RuleBasedCollator that
  340.     // has been previously streamed out using the streamOut() method.
  341.     // The 'fileName' parameter should contain a full pathname valid on
  342.     // the local environment.
  343.  
  344.     if (U_FAILURE(status))
  345.     {
  346.         return;
  347.     }
  348.  
  349.     if (dataIsOwned)
  350.     {
  351.         delete data;
  352.         data = 0;
  353.     }
  354.  
  355.     mPattern = 0;
  356.     isOverIgnore = FALSE;
  357.     setStrength(Collator::TERTIARY); // This is the default strength
  358.  
  359.     FileStream* ifs = T_FileStream_open(fileName, "rb");
  360.     if (ifs == 0) {
  361.         status = U_FILE_ACCESS_ERROR;
  362.         return;
  363.     }
  364.  
  365.     // The streamIn function does the actual work here...
  366.     RuleBasedCollatorStreamer::streamIn(this, ifs);
  367.  
  368.     if (!T_FileStream_error(ifs))
  369.     {
  370.         status = U_ZERO_ERROR;
  371.     }
  372.     else if (data && data->isBogus())
  373.     {
  374.         status = U_MEMORY_ALLOCATION_ERROR;
  375.         delete data;
  376.         data = 0;
  377.     }
  378.     else
  379.     {
  380.         status = U_MISSING_RESOURCE_ERROR;
  381.         delete data;
  382.         data = 0;
  383.     }
  384.  
  385. #ifdef COLLDEBUG
  386.     fprintf(stderr, "binary read %s size %d, %s\n", fileName, T_FileStream_size(ifs), errorName(status));
  387. #endif
  388.  
  389.     // We constructed the data when streaming it in, so we own it
  390.     dataIsOwned = TRUE;
  391.  
  392.     T_FileStream_close(ifs);
  393. }
  394.  
  395. RuleBasedCollator::RuleBasedCollator(   const Locale& desiredLocale,
  396.                                 UErrorCode& status)
  397.     : Collator(),
  398.       isOverIgnore(FALSE),
  399.       dataIsOwned(FALSE),
  400.       data(0),
  401.       sourceCursor(0),
  402.       targetCursor(0),
  403.       mPattern(0)
  404. {
  405.   if (U_FAILURE(status))
  406.     {
  407.       return;
  408.     }
  409.   
  410.   // Try to load, in order:
  411.   // 1. The desired locale's collation.
  412.   // 2. A fallback of the desired locale.
  413.   // 3. The default locale's collation.
  414.   // 4. A fallback of the default locale.
  415.   // 5. The default collation rules, which contains en_US collation rules.
  416.  
  417.   // To reiterate, we try:
  418.   // Specific:
  419.   //  language+country+variant
  420.   //  language+country
  421.   //  language
  422.   // Default:
  423.   //  language+country+variant
  424.   //  language+country
  425.   //  language
  426.   // Root: (aka DEFAULTRULES)
  427.  
  428.   UnicodeString localeName;
  429.   desiredLocale.getName(localeName);
  430.   enum { eTryDefaultLocale, eTryDefaultCollation, eDone } next = eTryDefaultLocale;
  431.     
  432.   for (;;)
  433.     {
  434.       if (localeName.size() == 0)
  435.     {
  436.       if (next == eDone)
  437.             {
  438.           // We've failed to load a locale, but should never return U_MISSING_RESOURCE_ERROR
  439.           UErrorCode intStatus = U_ZERO_ERROR;
  440.  
  441.           constructFromRules(RuleBasedCollator::DEFAULTRULES, intStatus);
  442.           if (intStatus == U_ZERO_ERROR)
  443.         {
  444.           status = U_USING_DEFAULT_ERROR;
  445.         }
  446.           else
  447.         {
  448.           status = intStatus;     // bubble back
  449.         }
  450.  
  451.           if (status == U_MEMORY_ALLOCATION_ERROR)
  452.         {
  453.           return;
  454.         }
  455.  
  456.           data->desiredLocale = desiredLocale;
  457.           desiredLocale.getName(localeName);
  458.           data->realLocaleName = localeName;
  459.           addToCache(localeName);
  460.  
  461.           setDecomposition(Normalizer::NO_OP);
  462.  
  463.           const UnicodeString& rules = getRules();
  464.           break;
  465.             }
  466.  
  467.       // We've exhausted our inheritance attempts with this locale.
  468.       // Try the next step.
  469.       switch (next)
  470.             {
  471.             case eTryDefaultLocale:
  472.           status = U_USING_DEFAULT_ERROR;
  473.           Locale::getDefault().getName(localeName);
  474.           next = eTryDefaultCollation;
  475.           break;
  476.  
  477.             case eTryDefaultCollation:
  478.           // There is no distinction between this condition of
  479.           // using a default collation object and the condition of
  480.           // using a default locale to get a collation object currently.
  481.           // That is, the caller can't distinguish based on UErrorCode.
  482.           status = U_USING_DEFAULT_ERROR;
  483.           localeName = ResourceBundle::kDefaultFilename;
  484.           next = eDone;
  485.           break;
  486.             }
  487.         }
  488.  
  489.       // First try to load the collation from the in-memory static cache.
  490.       // Note that all of the caching logic is handled here, and in the
  491.       // call to RuleBasedCollator::addToCache, below.
  492.       UErrorCode intStatus = U_ZERO_ERROR;
  493.  
  494.       constructFromCache(localeName, intStatus);
  495.       if (U_SUCCESS(intStatus))
  496.     {
  497.       break; // Done!
  498.     }
  499.  
  500.       // The collation we want is not in the cache.  The second thing
  501.       // to try is loading from a file, either binary or ASCII.  So:
  502.       // Try to load the locale's collation data.  This will try to load
  503.       // a binary collation file, or if that is unavailable, it will go
  504.       // to the text resource bundle file (with the corresponding name)
  505.       // and try to get the collation table there.
  506.       intStatus = U_ZERO_ERROR;
  507.       constructFromFile(desiredLocale, localeName, TRUE, intStatus);
  508.       if (U_SUCCESS(intStatus))
  509.         {
  510.       // If we succeeded in loading the collation from a file, now is the
  511.       // time to add it to the in-memory cache.  We record the real
  512.       // location at which the collation data was found, so we can reload
  513.       // the rule table quickly, if it is requested, in the future.
  514.       // See getRules().
  515.       data->desiredLocale = desiredLocale;
  516.       data->realLocaleName = localeName;
  517.       addToCache(localeName);
  518.  
  519.       setDecomposition(Normalizer::NO_OP);
  520.       break; // Done!
  521.         }
  522.       if (intStatus == U_MEMORY_ALLOCATION_ERROR)
  523.     {
  524.       status = intStatus;
  525.       return;
  526.         }
  527.  
  528.       // Having failed, chop off the end of the locale name, making
  529.       // it less specific, and try again.  Indicate the use of a
  530.       // fallback locale, unless we've already fallen through to
  531.       // a default locale -- then leave the status as is.
  532.       if (status == U_ZERO_ERROR)
  533.     {
  534.       status = U_USING_FALLBACK_ERROR;
  535.     }
  536.  
  537.       chopLocale(localeName);
  538.     }
  539. }
  540.  
  541. void
  542. RuleBasedCollator::constructFromFile(   const Locale&           locale,
  543.                                     const UnicodeString&    localeFileName,
  544.                                     bool_t                  tryBinaryFile,
  545.                                     UErrorCode&              status)
  546. {
  547.   // constructFromFile creates a collation object by reading from a
  548.   // file.  It does not employ the usual FILE search mechanism with
  549.   // locales, default locales, and base locales.  Instead, it tries to
  550.   // look only in files with the given localFileName.  It does,
  551.   // however, employ the LOCALE search mechanism.
  552.   
  553.   // This method maintains the binary collation files.  If a collation
  554.   // is not present in binary form, but is present in text form (in a
  555.   // resource bundle file), it will be loaded in text form, and then
  556.   // written to disk.
  557.   
  558.   // If tryBinaryFile is true, then try to load from the binary file first.
  559.  
  560.   if(U_FAILURE(status)) {
  561.     return;
  562.   }
  563.   
  564.   if(dataIsOwned) {
  565.     delete data;
  566.     data = 0;
  567.   }
  568.   
  569.   char *binaryFilePath = createPathName(Locale::getDataDirectory(), 
  570.                     localeFileName, kFilenameSuffix);
  571.   
  572.   if(tryBinaryFile) {
  573.     // Try to load up the collation from a binary file first
  574.     constructFromFile(binaryFilePath, status);
  575. #ifdef COLLDEBUG
  576.     cerr << localeFileName << " binary load " << errorName(status) << endl;
  577. #endif
  578.     if(U_SUCCESS(status) || status == U_MEMORY_ALLOCATION_ERROR) 
  579.       return;
  580.     }
  581.  
  582.   // Now try to load it up from a resource bundle text source file
  583.   ResourceBundle bundle(Locale::getDataDirectory(), localeFileName, status);
  584.  
  585.   // if there is no resource bundle file for the give locale, break out
  586.   if(U_FAILURE(status))
  587.     return;
  588.  
  589. #ifdef COLLDEBUG
  590.   cerr << localeFileName << " ascii load " << errorName(status) << endl;
  591. #endif
  592.  
  593.   // check and see if this resource bundle contains collation data
  594.   
  595.   UnicodeString colString;
  596.   UErrorCode intStatus = U_ZERO_ERROR;
  597.  
  598.   bundle.getString("CollationElements", colString, intStatus);
  599.   if(colString.isBogus()) {
  600.     status = U_MEMORY_ALLOCATION_ERROR;
  601.     return;
  602.   }
  603.  
  604.   // if this bundle doesn't contain collation data, break out
  605.   if(U_FAILURE(intStatus)) {
  606.     status = U_MISSING_RESOURCE_ERROR;
  607.     return;
  608.   }
  609.  
  610.   // Having loaded the collation from the resource bundle text file,
  611.   // now retrieve the CollationElements tagged data, merged with the
  612.   // default rules.  If that fails, use the default rules alone.
  613.  
  614.   colString.insert(0, DEFAULTRULES);
  615.   if(colString.isBogus()) {
  616.     status = U_MEMORY_ALLOCATION_ERROR;
  617.     return;
  618.   }
  619.     
  620.   constructFromRules(colString, intStatus);
  621.   if(intStatus == U_MEMORY_ALLOCATION_ERROR) {
  622.     status = U_MEMORY_ALLOCATION_ERROR;
  623.     return;
  624.   }
  625.   
  626.   if(intStatus != U_ZERO_ERROR)  {
  627.     status = U_USING_DEFAULT_ERROR;
  628.       
  629.     // predefined tables should contain correct grammar
  630.     intStatus = U_ZERO_ERROR;
  631.     constructFromRules(DEFAULTRULES, intStatus);
  632.     if(intStatus != U_ZERO_ERROR) {
  633.       status = intStatus;
  634.     }
  635.   } 
  636.   
  637. #ifdef COLLDEBUG
  638.   cerr << localeFileName << " ascii load " << (U_SUCCESS(status) ? "OK" : "Failed") << endl;
  639. #endif
  640.   
  641.   if(U_SUCCESS(status) && tryBinaryFile) {
  642.     // If we get a RuleBasedCollator result, even if it is derived
  643.     // from a default or a fallback, then we write it out as a
  644.     // binary file to the disk.  The next time the system wants to
  645.     // get this collation, it will load up very quickly from the
  646.     // binary file.
  647.     bool_t ok = writeToFile(binaryFilePath);
  648.     delete [] binaryFilePath;
  649. #ifdef COLLDEBUG
  650.     cerr << localeFileName << " binary write " << (ok? "OK" : "Failed") << endl;
  651. #endif
  652.   }
  653. }
  654.  
  655. RuleBasedCollator::~RuleBasedCollator()
  656. {
  657.     if (dataIsOwned)
  658.     {
  659.         delete data;
  660.     }
  661.  
  662.     data = 0;
  663.  
  664.     delete sourceCursor;
  665.     sourceCursor = 0;
  666.  
  667.     delete targetCursor;
  668.     targetCursor = 0;
  669.  
  670.     delete mPattern;
  671.     mPattern = 0;
  672. }
  673.  
  674. Collator*
  675. RuleBasedCollator::clone() const
  676. {
  677.     return new RuleBasedCollator(*this);
  678. }
  679.  
  680. // Create a CollationElementIterator object that will iterator over the elements
  681. // in a string, using the collation rules defined in this RuleBasedCollator
  682. CollationElementIterator*
  683. RuleBasedCollator::createCollationElementIterator(const UnicodeString& source) const
  684. {
  685.     UErrorCode status = U_ZERO_ERROR;
  686.     CollationElementIterator *newCursor = 0;
  687.  
  688.     newCursor = new CollationElementIterator(source, this, status);
  689.     if (U_FAILURE(status))
  690.     {
  691.         return NULL;
  692.     }
  693.  
  694.     return newCursor;
  695. }
  696.  
  697. // Create a CollationElementIterator object that will iterator over the elements
  698. // in a string, using the collation rules defined in this RuleBasedCollator
  699. CollationElementIterator*
  700. RuleBasedCollator::createCollationElementIterator(const CharacterIterator& source) const
  701. {
  702.     UErrorCode status = U_ZERO_ERROR;
  703.     CollationElementIterator *newCursor = 0;
  704.  
  705.     newCursor = new CollationElementIterator(source, this, status);
  706.     if (U_FAILURE(status))
  707.     {
  708.         return NULL;
  709.     }
  710.  
  711.     return newCursor;
  712. }
  713.  
  714. // Return a string representation of this collator's rules.
  715. // The string can later be passed to the constructor that takes a
  716. // UnicodeString argument, which will construct a collator that's
  717. // functionally identical to this one.
  718. // You can also allow users to edit the string in order to change
  719. // the collation data, or you can print it out for inspection, or whatever.
  720.  
  721. const UnicodeString&
  722. RuleBasedCollator::getRules() const
  723. {
  724.     if (mPattern != 0)
  725.     {
  726.         MergeCollation*& nonConstMPattern = *(MergeCollation**)&mPattern;
  727.         mPattern->emitPattern(data->ruleTable);
  728.         data->isRuleTableLoaded = TRUE;
  729.         delete nonConstMPattern;
  730.         nonConstMPattern = 0;
  731.     }
  732.     else if (!data->isRuleTableLoaded)
  733.     {
  734.         // At this point the caller wants the rules, but the rule table data
  735.         // is not loaded.  Furthermore, there is no mPattern object to load
  736.         // the rules from.  Therefore, we fetch the rules off the disk.
  737.         // Notice that we pass in a tryBinaryFile value of FALSE, since
  738.         // by design the binary file has NO rules in it!
  739.         RuleBasedCollator temp;
  740.         UErrorCode status = U_ZERO_ERROR;
  741.         temp.constructFromFile(data->desiredLocale, data->realLocaleName, FALSE, status);
  742.  
  743.         // We must check that mPattern is nonzero here, or we run the risk
  744.         // of an infinite loop.
  745.         if (U_SUCCESS(status) && temp.mPattern != 0)
  746.         {
  747.             data->ruleTable = temp.getRules();
  748.             data->isRuleTableLoaded = TRUE;
  749. #ifdef _DEBUG
  750.             // the following is useful for specific debugging purposes
  751.             // UnicodeString name;
  752.             // cerr << "Table collation rules loaded dynamically for "
  753.             //     << data->desiredLocale.getName(name)
  754.             //     << " at "
  755.             //     << data->realLocaleName
  756.             //     << ", " << dec << data->ruleTable.size() << " characters"
  757.             //     << endl;
  758. #endif
  759.         }
  760.         else
  761.         {
  762. #ifdef _DEBUG
  763.             UnicodeString name;
  764.             cerr << "Unable to load table collation rules dynamically for "
  765.                 << data->desiredLocale.getName(name)
  766.                 << " at "
  767.                 << data->realLocaleName
  768.                 << endl;
  769.             cerr << "Status " << errorName(status) << ", mPattern " << temp.mPattern << endl;
  770. #endif
  771.         }
  772.     }
  773.  
  774.     return data->ruleTable;
  775. }
  776.  
  777.  
  778. Collator::EComparisonResult
  779. RuleBasedCollator::compare( const UnicodeString& source,
  780.                             const UnicodeString& target,
  781.                             int32_t length) const
  782. {
  783.     UnicodeString source_togo;
  784.     UnicodeString target_togo;
  785.     UTextOffset begin=0;
  786.  
  787.     source.extract(begin, icu_min(length,source.size()), source_togo);
  788.     target.extract(begin, icu_min(length,target.size()), target_togo);
  789.     return (RuleBasedCollator::compare(source_togo, target_togo));
  790. }
  791.  
  792.  
  793. // Compare two strings using this collator
  794. Collator::EComparisonResult
  795. RuleBasedCollator::compare(const UnicodeString& source,
  796.                         const UnicodeString& target) const
  797. {
  798.     // check if source and target are valid strings
  799.     if (source.isBogus() || target.isBogus())
  800.     {
  801.         return Collator::EQUAL;
  802.     }
  803.  
  804.     Collator::EComparisonResult result = Collator::EQUAL;
  805.     UErrorCode status = U_ZERO_ERROR;
  806.  
  807.     // The basic algorithm here is that we use CollationElementIterators
  808.     // to step through both the source and target strings.  We compare each
  809.     // collation element in the source string against the corresponding one
  810.     // in the target, checking for differences.
  811.     //
  812.     // If a difference is found, we set <result> to LESS or GREATER to
  813.     // indicate whether the source string is less or greater than the target.
  814.     //
  815.     // However, it's not that simple.  If we find a tertiary difference
  816.     // (e.g. 'A' vs. 'a') near the beginning of a string, it can be
  817.     // overridden by a primary difference (e.g. "A" vs. "B") later in 
  818.     // the string.  For example, "AA" < "aB", even though 'A' > 'a'.
  819.     //
  820.     // To keep track of this, we use checkSecTer and checkTertiary to keep
  821.     // track of the strength of the most significant difference that has been
  822.     // found so far.  When we find a difference whose strength is greater than
  823.     // the previous ones, it overrides the last difference (if any) that
  824.     // was found.
  825.     //
  826.  
  827.     if (sourceCursor == NULL)
  828.     {
  829.         ((RuleBasedCollator *)this)->sourceCursor = createCollationElementIterator(source);
  830.     }
  831.     else
  832.     {
  833.         sourceCursor->setText(source, status);
  834.     }
  835.  
  836.     if (sourceCursor == NULL || U_FAILURE(status))
  837.     {
  838.         return Collator::EQUAL;
  839.     }
  840.  
  841.     if (targetCursor == NULL)
  842.     {
  843.         ((RuleBasedCollator *)this)->targetCursor = createCollationElementIterator(target);
  844.     }
  845.     else
  846.     {
  847.         targetCursor->setText(target, status);
  848.     }
  849.  
  850.     if (targetCursor == NULL || U_FAILURE(status))
  851.     {
  852.         return Collator::EQUAL;
  853.     }
  854.  
  855.     int32_t sOrder, tOrder;
  856.     bool_t gets = TRUE, gett = TRUE;
  857.     bool_t initialCheckSecTer = getStrength() >= Collator::SECONDARY;
  858.     bool_t checkSecTer = initialCheckSecTer;
  859.     bool_t checkTertiary = getStrength() >= Collator::TERTIARY;
  860.     bool_t isFrenchSec = data->isFrenchSec;
  861.     uint32_t pSOrder, pTOrder;
  862.  
  863.     while(TRUE)
  864.     {
  865.         // Get the next collation element in each of the strings, unless
  866.         // we've been requested to skip it.
  867.         if (gets)
  868.         {
  869.             sOrder = sourceCursor->next(status);
  870.  
  871.             if (U_FAILURE(status))
  872.             {
  873.                 return Collator::EQUAL;
  874.             }
  875.         }
  876.  
  877.         gets = TRUE;
  878.  
  879.         if (gett)
  880.         {
  881.             tOrder = targetCursor->next(status);
  882.  
  883.             if (U_FAILURE(status))
  884.             {
  885.                 return Collator::EQUAL;
  886.             }
  887.         }
  888.         
  889.         gett = TRUE;
  890.  
  891.         // If we've hit the end of one of the strings, jump out of the loop
  892.         if ((sOrder == CollationElementIterator::NULLORDER)||
  893.             (tOrder == CollationElementIterator::NULLORDER))
  894.         {
  895.             break;
  896.         }
  897.  
  898.         // If there's no difference at this position, we can skip to the
  899.         // next one.
  900.         pSOrder = CollationElementIterator::primaryOrder(sOrder);
  901.         pTOrder = CollationElementIterator::primaryOrder(tOrder);
  902.         if (sOrder == tOrder)
  903.         {
  904.             if (isFrenchSec && pSOrder != 0)
  905.             {
  906.                 if (!checkSecTer)
  907.                 {
  908.                     // in french, a secondary difference more to the right is stronger,
  909.                     // so accents have to be checked with each base element
  910.                     checkSecTer = initialCheckSecTer;
  911.  
  912.                     // but tertiary differences are less important than the first 
  913.                     // secondary difference, so checking tertiary remains disabled
  914.                     checkTertiary = FALSE;
  915.                 }
  916.             }
  917.  
  918.             continue;
  919.         }
  920.  
  921.         // Compare primary differences first.
  922.         if (pSOrder != pTOrder)
  923.         {
  924.             if (sOrder == 0)
  925.             {
  926.                 // The entire source element is ignorable.
  927.                 // Skip to the next source element, but don't fetch another target element.
  928.                 gett = FALSE;
  929.                 continue;
  930.             }
  931.  
  932.             if (tOrder == 0)
  933.             {
  934.                 gets = FALSE;
  935.                 continue;
  936.             }
  937.  
  938.             // The source and target elements aren't ignorable, but it's still possible
  939.             // for the primary component of one of the elements to be ignorable....
  940.             if (pSOrder == 0)  // primary order in source is ignorable
  941.             {
  942.                 // The source's primary is ignorable, but the target's isn't.  We treat ignorables
  943.                 // as a secondary difference, so remember that we found one.
  944.                 if (checkSecTer)
  945.                 {
  946.                     result = Collator::GREATER;  // (strength is SECONDARY)
  947.                     checkSecTer = FALSE;
  948.                 }
  949.  
  950.                 // Skip to the next source element, but don't fetch another target element.
  951.                 gett = FALSE;
  952.             }
  953.             else if (pTOrder == 0)
  954.             {
  955.                 // record differences - see the comment above.
  956.                 if (checkSecTer)
  957.                 {
  958.                     result = Collator::LESS;  // (strength is SECONDARY)
  959.                     checkSecTer = FALSE;
  960.                 }
  961.  
  962.                 // Skip to the next target element, but don't fetch another source element.
  963.                 gets = FALSE;
  964.             }
  965.             else
  966.             {
  967.                 // Neither of the orders is ignorable, and we already know that the primary
  968.                 // orders are different because of the (pSOrder != pTOrder) test above.
  969.                 // Record the difference and stop the comparison.
  970.                 if (pSOrder < pTOrder)
  971.                 {
  972.                     return Collator::LESS;  // (strength is PRIMARY)
  973.                 }
  974.  
  975.                 return Collator::GREATER;  // (strength is PRIMARY)
  976.             }
  977.         }
  978.         else
  979.         { // else of if ( pSOrder != pTOrder )
  980.             // primary order is the same, but complete order is different. So there
  981.             // are no base elements at this point, only ignorables (Since the strings are
  982.             // normalized)
  983.  
  984.             if (checkSecTer)
  985.             {
  986.                 // a secondary or tertiary difference may still matter
  987.                 uint32_t secSOrder = CollationElementIterator::secondaryOrder(sOrder);
  988.                 uint32_t secTOrder = CollationElementIterator::secondaryOrder(tOrder);
  989.  
  990.                 if (secSOrder != secTOrder)
  991.                 {
  992.                     // there is a secondary difference
  993.                     result = (secSOrder < secTOrder) ? Collator::LESS : Collator::GREATER;
  994.                                             // (strength is SECONDARY)
  995.                     checkSecTer = FALSE; 
  996.                     // (even in french, only the first secondary difference within
  997.                     //  a base character matters)
  998.                 }
  999.                 else
  1000.                 {
  1001.                     if (checkTertiary)
  1002.                     {
  1003.                         // a tertiary difference may still matter
  1004.                         uint32_t terSOrder = CollationElementIterator::tertiaryOrder(sOrder);
  1005.                         uint32_t terTOrder = CollationElementIterator::tertiaryOrder(tOrder);
  1006.  
  1007.                         if (terSOrder != terTOrder)
  1008.                         {
  1009.                             // there is a tertiary difference
  1010.                             result = (terSOrder < terTOrder) ? Collator::LESS : Collator::GREATER;
  1011.                                             // (strength is TERTIARY)
  1012.                             checkTertiary = FALSE;
  1013.                         }
  1014.                     }
  1015.                 }
  1016.             } // if (checkSecTer)
  1017.  
  1018.         }  // if ( pSOrder != pTOrder )
  1019.     } // while()
  1020.  
  1021.     if (sOrder != CollationElementIterator::NULLORDER)
  1022.     {
  1023.         // (tOrder must be CollationElementIterator::NULLORDER,
  1024.         //  since this point is only reached when sOrder or tOrder is NULLORDER.)
  1025.         // The source string has more elements, but the target string hasn't.
  1026.         do
  1027.         {
  1028.             if (CollationElementIterator::primaryOrder(sOrder) != 0)
  1029.             {
  1030.                 // We found an additional non-ignorable base character in the source string.
  1031.                 // This is a primary difference, so the source is greater
  1032.                 return Collator::GREATER; // (strength is PRIMARY)
  1033.             }
  1034.  
  1035.             if (CollationElementIterator::secondaryOrder(sOrder) != 0)
  1036.             {
  1037.                 // Additional secondary elements mean the source string is greater
  1038.                 if (checkSecTer)
  1039.                 {
  1040.                     result = Collator::GREATER;  // (strength is SECONDARY)
  1041.                     checkSecTer = FALSE;
  1042.                 }
  1043.             } 
  1044.         }
  1045.         while ((sOrder = sourceCursor->next(status)) != CollationElementIterator::NULLORDER);
  1046.     }
  1047.     else if (tOrder != CollationElementIterator::NULLORDER)
  1048.     {
  1049.         // The target string has more elements, but the source string hasn't.
  1050.         do
  1051.         {
  1052.             if (CollationElementIterator::primaryOrder(tOrder) != 0)
  1053.             {
  1054.                 // We found an additional non-ignorable base character in the target string.
  1055.                 // This is a primary difference, so the source is less
  1056.                 return Collator::LESS; // (strength is PRIMARY)
  1057.             }
  1058.  
  1059.             if (CollationElementIterator::secondaryOrder(tOrder) != 0)
  1060.             {
  1061.                 // Additional secondary elements in the target mean the source string is less
  1062.                 if (checkSecTer)
  1063.                 {
  1064.                     result = Collator::LESS;  // (strength is SECONDARY)
  1065.                     checkSecTer = FALSE;
  1066.                 }
  1067.             } 
  1068.         }
  1069.         while ((tOrder = targetCursor->next(status)) != CollationElementIterator::NULLORDER);
  1070.     }
  1071.  
  1072.  
  1073.     // For IDENTICAL comparisons, we use a bitwise character comparison
  1074.     // as a tiebreaker if all else is equal
  1075.     // NOTE: The java code compares result with 0, and 
  1076.     // puts the result of the string comparison directly into result
  1077.     if (result == Collator::EQUAL && getStrength() == IDENTICAL)
  1078.     {
  1079.         UnicodeString sourceDecomp, targetDecomp;
  1080.         int8_t comparison;
  1081.         
  1082.         Normalizer::normalize(source, getDecomposition(), 
  1083.                       0, sourceDecomp, status);
  1084.         Normalizer::normalize(target, getDecomposition(), 
  1085.                       0, targetDecomp, status);
  1086.         
  1087.         comparison = sourceDecomp.compare(targetDecomp);
  1088.  
  1089.         if (comparison < 0)
  1090.         {
  1091.             result = Collator::LESS;
  1092.         }
  1093.         else if (comparison == 0)
  1094.         {
  1095.             result = Collator::EQUAL;
  1096.         }
  1097.         else
  1098.         {
  1099.             result = Collator::GREATER;
  1100.         }
  1101.     }
  1102.  
  1103.     return result;
  1104. }
  1105.  
  1106. // Retrieve a collation key for the specified string
  1107. // The key can be compared with other collation keys using a bitwise comparison
  1108. // (e.g. memcmp) to find the ordering of their respective source strings.
  1109. // This is handy when doing a sort, where each sort key must be compared
  1110. // many times.
  1111. //
  1112. // The basic algorithm here is to find all of the collation elements for each
  1113. // character in the source string, convert them to an ASCII representation,
  1114. // and put them into the collation key.  But it's trickier than that.
  1115. // Each collation element in a string has three components: primary ('A' vs 'B'),
  1116. // secondary ('u' vs 'รผ'), and tertiary ('A' vs 'a'), and a primary difference
  1117. // at the end of a string takes precedence over a secondary or tertiary
  1118. // difference earlier in the string.
  1119. //
  1120. // To account for this, we put all of the primary orders at the beginning of the
  1121. // string, followed by the secondary and tertiary orders. Each set of orders is
  1122. // terminated by nulls so that a key for a string which is a initial substring of
  1123. // another key will compare less without any special case.
  1124. //
  1125. // Here's a hypothetical example, with the collation element represented as
  1126. // a three-digit number, one digit for primary, one for secondary, etc.
  1127. //
  1128. // String:              A     a     B    ร‰
  1129. // Collation Elements: 101   100   201  511
  1130. // Collation Key:      1125<null>0001<null>1011<null>
  1131. //
  1132. // To make things even trickier, secondary differences (accent marks) are compared
  1133. // starting at the *end* of the string in languages with French secondary ordering.
  1134. // But when comparing the accent marks on a single base character, they are compared
  1135. // from the beginning.  To handle this, we reverse all of the accents that belong
  1136. // to each base character, then we reverse the entire string of secondary orderings
  1137. // at the end.
  1138. //
  1139. CollationKey&
  1140. RuleBasedCollator::getCollationKey( const   UnicodeString&  source,
  1141.                                     CollationKey&   sortkey,
  1142.                                     UErrorCode&      status) const
  1143. {
  1144.     if (U_FAILURE(status))
  1145.     {
  1146.         status = U_ILLEGAL_ARGUMENT_ERROR;
  1147.         return sortkey.setToBogus();
  1148.     }
  1149.     
  1150.     if (source.isBogus())
  1151.     {
  1152.         status = U_MEMORY_ALLOCATION_ERROR;
  1153.         return sortkey.setToBogus();
  1154.     }
  1155.  
  1156.     if (source.size() == 0)
  1157.     {
  1158.         return sortkey.reset();
  1159.     }
  1160.  
  1161.     if (sourceCursor == NULL)
  1162.     {
  1163.         ((RuleBasedCollator *)this)->sourceCursor = createCollationElementIterator(source);
  1164.     }
  1165.     else
  1166.     {
  1167.         sourceCursor->setText(source, status);
  1168.     }
  1169.  
  1170.     if (sourceCursor == NULL || U_FAILURE(status))
  1171.     {
  1172.         return sortkey.setToBogus();
  1173.     }
  1174.  
  1175.     bool_t  compareSec   = (getStrength() >= Collator::SECONDARY);
  1176.     bool_t  compareTer   = (getStrength() >= Collator::TERTIARY);
  1177.     bool_t  compareIdent = (getStrength() == Collator::IDENTICAL);
  1178.     int32_t order        = 0;
  1179.     int32_t totalPrimary = 0;
  1180.     int32_t totalSec     = 0;
  1181.     int32_t totalTer     = 0;
  1182.     int32_t totalIdent     = 0;
  1183.     UnicodeString decomp;
  1184.  
  1185.     // iterate over the source, counting primary, secondary, and tertiary entries
  1186.     while((order = sourceCursor->next(status)) != CollationElementIterator::NULLORDER)
  1187.     {
  1188.         int32_t secOrder = CollationElementIterator::secondaryOrder(order);
  1189.         int32_t terOrder = CollationElementIterator::tertiaryOrder(order);
  1190.  
  1191.         if (U_FAILURE(status))
  1192.         {
  1193.             return sortkey.setToBogus();
  1194.         }
  1195.  
  1196.         if (! CollationElementIterator::isIgnorable(order))
  1197.         {
  1198.             totalPrimary += 1;
  1199.  
  1200.             if (compareSec)
  1201.             {
  1202.                 totalSec += 1;
  1203.             }
  1204.  
  1205.             if (compareTer)
  1206.             {
  1207.                 totalTer += 1;
  1208.             }
  1209.         }
  1210.         else
  1211.         {
  1212.             if (compareSec && secOrder != 0)
  1213.             {
  1214.                 totalSec += 1;
  1215.             }
  1216.  
  1217.             if (compareTer && terOrder != 0)
  1218.             {
  1219.                 totalTer += 1;
  1220.             }
  1221.         }
  1222.     }
  1223.  
  1224.     // count the null bytes after the entires
  1225.     totalPrimary += 1;
  1226.  
  1227.     if (compareSec)
  1228.     {
  1229.         totalSec += 1;
  1230.     }
  1231.  
  1232.     if (compareTer)
  1233.     {
  1234.         totalTer += 1;
  1235.     }
  1236.  
  1237.     if (compareIdent)
  1238.     {
  1239.       Normalizer::normalize(source, getDecomposition(),
  1240.                 0, decomp, status);
  1241.  
  1242.         if (U_SUCCESS(status))
  1243.         {
  1244.             totalIdent = decomp.size() + 1;
  1245.         }
  1246.     }
  1247.  
  1248.     // Compute total number of bytes to hold the entries
  1249.     // and make sure the key can hold them
  1250.     uint32_t size   = 2 * (totalPrimary + totalSec + totalTer + totalIdent);
  1251.  
  1252.     sortkey.ensureCapacity(size);
  1253.  
  1254.     if (sortkey.isBogus())
  1255.     {
  1256.         status = U_MEMORY_ALLOCATION_ERROR;
  1257.         return sortkey;
  1258.     }
  1259.  
  1260.     int32_t primaryCursor = 0;
  1261.     int32_t secCursor     = 2 * totalPrimary;
  1262.     int32_t secBase       = secCursor;
  1263.     int32_t preSecIgnore  = secBase;
  1264.     int32_t terCursor     = secCursor + (2 * totalSec);
  1265.     int32_t identCursor      = terCursor + (2 * totalTer);
  1266.  
  1267.     // reset source to the beginning
  1268.     sourceCursor->reset();
  1269.  
  1270.     // now iterate over the source computing the actual entries
  1271.     while((order = sourceCursor->next(status)) != CollationElementIterator::NULLORDER)
  1272.     {
  1273.         if (U_FAILURE(status))
  1274.         {
  1275.             return sortkey.reset();
  1276.         }
  1277.  
  1278.         int32_t primaryOrder = CollationElementIterator::primaryOrder(order);
  1279.         int32_t secOrder     = CollationElementIterator::secondaryOrder(order);
  1280.         int32_t terOrder     = CollationElementIterator::tertiaryOrder(order);
  1281.  
  1282.         if (! CollationElementIterator::isIgnorable(order))
  1283.         {
  1284.             primaryCursor = sortkey.storeBytes(primaryCursor, primaryOrder + SORTKEYOFFSET);
  1285.  
  1286.             if (compareSec)
  1287.             {
  1288.                 if (data->isFrenchSec && (preSecIgnore < secCursor))
  1289.                 {
  1290.                     sortkey.reverseBytes(preSecIgnore, secCursor);
  1291.                 }
  1292.  
  1293.                 secCursor = sortkey.storeBytes(secCursor, secOrder + SORTKEYOFFSET);
  1294.  
  1295.                 preSecIgnore = secCursor;
  1296.             }
  1297.  
  1298.             if (compareTer)
  1299.             {
  1300.                 terCursor = sortkey.storeBytes(terCursor, terOrder + SORTKEYOFFSET);
  1301.             }
  1302.         }
  1303.         else
  1304.         {
  1305.             if (compareSec && secOrder != 0)
  1306.             {
  1307.                 secCursor = sortkey.storeBytes(secCursor, secOrder + data->maxSecOrder + SORTKEYOFFSET);
  1308.             }
  1309.  
  1310.             if (compareTer && terOrder != 0)
  1311.             {
  1312.                 terCursor = sortkey.storeBytes(terCursor, terOrder + data->maxTerOrder + SORTKEYOFFSET);
  1313.             }
  1314.         }
  1315.     }
  1316.  
  1317.     // append 0 at the end of each portion.
  1318.     sortkey.storeBytes(primaryCursor, 0);
  1319.  
  1320.     if (compareSec)
  1321.     {
  1322.         if (data->isFrenchSec)
  1323.         {
  1324.             if (preSecIgnore < secCursor)
  1325.             {
  1326.                 sortkey.reverseBytes(preSecIgnore, secCursor);
  1327.             }
  1328.  
  1329.             sortkey.reverseBytes(secBase, secCursor);
  1330.         }
  1331.  
  1332.         sortkey.storeBytes(secCursor, 0);
  1333.     }
  1334.  
  1335.     if (compareTer)
  1336.     {
  1337.         sortkey.storeBytes(terCursor, 0);
  1338.     }
  1339.  
  1340.     if (compareIdent)
  1341.     {
  1342.         sortkey.storeUnicodeString(identCursor, decomp);
  1343.     }
  1344.  
  1345.     return sortkey;
  1346. }
  1347.  
  1348.  
  1349. // Build this collator's rule tables based on a string representation of the rules
  1350. // See the big diagram at the top of this file for an overview of how the tables
  1351. // are organized.
  1352. void
  1353. RuleBasedCollator::build(const UnicodeString&   pattern,
  1354.                             UErrorCode&      status)
  1355. {
  1356.     if (U_FAILURE(status))
  1357.     {
  1358.         return;
  1359.     }
  1360.  
  1361.     // This array maps Unicode characters to their collation ordering
  1362.     data->mapping = ucmp32_open(UNMAPPED);
  1363.  
  1364.     if (data->mapping->fBogus)
  1365.     {
  1366.         status = U_MEMORY_ALLOCATION_ERROR;
  1367.         return;
  1368.     }
  1369.  
  1370.     Collator::ECollationStrength aStrength = Collator::IDENTICAL;
  1371.     bool_t isSource = TRUE;
  1372.     int32_t i = 0;
  1373.     UnicodeString lastGroupChars;
  1374.     UnicodeString expChars;
  1375.     UnicodeString groupChars;
  1376.  
  1377.     if (pattern.size() == 0)
  1378.     {
  1379.         status = U_INVALID_FORMAT_ERROR;
  1380.         return;
  1381.     }
  1382.  
  1383.     // Build the merged collation entries
  1384.     // Since rules can be specified in any order in the string
  1385.     // (e.g. "c , C < d , D < e , E .... C < CH")
  1386.     // this splits all of the rules in the string out into separate
  1387.     // objects and then sorts them.  In the above example, it merges the
  1388.     // "C < CH" rule in just before the "C < D" rule.
  1389.  
  1390.     mPattern = new MergeCollation(pattern, getDecomposition(), status);
  1391.     if (U_FAILURE(status))
  1392.     {
  1393.         ucmp32_close(data->mapping);
  1394.         data->mapping = 0;
  1395.         delete mPattern;
  1396.         mPattern = 0;
  1397.         return;
  1398.     }
  1399.  
  1400.     int32_t order = 0;
  1401.  
  1402.     // Walk through each entry
  1403.     for (i = 0; i < mPattern->getCount(); ++i)
  1404.     {
  1405.         const PatternEntry* entry = mPattern->getItemAt(i);
  1406.         groupChars.remove();
  1407.         expChars.remove();
  1408.  
  1409.         // if entry is valid
  1410.         if (entry != NULL)
  1411.         {
  1412.             entry->getChars(groupChars);
  1413.  
  1414.             // check if french secondary needs to be turned on
  1415.             if ((groupChars.size() > 1) &&
  1416.                 (groupChars[groupChars.size()-(T_INT32(1))] == 0x0040))
  1417.             {
  1418.                 data->isFrenchSec = TRUE;
  1419.                 groupChars.remove(groupChars.size()-(T_INT32(1)));
  1420.             }
  1421.  
  1422.             order = increment((Collator::ECollationStrength)entry->getStrength(), order);
  1423.  
  1424.             if (entry->getExtension(expChars).size() != 0)
  1425.             {
  1426.                 // encountered an expanding character, where one character on input
  1427.                 // expands to several sort elements (e.g. 'รถ' --> 'o' 'e')
  1428.                 addExpandOrder(groupChars, expChars, order, status);
  1429.                 if (U_FAILURE(status))
  1430.                 {
  1431.                     return;
  1432.                 }
  1433.             }
  1434.             else if (groupChars.size() > 1)
  1435.             {
  1436.                 // encountered a contracting character, where several characters on input
  1437.                 // contract into one sort order.  For example, "ch" is treated as a single
  1438.                 // character in traditional Spanish sorting.
  1439.                 addContractOrder(groupChars, order, status);
  1440.                 if (U_FAILURE(status))
  1441.                 {
  1442.                     return;
  1443.                 }
  1444.             }
  1445.             else
  1446.             {
  1447.                 // Nothing out of the ordinary -- one character maps to one sort order
  1448.                 addOrder(groupChars[0], order, status);
  1449.                 if (U_FAILURE(status))
  1450.                 {
  1451.                     return;
  1452.                 }
  1453.             }
  1454.         }
  1455.     }
  1456.  
  1457.     // add expanding entries for pre-composed characters
  1458.     addComposedChars();
  1459.  
  1460.     // Fill in all the expanding chars values
  1461.     commit();
  1462.  
  1463.     // Compact the data mapping table
  1464.     ucmp32_compact(data->mapping, 1);
  1465. }
  1466.  
  1467. /**
  1468.  * Add expanding entries for pre-composed unicode characters so that this
  1469.  * collator can be used reasonably well with decomposition turned off.
  1470.  */
  1471.  void RuleBasedCollator::addComposedChars()
  1472.  {
  1473.     UnicodeString buf;
  1474.     UErrorCode status = U_ZERO_ERROR;
  1475.  
  1476.     // Iterate through all of the pre-composed characters in Unicode
  1477.     ComposedCharIter iter;
  1478.     UnicodeString decomp;
  1479.  
  1480.     while (iter.hasNext())
  1481.     {
  1482.         UChar c = iter.next();
  1483.         
  1484.         if (getCharOrder(c) == UNMAPPED)
  1485.         {
  1486.             // 
  1487.             // We don't already have an ordering for this pre-composed character.
  1488.             //
  1489.             // First, see if the decomposed string is already in our
  1490.             // tables as a single contracting-string ordering.
  1491.             // If so, just map the precomposed character to that order.
  1492.             //
  1493.             // TODO: What we should really be doing here is trying to find the
  1494.             // longest initial substring of the decomposition that is present
  1495.             // in the tables as a contracting character sequence, and find its
  1496.             // ordering.  Then do this recursively with the remaining chars
  1497.             // so that we build a list of orderings, and add that list to
  1498.             // the expansion table. 
  1499.             // That would be more correct but also significantly slower, so
  1500.             // I'm not totally sure it's worth doing.
  1501.             //
  1502.             iter.getDecomposition(decomp);
  1503.             int contractOrder = getContractOrder(decomp);
  1504.  
  1505.             if (contractOrder != UNMAPPED)
  1506.             {
  1507.                 addOrder(c, contractOrder, status);
  1508.             }
  1509.             else
  1510.             {
  1511.                 //
  1512.                 // We don't have a contracting ordering for the entire string
  1513.                 // that results from the decomposition, but if we have orders
  1514.                 // for each individual character, we can add an expanding
  1515.                 // table entry for the pre-composed character 
  1516.                 //
  1517.                 bool_t allThere = TRUE;
  1518.                 int32_t i;
  1519.  
  1520.                 for (i = 0; i < decomp.size(); i += 1)
  1521.                 {
  1522.                     if (getCharOrder(decomp[i]) == UNMAPPED)
  1523.                     {
  1524.                         allThere = FALSE;
  1525.                         break;
  1526.                     }
  1527.                 }
  1528.  
  1529.                 if (allThere)
  1530.                 {
  1531.                     buf.remove();
  1532.                     buf += c;
  1533.                     addExpandOrder(buf, decomp, UNMAPPED, status);
  1534.                 }
  1535.             }
  1536.         }
  1537.     }
  1538. }
  1539.     
  1540. // When the expanding character tables are built by addExpandOrder,
  1541. // it doesn't know what the final ordering of each character
  1542. // in the expansion will be.  Instead, it just puts the raw character
  1543. // code into the table, adding CHARINDEX as a flag.  Now that we've
  1544. // finished building the mapping table, we can go back and look up
  1545. // that character to see what its real collation order is and
  1546. // stick that into the expansion table.  That lets us avoid doing
  1547. // a two-stage lookup later.
  1548.  
  1549. void
  1550. RuleBasedCollator::commit()
  1551. {
  1552.     // if there are any expanding characters
  1553.     if (data->expandTable != NULL)
  1554.     {
  1555.         int32_t i;
  1556.         for (i = 0; i < data->expandTable->size(); i += 1)
  1557.         {
  1558.             VectorOfInt* valueList = data->expandTable->at(i);
  1559.             int32_t j;
  1560.             for (j = 0; j < valueList->size(); j++)
  1561.             {
  1562.                 // found a expanding character
  1563.                 // the expanding char value is not filled in yet
  1564.                 if ((valueList->at(j) < EXPANDCHARINDEX) &&
  1565.                     (valueList->at(j) > CHARINDEX))
  1566.                 {
  1567.                     // Get the real values for the non-filled entry
  1568.                     UChar ch = (UChar)(valueList->at(j) - CHARINDEX);
  1569.                     int32_t realValue = ucmp32_get(data->mapping, ch);
  1570.  
  1571.                     if (realValue == UNMAPPED)
  1572.                     {
  1573.                         // The real value is still unmapped, maybe it'signorable
  1574.                         valueList->atPut(j, IGNORABLEMASK & ch);
  1575.                     }
  1576.                     // fill in the value
  1577.                     else
  1578.                     {
  1579.                         valueList->atPut(j, realValue);
  1580.                     }
  1581.                 }
  1582.             }
  1583.         }
  1584.     }
  1585.  }
  1586.  
  1587. /**
  1588.  *  Increment of the last order based on the comparison level.
  1589.  */
  1590. int32_t
  1591. RuleBasedCollator::increment(Collator::ECollationStrength aStrength, int32_t lastValue)
  1592. {
  1593.     switch(aStrength)
  1594.     {
  1595.     case Collator::PRIMARY:
  1596.         // increment priamry order  and mask off secondary and tertiary difference
  1597.         lastValue += PRIMARYORDERINCREMENT;
  1598.         lastValue &= PRIMARYORDERMASK;
  1599.         isOverIgnore = TRUE;
  1600.         break;
  1601.  
  1602.     case Collator::SECONDARY:
  1603.         // increment secondary order and mask off tertiary difference
  1604.         lastValue += SECONDARYORDERINCREMENT;
  1605.         lastValue &= SECONDARYDIFFERENCEONLY;
  1606.  
  1607.         // record max # of ignorable chars with secondary difference
  1608.         if (isOverIgnore == FALSE)
  1609.         {
  1610.             data->maxSecOrder += 1;
  1611.         }
  1612.         break;
  1613.  
  1614.     case Collator::TERTIARY:
  1615.         // increment tertiary order
  1616.         lastValue += TERTIARYORDERINCREMENT;
  1617.  
  1618.         // record max # of ignorable chars with tertiary difference
  1619.         if (isOverIgnore == FALSE)
  1620.         {
  1621.             data->maxTerOrder += 1;
  1622.         }
  1623.         break;
  1624.     }
  1625.  
  1626.     return lastValue;
  1627. }
  1628.  
  1629. // Adds a character and its designated order into the collation table.
  1630. // This is the simple case, with no expansion or contraction
  1631. void
  1632. RuleBasedCollator::addOrder(UChar ch,
  1633.                          int32_t anOrder,
  1634.                          UErrorCode& status)
  1635. {
  1636.     if (U_FAILURE(status))
  1637.     {
  1638.         return;
  1639.     }
  1640.  
  1641.     // try to find the order of the char in the mapping table
  1642.     int32_t order = ucmp32_get(data->mapping, ch);
  1643.  
  1644.     if (order >= CONTRACTCHARINDEX)
  1645.     {
  1646.         // There's already an entry for this character that points to a contracting
  1647.         // character table.  Instead of adding the character directly to the mapping
  1648.         // table, we must add it to the contract table instead.
  1649.         key.remove();
  1650.         key += ch;
  1651.         if (key.isBogus())
  1652.         {
  1653.             status = U_MEMORY_ALLOCATION_ERROR;
  1654.             return;
  1655.         }
  1656.  
  1657.         addContractOrder(key, anOrder, status);
  1658.     }
  1659.     else
  1660.     {
  1661.         // add the entry to the mapping table, the same later entry replaces the previous one
  1662.         ucmp32_set(data->mapping, ch, anOrder);
  1663.     }
  1664. }
  1665.  
  1666. // Add an expanding-character entry to the table.
  1667. void
  1668. RuleBasedCollator::addExpandOrder(  const   UnicodeString& contractChars,
  1669.                                 const   UnicodeString& expandChars,
  1670.                                 int32_t anOrder,
  1671.                                 UErrorCode& status)
  1672. {
  1673.     if (U_FAILURE(status))
  1674.     {
  1675.         return;
  1676.     }
  1677.  
  1678.     // Create an expansion table entry
  1679.     int32_t tableIndex = addExpansion(anOrder, expandChars);
  1680.     
  1681.     // And add its index into the main mapping table
  1682.     if (contractChars.size() > 1)
  1683.     {
  1684.         addContractOrder(contractChars, tableIndex, status);
  1685.     }
  1686.     else
  1687.     {
  1688.         addOrder(contractChars[0], tableIndex, status);
  1689.     }
  1690. }
  1691.  
  1692. int32_t RuleBasedCollator::addExpansion(int32_t anOrder, const UnicodeString &expandChars)
  1693. {
  1694.     if (data->expandTable == NULL)
  1695.     {
  1696.         data->expandTable = new VectorOfPToExpandTable();
  1697.  
  1698.         if (data->expandTable == NULL)
  1699.         {
  1700.             return 0;
  1701.         }
  1702.     }
  1703.     
  1704.     // If anOrder is valid, we want to add it at the beginning of the list
  1705.     int32_t offset = (anOrder == UNMAPPED) ? 0 : 1;
  1706.     
  1707.     VectorOfInt *valueList = new VectorOfInt(expandChars.size() + offset);
  1708.  
  1709.     if (offset == 1)
  1710.     {
  1711.         valueList->atPut(0, anOrder);
  1712.     }
  1713.  
  1714.     int32_t i;
  1715.     for (i = 0; i < expandChars.size(); i += 1)
  1716.     {
  1717.         UChar ch = expandChars[i];
  1718.         int32_t mapValue = getCharOrder(ch);
  1719.         
  1720.         if (mapValue != UNMAPPED)
  1721.         {
  1722.             valueList->atPut(i + offset, mapValue);
  1723.         }
  1724.         else
  1725.         {
  1726.             // can't find it in the table, will be filled in by commit().
  1727.             valueList->atPut(i + offset, CHARINDEX + (int32_t)ch);
  1728.         }
  1729.     }
  1730.  
  1731.     // Add the expanding char list into the expansion table.
  1732.     int32_t tableIndex = EXPANDCHARINDEX + data->expandTable->size();
  1733.     data->expandTable->atPut(data->expandTable->size(), valueList);
  1734.     
  1735.     return tableIndex;
  1736. }
  1737.  
  1738. // Add a string of characters that contracts into a single ordering.
  1739. void
  1740. RuleBasedCollator::addContractOrder(const   UnicodeString& groupChars,
  1741.                                     int32_t anOrder,
  1742.                                     bool_t fwd,
  1743.                                     UErrorCode& status)
  1744. {
  1745.     if (U_FAILURE(status))
  1746.     {
  1747.         return;
  1748.     }
  1749.  
  1750.     if (data->contractTable == NULL)
  1751.     {
  1752.         data->contractTable = new VectorOfPToContractTable();
  1753.         if (data->contractTable->isBogus())
  1754.         {
  1755.             delete data->contractTable;
  1756.             data->contractTable = NULL;
  1757.             status = U_MEMORY_ALLOCATION_ERROR;
  1758.             return;
  1759.         }
  1760.     }
  1761.  
  1762.     // See if the initial character of the string already has a contract table.
  1763.     // e.g. for "ch", look for 'c'.
  1764.     int32_t entry = ucmp32_get(data->mapping, groupChars[0]);
  1765.     VectorOfPToContractElement *entryTable = getContractValues(entry - CONTRACTCHARINDEX);
  1766.  
  1767.     if (entryTable == NULL)
  1768.     {
  1769.         // We need to create a new table of contract entries for this base char
  1770.         int32_t tableIndex = CONTRACTCHARINDEX + data->contractTable->size();
  1771.         EntryPair *pair = NULL;
  1772.         UnicodeString substring;
  1773.  
  1774.         entryTable = new VectorOfPToContractElement();
  1775.         if (entryTable->isBogus())
  1776.         {
  1777.             delete entryTable;
  1778.             delete data->contractTable;
  1779.             data->contractTable = NULL;
  1780.             status = U_MEMORY_ALLOCATION_ERROR;
  1781.             return;
  1782.         }
  1783.  
  1784.         data->contractTable->atPut(data->contractTable->size(), entryTable);
  1785.         if (data->contractTable->isBogus())
  1786.         {
  1787.             delete entryTable;
  1788.             delete data->contractTable;
  1789.             data->contractTable = NULL;
  1790.             status = U_MEMORY_ALLOCATION_ERROR;
  1791.             return;
  1792.         }
  1793.             
  1794.  
  1795.         // Add the initial character's current ordering first. then
  1796.         // update its mapping to point to this contract table
  1797.         groupChars.extract(0, 1, substring);
  1798.         if (substring.isBogus())
  1799.         {
  1800.             delete entryTable;
  1801.             delete data->contractTable;
  1802.             data->contractTable = NULL;
  1803.             status = U_MEMORY_ALLOCATION_ERROR;
  1804.             return;
  1805.         }
  1806.  
  1807.         pair = new EntryPair(substring, entry);
  1808.  
  1809.         entryTable->atPut(0, pair);
  1810.         if (entryTable->isBogus())
  1811.         {
  1812.             delete entryTable;
  1813.             delete data->contractTable;
  1814.             data->contractTable = NULL;
  1815.             status = U_MEMORY_ALLOCATION_ERROR;
  1816.             return;
  1817.         }
  1818.  
  1819.         ucmp32_set(data->mapping, groupChars[0], tableIndex);
  1820.     }
  1821.  
  1822.     // Now add (or replace) this string in the table
  1823.     int32_t index = getEntry(entryTable, groupChars, fwd);
  1824.  
  1825.     if (index != UNMAPPED)
  1826.     {
  1827.         EntryPair *pair = (EntryPair *) entryTable->at(index);
  1828.         pair->value = anOrder;
  1829.     }
  1830.     else
  1831.     {
  1832.         EntryPair *pair = new EntryPair(groupChars, anOrder, fwd);
  1833.  
  1834.         entryTable->atPut(entryTable->size(), pair);
  1835.     }
  1836.     
  1837.     // If this was a forward mapping for a contracting string, also add a
  1838.     // reverse mapping for it, so that CollationElementIterator::previous
  1839.     // can work right
  1840.     if (fwd)
  1841.     {
  1842.         UnicodeString reverse(groupChars);
  1843.  
  1844.         if (reverse.isBogus())
  1845.         {
  1846.             delete entryTable;
  1847.             delete data->contractTable;
  1848.             data->contractTable = NULL;
  1849.             status = U_MEMORY_ALLOCATION_ERROR;
  1850.             return;
  1851.         }
  1852.  
  1853.         addContractOrder(reverse.reverse(), anOrder, FALSE, status);
  1854.     }
  1855. }
  1856.  
  1857. /**
  1858.  * If the given string has been specified as a contracting string
  1859.  * in this collation table, return its ordering.
  1860.  * Otherwise return UNMAPPED.
  1861.  */
  1862.  int32_t RuleBasedCollator::getContractOrder(const UnicodeString &groupChars) const
  1863. {
  1864.     int32_t result = UNMAPPED;
  1865.  
  1866.     if (data->contractTable != NULL)
  1867.     {
  1868.         VectorOfPToContractElement *entryTable = getContractValues(groupChars[0]);
  1869.  
  1870.         if (entryTable != NULL)
  1871.         {
  1872.             int32_t index = getEntry(entryTable, groupChars, TRUE);
  1873.  
  1874.             if (index != UNMAPPED)
  1875.             {
  1876.                 EntryPair *pair = entryTable->at(index);
  1877.  
  1878.                 result = pair->value;
  1879.             }
  1880.         }
  1881.     }
  1882.  
  1883.     return result;
  1884. }
  1885.  
  1886. int32_t RuleBasedCollator::getCharOrder(UChar ch) const
  1887. {
  1888.     int32_t order = ucmp32_get(data->mapping, ch);
  1889.     
  1890.     if (order >= CONTRACTCHARINDEX)
  1891.     {
  1892.         VectorOfPToContractElement *groupList = getContractValues(order - CONTRACTCHARINDEX);
  1893.         EntryPair *pair = groupList->at(0);
  1894.  
  1895.         order = pair->value;
  1896.     }
  1897.  
  1898.     return order;
  1899. }
  1900.     
  1901. // Create a hash code for this collation.  Just hash the main rule table --
  1902. // that should be good enough for almost any use.
  1903. int32_t
  1904. RuleBasedCollator::hashCode() const
  1905. {
  1906.     int32_t         value = 0;
  1907.     int32_t         c;
  1908.     int32_t         count = getRules().size();
  1909.     UTextOffset      pos = count - 1;
  1910.  
  1911.     if (count > 64)
  1912.     {
  1913.         count = 64; // only hash upto limit
  1914.     }
  1915.  
  1916.     int16_t i = 0;
  1917.  
  1918.     while (i < count)
  1919.     {
  1920.         c = data->ruleTable[pos];
  1921.         value = ((value << (c & 0x0f)) ^ (c << 8)) + (c ^ value);
  1922.         i += 1;
  1923.         pos -= 1;
  1924.     }
  1925.  
  1926.     if (value == 0)
  1927.     {
  1928.         value = 1;
  1929.     }
  1930.  
  1931.     return value;
  1932. }
  1933.  
  1934. // find the contracting char entry in the list
  1935. int32_t
  1936. RuleBasedCollator::getEntry(VectorOfPToContractElement* list,
  1937.                          const UnicodeString& name,
  1938.                          bool_t fwd)
  1939. {
  1940.     int32_t i;
  1941.  
  1942.     if (list != NULL)
  1943.     {
  1944.         for (i = 0; i < list->size(); i += 1)
  1945.         {
  1946.             EntryPair *pair = list->at(i);
  1947.  
  1948.             if ((pair != NULL) && (pair->fwd == fwd) && (pair->entryName == name))
  1949.             {
  1950.                 return i;
  1951.             }
  1952.         }
  1953.     }
  1954.  
  1955.     return RuleBasedCollator::UNMAPPED;
  1956. }
  1957.  
  1958. // look for the contracting list entry with the beginning char
  1959. VectorOfPToContractElement*
  1960. RuleBasedCollator::getContractValues(UChar ch) const
  1961. {
  1962.     int32_t index = ucmp32_get(data->mapping, ch);
  1963.     return getContractValues(index - CONTRACTCHARINDEX);
  1964. }
  1965.  
  1966. // look for the contracting list entry with the index
  1967. VectorOfPToContractElement*
  1968. RuleBasedCollator::getContractValues(int32_t    index) const
  1969. {
  1970.     if (data->contractTable != NULL)
  1971.     {
  1972.         if (index >= 0)
  1973.         {
  1974.             return data->contractTable->at(index);
  1975.         }
  1976.     }
  1977.     return NULL;
  1978. }
  1979.  
  1980. /**
  1981.   * Return the maximum length of any expansion sequences that end
  1982.   * with the specified comparison order.
  1983.   *
  1984.   * @param order a collation order returned by previous or next.
  1985.   * @return the maximum length of any expansion seuences ending
  1986.   *         with the specified order.
  1987.   *
  1988.   * @see CollationElementIterator#getMaxExpansion
  1989.   */
  1990. int32_t RuleBasedCollator::getMaxExpansion(int32_t order) const
  1991. {
  1992.     int32_t result = 1;
  1993.     
  1994.     if (data->expandTable != NULL)
  1995.     {
  1996.         // Right now this does a linear search through the entire
  1997.         // expandsion table.  If a collator had a large number of expansions,
  1998.         // this could cause a performance problem, but in practice that
  1999.         // rarely happens
  2000.         int32_t i;
  2001.         for (i = 0; i < data->expandTable->size(); i += 1)
  2002.         {
  2003.             VectorOfInt *valueList = data->expandTable->at(i);
  2004.             int32_t length = valueList->size();
  2005.             
  2006.             if (length > result && valueList->at(length-1) == order)
  2007.             {
  2008.                 result = length;
  2009.             }
  2010.         }
  2011.     }
  2012.  
  2013.     return result;
  2014. }
  2015.     
  2016. /**
  2017.  *  Get the entry of hash table of the expanding string in the collation
  2018.  *  table.
  2019.  *  @param idx the index of the expanding string value list
  2020.  */
  2021. VectorOfInt *RuleBasedCollator::getExpandValueList(int32_t order) const
  2022. {
  2023.     return data->expandTable->at(order - EXPANDCHARINDEX);
  2024. }
  2025.  
  2026. // Get the character order in the mapping table
  2027. int32_t
  2028. RuleBasedCollator::getUnicodeOrder(UChar ch) const
  2029. {
  2030.     return ucmp32_get(data->mapping, ch);
  2031. }
  2032.  
  2033.  
  2034. void RuleBasedCollatorStreamer::streamIn(RuleBasedCollator* collator, FileStream* is)
  2035. {
  2036.     if (!T_FileStream_error(is))
  2037.     {
  2038.         // Check that this is the correct file type
  2039.         int16_t id;
  2040.  
  2041.         T_FileStream_read(is, &id, sizeof(id));
  2042.         if (id != collator->FILEID)
  2043.         {
  2044.             // This isn't the right type of file.  Mark the ios
  2045.             // as failing and return.
  2046.             T_FileStream_setError(is); // force the stream to set its error flag
  2047.             return;
  2048.         }
  2049.  
  2050.         // Stream in large objects
  2051.         char isNull;
  2052.  
  2053.         T_FileStream_read(is, &isNull, sizeof(isNull));
  2054.         if (isNull)
  2055.         {
  2056.             delete collator->data;
  2057.             collator->data = NULL;
  2058.         }
  2059.         else
  2060.         {
  2061.             if (collator->data == NULL)
  2062.             {
  2063.                 collator->data = new TableCollationData;
  2064.             }
  2065.             
  2066.             collator->data->streamIn(is);
  2067.             if (collator->data->isBogus()) {
  2068.                 T_FileStream_setError(is); // force the stream to set its error flag
  2069.                 return;
  2070.             }
  2071.         }
  2072.  
  2073.         // Verify that the end marker is present
  2074.         T_FileStream_read(is, &id, sizeof(id));
  2075.         if (id != collator->FILEID)
  2076.         {
  2077.             // This isn't the right type of file.  Mark the ios
  2078.             // as failing and return.
  2079.             T_FileStream_setError(is); // force the stream to set its error flag
  2080.             return;
  2081.         }
  2082.  
  2083.         // Reset other data members
  2084.         collator->isOverIgnore = FALSE;
  2085.         collator->lastChar = 0;
  2086.         delete collator->mPattern;
  2087.         collator->mPattern = 0;
  2088.         collator->key.remove();
  2089.         collator->dataIsOwned = TRUE;
  2090.     }
  2091. }
  2092.  
  2093. void RuleBasedCollatorStreamer::streamOut(const RuleBasedCollator* collator, FileStream* os)
  2094. {
  2095.     if (!T_FileStream_error(os))
  2096.     {
  2097.         // We use a 16-bit ID code to identify this file.
  2098.         int16_t id = collator->FILEID;
  2099.         T_FileStream_write(os, &id, sizeof(id));
  2100.  
  2101.         // Stream out the data
  2102.         char isNull;
  2103.         isNull = (collator->data == 0);
  2104.         T_FileStream_write(os, &isNull, sizeof(isNull));
  2105.  
  2106.         if (!isNull)
  2107.         {
  2108.             collator->data->streamOut(os);
  2109.         }
  2110.  
  2111.         // Write out the ID to indicate the end
  2112.         T_FileStream_write(os, &id, sizeof(id));
  2113.     }
  2114. }
  2115.  
  2116. bool_t RuleBasedCollator::writeToFile(const char* fileName) const
  2117. {
  2118.     FileStream* ofs = T_FileStream_open(fileName, "wb");
  2119.     if (ofs != 0)
  2120.     {
  2121.         RuleBasedCollatorStreamer::streamOut(this, ofs);
  2122.     }
  2123.  
  2124. #ifdef COLLDEBUG
  2125.     fprintf(stderr, "binary write %s size %d %s\n", fileName, T_FileStream_size(ofs),
  2126.         (!T_FileStream_error(ofs) ? ", OK" : ", FAIL");
  2127. #endif
  2128.  
  2129.     bool_t err = T_FileStream_error(ofs) == 0;
  2130.  
  2131.     T_FileStream_close(ofs);
  2132.     return err;
  2133. }
  2134.  
  2135. void RuleBasedCollator::addToCache(const UnicodeString& key)
  2136. {
  2137.     // This method doesn't add the RuleBasedCollator itself to the cache.  Instead,
  2138.     // it adds the given RuleBasedCollator's data object to the TableCollationData
  2139.     // cache, and marks it as non-owned in the given RuleBasedCollator object.
  2140.     TableCollationData::addToCache(key, data);
  2141.     dataIsOwned = FALSE;
  2142. }
  2143.  
  2144. void
  2145. RuleBasedCollator::constructFromCache(const UnicodeString& key,
  2146.                                    UErrorCode& status)
  2147. {
  2148.     // Attempt to construct this RuleBasedCollator object from cached TableCollationData.
  2149.     // If no such data is in the cache, return false.
  2150.     if (U_FAILURE(status)) return;
  2151.     if (dataIsOwned)
  2152.     {
  2153.         delete data;
  2154.         data = NULL;
  2155.     }
  2156.  
  2157.     isOverIgnore = FALSE;
  2158.     lastChar = 0;
  2159.     mPattern = 0;
  2160.     setStrength(Collator::TERTIARY);
  2161.  
  2162.     dataIsOwned = FALSE;
  2163.     data = TableCollationData::findInCache(key);
  2164.     if (data == NULL)
  2165.     {
  2166.         status = U_MISSING_RESOURCE_ERROR;
  2167.     }
  2168. }
  2169.  
  2170. char*
  2171. RuleBasedCollator::createPathName(  const UnicodeString&    prefix,
  2172.                                 const UnicodeString&    name,
  2173.                                 const UnicodeString&    suffix)
  2174. {
  2175.     // Concatenate three elements to form a file name, and return it.
  2176.  
  2177.     UnicodeString   workingName(prefix);
  2178.     int32_t         size;
  2179.     char*           returnVal;
  2180.  
  2181.     workingName += name;
  2182.     workingName += suffix;
  2183.  
  2184.     size = workingName.size();
  2185.     returnVal = new char[size + 1];
  2186.     workingName.extract(0, size, returnVal);
  2187.     returnVal[size] = 0;
  2188.  
  2189.     return returnVal;
  2190. }
  2191.  
  2192. void
  2193. RuleBasedCollator::chopLocale(UnicodeString& localeName)
  2194. {
  2195.     // chopLocale removes the final element from a locale string.
  2196.     // For instance, "de_CH" becomes "de", and "de" becomes "".
  2197.     // "" remains "".
  2198.  
  2199.     int32_t     size = localeName.size();
  2200.     int32_t     i;
  2201.  
  2202.     for (i = size - 1; i > 0; i--)
  2203.     {
  2204.         if (localeName[i] == 0x005F)
  2205.         {
  2206.             break;
  2207.         }
  2208.     }
  2209.  
  2210.     if (i < 0)
  2211.     {
  2212.         i = 0;
  2213.     }
  2214.  
  2215.     localeName.remove(i, size - i);
  2216. }
  2217.  
  2218. //eof
  2219.