text: /********************************************************************
   * COPYRIGHT: 
   * Copyright (c) 2001-2005, International Business Machines Corporation and
   * others. All Rights Reserved.
   ********************************************************************/
  /************************************************************************
  *   This test program is intended for testing Replaceable class.
  *
  *   Date        Name        Description
  *   11/28/2001  hshih       Ported back from Java.
  * 
  ************************************************************************/
  
  #include "unicode/utypes.h"
  
  #if !UCONFIG_NO_TRANSLITERATION
  
  #include "ittrans.h"
  #include <string.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include "unicode/rep.h"
  #include "reptest.h"
  
  //---------------------------------------------
  // runIndexedTest
  //---------------------------------------------
  
      /**
       * This is a test class that simulates styled text.
       * It associates a style number (0..65535) with each character,
       * and maintains that style in the normal fashion:
       * When setting text from raw string or characters,<br>
       * Set the styles to the style of the first character replaced.<br>
       * If no characters are replaced, use the style of the previous character.<br>
       * If at start, use the following character<br>
       * Otherwise use NO_STYLE.
       */
  class TestReplaceable : public Replaceable {
      UnicodeString chars;
      UnicodeString styles;
      
      static const UChar NO_STYLE;
  
      static const UChar NO_STYLE_MARK;
  
      /**
       * The address of this static class variable serves as this class's ID
       * for ICU "poor man's RTTI".
       */
      static const char fgClassID;
  
  public:    
      TestReplaceable (const UnicodeString& text, 
                       const UnicodeString& newStyles) {
          chars = text;
          UnicodeString s;
          for (int i = 0; i < text.length(); ++i) {
              if (i < newStyles.length()) {
                  s.append(newStyles.charAt(i));
              } else {
                  if (text.charAt(i) == NO_STYLE_MARK) {
                      s.append(NO_STYLE);
                  } else {
                      s.append((UChar)(i + 0x0031));
                  }
              }
          }
          this->styles = s;
      }
  
      virtual Replaceable *clone() const {
          return new TestReplaceable(chars, styles);
      }
  
      ~TestReplaceable(void) {}
  
      UnicodeString getStyles() {
          return styles;
      }
      
      UnicodeString toString() {
          UnicodeString s = chars;
          s.append("{");
          s.append(styles);
          s.append("}");
          return s;
      }
  
      void extractBetween(int32_t start, int32_t limit, UnicodeString& result) const {
          chars.extractBetween(start, limit, result);
      }
  
      /**
       * ICU "poor man's RTTI", returns a UClassID for this class.
       *
       * @draft ICU 2.2
       */
      static inline UClassID getStaticClassID() { return (UClassID)&fgClassID; }
  
      /**
       * ICU "poor man's RTTI", returns a UClassID for the actual class.
       *
       * @draft ICU 2.2
       */
      virtual inline UClassID getDynamicClassID() const { return getStaticClassID(); }
  
  protected:
      virtual int32_t getLength() const {
          return chars.length();
      }
  
      virtual UChar getCharAt(int32_t offset) const{
          return chars.charAt(offset);
      }
  
      virtual UChar32 getChar32At(int32_t offset) const{
          return chars.char32At(offset);
      }
  
      void fixStyles(int32_t start, int32_t limit, int32_t newLen) {
          UChar newStyle = NO_STYLE;
          if (start != limit && styles.charAt(start) != NO_STYLE) {
              newStyle = styles.charAt(start);
          } else if (start > 0 && getCharAt(start-1) != NO_STYLE_MARK) {
              newStyle = styles.charAt(start-1);
          } else if (limit < styles.length()) {
              newStyle = styles.charAt(limit);
          }
          // dumb implementation for now.
          UnicodeString s;
          for (int i = 0; i < newLen; ++i) {
              // this doesn't really handle an embedded NO_STYLE_MARK
              // in the middle of a long run of characters right -- but
              // that case shouldn't happen anyway
              if (getCharAt(start+i) == NO_STYLE_MARK) {
                  s.append(NO_STYLE);
              } else {
                  s.append(newStyle);
              }
          }
          styles.replaceBetween(start, limit, s);
      }
  
      virtual void handleReplaceBetween(int32_t start, int32_t limit, const UnicodeString& text) {
          UnicodeString s;
          this->extractBetween(start, limit, s);
          if (s == text) return; // NO ACTION!
          this->chars.replaceBetween(start, limit, text);
          fixStyles(start, limit, text.length());
      }
      
  
      virtual void copy(int32_t start, int32_t limit, int32_t dest) {
          chars.copy(start, limit, dest);
          styles.copy(start, limit, dest);
      }
  };
  
  const char TestReplaceable::fgClassID=0;
  
  const UChar TestReplaceable::NO_STYLE  = 0x005F;
  
  const UChar TestReplaceable::NO_STYLE_MARK = 0xFFFF;
  
  void
  ReplaceableTest::runIndexedTest(int32_t index, UBool exec,
                                        const char* &name, char* /*par*/) {
      switch (index) {
          TESTCASE(0,TestReplaceableClass);
          default: name = ""; break;
      }
  }
  
  /*
   * Dummy Replaceable implementation for better API/code coverage.
   */
  class NoopReplaceable : public Replaceable {
  public:
      virtual int32_t getLength() const {
          return 0;
      }
  
      virtual UChar getCharAt(int32_t /*offset*/) const{
          return 0xffff;
      }
  
      virtual UChar32 getChar32At(int32_t /*offset*/) const{
          return 0xffff;
      }
  
      void extractBetween(int32_t /*start*/, int32_t /*limit*/, UnicodeString& result) const {
          result.remove();
      }
  
      virtual void handleReplaceBetween(int32_t /*start*/, int32_t /*limit*/, const UnicodeString &/*text*/) {
          /* do nothing */
      }
  
      virtual void copy(int32_t /*start*/, int32_t /*limit*/, int32_t /*dest*/) {
          /* do nothing */
      }
  
      static inline UClassID getStaticClassID() { return (UClassID)&fgClassID; }
      virtual inline UClassID getDynamicClassID() const { return getStaticClassID(); }
  
  private:
      static const char fgClassID;
  };
  
  const char NoopReplaceable::fgClassID=0;
  
  void ReplaceableTest::TestReplaceableClass(void) {
      UChar rawTestArray[][6] = {
          {0x0041, 0x0042, 0x0043, 0x0044, 0x0000, 0x0000}, // ABCD
          {0x0061, 0x0062, 0x0063, 0x0064, 0x00DF, 0x0000}, // abcd\u00DF
          {0x0061, 0x0042, 0x0043, 0x0044, 0x0000, 0x0000}, // aBCD
          {0x0041, 0x0300, 0x0045, 0x0300, 0x0000, 0x0000}, // A\u0300E\u0300
          {0x00C0, 0x00C8, 0x0000, 0x0000, 0x0000, 0x0000}, // \u00C0\u00C8
          {0x0077, 0x0078, 0x0079, 0x0000, 0x0000, 0x0000}, /* "wxy" */
          {0x0077, 0x0078, 0x0079, 0x007A, 0x0000, 0x0000}, /* "wxyz" */
          {0x0077, 0x0078, 0x0079, 0x007A, 0x0075, 0x0000}, /* "wxyzu" */
          {0x0078, 0x0079, 0x007A, 0x0000, 0x0000, 0x0000}, /* "xyz" */
          {0x0077, 0x0078, 0x0079, 0x0000, 0x0000, 0x0000}, /* "wxy" */
          {0xFFFF, 0x0078, 0x0079, 0x0000, 0x0000, 0x0000}, /* "*xy" */
          {0xFFFF, 0x0078, 0x0079, 0x0000, 0x0000, 0x0000}, /* "*xy" */
      };
      check("Lower", rawTestArray[0], "1234");
      check("Upper", rawTestArray[1], "123455"); // must map 00DF to SS
      check("Title", rawTestArray[2], "1234");
      check("NFC",   rawTestArray[3], "13");
      check("NFD",   rawTestArray[4], "1122");
      check("*(x) > A $1 B", rawTestArray[5], "11223");
      check("*(x)(y) > A $2 B $1 C $2 D", rawTestArray[6], "113322334");
      check("*(x)(y)(z) > A $3 B $2 C $1 D", rawTestArray[7], "114433225");
      // Disabled for 2.4.  TODO Revisit in 2.6 or later.
      //check("*x > a", rawTestArray[8], "223"); // expect "123"?
      //check("*x > a", rawTestArray[9], "113"); // expect "123"?
      //check("*x > a", rawTestArray[10], "_33"); // expect "_23"?
      //check("*(x) > A $1 B", rawTestArray[11], "__223");
  
      // improve API/code coverage
      NoopReplaceable noop;
      Replaceable *p;
      if((p=noop.clone())!=NULL) {
          errln("Replaceable::clone() does not return NULL");
          delete p;
      }
  
      if(!noop.hasMetaData()) {
          errln("Replaceable::hasMetaData() does not return TRUE");
      }
  
      // try to call the compiler-provided
      // UMemory/UObject/Replaceable assignment operators
      NoopReplaceable noop2;
      noop2=noop;
      if((p=noop2.clone())!=NULL) {
          errln("noop2.Replaceable::clone() does not return NULL");
          delete p;
      }
  
      // try to call the compiler-provided
      // UMemory/UObject/Replaceable copy constructors
      NoopReplaceable noop3(noop);
      if((p=noop3.clone())!=NULL) {
          errln("noop3.Replaceable::clone() does not return NULL");
          delete p;
      }
  }
  
  void ReplaceableTest::check(const UnicodeString& transliteratorName, 
                              const UnicodeString& test, 
                              const UnicodeString& shouldProduceStyles) 
  {
      UErrorCode status = U_ZERO_ERROR;
      TestReplaceable *tr = new TestReplaceable(test, "");
      UnicodeString expectedStyles = shouldProduceStyles;
      UnicodeString original = tr->toString();
  
      Transliterator* t;
      if (transliteratorName.charAt(0) == 0x2A /*'*'*/) {
          UnicodeString rules(transliteratorName);
          rules.remove(0,1);
          UParseError pe;
          t = Transliterator::createFromRules("test", rules, UTRANS_FORWARD,
                                              pe, status);
  
          // test clone()
          TestReplaceable *tr2 = (TestReplaceable *)tr->clone();
          if(tr2 != NULL) {
              delete tr;
              tr = tr2;
          }
      } else {
          t = Transliterator::createInstance(transliteratorName, UTRANS_FORWARD, status);
      }
      if (U_FAILURE(status)) {
          log("FAIL: failed to create the ");
          log(transliteratorName);
          errln(" transliterator.");
          delete tr;
          return;
      }
      t->transliterate(*tr);
      UnicodeString newStyles = tr->getStyles();
      if (newStyles != expectedStyles) {
          errln("FAIL Styles: " + transliteratorName + "{" + original + "} => "
              + tr->toString() + "; should be {" + expectedStyles + "}!");
      } else {
          log("OK: ");
          log(transliteratorName);
          log("(");
          log(original);
          log(") => ");
          logln(tr->toString());
      }
      delete tr;
      delete t;
  }
  
  #endif /* #if !UCONFIG_NO_TRANSLITERATION */
  