/*
 * FileOperation02InnerTest 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.file;

import ts.tester.function.*;
import ts.tester.function.print.*;
import java.io.*;
import java.util.*;
import ts.util.*;
import ts.util.text.*;

/**
 * {@link ts.util.file.FileOperation FileOperation}クラスの機能試験クラス。
 *
 * @author  佐藤隆之.
 * @version $Revision: 1.1.1.1 $, $Date: 2010-10-16 00:03:46 $
 */
public class FileOperation02InnerTest extends FunctionTester
{
  public static void main(String[] args)
  {
    try {
      run(FileOperation02InnerTest.class, (args.length == 0) ? null : args[0]);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  final File WORKDIR = new File("test/data/ts/util/file/FileOperation");
  File[] DIRS = new File[6];
  File[] FILES = new File[10];

  protected void preInvocation(String method)
  {
    WORKDIR.mkdirs();
    DIRS[0] = new File(WORKDIR, "DIRS0");
    DIRS[1] = new File(DIRS[0], "DIRS1");
    DIRS[2] = new File(DIRS[0], "DIRS2");
    DIRS[3] = new File(DIRS[1], "DIRS3");
    DIRS[4] = new File(DIRS[2], "DIRS4");
    DIRS[5] = new File(DIRS[2], "DIRS5");
    FILES[0] = new File(DIRS[0], "FILES0"); 
    FILES[1] = new File(DIRS[1], "FILES1"); 
    FILES[2] = new File(DIRS[2], "FILES2"); 
    FILES[3] = new File(DIRS[3], "FILES3"); 
    FILES[4] = new File(DIRS[4], "FILES4"); 
    FILES[5] = new File(DIRS[0], "FILES5"); 
    FILES[6] = new File(DIRS[1], "FILES6"); 
    FILES[7] = new File(DIRS[2], "FILES7"); 
    FILES[8] = new File(DIRS[3], "FILES8"); 
    FILES[9] = new File(DIRS[4], "FILES9"); 
    try {
      for (File d : DIRS) d.mkdirs();
      for (File f : FILES) f.createNewFile();
    } catch (Exception e) {}

    /*
       DIRS[0]
        +- DIRS[1]
          +- DIRS[3]
            +- FILES[3]
            +- FILES[8]
          +- FILES[1]
          +- FILES[6]
        +- DIRS[2]
          +- DIRS[4]
            +- FILES[4]
            +- FILES[9]
          +- DIRS[5]
          +- FILES[2]
          +- FILES[7]
        +- FILES[0]
        +- FILES[5]
    */
  }
  
  protected void postInvocation(String method)
  {
    try {
      FileOperation.deleteRecursive(WORKDIR);
    } catch (Exception e) {}
  }

  /* -- test case -- */

  public void listInner()
  {
    MSG("通常のファイルを指定した場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    Class[] types = { File.class, List.class };
    Object[] values = new Object[2];
    values[0] = FILES[0];
    values[1] = new LinkedList<File>();

    try {
      Object o = oi.invokeMethod(fo, "listInner", types, values);
      List lst = List.class.cast(o);
      EQUAL(lst.size(), 0);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void listInner_01()
  {
    MSG("空のディレクトリを指定した場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    Class[] types = { File.class, List.class };
    Object[] values = new Object[2];
    values[0] = FILES[0];
    values[1] = new LinkedList<File>();

    try {
      Object o = oi.invokeMethod(fo, "listInner", types, values);
      List lst = List.class.cast(o);
      EQUAL(lst.size(), 0);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void listInner_02()
  {
    MSG("空でないディレクトリを指定した場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    Class[] types = { File.class, List.class };
    Object[] values = new Object[2];
    values[0] = DIRS[0];
    values[1] = new LinkedList<File>();

    try {
      Object o = oi.invokeMethod(fo, "listInner", types, values);
      List lst = List.class.cast(o);
      EQUAL(lst.size(), 15);

      int i = 0;
      EQUAL(File.class.cast(lst.get(i++)), DIRS[1]);
      EQUAL(File.class.cast(lst.get(i++)), DIRS[3]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[3]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[8]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[1]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[6]);
      EQUAL(File.class.cast(lst.get(i++)), DIRS[2]);
      EQUAL(File.class.cast(lst.get(i++)), DIRS[4]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[4]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[9]);
      EQUAL(File.class.cast(lst.get(i++)), DIRS[5]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[2]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[7]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[0]);
      EQUAL(File.class.cast(lst.get(i++)), FILES[5]);
    } catch (Exception e) {
      NG(e);
    }
  }

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

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    Class[] types = { File.class, List.class };
    Object[] values = new Object[2];

    try {
      values[0] = null;
      values[1] = new LinkedList<File>();
      oi.invokeMethod(fo, "listInner", types, values);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      values[0] = DIRS[0];
      values[1] = null;
      oi.invokeMethod(fo, "listInner", types, values);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void listInner_04()
  {
    MSG("指定されたディレクトリの下位ファイルの一覧を取得できない場合。");

    File dir = new File(WORKDIR, "list") {
      final static long serialVersionUID = 1L;
      public File[] listFiles() { return null; }
    };
    dir.mkdirs();

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    Class[] types = { File.class, List.class };
    Object[] values = new Object[2];

    try {
      values[0] = dir;
      values[1] = new LinkedList<File>();
      oi.invokeMethod(fo, "listInner", types, values);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void listInner_05()
  {
    MSG("存在しないファイルを指定した場合。");


    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    Class[] types = { File.class, List.class };
    Object[] values = new Object[2];

    try {
      values[0] = new File(WORKDIR, "llll");
      values[1] = new LinkedList<File>();
      oi.invokeMethod(fo, "listInner", types, values);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void deleteInner()
  {
    MSG("通常のファイルの削除。");

    TRUE(FILES[0].exists());

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    try {
      oi.invokeMethod(fo, "deleteInner", File.class, FILES[0]);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(FILES[0].exists());
  }

  public void deleteInner_01()
  {
    MSG("空のディレクトリの削除。");

    TRUE(DIRS[5].exists());
    EQUAL(DIRS[5].list().length, 0);

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    try {
      oi.invokeMethod(fo, "deleteInner", File.class, DIRS[5]);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(DIRS[5].exists());
  }

  public void deleteInner_02()
  {
    MSG("空でないディレクトリの削除。");

    for (File d : DIRS) TRUE(d.exists());
    for (File f : FILES) TRUE(f.exists());

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    try {
      oi.invokeMethod(fo, "deleteInner", File.class, DIRS[0]);
    } catch (Exception e) {
      NG(e);
    }

    for (File d : DIRS) FALSE(d.exists());
    for (File f : FILES) FALSE(f.exists());
  }

  public void deleteInner_03()
  {
    MSG("指定されたファイルが存在しない場合。");

    File file = new File(WORKDIR, "aaa");
    FALSE(file.exists());

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    try {
      oi.invokeMethod(fo, "deleteInner", File.class, file);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

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

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    try {
      oi.invokeMethod(fo, "deleteInner", File.class, null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void deleteInner_05()
  {
    MSG("指定されたディレクトリの下位ファイルの一覧を取得できない場合。");

    File dir = new File(WORKDIR, "list") {
      final static long serialVersionUID = 1L;
      public File[] listFiles() { return null; }
    };
    dir.mkdirs();

    TRUE(dir.exists());

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fo = new FileOperation();
    try {
      oi.invokeMethod(fo, "deleteInner", File.class, dir);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(dir.exists());
  }

  public void moveInner()
  {
    MSG("通常のファイルの移動。");

    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    FileOperation fop = new FileOperation();

    try {
      File dstFile = FileOperation.getDestinationFile(FILES[4], DIRS[4], 
        WORKDIR);
      OK(dstFile.getPath());

      TRUE(FILES[3].exists());
      TRUE(dstFile.getParentFile().exists());
      FALSE(dstFile.exists());

      values[0] = FILES[3];
      values[1] = dstFile;
      oi.invokeMethod(fop, "moveInner", types, values);

      FALSE(FILES[3].exists());
      TRUE(dstFile.getParentFile().exists());
      TRUE(dstFile.exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_00()
  {
    MSG("ディレクトリの移動。");

    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    FileOperation fop = new FileOperation();

    try {
      File dstDir = new File(WORKDIR, "dest");
      File dstFile = FileOperation.getDestinationFile(DIRS[4], DIRS[0], dstDir);

      TRUE(DIRS[4].exists());
      TRUE(FILES[4].exists());
      TRUE(FILES[9].exists());
      FALSE(dstFile.getParentFile().exists());
      FALSE(dstFile.exists());

      values[0] = DIRS[4];
      values[1] = dstFile;

      oi.invokeMethod(fop, "moveInner", types, values);

      FALSE(DIRS[4].exists());
      TRUE(dstFile.exists());
      TRUE(FileOperation.getDestinationFile(FILES[4],DIRS[0],dstDir).exists());
      TRUE(FileOperation.getDestinationFile(FILES[9],DIRS[0],dstDir).exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_01()
  {
    MSG("移動元のファイルが存在しない場合。");

    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    FileOperation fop = new FileOperation();

    File srcFile = new File(WORKDIR, "fff");
    File dstFile = new File(WORKDIR, "ggg");

    FALSE(srcFile.exists());
    FALSE(dstFile.exists());
    TRUE(dstFile.getParentFile().exists());

    try {
      values[0] = srcFile;
      values[1] = dstFile;
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_02()
  {
    MSG("移動先のディレクトリでないファイルが既に存在する場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    TRUE(FILES[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());

    try {
      values[0] = FILES[3];
      values[1] = FILES[4];
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());

    try {
      values[0] = DIRS[3];
      values[1] = FILES[4];
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());
  }

  public void moveInner_03()
  {
    MSG("移動先のディレクトリが既に存在する場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    try {
      TRUE(DIRS[3].exists());
      TRUE(FILES[3].exists());
      TRUE(FILES[8].exists());
      TRUE(DIRS[4].exists());
      FALSE(fop.getDestinationFile(FILES[3], DIRS[3], DIRS[4]).exists());
      FALSE(fop.getDestinationFile(FILES[8], DIRS[3], DIRS[4]).exists());
  
      try {
        values[0] = DIRS[3];
        values[1] = DIRS[4];
        oi.invokeMethod(fop, "moveInner", types, values);
      } catch (Exception e) {
        NG(e);
      }
  
      FALSE(DIRS[3].exists());
      TRUE(DIRS[4].exists());
      TRUE(fop.getDestinationFile(FILES[3], DIRS[3], DIRS[4]).exists());
      TRUE(fop.getDestinationFile(FILES[8], DIRS[3], DIRS[4]).exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_04()
  {
    MSG("移動先にタイプの異なるファイルが既に存在する場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    File ddd = new File(WORKDIR, "aaa");
    File fff = new File(ddd, "DIRS1/DIRS3/FILES3");
    fff.mkdirs();

    try {
      values[0] = DIRS[0];
      values[1] = ddd;
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    for (File d : DIRS) TRUE(d.exists());
    for (File f : DIRS) TRUE(f.exists());
    try {
      List<File> lst = FileOperation.listRecursive(ddd);
      EQUAL(lst.size(), 3);
      EQUAL(lst.get(0), new File(ddd, "DIRS1"));
      EQUAL(lst.get(1), new File(ddd, "DIRS1/DIRS3"));
      EQUAL(lst.get(2), new File(ddd, "DIRS1/DIRS3/FILES3"));
      TRUE(new File(ddd, "DIRS1/DIRS3/FILES3").isDirectory());
    } catch (Exception e) {
      NG(e);
    }

    File dd = new File(WORKDIR, "bbb");
    File ff = new File(dd, "DIRS1/DIRS3");
    try {
      FileOperation.createNewFile(ff);
    } catch (Exception e) {
      NG(e);
    }

    try {
      values[0] = DIRS[0];
      values[1] = dd;
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    for (File d : DIRS) TRUE(d.exists());
    for (File f : DIRS) TRUE(f.exists());
    try {
      List<File> lst = FileOperation.listRecursive(dd);
      EQUAL(lst.size(), 2);
      EQUAL(lst.get(0), new File(dd, "DIRS1"));
      EQUAL(lst.get(1), new File(dd, "DIRS1/DIRS3"));
      FALSE(new File(dd, "DIRS1/DIRS3").isDirectory());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_05()
  {
    MSG("移動に失敗したときのロールバックの確認。");

    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    FileOperation fop = new FileOperation();
    File dstDir = new File(WORKDIR, "dest");

    File errFile = new File(dstDir, "DIRS2/DIRS4/FILES4");
    try {
      FileOperation.createNewFile(errFile);
    } catch (Exception e) {
      NG(e);
    }

    try {
      List<File> beforeLst = FileOperation.listRecursive(WORKDIR);

      try {
        values[0] = DIRS[0];
        values[1] = dstDir;
        oi.invokeMethod(fop, "moveInner", types, values);
        NG();
      } catch (FileAlreadyExistsException e) {
        OK(e);
      } catch (Exception e) {
        NG(e);
      }
  
      List<File> afterLst = FileOperation.listRecursive(WORKDIR);
      EQUAL(beforeLst.size(), afterLst.size());
      for (int i=0; i<beforeLst.size(); i++) {
        EQUAL(beforeLst.get(i), afterLst.get(i));
      }
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_06()
  {
    MSG("移動先の親ディレクトリが存在しない場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    File src = DIRS[3];
    File dst =new File(WORKDIR, "aaa");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    try {
      values[0] = src;
      values[1] = dst;
      oi.invokeMethod(fop, "moveInner", types, values);
    } catch (Exception e) {
      NG(e);
      e.printStackTrace();
    }

    FALSE(DIRS[3].exists());
    TRUE(dst.exists());
    TRUE(new File(WORKDIR, "aaa/FILES3").exists());
    TRUE(new File(WORKDIR, "aaa/FILES8").exists());
  }

  public void moveInner_07()
  {
    MSG("移動先の親ディレクトリが実はファイルの場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    File src = DIRS[3];
    File dst =new File(WORKDIR, "aaa/bbb/ccc");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    try {
      FileOperation.createNewFile(new File(WORKDIR, "aaa"));
    } catch (Exception e) {
      NG(e);
    }

    try {
      values[0] = src;
      values[1] = dst;
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    FALSE(dst.exists());
  }

  public void copyFile()
  {
    MSG("テキスト・ファイルをコピーした場合。");

    File src = new File("build.xml");
    File dst = new File(WORKDIR, "b.xml");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];
    values[0] = src;
    values[1] = dst;

    TRUE (src.exists());
    FALSE(dst.exists());
    try {
      oi.invokeMethod(fop, "copyFile", types, values);
    } catch (Exception e) {
      NG(e);
    }
    TRUE (src.exists());
    TRUE (dst.exists());

    boolean isDiff = false;
    BufferedReader br1 = null, br2 = null;
    try {
      br1 = new BufferedReader(new FileReader(src));
      br2 = new BufferedReader(new FileReader(dst));
      while (true) {
        String ln1 = br1.readLine();
        String ln2 = br2.readLine();
        if (ln1 == null || ln2 == null) {
          if (ln1 != ln2) {
            isDiff = true;
          }
          break;
        }
        else if (! ln1.equals(ln2)) {
          isDiff = true;
        }
      }
      if (isDiff) {
        NG(src.getPath() + " / " + dst.getPath());
      }
      else {
        OK(src.getPath() + " / " + dst.getPath());
      }
    } catch (Exception e) {
      NG(e);
    } finally {
      try { br1.close(); } catch (Exception e) {}
      try { br2.close(); } catch (Exception e) {}
    }
  }

  public void copyFile_0()
  {
    MSG("バイナリ・ファイルをコピーした場合。");

    File src = new File("classes/ts/util/file/FileOperation.class");
    File dst = new File(WORKDIR, "FileOperation.class");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];
    values[0] = src;
    values[1] = dst;

    TRUE (src.exists());
    FALSE(dst.exists());

    try {
      oi.invokeMethod(fop, "copyFile", types, values);
    } catch (Exception e) {
      NG(e);
    }

    TRUE (src.exists());
    TRUE (dst.exists());

    boolean isDiff = false;
    FileInputStream fs1 = null, fs2 = null;
    byte[] b1 = new byte[1024], b2 = new byte[1024];
    try {
      fs1 = new FileInputStream(src);
      fs2 = new FileInputStream(dst);
      while (true) {
        int n1 = fs1.read(b1, 0, b1.length);
        int n2 = fs2.read(b2, 0, b2.length);
        if (n1 != n2 ) {
          isDiff = true;
        }
        if (n1 <= 0 || n2 <= 0) {
          break;
        }
        for (int i=0; i<n1; i++) {
          byte bb1 = b1[i];
          byte bb2 = b2[i];
          if (bb1 != bb2) {
            isDiff = true;
            break;
          }
        }
      }
      if (isDiff) {
        NG(src.getPath() + " / " + dst.getPath());
      }
      else {
        OK(src.getPath() + " / " + dst.getPath());
      }
    } catch (Exception e) {
      NG(e);
    } finally {
      try { fs1.close(); } catch (Exception e) {}
      try { fs2.close(); } catch (Exception e) {}
    }
  }

  public void copyFile_1()
  {
    MSG("コピー元のファイルが存在しない場合。");

    File src = new File(WORKDIR, "bbb.ccc");
    File dst = new File(WORKDIR, "eee.fff");
    FALSE(src.exists());
    FALSE(dst.exists());

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];
    values[0] = src;
    values[1] = dst;

    try {
      oi.invokeMethod(fop, "copyFile", types, values);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      OK(e);
    }
  }

  public void copyFile_2()
  {
    MSG("コピー先のファイルが存在する場合。");

    File src = new File("build.xml");
    File dst = new File(WORKDIR, "ddd.eee");
    try {
      FileOperation.createNewFile(dst);
    } catch (Exception e) {
      NG(e);
    }

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];
    values[0] = src;
    values[1] = dst;

    TRUE(src.exists());
    TRUE(dst.exists());
    TRUE(src.length() > 0);
    TRUE(dst.length() == 0);

    try {
      oi.invokeMethod(fop, "copyFile", types, values);
    } catch (Exception e) {
      NG(e);
    }

    boolean isDiff = false;
    BufferedReader br1 = null, br2 = null;
    try {
      br1 = new BufferedReader(new FileReader(src));
      br2 = new BufferedReader(new FileReader(dst));
      while (true) {
        String ln1 = br1.readLine();
        String ln2 = br2.readLine();
        if (ln1 == null || ln2 == null) {
          if (ln1 != ln2) {
            isDiff = true;
          }
          break;
        }
        else if (! ln1.equals(ln2)) {
          isDiff = true;
        }
      }
      if (isDiff) {
        NG(src.getPath() + " / " + dst.getPath());
      }
      else {
        OK(src.getPath() + " / " + dst.getPath());
      }
    } catch (Exception e) {
      NG(e);
    } finally {
      if (br1 != null) try { br1.close(); } catch (Exception e) {}
      if (br2 != null) try { br2.close(); } catch (Exception e) {}
    }
  }

  public void copyFile_3()
  {
    MSG("コピー元のファイルがディレクトリの場合。");

    File src = new File(WORKDIR, "aaa");
    src.mkdirs();

    File dst = new File(WORKDIR, "bbb");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];
    values[0] = src;
    values[1] = dst;

    try {
      oi.invokeMethod(fop, "copyFile", types, values);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyFile_4()
  {
    MSG("コピー先と同じ名前のディレクトリが存在する場合。");

    File src = new File("build.xml");
    File dst = new File(WORKDIR, "bbb/build.xml");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];
    values[0] = src;
    values[1] = dst;

    dst.mkdirs();
    TRUE(src.exists());
    TRUE(dst.exists());
    FALSE(src.isDirectory());
    TRUE(dst.isDirectory());

    try {
      oi.invokeMethod(fop, "copyFile", types, values);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

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

    File src = new File("build.xml");
    File dst = new File(WORKDIR, "b.xml");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    try {
      values[0] = null;
      values[1] = dst;
      oi.invokeMethod(fop, "copyFile", types, values);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      values[0] = src;
      values[1] = null;
      oi.invokeMethod(fop, "copyFile", types, values);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner()
  {
    MSG("通常のファイルのコピー。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    try {
      File dst = FileOperation.getDestinationFile(FILES[3], DIRS[3], DIRS[4]);
      values[0] = FILES[3];
      values[1] = dst;

      TRUE(FILES[3].exists());
      FALSE(dst.exists());

      oi.invokeMethod(fop, "copyInner", types, values);

      TRUE(FILES[3].exists());
      TRUE(dst.exists());

    } catch (Exception e) {
      NG(e);
    }
  }

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

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    try {
      values[0] = FILES[3];
      values[1] = null;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      File dst = FileOperation.getDestinationFile(FILES[3], DIRS[3], DIRS[4]);
      values[0] = null;
      values[1] = dst;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_1()
  {
    MSG("コピー元のファイルが存在しない場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    File src = new File(WORKDIR, "abc");
    File dst = new File(WORKDIR, "def");
    FALSE(src.exists());
    FALSE(dst.exists());
    try {
      values[0] = src;
      values[1] = dst;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_2()
  {
    MSG("コピー先の親ディレクトリが存在しない場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    File src = DIRS[3];
    File dst =new File(WORKDIR, "aaa");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    try {
      values[0] = src;
      values[1] = dst;
      oi.invokeMethod(fop, "copyInner", types, values);
    } catch (Exception e) {
      NG(e);
      e.printStackTrace();
    }

    TRUE(DIRS[3].exists());
    TRUE(new File(WORKDIR, "aaa").exists());
    TRUE(new File(WORKDIR, "aaa/FILES3").exists());
    TRUE(new File(WORKDIR, "aaa/FILES8").exists());
  }

  public void copyInner_3()
  {
    MSG("コピー先のディレクトリでないファイルが既に存在する場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    TRUE(FILES[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());

    try {
      values[0] = FILES[3];
      values[1] = FILES[4];
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());

    try {
      values[0] = DIRS[3];
      values[1] = FILES[4];
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());
  }

  public void copyInner_5()
  {
    MSG("コピー先のディレクトリが既に存在する場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    try {
      TRUE(DIRS[3].exists());
      TRUE(FILES[3].exists());
      TRUE(FILES[8].exists());
      TRUE(DIRS[4].exists());
      FALSE(fop.getDestinationFile(FILES[3], DIRS[3], DIRS[4]).exists());
      FALSE(fop.getDestinationFile(FILES[8], DIRS[3], DIRS[4]).exists());
  
      try {
        values[0] = DIRS[3];
        values[1] = DIRS[4];
        oi.invokeMethod(fop, "copyInner", types, values);
      } catch (Exception e) {
        NG(e);
      }
  
      TRUE(DIRS[3].exists());
      TRUE(DIRS[4].exists());
      TRUE(fop.getDestinationFile(FILES[3], DIRS[3], DIRS[4]).exists());
      TRUE(fop.getDestinationFile(FILES[8], DIRS[3], DIRS[4]).exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_6()
  {
    MSG("空でないディレクトリのコピー。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    File src = DIRS[0];
    File dst = new File(WORKDIR, "dst");

    try {
      for (File d : DIRS) TRUE(d.exists());
      for (File f : FILES) TRUE(f.exists());
      for (File d : DIRS) FALSE(fop.getDestinationFile(d, src, dst).exists());
      for (File f : FILES) FALSE(fop.getDestinationFile(f, src, dst).exists());

      values[0] = src;
      values[1] = dst;
      oi.invokeMethod(fop, "copyInner", types, values);

      for (File d : DIRS) TRUE(d.exists());
      for (File f : FILES) TRUE(f.exists());
      for (File d : DIRS) TRUE(fop.getDestinationFile(d, src, dst).exists());
      for (File f : FILES) TRUE(fop.getDestinationFile(f, src, dst).exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_7()
  {
    MSG("コピーに失敗したときのロールバックの確認。");

    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    FileOperation fop = new FileOperation();
    File dstDir = new File(WORKDIR, "dest");

    File errFile = new File(dstDir, "DIRS2/DIRS4/FILES4");
    try {
      FileOperation.createNewFile(errFile);
    } catch (Exception e) {
      NG(e);
    }

    try {
      List<File> beforeLst = FileOperation.listRecursive(WORKDIR);

      try {
        values[0] = DIRS[0];
        values[1] = dstDir;
        oi.invokeMethod(fop, "copyInner", types, values);
        NG();
      } catch (FileAlreadyExistsException e) {
        OK(e);
      } catch (Exception e) {
        NG(e);
      }
  
      List<File> afterLst = FileOperation.listRecursive(WORKDIR);
      EQUAL(beforeLst.size(), afterLst.size());
      for (int i=0; i<beforeLst.size(); i++) {
        EQUAL(beforeLst.get(i), afterLst.get(i));
      }
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_8()
  {
    MSG("コピー先の親ディレクトリが実はファイルの場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    File src = DIRS[3];
    File dst =new File(WORKDIR, "aaa/bbb/ccc");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    try {
      FileOperation.createNewFile(new File(WORKDIR, "aaa"));
    } catch (Exception e) {
      NG(e);
    }

    try {
      values[0] = src;
      values[1] = dst;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    FALSE(dst.exists());
  }

  public void copyInner_9()
  {
    MSG("コピー先の親ディレクトリが存在しないときにエラーとなる場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class };
    Object[] values = new Object[2];

    @SuppressWarnings("serial")
    File src = new File(DIRS[3].getPath()) {
      public boolean isDirectory() { return false; }
      public boolean isFile() { return true; }
    };
    File dst =new File(WORKDIR, "aaa/bbb/ccc");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    try {
      values[0] = src;
      values[1] = dst;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    FALSE(dst.exists());
  }

  public void moveInner_withFilter()
  {
    MSG("通常のファイルの移動。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      File dstFile = FileOperation.getDestinationFile(FILES[4], DIRS[4], 
        WORKDIR);
      OK(dstFile.getPath());

      TRUE(FILES[3].exists());
      TRUE(dstFile.getParentFile().exists());
      FALSE(dstFile.exists());

      values[0] = FILES[3];
      values[1] = dstFile;
      values[2] = filter;
      oi.invokeMethod(fop, "moveInner", types, values);

      FALSE(FILES[3].exists());
      TRUE(dstFile.getParentFile().exists());
      TRUE(dstFile.exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_withFilter00()
  {
    MSG("ディレクトリの移動。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      File dstDir = new File(WORKDIR, "dest");
      File dstFile = FileOperation.getDestinationFile(DIRS[4], DIRS[0], dstDir);

      TRUE(DIRS[4].exists());
      TRUE(FILES[4].exists());
      TRUE(FILES[9].exists());
      FALSE(dstFile.getParentFile().exists());
      FALSE(dstFile.exists());

      values[0] = DIRS[4];
      values[1] = dstFile;
      values[2] = filter;
      oi.invokeMethod(fop, "moveInner", types, values);

      TRUE(DIRS[4].exists());
      FALSE(FILES[4].exists());
      FALSE(FILES[9].exists());
      TRUE(dstFile.exists());
      TRUE(FileOperation.getDestinationFile(FILES[4],DIRS[0],dstDir).exists());
      TRUE(FileOperation.getDestinationFile(FILES[9],DIRS[0],dstDir).exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_withFilter01()
  {
    MSG("移動元のファイルが存在しない場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    File srcFile = new File(WORKDIR, "fff");
    File dstFile = new File(WORKDIR, "ggg");

    FALSE(srcFile.exists());
    FALSE(dstFile.exists());
    TRUE(dstFile.getParentFile().exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = srcFile;
      values[1] = dstFile;
      values[2] = filter;
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_withFilter02()
  {
    MSG("移動先のディレクトリでないファイルが既に存在する場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = FILES[3];
      values[1] = FILES[4];
      values[2] = filter;
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());

    try {
      values[0] = DIRS[3];
      values[1] = FILES[4];
      values[2] = filter;
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());
  }

  public void moveInner_withFilter04()
  {
    MSG("移動先のディレクトリが既に存在する場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      TRUE(DIRS[3].exists());
      TRUE(FILES[3].exists());
      TRUE(FILES[8].exists());
      TRUE(DIRS[4].exists());
      FALSE(fop.getDestinationFile(FILES[3], DIRS[3], DIRS[4]).exists());
      FALSE(fop.getDestinationFile(FILES[8], DIRS[3], DIRS[4]).exists());
  
      try {
        values[0] = DIRS[3];
        values[1] = DIRS[4];
        values[2] = filter;
        oi.invokeMethod(fop, "moveInner", types, values);
      } catch (Exception e) {
        NG(e);
      }
  
      TRUE(DIRS[3].exists());
      FALSE(FILES[3].exists());
      FALSE(FILES[8].exists());
      TRUE(DIRS[4].exists());
      TRUE(fop.getDestinationFile(FILES[3], DIRS[3], DIRS[4]).exists());
      TRUE(fop.getDestinationFile(FILES[8], DIRS[3], DIRS[4]).exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_withFilter05()
  {
    MSG("移動に失敗したときのロールバックの確認。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    File dstDir = new File(WORKDIR, "dest");

    File errFile = new File(dstDir, "DIRS2/DIRS4/FILES4");
    try {
      FileOperation.createNewFile(errFile);
    } catch (Exception e) {
      NG(e);
    }

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      List<File> beforeLst = FileOperation.listRecursive(WORKDIR);

      try {
        values[0] = DIRS[0];
        values[1] = dstDir;
        values[2] = filter;
        oi.invokeMethod(fop, "moveInner", types, values);
        NG();
      } catch (FileAlreadyExistsException e) {
        OK(e);
      } catch (Exception e) {
        NG(e);
      }
  
      List<File> afterLst = FileOperation.listRecursive(WORKDIR);
      EQUAL(beforeLst.size(), afterLst.size());
      for (int i=0; i<beforeLst.size(); i++) {
        EQUAL(beforeLst.get(i), afterLst.get(i));
      }
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveInner_withFilter06()
  {
    MSG("移動先の親ディレクトリが存在しない場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    File src = DIRS[3];
    File dst =new File(WORKDIR, "aaa");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = src;
      values[1] = dst;
      values[2] = filter;
      oi.invokeMethod(fop, "moveInner", types, values);
    } catch (Exception e) {
      NG(e);
      e.printStackTrace();
    }

    TRUE(DIRS[3].exists());
    FALSE(FILES[3].exists());
    FALSE(FILES[8].exists());
    TRUE(dst.exists());
    TRUE(new File(WORKDIR, "aaa/FILES3").exists());
    TRUE(new File(WORKDIR, "aaa/FILES8").exists());
  }

  public void moveInner_withFilter07()
  {
    MSG("移動先の親ディレクトリが実はファイルの場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    File src = DIRS[3];
    File dst =new File(WORKDIR, "aaa/bbb/ccc");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    try {
      FileOperation.createNewFile(new File(WORKDIR, "aaa"));
    } catch (Exception e) {
      NG(e);
    }

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = src;
      values[1] = dst;
      values[2] = filter;
      oi.invokeMethod(fop, "moveInner", types, values);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    FALSE(dst.exists());
  }

  public void copyInner_withFilter()
  {
    MSG("通常のファイルのコピー。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      File dstFile = FileOperation.getDestinationFile(FILES[4], DIRS[4], 
        WORKDIR);
      OK(dstFile.getPath());

      TRUE(FILES[3].exists());
      TRUE(dstFile.getParentFile().exists());
      FALSE(dstFile.exists());

      values[0] = FILES[3];
      values[1] = dstFile;
      values[2] = filter;
      oi.invokeMethod(fop, "copyInner", types, values);

      TRUE(FILES[3].exists());
      TRUE(dstFile.getParentFile().exists());
      TRUE(dstFile.exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_withFilter00()
  {
    MSG("ディレクトリのコピー。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      File dstDir = new File(WORKDIR, "dest");
      File dstFile = FileOperation.getDestinationFile(DIRS[4], DIRS[0], dstDir);

      TRUE(DIRS[4].exists());
      TRUE(FILES[4].exists());
      TRUE(FILES[9].exists());
      FALSE(dstFile.getParentFile().exists());
      FALSE(dstFile.exists());

      values[0] = DIRS[4];
      values[1] = dstFile;
      values[2] = filter;
      oi.invokeMethod(fop, "copyInner", types, values);

      TRUE(DIRS[4].exists());
      TRUE(FILES[4].exists());
      TRUE(FILES[9].exists());
      TRUE(dstFile.exists());
      TRUE(FileOperation.getDestinationFile(FILES[4],DIRS[0],dstDir).exists());
      TRUE(FileOperation.getDestinationFile(FILES[9],DIRS[0],dstDir).exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_withFilter01()
  {
    MSG("コピー元のファイルが存在しない場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    File srcFile = new File(WORKDIR, "fff");
    File dstFile = new File(WORKDIR, "ggg");

    FALSE(srcFile.exists());
    FALSE(dstFile.exists());
    TRUE(dstFile.getParentFile().exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = srcFile;
      values[1] = dstFile;
      values[2] = filter;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_withFilter02()
  {
    MSG("コピー先のディレクトリでないファイルが既に存在する場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    TRUE(FILES[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = FILES[3];
      values[1] = FILES[4];
      values[2] = filter;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());

    try {
      values[0] = DIRS[3];
      values[1] = FILES[4];
      values[2] = filter;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(FILES[4].exists());
  }

  public void copyInner_withFilter04()
  {
    MSG("コピー先のディレクトリが既に存在する場合。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      TRUE(DIRS[3].exists());
      TRUE(FILES[3].exists());
      TRUE(FILES[8].exists());
      TRUE(DIRS[4].exists());
      FALSE(fop.getDestinationFile(FILES[3], DIRS[3], DIRS[4]).exists());
      FALSE(fop.getDestinationFile(FILES[8], DIRS[3], DIRS[4]).exists());
  
      try {
        values[0] = DIRS[3];
        values[1] = DIRS[4];
        values[2] = filter;
        oi.invokeMethod(fop, "copyInner", types, values);
      } catch (Exception e) {
        NG(e);
      }
  
      TRUE(DIRS[3].exists());
      TRUE(FILES[3].exists());
      TRUE(FILES[8].exists());
      TRUE(DIRS[4].exists());
      TRUE(fop.getDestinationFile(FILES[3], DIRS[3], DIRS[4]).exists());
      TRUE(fop.getDestinationFile(FILES[8], DIRS[3], DIRS[4]).exists());
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_withFilter05()
  {
    MSG("コピーに失敗したときのロールバックの確認。");

    FileOperation fop = new FileOperation();
    ObjectInspector oi = new ObjectInspector(this);
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    File dstDir = new File(WORKDIR, "dest");

    File errFile = new File(dstDir, "DIRS2/DIRS4/FILES4");
    try {
      FileOperation.createNewFile(errFile);
    } catch (Exception e) {
      NG(e);
    }

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      List<File> beforeLst = FileOperation.listRecursive(WORKDIR);

      try {
        values[0] = DIRS[0];
        values[1] = dstDir;
        values[2] = filter;
        oi.invokeMethod(fop, "copyInner", types, values);
        NG();
      } catch (FileAlreadyExistsException e) {
        OK(e);
      } catch (Exception e) {
        NG(e);
      }
  
      List<File> afterLst = FileOperation.listRecursive(WORKDIR);
      EQUAL(beforeLst.size(), afterLst.size());
      for (int i=0; i<beforeLst.size(); i++) {
        EQUAL(beforeLst.get(i), afterLst.get(i));
      }
    } catch (Exception e) {
      NG(e);
    }
  }

  public void copyInner_withFilter06()
  {
    MSG("コピー先の親ディレクトリが存在しない場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    File src = DIRS[3];
    File dst =new File(WORKDIR, "aaa");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = src;
      values[1] = dst;
      values[2] = filter;
      oi.invokeMethod(fop, "copyInner", types, values);
    } catch (Exception e) {
      NG(e);
      e.printStackTrace();
    }

    TRUE(DIRS[3].exists());
    TRUE(FILES[3].exists());
    TRUE(FILES[8].exists());
    TRUE(dst.exists());
    TRUE(new File(WORKDIR, "aaa/FILES3").exists());
    TRUE(new File(WORKDIR, "aaa/FILES8").exists());
  }

  public void copyInner_withFilter07()
  {
    MSG("コピー先の親ディレクトリが実はファイルの場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    File src = DIRS[3];
    File dst =new File(WORKDIR, "aaa/bbb/ccc");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    try {
      FileOperation.createNewFile(new File(WORKDIR, "aaa"));
    } catch (Exception e) {
      NG(e);
    }

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = src;
      values[1] = dst;
      values[2] = filter;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    FALSE(dst.exists());
  }

  public void copyInner_withFilter08()
  {
    MSG("コピー先の親ディレクトリが存在しないときにエラーとなる場合。");

    ObjectInspector oi = new ObjectInspector(this);
    FileOperation fop = new FileOperation();
    Class[] types = { File.class, File.class, FileFilter.class };
    Object[] values = new Object[3];

    @SuppressWarnings("serial")
    File src = new File(DIRS[3].getPath()) {
      public boolean isDirectory() { return false; }
      public boolean isFile() { return true; }
    };
    File dst =new File(WORKDIR, "aaa/bbb/ccc");
    TRUE(DIRS[3].exists());
    FALSE(dst.exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      values[0] = src;
      values[1] = dst;
      values[2] = filter;
      oi.invokeMethod(fop, "copyInner", types, values);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(DIRS[3].exists());
    FALSE(dst.exists());
  }
}
