/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : Utils.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */
package ciss.phase_viewer.common;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.JTable;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;

import org.apache.log4j.Logger;

import ciss.phase_viewer.settings.PropertiesManager;
import ciss.phase_viewer.settings.PropertyPanel;
import ciss.phase_viewer.ssh.hosts.HostList;

public class Utils {

    private static Logger logger = Logger.getLogger(Utils.class.getName());

    private static String FS = System.getProperty("file.separator");

    private Utils() {
    }

    /**
     * 引数のディレクトリーのサイズを再帰的に調べる.
     * 
     * @param dir
     *            目的のディレクトリー
     * @return サイズ; File#length()を積み重ねた値
     */
    public static final int getDirSize(File dir) {
        int size = 0;
        if (!dir.isDirectory())
            return size;
        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; i++)
            if (files[i].isDirectory())
                size += getDirSize(files[i]);
            else
                size += files[i].length();
        return size;
    }

    /**
     * 引数のファイルからPrintWriterを作成し, 返す. 失敗した場合null.
     * 
     * @param fileName
     *            PrintWriterを作成したいファイルのファイル名
     * @return fileNameに対応したPrintWriter
     */
    public static PrintWriter getPrintWriter(String fileName) {
        return getPrintWriter(fileName, false);
    }

    /**
     * 引数のファイルからPrintWriterを作成し, 返す. 失敗した場合null.
     * 
     * @param fileName
     *            PrintWriterを作成したいファイルのファイル名
     * @param append
     *            追加モードの場合true.
     * @return fileNameに対応したPrintWriter
     */
    public static PrintWriter getPrintWriter(String fileName, boolean append) {
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(new FileWriter(fileName, append));
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return null;
        }
        return writer;
    }

    /**
     * 引数のファイルからBufferedReaderを作成し, 返す. 失敗した場合null.
     * 
     * @param fileName
     *            BufferedReaderを作成したいファイルのファイル名.
     * @return fileNameに対応したBufferedReader.
     */
    public static BufferedReader getReader(String fileName) {
        if (!new File(fileName).exists())
            return null;
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(fileName));
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return null;
        }
        return reader;
    }

    /**
     * file_names.dataから引数の識別子のファイルのファイル名を取得するメソッド.
     * 
     * @param ident
     *            欲しいファイルの識別子
     * @param dir
     *            file_names.dataファイルの存在するディレクトリー
     */
    public static String getFileName(String ident, String dir) {
        HashMap map = new HashMap();
        if (!dir.endsWith(System.getProperty("file.separator")))
            dir += System.getProperty("file.separator");
        try {
            BufferedReader reader = new BufferedReader(new FileReader(dir
                    + "file_names.data"));
            String line = "";
            while ((line = reader.readLine()) != null) {
                String[] entry = line.split("=");
                if (entry != null && entry.length >= 2) {
                    String value = entry[1].replaceAll("'", "").trim();
                    map.put(entry[0].trim(), new File(value).getName());
                }
            }
            reader.close();
        } catch (Exception e) {
        }
        String ret = (String) map.get(ident);
        return ret;
    }

    /**
     * 擬ポテンシャルが格納されているディレクトリーを返す. basedir/data/pseudopotential なるディレクトリー
     */
    public static String getDefaultPPDir() {
        return HostList.getHostList().getHostInfo("localhost")
                .getProperty("basedir")
                + System.getProperty("file.separator")
                + "data"
                + System.getProperty("file.separator") + "pseudopotential";
    }

    /**
     * boots a text editor file specified in
     * $HOME/.phase-viewer/phase-viewer.ini.
     * 
     * @param textfile
     *            the input file for the text editor.
     */
    public static void bootTextViewer(String textfile) {
        File file = new File(textfile);
        if (file.exists()) {
            String editor = PropertiesManager.getGlobalProperties(
                    PropertiesManager.PROPERTIES_PVIEWER).getProperty("editor");
            if (editor == null) {
                logger.error("specify editor");
                return;
            }
            MyThread th = new MyThread(Utils.getPathDQ(editor) + " "
                    + Utils.getPathDQ(textfile));
        } else {
            logger.error("file non-existent");
        }
    }

    /**
     * 引数で与えられたStringを真偽値に変換. 変換則はphase入力ファイル仕様による.
     * 
     * @param str
     *            変換したい文字列.
     * @return 変換されたboolean値.
     */
    public static boolean getBool(String str) {
        if (str == null) {
            return false;
        }
        return str.trim().equalsIgnoreCase("on")
                || str.trim().equalsIgnoreCase("yes")
                || str.trim().equalsIgnoreCase("1")
                || str.trim().equalsIgnoreCase("true");
    }

    /**
     * 一番最初の文字を大文字にする.
     * 
     * @param input
     *            大文字にしたい文字列
     * @return 一文字目が大文字となった文字列
     */
    public static String firstLetterCaps(String input) {
        if (input == null) {
            return input;
        }
        String ret = input;
        char[] ch = input.toCharArray();
        if (ch != null && ch.length != 0) {
            ch[0] = Character.toUpperCase(ch[0]);
            ret = new String(ch, 0, ch.length);
        }
        return ret;
    }

    /**
     * とあるパスに対し, スペースをバックスラッシュでエスケープする.
     * 
     * @param pathName
     *            オリジナルのパス.
     * @return 処理が行われたパス.
     */
    private static String getPath(String pathName) {
        String FS = System.getProperty("file.separator");
        Vector pathVector = new Vector();

        StringTokenizer st = new StringTokenizer(pathName, FS);
        while (st.hasMoreTokens()) {
            pathVector.addElement(st.nextToken());
        }

        String path = new String();
        Vector newPathVector = new Vector();
        String replaceString = "\\\\ ";
        for (int i = 0; i < pathVector.size(); i++) {
            path = (String) pathVector.get(i);
            // System.out.println("old path: "+path);
            String newPath = path.replaceAll("\\s", replaceString);
            // System.out.println("new path: "+newPath);
            newPathVector.addElement(newPath);
        }

        if (newPathVector.size() == 1) {
            return (String) newPathVector.get(0);
        }

        path = new String();
        for (int i = 0; i < newPathVector.size(); i++) {
            path += FS + (String) newPathVector.get(i);
        }

        return path;
    }

    /**
     * システムのクリップボードへ文字列を渡すメソッド
     * 
     * @param str
     *            渡したい文字列
     */
    public static void toClipboard(String str) {
        Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringSelection ss = new StringSelection(str);
        clip.setContents(ss, ss);
    }

    /**
     * 文字列の中から, 数字を抜き出して返す.
     * 
     * @param string
     *            元の文字列
     * @return 数字は省かれたstring.
     */
    public static String stripNumber(String string) {
        char[] cha = string.toCharArray();
        Vector vec = new Vector();
        for (int i = 0; i < cha.length; i++) {
            if (!Character.isDigit(cha[i]))
                vec.addElement(new Character(cha[i]));
        }
        char[] retchar = new char[vec.size()];
        for (int i = 0; i < retchar.length; i++)
            retchar[i] = ((Character) vec.get(i)).charValue();
        return new String(retchar);
    }

    public static String NO_CANDIDATE = "(no candidate)";

    /**
     * 指定の擬ポテンシャルディレクトリーから指定の元素名の擬ポテンシャルを探す. ただし ファイル名しか見ていない. 元素名には,
     * 数字が入っていてもよい.
     * 
     * @param element
     *            元素名
     * @return 擬ポテンシャルファイルの配列; 見つけられない場合NO_CANDIDATEを返す.
     */
    public static String[] getPPCandidates(String element, String parentDir) {
        String strpp = getDefaultPPDir();
        if (parentDir != null)
            strpp = parentDir;
        File ppDir = new File(strpp);
        if (element == null || element.trim().length() == 0) {
            return new String[] { NO_CANDIDATE };
        }

        if (ppDir == null || !ppDir.isDirectory()) {
            return new String[] { NO_CANDIDATE };
        }

        String[] files = ppDir.list();
        PPOrder[] ppOrder = new PPOrder[files.length];
        for (int i = 0; i < files.length; i++)
            ppOrder[i] = new PPOrder(files[i]);
        Arrays.sort(ppOrder);

        Vector<String> ppfiles = new Vector<String>();
        for (int i = ppOrder.length - 1; i >= 0; i--) {
            String comp = ppOrder[i].ppName.split("_")[0] + "\\d*";
            if (element.matches(comp)) {
                ppfiles.addElement(ppOrder[i].ppName);
            }
        }

        if (ppfiles.size() == 0) {
            return new String[] { NO_CANDIDATE };
        }

        String[] ret = new String[ppfiles.size()];
        ppfiles.copyInto(ret);
        return ret;
    }

    /**
     * デフォルトの擬ポテンシャルディレクトリーから指定の元素名の擬ポテンシャルを探す. ただし ファイル名しか見ていない. 元素名には,
     * 数字が入っていてもよい.
     * 
     * @param element
     *            元素名
     * @return 擬ポテンシャルファイルの配列; 見つけられない場合NO_CANDIDATEを返す.
     */
    public static String[] getPPCandidates(String element) {
        return getPPCandidates(element, null);
    }

    /**
     * ダブルクォーテーションでパスをくくる. Windows環境ではこの操作を行うが, 他のOSではなにもせずオリジナルのパスをそのまま返す.
     * 
     * @param pathName
     *            オリジナルのパス.
     * @return 処理を行ったパス.
     */
    public static String getPathDQ(String pathName) {
        String osName = ConstParameters.osName;
        String ret = new String();

        if (osName.toLowerCase().trim().startsWith("windows")) {
            ret = "\"" + pathName + "\"";
        } else {
            ret = getPath(pathName);
        }

        return ret;
    }

    /**
     * 対象がWindowsか否かを明示的に指定.
     * 
     * @param pathName
     *            オリジナルのパス
     * @param isWindows
     *            対象がWindowsかいなか.
     * @return 処理を行ったパス.
     */
    public static String getPathDQ(String pathName, boolean isWindows) {
        String osName = ConstParameters.osName;
        String ret = "";
        if (isWindows) {
            ret = "\"" + pathName + "\"";
        } else {
            ret = getPath(pathName);
        }
        return ret;
    }

    /**
     * ダブルクォーテーションで囲まれた文字列のダブルクォーテーションをとりのぞく.
     * 
     * @param path
     *            ダブルクォーテーションを取り除きたい文字列.
     */
    public static String stripDQ(String path) {
        return path.trim().replaceAll("\"", "");
    }

    /**
     * とある文字列がダブルクォートでくくられているかどうか判定
     */
    public static boolean isDoubleQuoted(String foo) {
        return foo.trim().startsWith("\"") && foo.trim().endsWith("\"");
    }

    /**
     * シングルクォーテーションで囲まれた文字列のシングルクォーテーションをとりのぞく.
     * 
     * @param path
     *            シングルクォーテーションを取り除きたい文字列.
     */
    public static String stripSQ(String path) {
        return path.trim().replaceAll("\'", "");
    }

    /**
     * テーブルのエディターを停止するメソッド. こうすると, 編集が確定していない状態で``save''などとした場合でも 編集内容が反映される.
     * 
     * @param object
     *            現在JTableでしか動作しない.
     * @return stopが成功すれば真.
     */
    public static boolean stop(Object object) {
        boolean stopped = false;
        if (object instanceof JTable) {
            TableCellEditor editor = ((JTable) object).getCellEditor();
            try {
                editor.stopCellEditing();
                stopped = true;
            } catch (Exception exc) {
                stopped = false;
            }
        }

        return stopped;
    }

    /**
     * とあるStringが実は数字か否かを調べる. CharacterクラスのisDigitメソッドを使って判定.
     * 
     * @param str
     *            調べたいString.
     * @return 数字ならば真.
     */
    public static boolean isNumber(String str) {
        boolean ret = true;
        if (str == null || str.trim().length() == 0)
            return false;
        char[] ch = str.toCharArray();
        for (int i = 0; i < ch.length; i++) {
            if (!(Character.isDigit(ch[i]))) {
                ret = false;
            }
        }

        return ret;
    }

    /**
     * ディレクトリーを再帰的に消去する. java.io.Fileのdelete()メソッドと違い, その下のファイルが
     * あろうがなにがあろうが全て消去してしまう.
     * 
     * @param dir
     *            消去したいディレクトリー
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }

        // The directory is now empty so delete it
        return dir.delete();
    }

    private static String[] defaultIgnoreDir = { ".svn" };

    /**
     * 引数の下のディレクトリーの数を再帰的に調べる.
     * 
     * @param dir
     *            対象となるディレクトリー
     * @return ディレクトリーの数
     */
    public static int getTotalNumDir(File dir) {
        int numdir = 0;
        if (!dir.isDirectory())
            return numdir;
        File[] files = dir.listFiles();
        if (files == null)
            return 1;
        for (int i = 0; i < files.length; i++) {
            if (files[i].getName().equals(".svn"))
                continue;
            if (files[i].isDirectory()) {
                numdir++;
                numdir += getTotalNumDir(files[i]);
            }
        }
        return numdir;
    }

    /**
     * ディレクトリーを再帰的にコピー
     * 
     * @param fromDir
     * @param toDir
     * @param overwrite
     *            すでにあるファイルをも上書きする場合true
     */
    public static boolean copyDir(File fromDir, File toDir, boolean overwrite) {
        if (!fromDir.isDirectory() || !toDir.isDirectory()) {
            logger.debug("both args must be directories");
            return false;
        }
        String fromDirName = fromDir.getName();
        File totoDir = new File(toDir.getAbsolutePath() + FS + fromDirName);
        totoDir.mkdir();
        File[] fromFiles = fromDir.listFiles();
        for (int i = 0; i < fromFiles.length; i++) {
            if (!fromFiles[i].isDirectory()) {
                String name = fromFiles[i].getName();
                String copyName = totoDir.getAbsolutePath() + FS + name;
                if (new File(copyName).exists() && !overwrite)
                    continue;
                try {
                    ExternalProgramExecuter.copyBinary(
                            fromFiles[i].getAbsolutePath(), copyName);
                } catch (Exception exc) {
                    exc.printStackTrace();
                    return false;
                }
            } else {
                boolean ignore = false;
                String name = fromFiles[i].getName();
                for (int j = 0; j < defaultIgnoreDir.length; j++) {
                    if (name.equals(defaultIgnoreDir[j])) {
                        ignore = true;
                    }
                }
                if (!ignore)
                    copyDir(fromFiles[i], totoDir, overwrite);
            }
        }
        return true;
    }

    /**
     * ディレクトリーを再帰的にコピー
     * 
     * @param fromDir
     * @param toDir
     */
    public static boolean copyDir(File fromDir, File toDir) {
        return copyDir(fromDir, toDir, true);
    }

    /**
     * ディレクトリーを再帰的にコピー fromDirをリネームしたい場合こちらを使う.
     * 
     * @param fromDir
     * @param toDir
     * @param newname
     *            toDir/newnameとなる; この場合, newnameの中身はfromDirと同じ, みたいな感じ.
     */
    public static boolean copyDir(File fromDir, File toDir, String newname) {
        if (!fromDir.isDirectory() || !toDir.isDirectory()) {
            logger.debug("both args must be directories");
            return false;
        }
        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
        File tmpfromDir = new File(tmpDir
                + System.getProperty("file.separator") + fromDir.getName());
        deleteDir(tmpfromDir);
        if (!copyDir(fromDir, tmpDir)) {
            return false;
        }

        File toFile = new File(toDir.getAbsolutePath()
                + System.getProperty("file.separator") + newname);
        return tmpfromDir.renameTo(toFile);
    }

    public static void clearTableModel(TableModel model) {
        TableModelListener[] listeners = ((AbstractTableModel) model)
                .getTableModelListeners();
        for (int i = 0; i < listeners.length; i++) {
            model.removeTableModelListener(listeners[i]);
        }

        int row = model.getRowCount();
        int column = model.getColumnCount();
        logger.debug("clearing row, column: " + row + " " + column);

        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                model.setValueAt("", i, j);
            }
        }
        for (int i = 0; i < listeners.length; i++) {
            model.addTableModelListener(listeners[i]);
        }

    }

    public static void bootPropertyPanel() {
        PropertyPanel pp = new PropertyPanel();
    }

    public static double getNorm(double[] v) {
        double norm = 0.d;
        if (v != null) {
            for (int i = 0; i < v.length; i++) {
                norm += v[i] * v[i];
            }
        } else {
            // logger.error("v was null ... ");
        }
        return Math.sqrt(norm);
    }

    public static void normalize(double[] v) {
        if (v == null) {
            // logger.error("v was null ... ");
        }
        double norm = Utils.getNorm(v);
        for (int i = 0; i < v.length; i++) {
            v[i] = v[i] / norm;
        }
    }

    /**
     * ファイルの行数を取得
     * 
     * @param fileName
     *            ファイル名
     * @return ファイルの行数(エラー時-1)
     */
    public static int getNumLines(String fname) {
        int sum = 0;
        BufferedReader br = null;
        try {
            FileReader freader = new FileReader(fname);
            br = new BufferedReader(freader);
            while (br.readLine() != null) {
                sum++;
            }
            br.close();
        } catch (Exception exc) {
            logger.error(exc);
            return -1;
        } finally {
            try {
                br.close();
            } catch (IOException ioe) {
                logger.error(ioe);
                return -1;
            }
        }
        return sum;
    }

    /**
     * テスト用メイン
     */
    public static void main(String[] args) {
        System.out.println("original path: " + args[0]);
        System.out.println("parsed path: " + Utils.getPath(args[0]));
    }

    /**
     * 一時ファイルを取得
     * 
     * @return 一時ファイル
     */
    public static File getTmpFile() {
        String now = String.valueOf(java.util.Calendar.getInstance()
                .getTimeInMillis());
        String tmpDir = System.getProperty("java.io.tmpdir");
        String tmpFile = tmpDir + System.getProperty("file.separator") + "tmp"
                + now;
        return new File(tmpFile);
    }

    /**
     * Windowsの場合, ファイルセパレーターに'\'が使われる. このままだと正規表現を利用するときなどに頗る都合が悪いので,
     * そこをエスケープした文字列を返す. backslashが存在しない場合何もしない.
     * 
     * @param path
     *            元の文字列
     * @return バックスラッシュをエスケープした文字列
     */
    public static String escapeBackSlash(String path) {
        if (path == null || path.length() == 0) {
            return path;
        }
        Character bs = new Character('\\');
        char[] array = path.toCharArray();
        String ret = "";
        for (int i = 0; i < array.length; i++) {
            ret += String.valueOf(array[i]);
            if (array[i] == '\\') {
                ret += "\\";
            }
        }
        return ret;
    }

    /**
     * 配列の中の重複する要素を削除.
     * 
     * @param arg
     *            この操作を行う配列.
     */
    public static Vector removeDupli(Object[] arg) {
        if (arg == null) {
            return null;
        }
        java.util.Arrays.sort(arg);
        Vector vec = new Vector();
        for (int i = 0; i < arg.length; i++) {
            if (vec.indexOf(arg[i]) < 0)
                vec.addElement(arg[i]);
        }
        return vec;
    }

    /**
     * Vectorの中の重複する要素を削除.
     * 
     * @param arg
     *            この操作を行う配列.
     */
    public static Vector removeDupli(Vector vector) {
        if (vector == null || vector.size() == 0)
            return null;
        Object[] objs = new Object[vector.size()];
        vector.copyInto(objs);
        return removeDupli(objs);
    }

    /**
     * 引数で与えられたファイルから文字列を生成し, 返す. 改行コードの変換などは一切行わない.
     * 
     * @param file
     *            文字列を抽出したいファイルへのパス.
     * @return ファイルの全内容の文字列.
     */
    public static String getStringFromFile(String file) {
        String ret = "";
        InputStream in = null;
        Vector vec = new Vector();
        try {
            in = new BufferedInputStream(new FileInputStream(file));
            int b;
            while ((b = in.read()) != -1) {
                vec.addElement(new Byte((byte) b));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        byte[] by = new byte[vec.size()];

        for (int i = 0; i < vec.size(); i++) {
            by[i] = ((Byte) vec.get(i)).byteValue();
        }

        ret = new String(by);
        return ret;
    }

    /**
     * 指定のパスからjava.util.Propertiesのインスタンスを生成
     * 
     * @param path
     *            このパスからPropertiesインスタンスを作成する.
     * @return 指定のPropertiesインスタンス; 失敗した場合nullを返す.
     */
    public static Properties getPropertiesFrom(String path) {
        Properties properties = new Properties();
        InputStream inp = null;
        try {
            inp = Utils.class.getResourceAsStream(path);
            properties.load(inp);
        } catch (IOException ioe) {
            logger.error("failed to load property file...");
            return null;
        } finally {
            try {
                if (inp != null) {
                    inp.close();
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
        return properties;
    }

    /**
     * 二つのパスより相対パスを取得.「最初の引数のディレクトリーからみて二番目の引数のディレクトリーはどこにあるか」
     * 
     * @param pathFrom
     *            このディレクトリーから見た相対パス
     * @param pathTo
     *            このディレクトリーへの相対パス
     * @param forwardSlash
     *            この変数がtrueの時, パスセパレーターは強引に'/'が使われる. Windows対策
     */
    public static String getRelativePath(String pathFrom, String pathTo,
            boolean forwardSlash) {
        if (pathFrom == null || pathTo == null) {
            return null;
        }
        String FS = System.getProperty("file.separator");
        if (forwardSlash)
            FS = "/";

        if (new File(pathFrom).equals(new File(pathTo))) {
            return "." + FS;
        }
        String pathFrom_ = pathFrom.trim();
        String pathTo_ = pathTo.trim();
        String filesep = "/";
        if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
            pathFrom_ = pathFrom_.toLowerCase();
            pathTo_ = pathTo_.toLowerCase();
            filesep = "\\\\";
        }
        String[] arfrom = pathFrom_.split(filesep);
        String[] arto = pathTo_.split(filesep);
        if (arfrom.length == 0 || arto.length == 0) {
            return null;
        }
        int leng = arfrom.length;
        if (leng > arto.length) {
            leng = arto.length;
        }
        int commonIndex = leng;
        for (int i = 0; i < leng; i++) {
            if (!arfrom[i].equals(arto[i])) {
                commonIndex = i;
                break;
            }
        }

        String commonPrefix = arfrom[0];
        for (int i = 1; i < commonIndex; i++) {
            commonPrefix += FS + arfrom[i];
        }
        logger.debug("common prefix: " + commonPrefix);
        String suffix = "";
        if (commonPrefix.length() < pathTo.length()) {
            suffix = pathTo.substring(commonPrefix.length() + 1,
                    pathTo.length()).trim();
        }
        String prefix = "";
        if (commonPrefix.length() < pathFrom.length()) {
            String tmp = pathFrom.substring(commonPrefix.length() + 1,
                    pathFrom.length()).trim();
            logger.debug("pathfrom - common: " + tmp);
            String[] array = tmp.split(filesep);
            if (array != null && array.length != 0) {
                for (int i = 0; i < array.length; i++) {
                    if (array[i].trim().length() != 0) {
                        prefix += ".." + FS;
                    }
                }
            }
        }
        return prefix + suffix;
    }

    /**
     * 二つのパスより相対パスを取得.「最初の引数のディレクトリーからみて二番目の引数のディレクトリーはどこにあるか」
     * 
     * @param pathFrom
     *            このディレクトリーから見た相対パス
     * @param pathTo
     *            このディレクトリーへの相対パス
     */
    public static String getRelativePath(String pathFrom, String pathTo) {
        return getRelativePath(pathFrom, pathTo, false);
    }

    /**
     * オペレーティングシステムがWindowsか否かを判定(Windowsの場合はいろいろな局面で特殊な処理が必要).
     * 
     * @return 現OSがWindowsな場合true.
     */
    public static boolean isWindows() {
        return System.getProperty("os.name").toLowerCase()
                .startsWith("windows");
    }

    /**
     * 指定のディレクトリーの下に, 指定の名前のファイルが作成可能かどうかを判定する.
     * 
     * @param parentDir
     *            ディレクトリー. この下にファイルが作成可能かどうかチェック.
     * @param fname
     *            この名前のファイル名が作成できるかどうかチェック.
     * @return 作成可能, と思われる場合true.
     */
    public static boolean isCreatable(File parentDir, String fname) {
        if (fname == null || fname.trim().length() == 0)
            return false;
        File[] files = parentDir.listFiles();
        if (files.length == 0)
            return true;
        File testFile = new File(parentDir, fname.trim());
        for (int i = 0; i < files.length; i++)
            if (files[i].equals(testFile))
                return false;
        boolean btest = testFile.mkdir();
        if (!btest)
            return false;

        testFile.delete();
        return true;
    }

    public static int parseJREVersion() {
        String ver = System.getProperty("java.version");
        String vers = ver.substring(0, 5);
        String[] verss = vers.split("\\.");
        String str = "";
        for (int i = 0; i < verss.length; i++)
            str += verss[i];
        return Integer.parseInt(str);
    }

}

/**
 * Ordering of the PP files; prefer nc && us pps compared to paw pps.
 * 
 * @author jkoga
 */
class PPOrder implements Comparable<PPOrder> {
    String ppName = "";

    PPOrder(String ppName) {
        this.ppName = ppName;
    }

    public int compareTo(PPOrder arg0) {
        if (this == arg0)
            return 0;
        PPOrder ppOrder = ((PPOrder) arg0);
        if (ppOrder.ppName.equals(ppName))
            return 0;
        if (ppOrder.ppName.contains("paw") && !this.ppName.contains("paw"))
            return 1;
        if (!ppOrder.ppName.contains("paw") && this.ppName.contains("paw"))
            return -1;
        // return ppName.compareTo(ppOrder.ppName);
        return ppOrder.ppName.compareTo(this.ppName);
    }
}