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

  1. /******************************************************************************
  2.  * COPYRIGHT:                                                               
  3.  *  (C) Copyright Taligent, Inc., 1996
  4.  *  (C) Copyright IBM Corp. 1996-1998
  5.  *  Licensed Material - Program-Property of IBM - All Rights Reserved.
  6.  *  US Government Users Restricted Rights - Use, duplication, or disclosure
  7.  *  restricted by GSA ADP Schedule Contact with IBM Corp.
  8.  *
  9.  ******************************************************************************
  10.  */
  11. //=============================================================================
  12. //
  13. // File mergecol.cpp
  14. //
  15. // Contains MergeCollation.  This classes job is to take one or more
  16. // strings that represent the orderings in a collation, in the form 
  17. // "a , A < b , B ....".  MergeCollation parses the string into a list of
  18. // PatternEntry objects that are sorted by their position in the collation
  19. // ordering.  The input string is allowed to have elements out of order, e.g.
  20. // "... b < c < d < e .....   c < ch".  After being parsed by MergeCollation,
  21. // the pattern entries will be in the proper order: "b", "c", "ch", "d", "e"
  22. //
  23. // Created by: Helena Shih
  24. //
  25. // Modification History:
  26. //
  27. //  Date        Name        Description
  28. //  3/5/97      mark        Cleaned up fixEntry().  Added constants BYTEPOWER
  29. //                          and BYTEMASK to replace BYTESIZE.
  30. //  6/17/97     helena      In getPattern, added the queue-up list for entries 
  31. //                          with the same extension chars.
  32. //  6/23/97     helena      Adding comments to make code more readable.
  33. //  8/13/98     erm         Synched up with 1.2 version of MergeCollation.java
  34. // 04/23/99     stephen     Removed EDecompositionMode, merged with
  35. //                          Normalizer::EMode
  36. //=============================================================================
  37.  
  38. #include "mergecol.h"
  39.  
  40. #include "tables.h"
  41.  
  42. #ifdef _DEBUG
  43. #include "unistrm.h"
  44. #endif
  45.  
  46. const   int32_t         MergeCollation::BITARRAYSIZE = 8192;
  47. const   uint8_t         MergeCollation::BITARRAYMASK = 0x1;
  48. const   int32_t         MergeCollation::BYTEPOWER = 3;
  49. const   int32_t         MergeCollation::BYTEMASK = (1 << BYTEPOWER) - 1;
  50.  
  51.  /**
  52.  * Creates from a pattern.
  53.  * If the input pattern is incorrect, error code will be set.
  54.  * @param pattern the merge collation pattern
  55.  * @param success error code input/output parameter.
  56.  */
  57. MergeCollation::MergeCollation(const    UnicodeString&  pattern,
  58.                                Normalizer::EMode decompMode,
  59.                    UErrorCode&      status)
  60.     : lastEntry(NULL), saveEntry(NULL)
  61. {
  62.   patterns = new VectorOfPointersToPatternEntry();
  63.   
  64.   
  65.   if (patterns == NULL)
  66.     {
  67.       status = U_MEMORY_ALLOCATION_ERROR;
  68.       return;
  69.     }
  70.  
  71.   statusArray = new uint8_t[BITARRAYSIZE];
  72.  
  73.   if (statusArray == NULL)
  74.     {
  75.       delete patterns;
  76.       status = U_MEMORY_ALLOCATION_ERROR;
  77.       return;
  78.     }
  79.  
  80.   int32_t i;
  81.   for (i = 0; i < BITARRAYSIZE; i += 1)
  82.     {
  83.       statusArray[i] = 0;
  84.     }
  85.  
  86.   setPattern(pattern, decompMode, status);
  87.   if (U_FAILURE(status))
  88.     {
  89.       delete [] statusArray;
  90.       statusArray = NULL;
  91.     }
  92. }
  93.  
  94. /**
  95.  * Copy constructor
  96.  * @param other the source merge collation object to be constructed with
  97.  */
  98. MergeCollation::MergeCollation(const    MergeCollation& other)
  99.     : lastEntry(NULL), saveEntry(NULL)
  100. {
  101.   // This copy ctor does a deep copy - it duplicates the PatternEntry
  102.   // objects as well as the vector object
  103.   patterns = new VectorOfPointersToPatternEntry(*other.patterns);
  104.  
  105.   int32_t i;
  106.   statusArray = new uint8_t[BITARRAYSIZE];
  107.   for (i = 0; i < BITARRAYSIZE; i += 1)
  108.     {
  109.       statusArray[i] = other.statusArray[i];
  110.     }
  111. }
  112.  
  113. // Assignment operator.  Does a deep copy.
  114. const MergeCollation&
  115. MergeCollation::operator=(const MergeCollation& other)
  116. {
  117.   if (this != &other)
  118.   {
  119.     *patterns = *other.patterns;
  120.     lastEntry = 0;
  121.     saveEntry = 0;
  122.  
  123.     int32_t i;
  124.     for (i = 0; i < BITARRAYSIZE; i += 1)
  125.     {
  126.       statusArray[i] = other.statusArray[i];
  127.     }
  128.   }
  129.  
  130.   return *this;
  131. }
  132.  
  133. /**
  134.  * Destructor
  135.  */
  136. MergeCollation::~MergeCollation()
  137. {
  138.   delete patterns;
  139.   delete [] statusArray;
  140. }
  141.  
  142. /**
  143.  * recovers current pattern as a string.
  144.  * Basically, this runs through the PatternEntry array and outputs
  145.  * @param result the string into which the pattern is recovered
  146.  * the proper string for each element.
  147.  * @param withWhiteSpace puts spacing around the entries, and \n
  148.  * before & and <
  149.  */
  150. UnicodeString&
  151. MergeCollation::getPattern(UnicodeString& result, bool_t withWhiteSpace) const
  152. {
  153.  
  154.   int32_t i;
  155.   PatternEntry *tmp = NULL;
  156.   VectorOfPointer *extList = NULL;
  157.  
  158.   result.remove();
  159.  
  160.   for (i = 0; i < patterns->size(); i += 1)
  161.     {
  162.       PatternEntry* entry = patterns->at(i);
  163.  
  164.       if (entry != NULL)
  165.     {
  166.       // if the entry is an expanding ligature, queue up the entries until
  167.       // the last same ligature has been processed.
  168.       if (entry->extension.size() != 0)
  169.         {
  170.           if (extList == NULL)
  171.         {
  172.           extList = new VectorOfPointer();
  173.         }
  174.  
  175.           extList->atInsert(extList->size(), (const void*&)entry);
  176.             }
  177.       else
  178.         {
  179.           // Process the queue-up list in reverse order to get the correct
  180.           // pattern.
  181.           if (extList != NULL)
  182.         {
  183.           const PatternEntry *last = findLastWithNoExtension(i - 1);
  184.  
  185.           for (int32_t j = extList->size() - 1; j >= 0 ; j -= 1)
  186.             {
  187.               tmp = (PatternEntry*)(extList->at(j));
  188.               tmp->addToBuffer(result, FALSE, withWhiteSpace, last);
  189.                     }
  190.  
  191.           delete extList;
  192.           extList = NULL;
  193.                 }
  194.  
  195.           entry->addToBuffer(result, FALSE, withWhiteSpace, NULL);
  196.             }
  197.         }
  198.     }
  199.  
  200.   // Complete the queue-up list if it isn't empty
  201.   if (extList != NULL)
  202.     {
  203.       const PatternEntry *last = findLastWithNoExtension(i - 1);
  204.  
  205.       for (int32_t j = extList->size() - 1; j >= 0 ; j -= 1)
  206.     {
  207.       tmp = (PatternEntry*)(extList->at(j));
  208.       tmp->addToBuffer(result, FALSE, withWhiteSpace, last);
  209.         }
  210.  
  211.       delete extList;
  212.     }
  213.  
  214.  
  215.   return result;
  216.  
  217. /**
  218.  * emits the pattern for collation builder.
  219.  * @param result the string into which the pattern is recovered
  220.  * @param withWhiteSpace puts spacing around the entries, and \n
  221.  * before & and <
  222.  * @return emits the string in the format understable to the collation
  223.  * builder.
  224.  */
  225. UnicodeString&
  226. MergeCollation::emitPattern(UnicodeString& result, bool_t withWhiteSpace) const 
  227. {
  228.   int32_t i;
  229.  
  230.   result.remove();
  231.  
  232.   for (i = 0; i < patterns->size(); i += 1)
  233.     {
  234.       PatternEntry *entry = (PatternEntry *)patterns->at(i);
  235.  
  236.       if (entry != NULL)
  237.     {
  238.       entry->addToBuffer(result, TRUE, withWhiteSpace, NULL);
  239.         }
  240.     }
  241.  
  242.   return result;
  243. }
  244.  
  245. /**
  246.  * sets the pattern.
  247.  */
  248. void MergeCollation::setPattern(const   UnicodeString&  pattern,
  249.                                 Normalizer::EMode decompMode,
  250.                 UErrorCode&      success)
  251. {
  252.   if (U_FAILURE(success))
  253.     {
  254.       return;
  255.     }
  256.  
  257.   patterns->clear();
  258.  
  259.   addPattern(pattern, decompMode, success);
  260.   if (U_FAILURE(success))
  261.     {
  262.       delete patterns;
  263.       patterns = NULL;
  264.     }
  265. }
  266.  
  267. /**
  268.  * adds a pattern string to the current list of patterns
  269.  * @param pattern the new pattern to be added
  270.  */
  271. void MergeCollation::addPattern(const   UnicodeString&  pattern,
  272.                                 Normalizer::EMode decompMode,
  273.                 UErrorCode&      success)
  274. {
  275.   if (U_FAILURE(success) || (pattern.size() == 0))
  276.     {
  277.       return;
  278.     }
  279.  
  280.   PatternEntry::Parser *parser = new PatternEntry::Parser(pattern, decompMode);
  281.     
  282.   PatternEntry *entry = parser->next(success);
  283.  
  284.   while (entry != NULL)
  285.     {
  286.       if (U_FAILURE(success))
  287.     {
  288.       delete entry;
  289.       break;
  290.     }
  291.  
  292.       fixEntry(entry, success);
  293.  
  294.       if (U_FAILURE(success))
  295.     {
  296.       delete entry;
  297.       break;
  298.     }
  299.  
  300.       entry = parser->next(success);
  301.     }
  302. }
  303.  
  304. /**
  305.  * gets count of separate entries
  306.  * @return the size of pattern entries
  307.  */
  308. int32_t 
  309. MergeCollation::getCount() const {
  310.   return patterns->size();
  311. }   
  312.  
  313. /**
  314.  * gets count of separate entries
  315.  * @param index the offset of the desired pattern entry
  316.  * @return the requested pattern entry
  317.  */
  318. const PatternEntry* MergeCollation::getItemAt(UTextOffset index) const {
  319.   return patterns->at(index);
  320. }
  321.  
  322. // Find the last no-extension entry.
  323. const PatternEntry* MergeCollation::findLastWithNoExtension(int32_t i) const {
  324.   for (--i;i >= 0; --i) {
  325.     PatternEntry* entry = patterns->at(i);
  326.     if ((entry != 0) && (entry->extension.size() == 0)) {
  327.       return entry;
  328.     }
  329.   }
  330.   return 0;
  331. }
  332.  
  333. // Add a new PatternEntry to this MergeCollation's ordered list
  334. // of entries.
  335. //
  336. // If the strength is RESET, then just change the lastEntry to
  337. // be the current. (If the current is not in patterns, signal an error).
  338. //
  339. // If not, then remove the current entry, and add it after lastEntry
  340. // (which is usually at the end).
  341. //
  342. void MergeCollation::fixEntry(PatternEntry* newEntry,
  343.                               UErrorCode&    success)
  344. {
  345.   UnicodeString excess;
  346.   bool_t changeLastEntry = TRUE;
  347.  
  348.   if (newEntry->strength != PatternEntry::RESET)
  349.     {
  350.       int32_t oldIndex = -1;
  351.  
  352.       // Use statusArray to mark if a unicode character has been
  353.       // added in the table or not.  The same later entry will 
  354.       // replace the previous one.  This will improve the single
  355.       // char entries dramatically which is the majority of the 
  356.       // entries.
  357.       if (newEntry->chars.size() == 1)
  358.     {
  359.       UChar c = newEntry->chars[0];
  360.       int32_t statusIndex = c >> BYTEPOWER;
  361.       uint8_t bitClump = statusArray[statusIndex];
  362.       uint8_t setBit = BITARRAYMASK << (c & BYTEMASK);
  363.  
  364.       if (bitClump != 0 && (bitClump & setBit) != 0)
  365.             {
  366.           int32_t i = 0;
  367.  
  368.           // Find the previous entry with the same key
  369.           for (i = patterns->size() - 1; i >= 0; i -= 1)
  370.         {
  371.           PatternEntry *entry = patterns->at(i);
  372.  
  373.           if ((entry != 0) &&
  374.               (entry->chars == newEntry->chars))
  375.             {
  376.               oldIndex = i;
  377.               break;
  378.                     }
  379.                 }
  380.         }
  381.       else
  382.         {
  383.           // We're going to add an element that starts with this
  384.           // character, so go ahead and set its bit.
  385.           statusArray[statusIndex] = (uint8_t)(bitClump | setBit);
  386.             } 
  387.         }
  388.       else
  389.     {
  390.       oldIndex = patterns->lastIndexOf(newEntry);
  391.         }
  392.  
  393.       if (oldIndex != -1)
  394.     {
  395.       PatternEntry *p = patterns->orphanAt(oldIndex);
  396.       delete p;
  397.         }
  398.  
  399.       // Find the insertion point for the new entry.
  400.       int32_t lastIndex = findLastEntry(lastEntry, excess, success);
  401.  
  402.       if (U_FAILURE(success))
  403.     {
  404.       return;
  405.     }
  406.  
  407.       // Do not change the last entry if the new entry is a expanding character
  408.       if (excess.size() != 0)
  409.     {
  410.       // newEntry.extension = excess + newEntry.extensions;
  411.       newEntry->extension.insert(0, excess);
  412.       if (lastIndex != patterns->size())
  413.         {
  414.           lastEntry = saveEntry;
  415.           changeLastEntry = FALSE;
  416.             }
  417.         }
  418.  
  419.       // Add the entry at the end or insert it in the middle
  420.       if (lastIndex == patterns->size())
  421.     {
  422.       patterns->atPut(lastIndex, newEntry);
  423.       saveEntry = newEntry;
  424.  
  425.         }
  426.       else
  427.     {
  428.       patterns->atInsert(lastIndex, newEntry);  // add at end
  429.         }
  430.     }
  431.     
  432.   if (changeLastEntry)
  433.     {
  434.       lastEntry = newEntry;
  435.     }
  436. }
  437.  
  438. int32_t
  439. MergeCollation::findLastEntry(const PatternEntry*   lastEntry,
  440.                   UnicodeString&  excess,
  441.                   UErrorCode&      success) const
  442. {
  443.   if (U_FAILURE(success))
  444.     {
  445.       return -1;
  446.     }
  447.  
  448.   if (lastEntry == NULL)
  449.     {
  450.       return 0;
  451.     }
  452.   else if (lastEntry->strength != PatternEntry::RESET)
  453.     {
  454.       int32_t oldIndex = -1;
  455.  
  456.       // If the last entry is a single char entry and has been installed, 
  457.       // that means the last index is the real last index.
  458.       if (lastEntry->chars.size() == 1)
  459.     {
  460.       int32_t index = lastEntry->chars[0] >> BYTEPOWER;
  461.  
  462.       if ((statusArray[index] & 
  463.            (uint8_t)(BITARRAYMASK << (lastEntry->chars[0] & BYTEMASK))) != 0)
  464.         {
  465.           oldIndex = patterns->lastIndexOf(lastEntry);
  466.             }
  467.         }
  468.       else
  469.     {
  470.       oldIndex = patterns->lastIndexOf(lastEntry);
  471.         }
  472.  
  473.       // must exist!
  474.       if (oldIndex == -1)
  475.     {
  476.       success = U_INVALID_FORMAT_ERROR;
  477.       return oldIndex;
  478.         }
  479.  
  480.       return oldIndex + 1;
  481.     }
  482.   else
  483.     {
  484.       // We're doing a reset, i.e. inserting a new ordering at the position
  485.       // just after the entry corresponding to lastEntry's first character
  486.       int32_t i;
  487.  
  488.       // Search backwards for string that contains this one;
  489.       // most likely entry is last one
  490.       for (i = patterns->size() - 1; i >= 0; i -= 1)
  491.     {
  492.       PatternEntry* entry = patterns->at(i);
  493.       UnicodeString buffer;
  494.       if (entry != 0)
  495.         {
  496.           //
  497.           // Look for elements with the same beginning key.  The extra
  498.           // characters will be the expanding portion.  This handles cases like
  499.           // "& Question-mark < '?'".  We find the existing PatternEntry that matches
  500.           // the longest possible substring of "Question-mark", which will probably
  501.           // be 'Q'.  We save the characters that didn't match ("uestion-mark" in
  502.           // this case), and then return the next index.
  503.           //
  504.           if (entry->chars.compareBetween(0, entry->chars.size(),
  505.                           lastEntry->chars,0,entry->chars.size()) == 0)
  506.         {
  507.           lastEntry->chars.extractBetween(entry->chars.size(), 
  508.                           lastEntry->chars.size(),
  509.                           buffer);
  510.           excess += buffer;
  511.           break;
  512.                 }
  513.         }
  514.         }
  515.  
  516.       if (i == -1)
  517.     {
  518.       success = U_INVALID_FORMAT_ERROR;
  519.       return i;
  520.         }
  521.  
  522.       return i + 1;
  523.     }
  524. }
  525.