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

  1. /*
  2. **********************************************************************
  3. *   Copyright (C) 1999 Alan Liu and others. All rights reserved.
  4. **********************************************************************
  5. *   Date        Name        Description
  6. *   10/22/99    alan        Creation.
  7. **********************************************************************
  8. */
  9.  
  10. #include "rbbi.h"
  11. #include "rbbi_bld.h"
  12.  
  13. /**
  14.  * A token used as a character-category value to identify ignore characters
  15.  */
  16. int8_t RuleBasedBreakIterator::IGNORE = -1;
  17.  
  18. /**
  19.  * The state number of the starting state
  20.  */
  21. int16_t RuleBasedBreakIterator::START_STATE = 1;
  22.  
  23. /**
  24.  * The state-transition value indicating "stop"
  25.  */
  26. int16_t RuleBasedBreakIterator::STOP_STATE = 0;
  27.  
  28. //=======================================================================
  29. // constructors
  30. //=======================================================================
  31.  
  32. /**
  33.  * Constructs a RuleBasedBreakIterator according to the description
  34.  * provided.  If the description is malformed, throws an
  35.  * IllegalArgumentException.  Normally, instead of constructing a
  36.  * RuleBasedBreakIterator directory, you'll use the factory methods
  37.  * on BreakIterator to create one indirectly from a description
  38.  * in the framework's resource files.  You'd use this when you want
  39.  * special behavior not provided by the built-in iterators.
  40.  */
  41. RuleBasedBreakIterator::RuleBasedBreakIterator(const UnicodeString& description) {
  42.     this.description = description;
  43.     
  44.     // the actual work is done by the Builder class
  45.     Builder builder;
  46.     builder.buildBreakIterator(*this, description);
  47. }
  48.  
  49. //=======================================================================
  50. // boilerplate
  51. //=======================================================================
  52. /**
  53.  * Clones this iterator.
  54.  * @return A newly-constructed RuleBasedBreakIterator with the same
  55.  * behavior as this one.
  56.  */
  57. RuleBasedBreakIterator* RuleBasedBreakIterator::clone() const {
  58.     return new RuleBasedBreakIterator(*this);
  59. }
  60.  
  61. /**
  62.  * Returns true if both BreakIterators are of the same class, have the same
  63.  * rules, and iterate over the same text.
  64.  */
  65. bool_t RuleBasedBreakIterator::operator==(const RuleBasedBreakIterator& that) {
  66.     return description.equals(((RuleBasedBreakIterator)that).description)
  67.         && text.equals(((RuleBasedBreakIterator)that).text);
  68. }
  69.  
  70. /**
  71.  * Returns the description used to create this iterator
  72.  */
  73. UnicodeString RuleBasedBreakIterator::toString() {
  74.     return description;
  75. }
  76.  
  77. /**
  78.  * Compute a hashcode for this BreakIterator
  79.  * @return A hash code
  80.  */
  81. int32_t RuleBasedBreakIterator::hashCode() {
  82.     return description.hashCode();
  83. }
  84.  
  85. //=======================================================================
  86. // BreakIterator overrides
  87. //=======================================================================
  88. /**
  89.  * Sets the current iteration position to the beginning of the text.
  90.  * (i.e., the CharacterIterator's starting offset).
  91.  * @return The offset of the beginning of the text.
  92.  */
  93. int32_t RuleBasedBreakIterator::first() {
  94.     CharacterIterator t = getText();
  95.  
  96.     t.first();
  97.     return t.getIndex();
  98. }
  99.  
  100. /**
  101.  * Sets the current iteration position to the end of the text.
  102.  * (i.e., the CharacterIterator's ending offset).
  103.  * @return The text's past-the-end offset.
  104.  */
  105. int32_t RuleBasedBreakIterator::last() {
  106.     CharacterIterator t = getText();
  107.  
  108.     // I'm not sure why, but t.last() returns the offset of the last character,
  109.     // rather than the past-the-end offset
  110.     t.setIndex(t.getEndIndex());
  111.     return t.getIndex();
  112. }
  113.  
  114. /**
  115.  * Advances the iterator either forward or backward the specified number of steps.
  116.  * Negative values move backward, and positive values move forward.  This is
  117.  * equivalent to repeatedly calling next() or previous().
  118.  * @param n The number of steps to move.  The sign indicates the direction
  119.  * (negative is backwards, and positive is forwards).
  120.  * @return The character offset of the boundary position n boundaries away from
  121.  * the current one.
  122.  */
  123. int32_t RuleBasedBreakIterator::next(int32_t n) {
  124.     int32_t result = current();
  125.     while (n > 0) {
  126.         result = handleNext();
  127.         --n;
  128.     }
  129.     while (n < 0) {
  130.         result = previous();
  131.         ++n;
  132.     }
  133.     return result;
  134. }
  135.  
  136. /**
  137.  * Advances the iterator to the next boundary position.
  138.  * @return The position of the first boundary after this one.
  139.  */
  140. int32_t RuleBasedBreakIterator::next() {
  141.     return handleNext();
  142. }
  143.  
  144. /**
  145.  * Advances the iterator backwards, to the last boundary preceding this one.
  146.  * @return The position of the last boundary position preceding this one.
  147.  */
  148. int32_t RuleBasedBreakIterator::previous() {
  149.     // if we're already sitting at the beginning of the text, return DONE
  150.     CharacterIterator text = getText();
  151.     if (current() == text.getBeginIndex())
  152.         return BreakIterator.DONE;
  153.  
  154.     // set things up.  handlePrevious() will back us up to some valid
  155.     // break position before the current position (we back our internal
  156.     // iterator up one step to prevent handlePrevious() from returning
  157.     // the current position), but not necessarily the last one before
  158.     // where we started
  159.     int32_t start = current();
  160.     text.previous();
  161.     int32_t lastResult = handlePrevious();
  162.     int32_t result = lastResult;
  163.     
  164.     // iterate forward from the known break position until we pass our
  165.     // starting point.  The last break position before the starting
  166.     // point is our return value
  167.     while (result != BreakIterator.DONE && result < start) {
  168.         lastResult = result;
  169.         result = handleNext();
  170.     }
  171.     
  172.     // set the current iteration position to be the last break position
  173.     // before where we started, and then return that value
  174.     text.setIndex(lastResult);
  175.     return lastResult;
  176. }
  177.  
  178. /**
  179.  * Sets the iterator to refer to the first boundary position following
  180.  * the specified position.
  181.  * @offset The position from which to begin searching for a break position.
  182.  * @return The position of the first break after the current position.
  183.  */
  184. int32_t RuleBasedBreakIterator::following(int32_t offset) {
  185.     // if the offset passed in is already past the end of the text,
  186.     // just return DONE
  187.     CharacterIterator text = getText();
  188.     if (offset == text.getEndIndex())
  189.         return BreakIterator.DONE;
  190.  
  191.     // otherwise, set our internal iteration position (temporarily)
  192.     // to the position passed in.  If this is the _beginning_ position,
  193.     // then we can just use next() to get our return value
  194.     text.setIndex(offset);
  195.     if (offset == text.getBeginIndex())
  196.         return handleNext();
  197.  
  198.     // otherwise, we have to sync up first.  Use handlePrevious() to back
  199.     // us up to a known break position before the specified position (if
  200.     // we can determine that the specified position is a break position,
  201.     // we don't back up at all).  This may or may not be the last break
  202.     // position at or before our starting position.  Advance forward
  203.     // from here until we've passed the starting position.  The position
  204.     // we stop on will be the first break position after the specified one.
  205.     int32_t result = handlePrevious();
  206.     while (result != BreakIterator.DONE && result <= offset)
  207.         result = handleNext();
  208.     return result;
  209. }
  210.  
  211. /**
  212.  * Sets the iterator to refer to the last boundary position before the
  213.  * specified position.
  214.  * @offset The position to begin searching for a break from.
  215.  * @return The position of the last boundary before the starting position.
  216.  */
  217. int32_t RuleBasedBreakIterator::preceding(int32_t offset) {
  218.     // if we start by updating the current iteration position to the
  219.     // position specified by the caller, we can just use previous()
  220.     // to carry out this operation
  221.     CharacterIterator text = getText();
  222.     text.setIndex(offset);
  223.     return previous();
  224. }
  225.  
  226. /**
  227.  * Returns true if the specfied position is a boundary position.  As a side
  228.  * effect, leaves the iterator pointing to the first boundary position at
  229.  * or after "offset".
  230.  * @param offset the offset to check.
  231.  * @return True if "offset" is a boundary position.
  232.  */
  233. bool_t RuleBasedBreakIterator::isBoundary(int32_t offset) {
  234.     // 0 is always a boundary position (I suspect this code is wrong; I think
  235.     // we're supposed to be comparing "offset" against text.getBeginIndex(). )
  236.     if (offset == 0)
  237.         return TRUE;
  238.         
  239.     // otherwise, we can use following() on the position before the specified
  240.     // one and return true of the position we get back is the one the user
  241.     // specified
  242.     else
  243.         return following(offset - 1) == offset;
  244. }
  245.  
  246. /**
  247.  * Returns the current iteration position.
  248.  * @return The current iteration position.
  249.  */
  250. int32_t RuleBasedBreakIterator::current() {
  251.     return getText().getIndex();
  252. }
  253.  
  254. /**
  255.  * Return a CharacterIterator over the text being analyzed.  This version
  256.  * of this method returns the actual CharacterIterator we're using internally.
  257.  * Changing the state of this iterator can have undefined consequences.  If
  258.  * you need to change it, clone it first.
  259.  * @return An iterator over the text being analyzed.
  260.  */
  261. CharacterIterator RuleBasedBreakIterator::getText() {
  262.     // The iterator is initialized pointing to no text at all, so if this
  263.     // function is called while we're in that state, we have to fudge an
  264.     // an iterator to return.
  265.     if (text == 0)
  266.         text = new StringCharacterIterator("");
  267.     return text;
  268. }
  269.  
  270. /**
  271.  * Set the iterator to analyze a new piece of text.  This function resets
  272.  * the current iteration position to the beginning of the text.
  273.  * @param newText An iterator over the text to analyze.
  274.  */
  275. void RuleBasedBreakIterator::setText(CharacterIterator newText) {
  276.     text = newText;
  277.     text.first();
  278. }
  279. //=======================================================================
  280. // implementation
  281. //=======================================================================
  282. /**
  283.  * This method is the actual implementation of the next() method.  All iteration
  284.  * vectors through here.  This method initializes the state machine to state 1
  285.  * and advances through the text character by character until we reach the end
  286.  * of the text or the state machine transitions to state 0.  We update our return
  287.  * value every time the state machine passes through a possible end state.
  288.  */
  289. int32_t RuleBasedBreakIterator::handleNext() {
  290.     // if we're already at the end of the text, return DONE.
  291.     CharacterIterator text = getText();
  292.     if (text.getIndex() == text.getEndIndex())
  293.         return BreakIterator.DONE;
  294.  
  295.     // no matter what, we always advance at least one character forward
  296.     int32_t result = text.getIndex() + 1;
  297.     
  298.     // begin in state 1
  299.     int32_t state = START_STATE;
  300.     int32_t category;
  301.     UChar c = text.current();
  302.  
  303.     // loop until we reach the end of the text or transition to state 0
  304.     while (c != CharacterIterator.DONE && state != STOP_STATE) {
  305.  
  306.         // look up the current character's character category (which tells us
  307.         // which column in the state table to look at)
  308.         category = lookupCategory(c);
  309.         
  310.         // if the character isn't an ignore character, look up a state
  311.         // transition in the state table
  312.         if (category != IGNORE) {
  313.             state = lookupState(state, category);
  314.         }
  315.         
  316.         // if the state we've just transitioned to is an accepting state,
  317.         // update our return value to be the current iteration position
  318.         if (endStates[state])
  319.             result = text.getIndex() + 1;
  320.         c = text.next();
  321.     }
  322.     text.setIndex(result);
  323.     return result;
  324. }
  325.  
  326. /**
  327.  * This method backs the iterator back up to a "safe position" in the text.
  328.  * This is a position that we know, without any context, must be a break position.
  329.  * The various calling methods then iterate forward from this safe position to
  330.  * the appropriate position to return.  (For more information, see the description
  331.  * of buildBackwardsStateTable() in RuleBasedBreakIterator.Builder.)
  332.  */
  333. int32_t RuleBasedBreakIterator::handlePrevious() {
  334.     CharacterIterator text = getText();
  335.     int32_t state = START_STATE;
  336.     int32_t category = 0;
  337.     int32_t lastCategory = 0;
  338.     UChar c = text.current();
  339.     
  340.     // loop until we reach the beginning of the text or transition to state 0
  341.     while (c != CharacterIterator.DONE && state != STOP_STATE) {
  342.  
  343.         // save the last character's category and look up the current
  344.         // character's category
  345.         lastCategory = category;
  346.         category = lookupCategory(c);
  347.         
  348.         // if the current character isn't an ignore character, look up a
  349.         // state transition in the backwards state table
  350.         if (category != IGNORE)
  351.             state = lookupBackwardState(state, category);
  352.             
  353.         // then advance one character backwards
  354.         c = text.previous();
  355.     }
  356.     
  357.     // if we didn't march off the beginning of the text, we're either one or two
  358.     // positions away from the real break position.  (One because of the call to
  359.     // previous() at the end of the loop above, and another because the character
  360.     // that takes us into the stop state will always be the character BEFORE
  361.     // the break position.)
  362.     if (c != CharacterIterator.DONE) {
  363.         if (lastCategory != IGNORE)
  364.             text.setIndex(text.getIndex() + 2);
  365.         else
  366.             text.next();
  367.     }
  368.     return text.getIndex();
  369. }
  370.  
  371. /**
  372.  * Looks up a character's category (i.e., its category for breaking purposes,
  373.  * not its Unicode category)
  374.  */
  375. int32_t RuleBasedBreakIterator::lookupCategory(UChar c) {
  376.     return UCharCategoryTable.elementAt(c);
  377. }
  378.  
  379. /**
  380.  * Given a current state and a character category, looks up the
  381.  * next state to transition to in the state table.
  382.  */
  383. int32_t RuleBasedBreakIterator::lookupState(int32_t state, int32_t category) {
  384.     return stateTable[state * numCategories + category];
  385. }
  386.  
  387. /**
  388.  * Given a current state and a character category, looks up the
  389.  * next state to transition to in the backwards state table.
  390.  */
  391. int32_t RuleBasedBreakIterator::lookupBackwardState(int32_t state, int32_t category) {
  392.     return backwardsStateTable[state * numCategories + category];
  393. }
  394.