/*
 * StringOperationTest class.
 *
 * Copyright (C) 2007 SATOH Takayuki All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package ts.util.text;

import ts.tester.function.coverage.FunctionTester;
import ts.tester.function.print.*;

/**
 * {@link ts.util.text.StringOperation StringOperation}クラスの機能試験クラス。
 *
 * @author  佐藤隆之.
 * @version $Revision: 1.1.1.1 $, $Date: 2010-10-16 00:03:53 $
 */
public class StringOperationTest extends FunctionTester
{
  public static void main(String[] args)
  {
    try {
      PrinterGroup group = new PrinterGroup();
      group.addPrinter(new ConsolePrinter());
      group.addPrinter(new HtmlPrinter("SATOH Takayuki"));
      setPrinter(group);

      run(StringOperationTest.class, (args.length == 0) ? null : args[0]);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  protected void preTesting()
  {
    MSG("StringOperation クラスの機能試験を実行する。");
  }
  

  /* -- test case -- */

  public void constructor()
  {
    MSG("デフォルト・コンストラクタ。");

    NOTNULL(new StringOperation());
  }

  public void isEmpty()
  {
    MSG("文字列が空かどうかの判定。");

    TRUE(StringOperation.isEmpty(""));
    FALSE(StringOperation.isEmpty(" "));
    FALSE(StringOperation.isEmpty("a"));
    FALSE(StringOperation.isEmpty("あいうえお"));
    FALSE(StringOperation.isEmpty("\uD800\uDC32\uD800\uDC33"));
  }

  public void isEmpty_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.isEmpty(null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void isWhitespaces()
  {
    MSG("空白文字からなるかどうかの判定。");

    TRUE(StringOperation.isWhitespaces(" "));
    TRUE(StringOperation.isWhitespaces("\t"));
    TRUE(StringOperation.isWhitespaces("    \t　　"));

    FALSE(StringOperation.isWhitespaces(""));
    FALSE(StringOperation.isWhitespaces("  a  "));
    FALSE(StringOperation.isWhitespaces(" \uD800\uDC32か\uD800\uDC33  "));
  }

  public void isWhitespaces_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.isWhitespaces(null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void isUnicodeBlock()
  {
    MSG("指定されたUnicode文字ブロックに含まれているか判定。");

    TRUE(StringOperation.isUnicodeBlock("abc", 
      Character.UnicodeBlock.BASIC_LATIN));

    FALSE(StringOperation.isUnicodeBlock("abあ", 
      Character.UnicodeBlock.BASIC_LATIN));

    FALSE(StringOperation.isUnicodeBlock("", 
      Character.UnicodeBlock.BASIC_LATIN));
  }

  public void isUnicodeBlock_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.isUnicodeBlock(null, Character.UnicodeBlock.BASIC_LATIN);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.isUnicodeBlock("abc", null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void isDigits()
  {
    MSG("数字だけで構成されているかを判定。");

    TRUE(StringOperation.isDigits("01234"));
    TRUE(StringOperation.isDigits("０１２３４"));
    FALSE(StringOperation.isDigits("01ab"));
    FALSE(StringOperation.isDigits(""));
  }

  public void isDigits_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.isDigits(null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void characterAt()
  {
    MSG("指定されたインデックスの文字を取得。");

    EQUAL(StringOperation.characterAt("abcdefg", 0), "a");
    EQUAL(StringOperation.characterAt("abcdefg", 1), "b");
    EQUAL(StringOperation.characterAt("abcdefg", 2), "c");
    EQUAL(StringOperation.characterAt("abcdefg", 3), "d");
    EQUAL(StringOperation.characterAt("abcdefg", 4), "e");
    EQUAL(StringOperation.characterAt("abcdefg", 5), "f");
    EQUAL(StringOperation.characterAt("abcdefg", 6), "g");

    EQUAL(StringOperation.characterAt("あいuえo", 0), "あ");
    EQUAL(StringOperation.characterAt("あいuえo", 1), "い");
    EQUAL(StringOperation.characterAt("あいuえo", 2), "u");
    EQUAL(StringOperation.characterAt("あいuえo", 3), "え");
    EQUAL(StringOperation.characterAt("あいuえo", 4), "o");

    EQUAL(StringOperation.characterAt("\uD800\uDC32\uD800\uDC33", 0),
      "\uD800\uDC32");
    EQUAL(StringOperation.characterAt("\uD800\uDC32\uD800\uDC33", 1),
      "\uD800\uDC33");
  }

  public void characterAt_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.characterAt(null, 0);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void characterAt_2()
  {
    MSG("引数のインデックスが範囲外の場合。");

    try {
      StringOperation.characterAt("", 0);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.characterAt("abc", -1);
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.characterAt("abc", 4);
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.characterAt("\uD800\uDC32\uD800\uDC33", -1);
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.characterAt("\uD800\uDC32\uD800\uDC33", 2);
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void substring()
  {
    MSG("部分文字列の取得。");

    EQUAL(StringOperation.substring("", 0), "");
    EQUAL(StringOperation.substring("abc", 0), "abc");
    EQUAL(StringOperation.substring("abc", 1), "bc");
    EQUAL(StringOperation.substring("abc", 2), "c");
    EQUAL(StringOperation.substring("abc", 3), "");

    EQUAL(StringOperation.substring("あいabう", 0), "あいabう");
    EQUAL(StringOperation.substring("あいabう", 1), "いabう");
    EQUAL(StringOperation.substring("あいabう", 2), "abう");
    EQUAL(StringOperation.substring("あいabう", 3), "bう");
    EQUAL(StringOperation.substring("あいabう", 4), "う");
    EQUAL(StringOperation.substring("あいabう", 5), "");

    String s = "a\uD800\uDC32か\uD800\uDC33";
    EQUAL(StringOperation.substring(s, 0), "a\uD800\uDC32か\uD800\uDC33");
    EQUAL(StringOperation.substring(s, 1), "\uD800\uDC32か\uD800\uDC33");
    EQUAL(StringOperation.substring(s, 2), "か\uD800\uDC33");
    EQUAL(StringOperation.substring(s, 3), "\uD800\uDC33");
    EQUAL(StringOperation.substring(s, 4), "");
  }

  public void substring_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.substring(null, 0);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void substring_2()
  {
    MSG("引数のインデックスが範囲外の場合。");
    
    try {
      StringOperation.substring("abc", -1);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.substring("abc", 4);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.substring("a\uD800\uDC32か\uD800\uDC33", -1);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.substring("a\uD800\uDC32か\uD800\uDC33", 5);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void substring_begin_end()
  {
    MSG("部分文字列の取得。");

    EQUAL(StringOperation.substring("", 0, 0), "");
    EQUAL(StringOperation.substring("abc", 0, 0), "");
    EQUAL(StringOperation.substring("abc", 0, 3), "abc");
    EQUAL(StringOperation.substring("abc", 1, 2), "b");
    EQUAL(StringOperation.substring("abc", 2, 3), "c");
    EQUAL(StringOperation.substring("abc", 3, 3), "");

    EQUAL(StringOperation.substring("あいabう", 0, 5), "あいabう");
    EQUAL(StringOperation.substring("あいabう", 1, 5), "いabう");
    EQUAL(StringOperation.substring("あいabう", 2, 4), "ab");
    EQUAL(StringOperation.substring("あいabう", 3, 3), "");

    String s = "a\uD800\uDC32か\uD800\uDC33";
    EQUAL(StringOperation.substring(s, 0, 4), "a\uD800\uDC32か\uD800\uDC33");
    EQUAL(StringOperation.substring(s, 0, 3), "a\uD800\uDC32か");
    EQUAL(StringOperation.substring(s, 0, 2), "a\uD800\uDC32");
    EQUAL(StringOperation.substring(s, 0, 1), "a");
    EQUAL(StringOperation.substring(s, 0, 0), "");
    EQUAL(StringOperation.substring(s, 1, 4), "\uD800\uDC32か\uD800\uDC33");
    EQUAL(StringOperation.substring(s, 1, 3), "\uD800\uDC32か");
    EQUAL(StringOperation.substring(s, 1, 2), "\uD800\uDC32");
    EQUAL(StringOperation.substring(s, 1, 1), "");
    EQUAL(StringOperation.substring(s, 2, 4), "か\uD800\uDC33");
    EQUAL(StringOperation.substring(s, 2, 3), "か");
    EQUAL(StringOperation.substring(s, 2, 2), "");
    EQUAL(StringOperation.substring(s, 3, 4), "\uD800\uDC33");
    EQUAL(StringOperation.substring(s, 3, 3), "");
    EQUAL(StringOperation.substring(s, 4, 4), "");
  }

  public void substring_bgn_end_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.substring(null, 0, 0);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void substring_bgn_end_2()
  {
    MSG("引数のインデックスが範囲外の場合。");
    
    try {
      StringOperation.substring("abc", -1, 2);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
    try {
      StringOperation.substring("abc", 1, 4);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
    try {
      StringOperation.substring("abc", 2, 1);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.substring("a\uD800\uDC32か\uD800\uDC33", -1, 3);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
    try {
      StringOperation.substring("a\uD800\uDC32か\uD800\uDC33", 1, 5);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
    try {
      StringOperation.substring("a\uD800\uDC32か\uD800\uDC33", 3, 2);
      NG();
    } catch (IndexOutOfBoundsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void indexOf()
  {
    MSG("文字列の探索。");

    EQUAL(StringOperation.indexOf("", ""), 0);
    EQUAL(StringOperation.indexOf("abcdefg", ""), 0);
    EQUAL(StringOperation.indexOf("abcdefg", "a"), 0);
    EQUAL(StringOperation.indexOf("abcdefg", "b"), 1);
    EQUAL(StringOperation.indexOf("abcdefg", "c"), 2);
    EQUAL(StringOperation.indexOf("abcdefg", "d"), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "e"), 4);
    EQUAL(StringOperation.indexOf("abcdefg", "f"), 5);
    EQUAL(StringOperation.indexOf("abcdefg", "g"), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "abc"), 0);
    EQUAL(StringOperation.indexOf("abcdefg", "def"), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "abcdefg"), 0);
    EQUAL(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","か"), 2);
    EQUAL(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33"), 3);
  }

  public void indexOf_1()
  {
    MSG("文字列が見つからない場合。");

    TRUE(StringOperation.indexOf("", "a") < 0);
    TRUE(StringOperation.indexOf("abcdefg", "h") < 0);
    TRUE(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC34") < 0);
    TRUE(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33\uD800\uDC34") < 0);
  }

  public void indexOf_2()
  {
    MSG("引数にヌルが指定された場合。");

    try {
      StringOperation.indexOf(null, "a");
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.indexOf("abc", null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void indexOf_from()
  {
    MSG("文字列の探索。");
    EQUAL(StringOperation.indexOf("", "", -1), 0);
    EQUAL(StringOperation.indexOf("", "", 0), 0);
    TRUE(StringOperation.indexOf("", "", 1) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "", -1), 0);
    EQUAL(StringOperation.indexOf("abcdefg", "", 0), 0);
    EQUAL(StringOperation.indexOf("abcdefg", "", 1), 1);
    EQUAL(StringOperation.indexOf("abcdefg", "", 2), 2);
    EQUAL(StringOperation.indexOf("abcdefg", "", 3), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "", 4), 4);
    EQUAL(StringOperation.indexOf("abcdefg", "", 5), 5);
    EQUAL(StringOperation.indexOf("abcdefg", "", 6), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "", 7), 7);
    TRUE(StringOperation.indexOf("abcdefg", "", 8) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "a", -1), 0);
    EQUAL(StringOperation.indexOf("abcdefg", "a", 0), 0);
    TRUE(StringOperation.indexOf("abcdefg", "a", 1) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "b", -1), 1);
    EQUAL(StringOperation.indexOf("abcdefg", "b", 0), 1);
    EQUAL(StringOperation.indexOf("abcdefg", "b", 1), 1);
    TRUE(StringOperation.indexOf("abcdefg", "b", 2) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "c", -1), 2);
    EQUAL(StringOperation.indexOf("abcdefg", "c", 0), 2);
    EQUAL(StringOperation.indexOf("abcdefg", "c", 1), 2);
    EQUAL(StringOperation.indexOf("abcdefg", "c", 2), 2);
    TRUE(StringOperation.indexOf("abcdefg", "c", 3) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "d", -1), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "d", 0), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "d", 1), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "d", 2), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "d", 3), 3);
    TRUE(StringOperation.indexOf("abcdefg", "d", 4) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "e", -1), 4);
    EQUAL(StringOperation.indexOf("abcdefg", "e", 0), 4);
    EQUAL(StringOperation.indexOf("abcdefg", "e", 1), 4);
    EQUAL(StringOperation.indexOf("abcdefg", "e", 2), 4);
    EQUAL(StringOperation.indexOf("abcdefg", "e", 3), 4);
    EQUAL(StringOperation.indexOf("abcdefg", "e", 4), 4);
    TRUE(StringOperation.indexOf("abcdefg", "e", 5) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "f", -1), 5);
    EQUAL(StringOperation.indexOf("abcdefg", "f", 0), 5);
    EQUAL(StringOperation.indexOf("abcdefg", "f", 1), 5);
    EQUAL(StringOperation.indexOf("abcdefg", "f", 2), 5);
    EQUAL(StringOperation.indexOf("abcdefg", "f", 3), 5);
    EQUAL(StringOperation.indexOf("abcdefg", "f", 4), 5);
    EQUAL(StringOperation.indexOf("abcdefg", "f", 5), 5);
    TRUE(StringOperation.indexOf("abcdefg", "f", 6) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "g", -1), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "g", 0), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "g", 1), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "g", 2), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "g", 3), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "g", 4), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "g", 5), 6);
    EQUAL(StringOperation.indexOf("abcdefg", "g", 6), 6);
    TRUE(StringOperation.indexOf("abcdefg", "g", 7) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "abc", -1), 0);
    EQUAL(StringOperation.indexOf("abcdefg", "abc", 0), 0);
    TRUE(StringOperation.indexOf("abcdefg", "abc", 1) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "def", -1), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "def", 0), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "def", 1), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "def", 2), 3);
    EQUAL(StringOperation.indexOf("abcdefg", "def", 3), 3);
    TRUE(StringOperation.indexOf("abcdefg", "def", 4) < 0);
    EQUAL(StringOperation.indexOf("abcdefg", "abcdefg", -1), 0);
    EQUAL(StringOperation.indexOf("abcdefg", "abcdefg", 0), 0);
    TRUE(StringOperation.indexOf("abcdefg", "abcdefg", 1) < 0);
    EQUAL(StringOperation.indexOf("a\uD800\uDC32か\uD800\uDC33","か", -1), 2);
    EQUAL(StringOperation.indexOf("a\uD800\uDC32か\uD800\uDC33","か", 0), 2);
    EQUAL(StringOperation.indexOf("a\uD800\uDC32か\uD800\uDC33","か", 1), 2);
    EQUAL(StringOperation.indexOf("a\uD800\uDC32か\uD800\uDC33","か", 2), 2);
    TRUE(StringOperation.indexOf("a\uD800\uDC32か\uD800\uDC33","か", 3) < 0);
    EQUAL(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33", -1), 3);
    EQUAL(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33", 0), 3);
    EQUAL(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33", 1), 3);
    EQUAL(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33", 2), 3);
    EQUAL(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33", 3), 3);
    TRUE(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33", 4) < 0);

    TRUE(StringOperation.indexOf("", "a", 0) < 0);
    TRUE(StringOperation.indexOf("abcdefg", "h", 0) < 0);
    TRUE(StringOperation.indexOf("abcdefg", "h", 3) < 0);
    TRUE(StringOperation.indexOf("abcdefg", "h", 6) < 0);
    TRUE(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC34", 0) < 0);
    TRUE(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC34", 2) < 0);
    TRUE(StringOperation.indexOf(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC34", 3) < 0);
  }

  public void indexOf_from_2()
  {
    MSG("引数にヌルが指定された場合。");

    try {
      StringOperation.indexOf(null, "a");
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.indexOf("abc", null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void lastIndexOf()
  {
    MSG("指定した文字列が最後に現れるインデックスの取得。");
    EQUAL(StringOperation.lastIndexOf("", ""), 0); 

    EQUAL(StringOperation.lastIndexOf("abcd", ""), 4); 
    EQUAL(StringOperation.lastIndexOf("abcd", "a"), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "a"), 2); 
    EQUAL(StringOperation.lastIndexOf("abad", "d"), 3); 
    EQUAL(StringOperation.lastIndexOf("abad", "ab"), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "ad"), 2); 
    EQUAL(StringOperation.lastIndexOf("abad", "abad"), 0); 
    TRUE(StringOperation.lastIndexOf("abad", "abac") < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "c") < 0); 

    EQUAL(StringOperation.lastIndexOf("あいabう", "あいabう"), 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "いab"), 1);
    EQUAL(StringOperation.lastIndexOf("あいabう", "う"), 4);
    EQUAL(StringOperation.lastIndexOf("あいabう", ""), 5);
    TRUE(StringOperation.lastIndexOf("あいabう", "え") < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc") < 0);

    String s = "a\uD800\uDC32か\uD800\uDC33";
    EQUAL(StringOperation.lastIndexOf(s, "a\uD800\uDC32か\uD800\uDC33"), 0);
    EQUAL(StringOperation.lastIndexOf(s, "\uD800\uDC32か"), 1);
    EQUAL(StringOperation.lastIndexOf(s, "か\uD800\uDC33"), 2);
    EQUAL(StringOperation.lastIndexOf(s, "\uD800\uDC33"), 3);
    TRUE(StringOperation.lastIndexOf(s, "a\uD800\uDC32\uD800\uDC33") < 0);
  }

  public void lastIndexOf_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.lastIndexOf(null, "a", 0);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.lastIndexOf("abc", null, 0);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void lastIndexOf_From()
  {
    TRUE(StringOperation.lastIndexOf("abcd", "", -1) < 0); 
    EQUAL(StringOperation.lastIndexOf("abcd", "", 0), 0); 
    EQUAL(StringOperation.lastIndexOf("abcd", "", 1), 1); 
    EQUAL(StringOperation.lastIndexOf("abcd", "", 2), 2); 
    EQUAL(StringOperation.lastIndexOf("abcd", "", 3), 3); 
    EQUAL(StringOperation.lastIndexOf("abcd", "", 4), 4); 
    EQUAL(StringOperation.lastIndexOf("abcd", "", 5), 4); 

    TRUE (StringOperation.lastIndexOf("abcd", "a", -1) < 0); 
    EQUAL(StringOperation.lastIndexOf("abcd", "a", 0), 0); 
    EQUAL(StringOperation.lastIndexOf("abcd", "a", 1), 0); 
    EQUAL(StringOperation.lastIndexOf("abcd", "a", 2), 0); 
    EQUAL(StringOperation.lastIndexOf("abcd", "a", 3), 0); 
    EQUAL(StringOperation.lastIndexOf("abcd", "a", 4), 0); 
    EQUAL(StringOperation.lastIndexOf("abcd", "a", 5), 0); 

    TRUE (StringOperation.lastIndexOf("abad", "a", -1) < 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "a", 0), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "a", 1), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "a", 2), 2); 
    EQUAL(StringOperation.lastIndexOf("abad", "a", 3), 2); 
    EQUAL(StringOperation.lastIndexOf("abad", "a", 4), 2); 
    EQUAL(StringOperation.lastIndexOf("abad", "a", 5), 2); 

    TRUE (StringOperation.lastIndexOf("abad", "d", -1) < 0); 
    TRUE (StringOperation.lastIndexOf("abad", "d", 0) < 0); 
    TRUE (StringOperation.lastIndexOf("abad", "d", 1) < 0); 
    TRUE (StringOperation.lastIndexOf("abad", "d", 2) < 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "d", 3), 3); 
    EQUAL(StringOperation.lastIndexOf("abad", "d", 4), 3); 
    EQUAL(StringOperation.lastIndexOf("abad", "d", 5), 3); 

    TRUE (StringOperation.lastIndexOf("abad", "ab", -1) < 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "ab", 0), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "ab", 1), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "ab", 2), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "ab", 3), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "ab", 4), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "ab", 5), 0); 

    TRUE (StringOperation.lastIndexOf("abad", "ad", -1) < 0); 
    TRUE (StringOperation.lastIndexOf("abad", "ad", 0) < 0); 
    TRUE (StringOperation.lastIndexOf("abad", "ad", 1) < 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "ad", 2), 2); 
    EQUAL(StringOperation.lastIndexOf("abad", "ad", 3), 2); 
    EQUAL(StringOperation.lastIndexOf("abad", "ad", 4), 2); 
    EQUAL(StringOperation.lastIndexOf("abad", "ad", 5), 2); 

    TRUE (StringOperation.lastIndexOf("abad", "abad", -1) < 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "abad", 0), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "abad", 1), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "abad", 2), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "abad", 3), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "abad", 4), 0); 
    EQUAL(StringOperation.lastIndexOf("abad", "abad", 5), 0); 

    TRUE(StringOperation.lastIndexOf("abad", "abac", -1) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "abac", 0) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "abac", 1) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "abac", 2) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "abac", 3) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "abac", 4) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "abac", 5) < 0); 

    TRUE(StringOperation.lastIndexOf("abad", "c", -1) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "c", 0) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "c", 1) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "c", 2) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "c", 3) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "c", 4) < 0); 
    TRUE(StringOperation.lastIndexOf("abad", "c", 5) < 0); 

    TRUE (StringOperation.lastIndexOf("あいabう", "あいabう", -1) < 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "あいabう", 0), 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "あいabう", 1), 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "あいabう", 2), 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "あいabう", 3), 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "あいabう", 4), 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "あいabう", 5), 0);

    TRUE (StringOperation.lastIndexOf("あいabう", "いab", -1) < 0);
    TRUE (StringOperation.lastIndexOf("あいabう", "いab", 0) < 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "いab", 1), 1);
    EQUAL(StringOperation.lastIndexOf("あいabう", "いab", 2), 1);
    EQUAL(StringOperation.lastIndexOf("あいabう", "いab", 3), 1);
    EQUAL(StringOperation.lastIndexOf("あいabう", "いab", 4), 1);
    EQUAL(StringOperation.lastIndexOf("あいabう", "いab", 5), 1);

    TRUE (StringOperation.lastIndexOf("あいabう", "う", -1) < 0);
    TRUE (StringOperation.lastIndexOf("あいabう", "う", 0) < 0);
    TRUE (StringOperation.lastIndexOf("あいabう", "う", 1) < 0);
    TRUE (StringOperation.lastIndexOf("あいabう", "う", 2) < 0);
    TRUE (StringOperation.lastIndexOf("あいabう", "う", 3) < 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "う", 4), 4);
    EQUAL(StringOperation.lastIndexOf("あいabう", "う", 5), 4);

    TRUE (StringOperation.lastIndexOf("あいabう", "", -1) < 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "", 0), 0);
    EQUAL(StringOperation.lastIndexOf("あいabう", "", 1), 1);
    EQUAL(StringOperation.lastIndexOf("あいabう", "", 2), 2);
    EQUAL(StringOperation.lastIndexOf("あいabう", "", 3), 3);
    EQUAL(StringOperation.lastIndexOf("あいabう", "", 4), 4);
    EQUAL(StringOperation.lastIndexOf("あいabう", "", 4), 4);

    TRUE(StringOperation.lastIndexOf("あいabう", "え", -1) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "え", 0) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "え", 1) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "え", 2) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "え", 3) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "え", 4) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "え", 5) < 0);

    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc", -1) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc", 0) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc", 1) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc", 2) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc", 3) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc", 4) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc", 5) < 0);
    TRUE(StringOperation.lastIndexOf("あいabう", "あいabうc", 6) < 0);

    String s = "a\uD800\uDC32か\uD800\uDC33";
    TRUE (StringOperation.lastIndexOf(s, "a\uD800\uDC32か\uD800\uDC33",-1) < 0);
    EQUAL(StringOperation.lastIndexOf(s, "a\uD800\uDC32か\uD800\uDC33", 0), 0);
    EQUAL(StringOperation.lastIndexOf(s, "a\uD800\uDC32か\uD800\uDC33", 1), 0);
    EQUAL(StringOperation.lastIndexOf(s, "a\uD800\uDC32か\uD800\uDC33", 2), 0);
    EQUAL(StringOperation.lastIndexOf(s, "a\uD800\uDC32か\uD800\uDC33", 3), 0);
    EQUAL(StringOperation.lastIndexOf(s, "a\uD800\uDC32か\uD800\uDC33", 4), 0);

    TRUE (StringOperation.lastIndexOf(s, "\uD800\uDC32か", -1) < 0);
    TRUE (StringOperation.lastIndexOf(s, "\uD800\uDC32か", 0) < 0);
    EQUAL(StringOperation.lastIndexOf(s, "\uD800\uDC32か", 1), 1);
    EQUAL(StringOperation.lastIndexOf(s, "\uD800\uDC32か", 2), 1);
    EQUAL(StringOperation.lastIndexOf(s, "\uD800\uDC32か", 3), 1);
    EQUAL(StringOperation.lastIndexOf(s, "\uD800\uDC32か", 4), 1);

    TRUE (StringOperation.lastIndexOf(s, "か\uD800\uDC33", -1) < 0);
    TRUE (StringOperation.lastIndexOf(s, "か\uD800\uDC33", 0) < 0);
    TRUE (StringOperation.lastIndexOf(s, "か\uD800\uDC33", 1) < 0);
    EQUAL(StringOperation.lastIndexOf(s, "か\uD800\uDC33", 2), 2);
    EQUAL(StringOperation.lastIndexOf(s, "か\uD800\uDC33", 3), 2);
    EQUAL(StringOperation.lastIndexOf(s, "か\uD800\uDC33", 4), 2);

    TRUE (StringOperation.lastIndexOf(s, "\uD800\uDC33", -1) < 0);
    TRUE (StringOperation.lastIndexOf(s, "\uD800\uDC33", 0) < 0);
    TRUE (StringOperation.lastIndexOf(s, "\uD800\uDC33", 1) < 0);
    TRUE (StringOperation.lastIndexOf(s, "\uD800\uDC33", 2) < 0);
    EQUAL(StringOperation.lastIndexOf(s, "\uD800\uDC33", 3), 3);
    EQUAL(StringOperation.lastIndexOf(s, "\uD800\uDC33", 4), 3);

    TRUE(StringOperation.lastIndexOf(s, "a\uD800\uDC32\uD800\uDC33", -1) < 0);
    TRUE(StringOperation.lastIndexOf(s, "a\uD800\uDC32\uD800\uDC33", 0) < 0);
    TRUE(StringOperation.lastIndexOf(s, "a\uD800\uDC32\uD800\uDC33", 1) < 0);
    TRUE(StringOperation.lastIndexOf(s, "a\uD800\uDC32\uD800\uDC33", 2) < 0);
    TRUE(StringOperation.lastIndexOf(s, "a\uD800\uDC32\uD800\uDC33", 3) < 0);
    TRUE(StringOperation.lastIndexOf(s, "a\uD800\uDC32\uD800\uDC33", 4) < 0);

    TRUE(StringOperation.lastIndexOf("abad", "c", -100) < 0);
  }

  public void lastIndexOf_From_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.lastIndexOf(null, "a", 1);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.lastIndexOf("abc", null, 1);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void startsWith()
  {
    MSG("指定のプリフィックスで開始しているかの判定");

    TRUE(StringOperation.startsWith("", ""));
    TRUE(StringOperation.startsWith("abc", ""));
    TRUE(StringOperation.startsWith("あいう", ""));
    TRUE(StringOperation.startsWith("\uD800\uDC32か\uD800\uDC33", ""));

    TRUE(StringOperation.startsWith("abc", "a"));
    TRUE(StringOperation.startsWith("あいう", "あ"));
    TRUE(StringOperation.startsWith("\uD800\uDC32か\uD800\uDC33",
      "\uD800\uDC32"));

    TRUE(StringOperation.startsWith("abc", "ab"));
    TRUE(StringOperation.startsWith("あいう", "あい"));
    TRUE(StringOperation.startsWith("\uD800\uDC32か\uD800\uDC33",
      "\uD800\uDC32か"));

    TRUE(StringOperation.startsWith("abc", "abc"));
    TRUE(StringOperation.startsWith("あいう", "あいう"));
    TRUE(StringOperation.startsWith("\uD800\uDC32か\uD800\uDC33",
      "\uD800\uDC32か\uD800\uDC33"));

    FALSE(StringOperation.startsWith("abc", "abcd"));
    FALSE(StringOperation.startsWith("あいう", "あいうえ"));
    FALSE(StringOperation.startsWith("\uD800\uDC32か\uD800\uDC33",
      "き\uD800\uDC32か\uD800\uDC33\uD800\uDC34"));

    FALSE(StringOperation.startsWith("abc", "c"));
    FALSE(StringOperation.startsWith("あいう", "う"));
    FALSE(StringOperation.startsWith("\uD800\uDC32か\uD800\uDC33",
      "\uD800\uDC33"));
  }

  public void startsWith_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.startsWith(null, "a");
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.startsWith("abc", null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void endsWith()
  {
    MSG("指定のサフィックスで終了しているかの判定");

    TRUE(StringOperation.endsWith("", ""));
    TRUE(StringOperation.endsWith("abc", ""));
    TRUE(StringOperation.endsWith("あいう", ""));
    TRUE(StringOperation.endsWith("\uD800\uDC32か\uD800\uDC33", ""));

    TRUE(StringOperation.endsWith("abc", "c"));
    TRUE(StringOperation.endsWith("あいう", "う"));
    TRUE(StringOperation.endsWith("\uD800\uDC32か\uD800\uDC33","\uD800\uDC33"));

    TRUE(StringOperation.endsWith("abc", "bc"));
    TRUE(StringOperation.endsWith("あいう", "いう"));
    TRUE(StringOperation.endsWith("\uD800\uDC32か\uD800\uDC33",
      "か\uD800\uDC33"));

    TRUE(StringOperation.endsWith("abc", "abc"));
    TRUE(StringOperation.endsWith("あいう", "あいう"));
    TRUE(StringOperation.endsWith("\uD800\uDC32か\uD800\uDC33",
      "\uD800\uDC32か\uD800\uDC33"));

    FALSE(StringOperation.endsWith("abc", "xabc"));
    FALSE(StringOperation.endsWith("あいう", "んあいう"));
    FALSE(StringOperation.endsWith("\uD800\uDC32か\uD800\uDC33",
      "き\uD800\uDC32か\uD800\uDC33"));

    FALSE(StringOperation.endsWith("abc", "a"));
    FALSE(StringOperation.endsWith("あいう", "あ"));
    FALSE(StringOperation.endsWith("\uD800\uDC32か\uD800\uDC33",
      "\uD800\uDC32"));
  }

  public void endsWith_1()
  {
    MSG("引数がヌルの場合。");

    try {
      StringOperation.endsWith(null, "a");
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.endsWith("abc", null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void contains()
  {
    MSG("文字列が含まれているかの探索。");

    TRUE(StringOperation.contains("", ""));
    TRUE(StringOperation.contains("abcdefg", ""));
    TRUE(StringOperation.contains("abcdefg", "a"));
    TRUE(StringOperation.contains("abcdefg", "b"));
    TRUE(StringOperation.contains("abcdefg", "c"));
    TRUE(StringOperation.contains("abcdefg", "d"));
    TRUE(StringOperation.contains("abcdefg", "e"));
    TRUE(StringOperation.contains("abcdefg", "f"));
    TRUE(StringOperation.contains("abcdefg", "g"));
    TRUE(StringOperation.contains("abcdefg", "abc"));
    TRUE(StringOperation.contains("abcdefg", "def"));
    TRUE(StringOperation.contains("abcdefg", "abcdefg"));
    TRUE(StringOperation.contains(
      "a\uD800\uDC32か\uD800\uDC33","か"));
    TRUE(StringOperation.contains(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33"));
  }

  public void contains_1()
  {
    MSG("文字列が見つからない場合。");

    FALSE(StringOperation.contains("", "a"));
    FALSE(StringOperation.contains("abcdefg", "h"));
    FALSE(StringOperation.contains(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC34"));
    FALSE(StringOperation.contains(
      "a\uD800\uDC32か\uD800\uDC33","\uD800\uDC33\uD800\uDC34"));
  }

  public void contains_2()
  {
    MSG("引数にヌルが指定された場合。");

    try {
      StringOperation.contains(null, "a");
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      StringOperation.contains("abc", null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void offsetByCodePoints()
  {
    String str = "abcde";
    String sub = str.substring(2);
    EQUAL(sub, "cde");
    EQUAL(str.offsetByCodePoints(0, 5), 5);
    KNOWNBUG("sub.offsetByCodePoints(0, 3) != 3");

    EQUAL(StringOperation.offsetByCodePoints(str, 0, 5), 5);
    EQUAL(StringOperation.offsetByCodePoints(sub, 0, 3), 3);
  }
}

