package com.xiongyingqi.utils.code;

import com.xiongyingqi.util.EntityHelper;
import com.xiongyingqi.util.FileHelper;
import com.xiongyingqi.util.StringHelper;

import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by 瑛琪xiongyingqi.com on 2014/3/31 0031.
 */
public class CodeProcessor {

    /**
     * 查找类、继承类、实现接口的定义
     */
    private final Pattern PATTERN_FIND_CLASS_SEGMENT = Pattern.compile("((public|private|protected)[\\s]+)?[\\s]*class[\\s]*[\\w\\d]+[\\s]*([\\s]+extends[\\s]+[\\w\\d]+)?[\\s]*([\\s]+implements[\\s]+[\\w\\d]+(\\,[\\s]*[\\w\\d]+[\\s]*)*)?[\\s]*\\{");

    /**
     * 查找类定义
     */
    private final Pattern PATTERN_FIND_CLASS_NAME = Pattern.compile("((public|private|protected)[\\s]+)?[\\s]*class[\\s]*[\\w\\d]+[\\s]*");
    /**
     * 查找class+类名
     */
    private final Pattern PATTERN_FIND_CLASS_NAME_WITH_CLASS = Pattern.compile("class[\\s]+[\\w\\d]+");

    /**
     * 查找接口定义
     */
    private final Pattern PATTERN_FIND_INTERCES = Pattern.compile("implements[\\s]+[\\w\\d]+(\\,[\\s]*[\\w\\d]+[\\s]*)*");

    /**
     * 查找继承类定义
     */
    private final Pattern PATTERN_FIND_SUPER_CLASS = Pattern.compile("extends[\\s]+[\\w\\d]+");
    /**
     * 查找包定义
     */
    private final Pattern PATTERN_FIND_PACKAGE = Pattern.compile("package[\\s]+[\\w\\d]+(\\.[\\w\\d]+)*\\;");

    /**
     * 查找包定义的名称
     */
    private final Pattern PATTERN_FIND_PACKAGE_NAME = Pattern.compile("[\\w\\d]+(\\.[\\w\\d]+)*");

    /**
     * 查找导入的定义
     */
    private final Pattern PATTERN_FIND_IMPORT = Pattern.compile("import[\\s]+[\\w\\d]+(\\.([\\w\\d]+|[\\*]+)+)*\\;");
    /**
     * 查找导入的定义的名称
     */
    private final Pattern PATTERN_FIND_IMPORT_NAME = Pattern.compile("[\\w\\d]+(\\.([\\w\\d]+|[\\*]+)+)*");

    /**
     * 查找注解
     */
    private final Pattern PATTERN_FIND_ANNOTAION = Pattern.compile("@\\w+(\\(.*?\\)){0,1}");

    private File file;
    private String content;
    private CodeBuilder builder;

    public CodeProcessor(File file, CodeBuilder builder) {
        this.file = file;
        this.builder = builder;
        begin();
    }

    private void begin() {
        if (file == null) {
            return;
        }
        content = readFile(this.file);

        checkClass();
        checkImport();
        checkCodeFragment();
        checkAnnotations();
        removeImport();
//                EntityHelper.print(content);
        writeFile();
        System.out.println("文件：\"" + file + "\"处理完成。");

    }

    private String readFile(File file) {
        return FileHelper.readFileToString(file);
    }

    /**
     * 刷新文件内容
     */
    private void writeFile() {
        FileHelper.writeStringToFile(file, content);
    }

    // ---------------------------------------- 包定义和导入包 ----------------------------------------
    private String findPackage() {
        Matcher matcher = PATTERN_FIND_PACKAGE.matcher(content);
        if (matcher.find()) {
            return matcher.group();
        }
        return null;
    }

    private String findPackageName(String packageDefine) {
        Matcher matcher = PATTERN_FIND_PACKAGE_NAME.matcher(packageDefine.substring("package".length()));
        if (matcher.find()) {
            return matcher.group();
        }
        return null;
    }

    private Set<String> findImports(String content) {
        Set<String> knowImports = new HashSet<String>();
        Matcher matcher = PATTERN_FIND_IMPORT.matcher(content);
        while (matcher.find()) {
            String found = matcher.group().substring("import".length());
            Matcher matcherImportName = PATTERN_FIND_IMPORT_NAME.matcher(found);
            if (matcherImportName.find()) {
                knowImports.add(matcherImportName.group());
            }
        }
        return knowImports;
    }

    /**
     * 检查导入包
     *
     * @return
     */
    private void checkImport() {
        Set<String> classImports = new HashSet<String>();
        classImports.addAll(builder.getImports());

        classImports.removeAll(findImports(content));// 移除已经导入的包

        Set<String> classImportsToRemove = new HashSet<String>();// 如果要导入的包与类所在的package一致，则忽略导入

        final String packageStr = findPackage();// 类所定义的包路径
        if (packageStr != null) {//如果定义了包路径，则在包路径后面新增导入
            String packageName = findPackageName(packageStr);
            for (String impt : classImports) {
                int point = impt.lastIndexOf(".");
                String pkgNameOfImport = impt.substring(0, point);//截取import的类所属包
                if (pkgNameOfImport.equals(packageName)) {// 如果包已经导入，则取消导入
                    classImportsToRemove.add(impt);
                }
            }
            classImports.removeAll(classImportsToRemove);

            //如果定义了包路径，则在包路径后面新增导入
            StringBuilder builder = new StringBuilder(packageStr);
            builder.append(StringHelper.line());
            for (String toImport : classImports) {
                builder.append(StringHelper.line());
                builder.append("import ");
                builder.append(toImport);
                builder.append(";");
            }
            content = StringHelper.replaceFirst(content, packageStr, builder.toString());
        } else {// 如果没有定义包，则在头部导入包
            StringBuilder builder = new StringBuilder();
            for (String toImport : classImports) {
                builder.append(StringHelper.line());
                builder.append("import ");
                builder.append(toImport);
                builder.append(";");
            }
            content = builder.append(content).toString();
        }

    }

    // ---------------------------------------- 包定义和导入包 ----------------------------------------

    // ---------------------------------------- class片段 ----------------------------------------

    /**
     * 检查class定义段
     */
    private void checkClass() {
        Matcher findClassMatcher = PATTERN_FIND_CLASS_SEGMENT.matcher(content);

        if (findClassMatcher.find()) {
            final String classDefine = findClassMatcher.group();// 查找类定义

            String classDefineReplace = checkSuperClass(classDefine);
            classDefineReplace = checkInterfaces(classDefineReplace);
            content = StringHelper.replaceFirst(content, classDefine, classDefineReplace);
        }
    }

    /**
     * 查找已有的接口定义
     *
     * @param interfaceDefineString 接口定义字符串
     * @return
     */
    private Set<String> findInterfaces(String interfaceDefineString) {
        String interfaceStrings = StringHelper.replaceFirst(interfaceDefineString, "implements", "");
        String[] interfaces = interfaceStrings.split(",");
        Set<String> stringSet = new HashSet<String>();
        for (String interfaceString : interfaces) {
            stringSet.add(interfaceString.trim());
        }
        return stringSet;
    }

    /**
     * 检查implements关键字
     *
     * @param classSegment
     * @return
     */
    private String checkInterfaces(String classSegment) {
        if (builder.getInterfaces() == null || builder.getInterfaces().size() == 0) {
            return classSegment;
        }

        Matcher matcher = PATTERN_FIND_INTERCES.matcher(classSegment);

        if (matcher.find()) {
            final String interfaceDefineString = matcher.group();

            Set<String> interfaceSet = findInterfaces(interfaceDefineString);

            StringBuilder interfaceDefineStringReplace = new StringBuilder(interfaceDefineString);
            for (String interfaceName : builder.getInterfaces()) {
                if (interfaceSet.contains(interfaceName)) {// 如果已经存在接口的定义，则跳过该定义
                    continue;
                }
                interfaceDefineStringReplace.append(", ");
                interfaceDefineStringReplace.append(interfaceName);
            }
            classSegment = StringHelper.replaceFirst(classSegment, interfaceDefineString, interfaceDefineStringReplace.toString());
        } else {
            final String interfaceDefineString = "{";
            StringBuilder interfaceDefineStringReplace = new StringBuilder("implements ");
            boolean isFirstInterface = true;
            for (String interfaceName : builder.getInterfaces()) {
                if (!isFirstInterface) {
                    interfaceDefineStringReplace.append(", ");
                }
                if (isFirstInterface) {
                    isFirstInterface = false;
                }

                interfaceDefineStringReplace.append(interfaceName);
            }
            interfaceDefineStringReplace.append(interfaceDefineString);
            classSegment = StringHelper.replaceFirst(classSegment, interfaceDefineString, interfaceDefineStringReplace.toString());
        }
        return classSegment;
    }

    private String findClassName(String classDefine) {
        Matcher classNameMatcher = PATTERN_FIND_CLASS_NAME_WITH_CLASS.matcher(classDefine);
        if (classNameMatcher.find()) {
            String classNameWithClass = classNameMatcher.group();// class ClassName
            return StringHelper.replaceFirst(classNameWithClass, "class", "").trim();
        }
        return null;
    }

    /**
     * 检查extends关键字
     *
     * @param classSegment
     * @return
     */
    private String checkSuperClass(String classSegment) {
        if (builder.isRemoveSuperClass() || builder.getSuperClass() != null) {
            Matcher matcher = PATTERN_FIND_SUPER_CLASS.matcher(classSegment);
            String superClassString = "";
            if (builder.getSuperClass() != null) {
                superClassString = "extends " + builder.getSuperClass() + " ";
            }

            if (matcher.find()) {
                String replacement = matcher.group();
                classSegment = StringHelper.replaceFirst(classSegment, replacement, superClassString);
            } else {
                Matcher classNameMatcher = PATTERN_FIND_CLASS_NAME.matcher(classSegment);
                if (classNameMatcher.find()) {
                    String className = classNameMatcher.group();
                    String justClassName = findClassName(className);
                    if (justClassName != null && justClassName.equals(builder.getSuperClass())) {
                        return classSegment;
                    }
                    classSegment = StringHelper.replaceFirst(classSegment, className, className + superClassString);
                }
            }

        }
        return classSegment;
    }
    // ---------------------------------------- class片段 ----------------------------------------

    // ---------------------------------------- 代码段 ----------------------------------------

    private void checkCodeFragment() {
        Matcher matcher = PATTERN_FIND_CLASS_SEGMENT.matcher(content);
        if (matcher.find()) {
            final String classDefine = matcher.group();
            StringBuilder builder = new StringBuilder(classDefine);
            for (String codeFragment : this.builder.getCodeFragments()) {
                builder.append(StringHelper.line());
                builder.append(codeFragment);
            }
            content = StringHelper.replaceFirst(content, classDefine, builder.toString());
        }
    }
    // ---------------------------------------- 代码段 ----------------------------------------

    // -------------------------- 移除注解 --------------------------
    private void checkAnnotations() {
        if (!builder.isRemoveAnnotations()) {
            return;
        }
        Matcher matcher = PATTERN_FIND_ANNOTAION.matcher(content);
        while (matcher.find()) {
            final String annotation = matcher.group();
//            StringBuilder builder = new StringBuilder(annotation);
//            for (String codeFragment : this.builder.getCodeFragments()) {
//                builder.append(StringHelper.line());
//                builder.append(codeFragment);
//            }
            content = StringHelper.replaceFirst(content, annotation, "");
            EntityHelper.print(content);
        }
    }

    private void removeImport() {
        Set<String> removeImports = builder.getRemoveImports();
        if (removeImports == null || removeImports.size() == 0) {
            return;
        }

        Matcher matcher = PATTERN_FIND_IMPORT.matcher(content);
        Collection<String> found = new HashSet<String>();
        while (matcher.find()) {
            found.add(matcher.group());
//            StringBuilder builder = new StringBuilder(annotation);
//            for (String codeFragment : this.builder.getCodeFragments()) {
//                builder.append(StringHelper.line());
//                builder.append(codeFragment);
//            }
//            content = StringHelper.replaceFirst(content, annotation, "");
//            EntityHelper.print(content);
        }

        for (String s : found) {
            for (String removeImport : removeImports) {
                if(s.contains(removeImport)){
                    content = StringHelper.replaceFirst(content, s, "");
                }
            }
        }
    }

}


/**
 * spark_src
 */
package com.xiongyingqi.util;

/**
 * @author 瑛琪 xiongyingqi.com
 * @version 2013-8-28 下午5:22:25
 */

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;

public class GetDocumentPathDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(getMyDocumentsFromWinRegistry());
    }

    private static final String REGQUERY_UTIL = "reg query ";
    private static final String REGSTR_TOKEN = "REG_SZ";
    private static final String REGDWORD_TOKEN = "REG_DWORD";

    private static final String PERSONAL_FOLDER_CMD = REGQUERY_UTIL
            + "\"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\"
            + "Explorer\\Shell Folders\" /v Personal";

    /**
     * returns the Path to the "My Documents" folder or null
     *
     * @return {@link String}
     */
    public static String getMyDocumentsFromWinRegistry() {
        try {
            Process process = Runtime.getRuntime().exec(PERSONAL_FOLDER_CMD);
            StreamReader streamreader = new StreamReader(process.getInputStream());

            streamreader.start();
            process.waitFor();
            streamreader.join();

            String result = streamreader.getResult();
            int p = result.indexOf(REGSTR_TOKEN);

            if (p == -1)
                return null;

            return result.substring(p + REGSTR_TOKEN.length()).trim();
        } catch (Exception e) {
            return null;
        }
    }

    static class StreamReader extends Thread {
        private InputStream is;
        private StringWriter sw;

        StreamReader(InputStream is) {
            this.is = is;
            sw = new StringWriter();
        }

        @Override
        public void run() {
            try {
                /**
                 * 读取中文乱码问题
                 */
                /*
				 * int c; while ((c = is.read()) != -1) sw.write(c);
				 */

                /**
                 * 解决中文乱码问题
                 */
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                int c = -1;
                while ((c = is.read()) != -1) {
                    bos.write(c);
                }
                sw.write(new String(bos.toString("GBK").getBytes("UTF-8")));
            } catch (IOException e) {
                System.err.println(e);
            }
        }

        String getResult() {
            return sw.toString();
        }
    }
}


package com.xiongyingqi.util;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Random;

/**
 * Created by 瑛琪xiongyingqi.com on 2014/4/15 0015.
 */
public class PhoneNumberAddress {

    public static XmlProvince getMobileAddress(String mobileNumber) throws MalformedURLException {
        String urlString = "http://life.tenpay.com/cgi-bin/mobile/MobileQueryAttribution.cgi?chgmobile=" + mobileNumber;
        URL url = new URL(urlString);

        XmlProvince result = null;
        JAXBContext jc;
        try {
            jc = JAXBContext.newInstance(XmlProvince.class);
            Unmarshaller u = jc.createUnmarshaller();

            try {
                StreamSource streamSource = new StreamSource(url.openStream());
                JAXBElement<XmlProvince> element = u.unmarshal(streamSource, XmlProvince.class);
                result = element.getValue();
                EntityHelper.print(result);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (JAXBException e) {
            e.printStackTrace();
        }

//
//        SAXReader saxReader = new SAXReader();
//        try {
//            Document document = saxReader.read(url);
//
//
//            Element root = document.getRootElement();
//            for (Iterator<Element> iterator = root.elementIterator(); iterator.hasNext(); ) {
//                Element element = iterator.next();
//                EntityHelper.print(element.getName());
//                EntityHelper.print(element.getStringValue());
//            }
//        } catch (DocumentException e) {
//            e.printStackTrace();
//        }
//
//
//        String jsonString = null;
//        BufferedReader buffer;
//        StringBuffer sb = new StringBuffer();
////        URL url = new URL(urlString);
//        try {
//            InputStream in = url.openStream();
//
//            buffer = new BufferedReader(new InputStreamReader(in, "gb2312"));
//            String line = null;
//            while ((line = buffer.readLine()) != null) {
//                sb.append(line);
//            }
//            in.close();
//            buffer.close();
//            jsonString = sb.toString();
//            EntityHelper.print(jsonString);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        return result;
    }

    /**
     * Test the phone number from which the city is using Taobao API
     *
     * @return
     * @param mobileNumber phone number
     */
    public static String calcMobileCity(String mobileNumber) throws MalformedURLException {
        ObjectMapper objectMapper = new ObjectMapper();

        String jsonString = null;
        String urlString = "http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=" + mobileNumber;
        StringBuffer sb = new StringBuffer();
        BufferedReader buffer;
        URL url = new URL(urlString);
        try {
            InputStream in = url.openStream();

//solve the garbage problem
            buffer = new BufferedReader(new InputStreamReader(in, "gb2312"));
            String line = null;
            while ((line = buffer.readLine()) != null) {
                sb.append(line);
            }
            in.close();
            buffer.close();
            // System.out.println(sb.toString());
            jsonString = sb.toString();
            EntityHelper.print(jsonString);
//replace \
            jsonString = jsonString.replaceAll("^[__]\\w{14}+[_ = ]+", "[");
            // System.out.println(jsonString+"]");
            String jsonString2 = jsonString + "]";
//json object into the STRING
//            array = JSONArray.fromObject(jsonString2);

//Get JSONArray of JSONObject object , easy to read array of key-value pairs in
//            jsonObject = array.getJSONObject(0);

        } catch (Exception e) {
            e.printStackTrace();
        }
//        return jsonObject.getString("province");
        return null;
    }

    /**
     * <?xml version="1.0" encoding="gb2312" ?>
     * <root>
     * <ENV_CgiName>/cgi-bin/mobile/MobileQueryAttribution.cgi</ENV_CgiName>
     * <ENV_ClientAgent>Java/1.7.0_51</ENV_ClientAgent>
     * <ENV_ClientIp>59.40.152.213</ENV_ClientIp>
     * <ENV_QueryString>chgmobile=15888888888</ENV_QueryString>
     * <ENV_RequestMethod>GET</ENV_RequestMethod>
     * <ENV_referer></ENV_referer>
     * <chgmobile>15888888888</chgmobile>
     * <city>杭州 </city>
     * <province>浙江 </province>
     * <retcode>0</retcode>
     * <retmsg>OK</retmsg>
     * <supplier>移动 </supplier>
     * <tid></tid>
     * </root>
     */
    static class XmlProvince extends EntityHelper {
        private String chgmobile;
        private String city;
        private String province;
        private String retcode;
        private String retmsg;
        private String supplier;
        private String tid;

        public String getChgmobile() {
            return chgmobile;
        }

        public void setChgmobile(String chgmobile) {
            this.chgmobile = chgmobile;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getProvince() {
            return province;
        }

        public void setProvince(String province) {
            this.province = province;
        }

        public String getRetcode() {
            return retcode;
        }

        public void setRetcode(String retcode) {
            this.retcode = retcode;
        }

        public String getRetmsg() {
            return retmsg;
        }

        public void setRetmsg(String retmsg) {
            this.retmsg = retmsg;
        }

        public String getSupplier() {
            return supplier;
        }

        public void setSupplier(String supplier) {
            this.supplier = supplier;
        }

        public String getTid() {
            return tid;
        }

        public void setTid(String tid) {
            this.tid = tid;
        }
    }

    static class PhoneProvince {
        /**
         * 前缀
         */
        private String mts;
        /**
         * 省份
         */
        private String province;
        /**
         * 服务商
         */
        private String catName;
        private String telString;
        private String areaVid;
        private String ispVid;

        public String getMts() {
            return mts;
        }

        public void setMts(String mts) {
            this.mts = mts;
        }

        public String getProvince() {
            return province;
        }

        public void setProvince(String province) {
            this.province = province;
        }

        public String getCatName() {
            return catName;
        }

        public void setCatName(String catName) {
            this.catName = catName;
        }

        public String getTelString() {
            return telString;
        }

        public void setTelString(String telString) {
            this.telString = telString;
        }

        public String getAreaVid() {
            return areaVid;
        }

        public void setAreaVid(String areaVid) {
            this.areaVid = areaVid;
        }

        public String getIspVid() {
            return ispVid;
        }

        public void setIspVid(String ispVid) {
            this.ispVid = ispVid;
        }
    }

//    public static JSONObject calcMobilesCities(List<String> mobileNumbers) throws MalformedURLException{
//        JSONObject jsonNumberCity = new JSONObject();
//        for(String mobileNumber : mobileNumbers){
//            jsonNumberCity.put(mobileNumber, calcMobileCity(mobileNumber));            ;
//        }
//        return jsonNumberCity;
//    }
    public static void main(String[] args) throws Exception {
        TimerHelper.getTime();
        Random random = new Random();

        for (int i = 0; i < 100; i++) {
            long number = 13000000000L + random.nextInt(1000000000);
            String testMobileNumber = "" + number;
//            System.out.println(getMobileAddress(testMobileNumber));
            ThreadPool.invoke(null, PhoneNumberAddress.class.getMethod("getMobileAddress", String.class), testMobileNumber);
        }
        ThreadPool.shutDown();
        System.out.println(TimerHelper.getTime());
//        System.out.println(calcMobilesCities(mobileList).toString());
    }
}


/**
 * spark_src
 */
package com.xiongyingqi.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author XiongYingqi
 * @version 2013-6-25 下午9:41:57
 */
public class ComparatorHelper<T> {

    @SuppressWarnings("unchecked")
    public List sortByNumber(Collection<? extends T> collection, final boolean asc) {
        List list = new ArrayList();
        list.addAll(collection);
        Collections.sort(list, new Comparator<T>() {
            @Override
            public int compare(T o1, T o2) {
                int result = 0;
                if (o1 instanceof Number && o1 instanceof Number) {
                    Number number1 = (Number) o1;
                    Number number2 = (Number) o2;
                    if (number1.hashCode() < number2.hashCode()) {
                        result = -1;
                    } else if (number1.hashCode() > number2.hashCode()) {
                        result = -1;
                    }
                    if (!asc) {
                        result = -result;
                    }
                }
                return result;
            }
        });
        return list;
    }

    /**
     * 按照日期进行排序

     * 2013-6-25 下午9:46:45
     *
     * @param collection 要排序的集合
     * @param field      指定排序的时间字段
     * @param asc        是否按正序排序
     * @return List
     */
    @SuppressWarnings("unchecked")
    public List sortByDateTime(Collection<? extends T> collection, final Field field,
                               final boolean asc) {
        if (collection == null) {
            return null;
        }
        List list = new ArrayList();
        list.addAll(collection);
        Collections.sort(list, new Comparator<T>() {
            @Override
            public int compare(T o1, T o2) {
                Object object = invokeMethod(o1, field);
                Object object2 = invokeMethod(o2, field);
                if (object == null || object2 == null) {
                    return 0;
                }
                int value = 0;
                if (object instanceof Date) {
                    Date v1 = (Date) object;
                    Date v2 = (Date) object2;

                    if (v1.getTime() < v2.getTime()) {
                        value = -1;
                    } else if (v1.getTime() > v2.getTime()) {
                        value = 1;
                    }
                    if (!asc) {
                        value = -value;
                    }
                }
                return value;
            }

        });
        return list;
    }

    /**
     * 按照字符串进行排序

     * 2013-6-25 下午9:46:45
     *
     * @param collection 要排序的集合
     * @param field      指定排序的时间字段
     * @param asc        是否按正序排序
     * @return List
     */
    @SuppressWarnings("unchecked")
    public Collection sortByString(Collection<? extends T> collection, final Field field,
                                   final boolean asc) {
        if (collection == null) {
            return null;
        }
        List list = new ArrayList();
        list.addAll(collection);
        Collections.sort(list, new Comparator<T>() {
            @Override
            public int compare(T o1, T o2) {
                Object object = invokeMethod(o1, field);
                Object object2 = invokeMethod(o2, field);
                if (object == null || object2 == null) {
                    return 0;
                }
                int value = 0;
                if (object instanceof String) {
                    String v1 = (String) object;
                    String v2 = (String) object2;
                    if (v1.hashCode() < v2.hashCode()) {
                        value = -1;
                    } else {
                        value = 1;
                    }
                    if (!asc) {
                        value = -value;
                    }
                }
                return value;
            }

        });
        return list;
    }

    private Object invokeMethod(Object target, Field filed) {
        Object returnObject = null;
        Class clazz = target.getClass();
        String fieldName = filed.getName();
        String methodName = "get" + fieldName.substring(0, 1).toUpperCase()
                + fieldName.substring(1);
        try {
            Method method = clazz.getMethod(methodName);
            returnObject = method.invoke(target);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return returnObject;
    }

    static class Test {
        String i;

        /**
         * String
         *
         * @return the i
         */
        public String getI() {
            return i;
        }

        /**
         * String
         *
         * @param i the i to set
         */
        public void setI(String i) {
            this.i = i;
        }

        /**
         *

         * 2013-9-11 下午12:01:52
         *
         * @see Object#hashCode()
         */
        @Override
        public int hashCode() {
            return i.hashCode();
        }

    }

    public static void main(String[] args) {
        Random random = new Random();
        Collection<Test> collection = new ArrayList<Test>();
        for (int i = 0; i < 100; i++) {
            String string = random.nextInt(1000) + "";
            Test test = new Test();
            test.setI(string);
            collection.add(test);
        }
        ComparatorHelper comparatorHelper = new ComparatorHelper<Test>();
        try {
            collection = comparatorHelper.sortByString(collection,
                    Test.class.getDeclaredField("i"), true);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        for (Iterator iterator = collection.iterator(); iterator.hasNext(); ) {
            Test test = (Test) iterator.next();
            System.out.println(test.getI());
        }
    }
}


/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xiongyingqi.util.xml;

import com.xiongyingqi.util.Assert;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;

/**
 * Contains common behavior relating to {@link javax.xml.transform.Transformer Transformers}, and the
 * {@code javax.xml.transform} package in general.
 *
 * @author Rick Evans
 * @author Juergen Hoeller
 * @since 2.5.5
 */
public abstract class TransformerUtils {

    /**
     * The indent amount of characters if {@link #enableIndenting(javax.xml.transform.Transformer) indenting is enabled}.
     *
Defaults to "2".
     */
    public static final int DEFAULT_INDENT_AMOUNT = 2;

    /**
     * Enable indenting for the supplied {@link javax.xml.transform.Transformer}.
If the underlying XSLT engine is
     * Xalan, then the special output key {@code indent-amount} will be also be set to a value of {@link
     * #DEFAULT_INDENT_AMOUNT} characters.
     *
     * @param transformer the target transformer
     * @see javax.xml.transform.Transformer#setOutputProperty(String, String)
     * @see javax.xml.transform.OutputKeys#INDENT
     */
    public static void enableIndenting(Transformer transformer) {
        enableIndenting(transformer, DEFAULT_INDENT_AMOUNT);
    }

    /**
     * Enable indenting for the supplied {@link javax.xml.transform.Transformer}.
If the underlying XSLT engine is
     * Xalan, then the special output key {@code indent-amount} will be also be set to a value of {@link
     * #DEFAULT_INDENT_AMOUNT} characters.
     *
     * @param transformer  the target transformer
     * @param indentAmount the size of the indent (2 characters, 3 characters, etc.)
     * @see javax.xml.transform.Transformer#setOutputProperty(String, String)
     * @see javax.xml.transform.OutputKeys#INDENT
     */
    public static void enableIndenting(Transformer transformer, int indentAmount) {
        Assert.notNull(transformer, "Transformer must not be null");
        Assert.isTrue(indentAmount > -1, "The indent amount cannot be less than zero : got " + indentAmount);
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        try {
            // Xalan-specific, but this is the most common XSLT engine in any case
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indentAmount));
        } catch (IllegalArgumentException ignored) {
        }
    }

    /**
     * Disable indenting for the supplied {@link javax.xml.transform.Transformer}.
     *
     * @param transformer the target transformer
     * @see javax.xml.transform.OutputKeys#INDENT
     */
    public static void disableIndenting(Transformer transformer) {
        Assert.notNull(transformer, "Transformer must not be null");
        transformer.setOutputProperty(OutputKeys.INDENT, "no");
    }

}


/**
 * YIXUN_2.0
 */
package com.xiongyingqi.util;

import java.util.HashMap;
import java.util.Map;

/**
 * 计时器
 *
 * @author 瑛琪
 * @version 2013-7-26 下午6:05:52
 */
public class TimerHelper {
    static class TimerHolder extends EntityHelper {
        /**
         * 调用计时器的对象
         */
        Object object;
        /**
         * 调用计时器的线程
         */
        Thread thread;

        /**
         * Object
         *
         * @return the object
         */
        public Object getObject() {
            return this.object;
        }

        /**
         * Object
         *
         * @param object the object to set
         */
        public void setObject(Object object) {
            this.object = object;
        }

        /**
         * Thread
         *
         * @return the thread
         */
        public Thread getThread() {
            return this.thread;
        }

        /**
         * Thread
         *
         * @param thread the thread to set
         */
        public void setThread(Thread thread) {
            this.thread = thread;
        }

    }

    private static Map<TimerHolder, Long> holderObjects = new HashMap<TimerHolder, Long>();
    private static Map<Integer, Long> idTimeMap = new HashMap<Integer, Long>();

    /**
     * 获取与上个计时点以毫秒为单位的时差 ，如果是第一个计时点，则开始即时并返回0

     * 此方法是以线程和调用类来区别计时的，即：不同的类或不同的线程都会导致计时器的从0开始计时

     * 2013-7-26 下午6:21:40
     *
     * @return long
     */
    public static long getTime() {
        // StackTraceHelper.printStackTrace();
        StackTraceElement[] stackTraceElements = StackTraceHelper.getStackTrace();
        StackTraceElement stackTraceElement = stackTraceElements[2]; // 调用本类的对象类型堆栈
        String clazz = stackTraceElement.getClassName(); // 调用本类的对象类型
        Thread thread = Thread.currentThread();
        TimerHolder holder = new TimerHolder();
        holder.setObject(clazz);
        holder.setThread(thread);
        // String methodName = stackTraceElement.getMethodName();
        // int lineNumber = stackTraceElement.getLineNumber();
        // long hashCode = 17;
        // hashCode = 37 * hashCode + clazz.hashCode();
        // hashCode = 37 * hashCode + methodName.hashCode();
        // hashCode = 37 * hashCode + lineNumber;
        // System.out.println("stackTraceElement.getClassName() ========= " +
        // stackTraceElement.getClassName());
        Long startTime = holderObjects.get(holder);
        // System.out.println(stackTraceElement);
        if (startTime == null) {
            startTime = System.currentTimeMillis();
        }
        long time = System.currentTimeMillis() - startTime;
        startTime = System.currentTimeMillis();
        holderObjects.put(holder, startTime);
        return time;
    }

    public static long getTime(int id) {
        // StackTraceHelper.printStackTrace();
        StackTraceElement[] stackTraceElements = StackTraceHelper.getStackTrace();
        StackTraceElement stackTraceElement = stackTraceElements[2]; // 调用本类的对象类型堆栈
        String clazz = stackTraceElement.getClassName(); // 调用本类的对象类型
        // String methodName = stackTraceElement.getMethodName();
        // int lineNumber = stackTraceElement.getLineNumber();
        // long hashCode = 17;
        // hashCode = 37 * hashCode + clazz.hashCode();
        // hashCode = 37 * hashCode + methodName.hashCode();
        // hashCode = 37 * hashCode + lineNumber;
        // System.out.println("stackTraceElement.getClassName() ========= " +
        // stackTraceElement.getClassName());
        Long startTime = idTimeMap.get(id);
        // System.out.println(stackTraceElement);
        if (startTime == null) {
            startTime = System.currentTimeMillis();
        }
        long time = System.currentTimeMillis() - startTime;
        startTime = System.currentTimeMillis();
        idTimeMap.put(id, startTime);
        return time;
    }

    public static void main(String[] args) {
        System.out.println(getTime());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getTime());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getTime());
    }
}


/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xiongyingqi.util.xml;

import com.xiongyingqi.util.StringUtils;
import org.xml.sax.*;

import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Abstract base class for SAX {@code XMLReader} implementations that use StAX as a basis.
 *
 * @author Arjen Poutsma
 * @see #setContentHandler(org.xml.sax.ContentHandler)
 * @see #setDTDHandler(org.xml.sax.DTDHandler)
 * @see #setEntityResolver(org.xml.sax.EntityResolver)
 * @see #setErrorHandler(org.xml.sax.ErrorHandler)
 * @since 3.0
 */
abstract class AbstractStaxXMLReader extends AbstractXMLReader {

    private static final String NAMESPACES_FEATURE_NAME = "http://xml.org/sax/features/namespaces";

    private static final String NAMESPACE_PREFIXES_FEATURE_NAME = "http://xml.org/sax/features/namespace-prefixes";

    private static final String IS_STANDALONE_FEATURE_NAME = "http://xml.org/sax/features/is-standalone";

    private boolean namespacesFeature = true;

    private boolean namespacePrefixesFeature = false;

    private Boolean isStandalone;

    private final Map<String, String> namespaces = new LinkedHashMap<String, String>();

    @Override
    public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
        if (NAMESPACES_FEATURE_NAME.equals(name)) {
            return this.namespacesFeature;
        } else if (NAMESPACE_PREFIXES_FEATURE_NAME.equals(name)) {
            return this.namespacePrefixesFeature;
        } else if (IS_STANDALONE_FEATURE_NAME.equals(name)) {
            if (this.isStandalone != null) {
                return this.isStandalone;
            } else {
                throw new SAXNotSupportedException("startDocument() callback not completed yet");
            }
        } else {
            return super.getFeature(name);
        }
    }

    @Override
    public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
        if (NAMESPACES_FEATURE_NAME.equals(name)) {
            this.namespacesFeature = value;
        } else if (NAMESPACE_PREFIXES_FEATURE_NAME.equals(name)) {
            this.namespacePrefixesFeature = value;
        } else {
            super.setFeature(name, value);
        }
    }

    protected void setStandalone(boolean standalone) {
        this.isStandalone = standalone;
    }

    /**
     * Indicates whether the SAX feature {@code http://xml.org/sax/features/namespaces} is turned on.
     */
    protected boolean hasNamespacesFeature() {
        return this.namespacesFeature;
    }

    /**
     * Indicates whether the SAX feature {@code http://xml.org/sax/features/namespaces-prefixes} is turned on.
     */
    protected boolean hasNamespacePrefixesFeature() {
        return this.namespacePrefixesFeature;
    }

    /**
     * Convert a {@code QName} to a qualified name, as used by DOM and SAX.
     * The returned string has a format of {@code prefix:localName} if the
     * prefix is set, or just {@code localName} if not.
     *
     * @param qName the {@code QName}
     * @return the qualified name
     */
    protected String toQualifiedName(QName qName) {
        String prefix = qName.getPrefix();
        if (!StringUtils.hasLength(prefix)) {
            return qName.getLocalPart();
        } else {
            return prefix + ":" + qName.getLocalPart();
        }
    }

    /**
     * Parse the StAX XML reader passed at construction-time.
     *
NOTE:: The given {@code InputSource} is not read, but ignored.
     *
     * @param ignored is ignored
     * @throws org.xml.sax.SAXException a SAX exception, possibly wrapping a {@code XMLStreamException}
     */
    @Override
    public final void parse(InputSource ignored) throws SAXException {
        parse();
    }

    /**
     * Parse the StAX XML reader passed at construction-time.
     *
NOTE:: The given system identifier is not read, but ignored.
     *
     * @param ignored is ignored
     * @throws org.xml.sax.SAXException A SAX exception, possibly wrapping a {@code XMLStreamException}
     */
    @Override
    public final void parse(String ignored) throws SAXException {
        parse();
    }

    private void parse() throws SAXException {
        try {
            parseInternal();
        } catch (XMLStreamException ex) {
            Locator locator = null;
            if (ex.getLocation() != null) {
                locator = new StaxLocator(ex.getLocation());
            }
            SAXParseException saxException = new SAXParseException(ex.getMessage(), locator, ex);
            if (getErrorHandler() != null) {
                getErrorHandler().fatalError(saxException);
            } else {
                throw saxException;
            }
        }
    }

    /**
     * Template-method that parses the StAX reader passed at construction-time.
     */
    protected abstract void parseInternal() throws SAXException, XMLStreamException;

    /**
     * Starts the prefix mapping for the given prefix.
     *
     * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
     */
    protected void startPrefixMapping(String prefix, String namespace) throws SAXException {
        if (getContentHandler() != null) {
            if (prefix == null) {
                prefix = "";
            }
            if (!StringUtils.hasLength(namespace)) {
                return;
            }
            if (!namespace.equals(namespaces.get(prefix))) {
                getContentHandler().startPrefixMapping(prefix, namespace);
                namespaces.put(prefix, namespace);
            }
        }
    }

    /**
     * Ends the prefix mapping for the given prefix.
     *
     * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
     */
    protected void endPrefixMapping(String prefix) throws SAXException {
        if (getContentHandler() != null) {
            if (namespaces.containsKey(prefix)) {
                getContentHandler().endPrefixMapping(prefix);
                namespaces.remove(prefix);
            }
        }
    }

    /**
     * Implementation of the {@code Locator} interface that is based on a StAX {@code Location}.
     *
     * @see org.xml.sax.Locator
     * @see javax.xml.stream.Location
     */
    private static class StaxLocator implements Locator {

        private Location location;

        protected StaxLocator(Location location) {
            this.location = location;
        }

        @Override
        public String getPublicId() {
            return location.getPublicId();
        }

        @Override
        public String getSystemId() {
            return location.getSystemId();
        }

        @Override
        public int getLineNumber() {
            return location.getLineNumber();
        }

        @Override
        public int getColumnNumber() {
            return location.getColumnNumber();
        }
    }

}


/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xiongyingqi.util.concurrent;

import com.xiongyingqi.util.Assert;

import java.util.LinkedList;
import java.util.Queue;

/**
 * Registry for {@link ListenableFutureCallback} instances.
 *

 *
Inspired by {@code com.google.common.util.concurrent.ExecutionList}.
 *
 * @author Arjen Poutsma
 * @since 4.0
 */
public class ListenableFutureCallbackRegistry<T> {

    private final Queue<ListenableFutureCallback<? super T>> callbacks =
            new LinkedList<ListenableFutureCallback<? super T>>();

    private State state = State.NEW;

    private Object result = null;

    private final Object mutex = new Object();

    /**
     * Adds the given callback to this registry.
     *
     * @param callback the callback to add
     */
    @SuppressWarnings("unchecked")
    public void addCallback(ListenableFutureCallback<? super T> callback) {
        Assert.notNull(callback, "'callback' must not be null");

        synchronized (mutex) {
            switch (state) {
                case NEW:
                    callbacks.add(callback);
                    break;
                case SUCCESS:
                    callback.onSuccess((T) result);
                    break;
                case FAILURE:
                    callback.onFailure((Throwable) result);
                    break;
            }
        }
    }

    /**
     * Triggers a {@link ListenableFutureCallback#onSuccess(Object)} call on all added
     * callbacks with the given result
     *
     * @param result the result to trigger the callbacks with
     */
    public void success(T result) {
        synchronized (mutex) {
            state = State.SUCCESS;
            this.result = result;

            while (!callbacks.isEmpty()) {
                callbacks.poll().onSuccess(result);
            }
        }
    }

    /**
     * Triggers a {@link ListenableFutureCallback#onFailure(Throwable)} call on all added
     * callbacks with the given {@code Throwable}.
     *
     * @param t the exception to trigger the callbacks with
     */
    public void failure(Throwable t) {
        synchronized (mutex) {
            state = State.FAILURE;
            this.result = t;

            while (!callbacks.isEmpty()) {
                callbacks.poll().onFailure(t);
            }
        }
    }

    private enum State {NEW, SUCCESS, FAILURE}

}


/**
 * YIXUN_1.5_EE
 */
package com.xiongyingqi.util;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * MD5加密帮助类
 *
 * @author 瑛琪 xiongyingqi.com
 * @version 2013-11-4 下午6:06:54
 */
public class MD5Helper {
    /**
     * MD5 加密， 内容默认使用UTF-8编码，结果为小写

     * 2013-11-4 下午6:33:06
     *
     * @param message 要加密的内容
     * @return 加密后的信息
     */
    public static String encrypt(String message) {
        return encrypt(message, "UTF-8", false);
    }

    /**
     * MD5 加密， 结果默认为小写

     * 2013-11-4 下午6:33:06
     *
     * @param message 要加密的内容
     * @param encode  message的编码方式
     * @return 加密后的信息
     */
    public static String encrypt(String message, String encode) {
        return encrypt(message, encode, false);
    }

    /**
     * MD5 加密， 内容默认使用UTF-8编码

     * 2013-11-4 下午6:33:06
     *
     * @param message     要加密的内容
     * @param toUpperCase 是否转换成大写
     * @return 加密后的信息
     */
    public static String encrypt(String message, boolean toUpperCase) {
        return encrypt(message, "UTF-8", toUpperCase);
    }

    /**
     * MD5 加密

     * 2013-11-4 下午6:33:06
     *
     * @param message     要加密的内容
     * @param encode      message的编码方式
     * @param toUpperCase 是否转换成大写
     * @return 加密后的信息
     */
    public static String encrypt(String message, String encode, boolean toUpperCase) {
        MessageDigest messageDigest = null;

        try {
            messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.reset();
            messageDigest.update(message.getBytes(encode));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        byte[] byteArray = messageDigest.digest();

        StringBuffer md5StrBuff = new StringBuffer();

        for (int i = 0; i < byteArray.length; i++) {
            if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) {
                md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));
            } else {
                String hex = Integer.toHexString(0xFF & byteArray[i]);
                if (toUpperCase) {
                    hex = hex.toUpperCase();
                }
                md5StrBuff.append(hex);
            }
        }
        return md5StrBuff.toString();
    }

    public static void main(String[] args) {
        System.out.println(encrypt("", true));
        System.out.println(encrypt("value"));
    }

}


/*
 * FileUtil.java, helpers for disk I/O.
 * Copyright (C) 2001 - 2010 Achim Westermann.
 *
 * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * If you modify or optimize the code in a useful way please let me know.
 * Achim.Westermann@gmx.de
 */
package info.monitorenter.util;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.StringTokenizer;

/**
 * Utility class for file operations.
 *

 * For methods that are not static get the singleton instance via
 * {@link #getInstance()}.
 *

 *
 * @author Achim Westermann
 *
 * @version 1.1
 */
public final class FileUtil extends Object {

  /** The singleton instance of this class. */
  private static FileUtil instance;

  /**
   * Cuts all path information of the String representation of the given URL.
   *

   *
   *

   *
   *  "file//c:/work/programming/anyfile.jar" --> "anyfile.jar"
   *  "http://jamwg.de"                       --> "" // No file part.
   *  "ftp://files.com/directory2/"           --> "" // File part of URL denotes a directory.
   *
   *

   *
   * Assuming, that '/' is the current file separator character.
   *

   *
   * @param path
   *          the absolute file path you want the mere file name of.
   *
   * @return the {@link java.util.Map.Entry} consisting of path
   *         information and file name.
   */
  public static Map.Entry<String, String> cutDirectoryInformation(final java.net.URL path) {
    Map.Entry<String, String> ret = null;
    String pre;
    String suf;
    String parse;
    final StringBuffer tmp = new StringBuffer();
    parse = path.toExternalForm();
    if (parse.endsWith("/")) {
      pre = parse;
      suf = "";
    } else {
      final StringTokenizer tokenizer = new StringTokenizer(path.getFile(), "/");
      tmp.append(path.getProtocol());
      tmp.append(":");
      tmp.append(path.getHost());
      pre = "";
      while (tokenizer.hasMoreElements()) {
        tmp.append(pre);
        pre = tokenizer.nextToken();
        tmp.append("/");
      }
      suf = pre;
      pre = tmp.toString();
    }
    ret = new Entry<String, String>(pre, suf);
    return ret;
  }

  /**
   * Cuts the path information of the String that is interpreted as a filename
   * into the directory part and the file part. The current operating system's
   * path separator is used to cut all path information from the String.
   *

   *
   *

   *
   *  "c:/work/programming/anyfile.jar" --> Map.Entry("c:/work/programming/","anyfile.jar");
   *  "anyfile.jar"                     --> Map.Entry(new File(".").getAbsolutePath(),"anyfile.jar");
   *  "c:/directory1/directory2/"       --> Map.Entry("c:/directory1/directory2/","");
   *  "c:/directory1/directory2"        --> Map.Entry("c:/directory1/directory2/",""); // directory2 is a dir!
   *  "c:/directory1/file2"             --> Map.Entry("c:/directory1/","file2");       // file2 is a file!
   *  "c:/"                             --> Map.Entry("c:/","");
   *
   *

   *
   * Assuming, that '/' is the current file separator character.
   *

   *
   * If your string is retrieved from an <tt>URL</tt> instance, use
   * <tt>cutDirectoryInformation(URL path)</tt> instead, because URL's do not
   * depend on the operating systems file separator!
   *

   *
   * @param path
   *          the absolute file path you want the mere file name of.
   *
   * @return the {@link java.util.Map.Entry} consisting of path
   *         information and file name.
   */
  public static Map.Entry<String, String> cutDirectoryInformation(final String path) {
    final StringBuffer dir = new StringBuffer();
    String file = "";
    final String fileseparator = System.getProperty("file.separator");
    final StringTokenizer tokenizer = new StringTokenizer(path, fileseparator);
    final int size = tokenizer.countTokens();
    switch (size) {
      case 0:
        dir.append(new File(".").getAbsolutePath());
        break;

      case 1:
        final File test = new File(tokenizer.nextToken());
        if (new File(path).isDirectory()) {
          dir.append(test.getAbsolutePath());
        } else {
          dir.append(new File(".").getAbsolutePath());
          file = path;
        }
        break;

      default:
        String token;
        while (tokenizer.hasMoreElements()) {
          // reuse String file separator: bad style...
          token = tokenizer.nextToken();
          if (tokenizer.hasMoreTokens()) {
            dir.append(token);
            dir.append(fileseparator);
          } else {
            if (new File(path).isFile()) {
              file = token;
            } else {
              dir.append(token);
            }
          }
        }
    }

    return new Entry<String, String>(dir.toString(), file);
  }

  /**
   * Cuts a String into the part before the last dot and after the last dot. If
   * only one dot is contained on the first position, it will completely be used
   * as prefix part.
   *

   *
   *

   * Map.Entry entry = FileUtil.getPotentialExtension("A.Very.Strange.Name.txt");
   * String prefix = (String) entry.getKey(); // prefix is "A.Very.Strange.Name".
   * String suffix = (String) entry.getValue(); // suffix is "txt";
   *
   * entry = FileUtil.getPotentialExtension(".profile");
   * String prefix = (String) entry.getKey(); // prefix is ".profile".
   * String suffix = (String) entry.getValue(); // suffix is "";
   *
   * entry = FileUtil.getPotentialExtension("bash");
   * String prefix = (String) entry.getKey(); // prefix is "bash".
   * String suffix = (String) entry.getValue(); // suffix is "";
   *
   *

   *
   *

   *
   *
   * @param filename
   *          A String that is interpreted to be a file name: The last dot ('.')
   *          is interpreted to be the extension delimiter.
   *
   * @return A <tt> java.util.Map.Entry</tt> instance containing a String for
   *         the filename at the key side and a String for the extension at the
   *         value side.
   */
  public static Map.Entry<String, String> cutExtension(final String filename) {
    String prefix;
    String suffix = null;
    final StringTokenizer tokenizer = new StringTokenizer(filename, ".");
    int tokenCount = tokenizer.countTokens();
    if (tokenCount > 1) {
      final StringBuffer prefCollect = new StringBuffer();
      while (tokenCount > 1) {
        tokenCount--;
        prefCollect.append(tokenizer.nextToken());
        if (tokenCount > 1) {
          prefCollect.append(".");
        }
      }
      prefix = prefCollect.toString();
      suffix = tokenizer.nextToken();
    } else {
      prefix = filename;
      suffix = "";
    }
    return new Entry<String, String>(prefix, suffix);
  }

  /**
   * Finds a filename based on the given name. If a file with the given name
   * does not exist, <tt>name</tt> will be returned.
   *

   *
   * Else:
   *
   *

   *  "myFile.out"     --> "myFile_0.out"
   *  "myFile_0.out"   --> "myFile_1.out"
   *  "myFile_1.out"   --> "myFile_2.out"
   *  ....
   *

   *
   *

   *
   * The potential extension is preserved, but a number is appended to the
   * prefix name.
   *

   *
   * @param name
   *          A desired file name.
   *
   * @return A String that sticks to the naming convention of the given String
   *         but is unique in the directory scope of argument <tt>name</tt>.
   */
  public static String getDefaultFileName(final String name) {
    String result;
    File f = new File(name);
    if (!f.exists()) {
      result = f.getAbsolutePath();
    } else {
      final Map.Entry<String, String> cut = FileUtil.cutExtension(name);
      final String prefix = cut.getKey();
      final String suffix = cut.getValue();
      int num = 0;
      while (f.exists()) {
        f = new File(prefix + '_' + num + '.' + suffix);
        num++;
      }
      result = f.getAbsolutePath();
    }
    return result;
  }

  /**
   * Returns the singleton instance of this class.
   *

   *
   * @return the singleton instance of this class.
   */
  public static FileUtil getInstance() {
    if (FileUtil.instance == null) {
      FileUtil.instance = new FileUtil();
    }
    return FileUtil.instance;
  }

  /**
   * Tests wether the given file only contains ASCII characters if interpreted
   * by reading bytes (16 bit).
   *

   * This does not mean that the file is really an ASCII text file. It just
   * might be viewed with an editor showing only valid ASCII characters.
   *

   *
   * @param f
   *          the file to test.
   *
   * @return true if all bytes in the file are in the ASCII range.
   *
   * @throws java.io.IOException
   *           on a bad day.
   */
  public static boolean isAllASCII(final File f) throws IOException {
    return FileUtil.isAllASCII(new FileInputStream(f));
  }

  /**
   * Tests wether the given input stream only contains ASCII characters if
   * interpreted by reading bytes (16 bit).
   *

   * This does not mean that the underlying content is really an ASCII text
   * file. It just might be viewed with an editor showing only valid ASCII
   * characters.
   *

   *
   * @param in
   *          the stream to test.
   *
   * @return true if all bytes in the given input stream are in the ASCII range.
   *
   * @throws java.io.IOException
   *           on a bad day.
   */
  public static boolean isAllASCII(final InputStream in) throws IOException {
    boolean ret = true;
    int read = -1;
    do {
      read = in.read();
      if (read > 0x7F) {
        ret = false;
        break;
      }

    } while (read != -1);
    return ret;
  }

  /**
   * Tests, wether the content of the given file is identical at character
   * level, when it is opened with both different Charsets.
   *

   * This is most often the case, if the given file only contains ASCII codes
   * but may also occur, when both codepages cover common ranges and the
   * document only contains values m_out of those ranges (like the EUC-CN
   * charset contains all mappings from BIG5).
   *

   *
   * @param document
   *          the file to test.
   *
   * @param a
   *          the first character set to interpret the document in.
   *
   * @param b
   *          the 2nd character set to interpret the document in.
   *
   * @throws java.io.IOException
   *           if something goes wrong.
   *
   * @return true if both files have all equal contents if they are interpreted
   *         as character data in both given encodings (they may differ at
   *         binary level if both charsets are different).
   */
  public static boolean isEqual(final File document, final Charset a, final Charset b)
      throws IOException {
    boolean ret = true;
    FileInputStream aIn = null;
    FileInputStream bIn = null;
    InputStreamReader aReader = null;
    InputStreamReader bReader = null;
    try {
      aIn = new FileInputStream(document);
      bIn = new FileInputStream(document);
      aReader = new InputStreamReader(aIn, a);
      bReader = new InputStreamReader(bIn, b);
      int readA = -1;
      int readB = -1;
      do {
        readA = aReader.read();
        readB = bReader.read();
        if (readA != readB) {
          // also the case, if one is at the end earlier...
          ret = false;
          break;
        }
      } while ((readA != -1) && (readB != -1));
      return ret;
    } finally {
      if (aReader != null) {
        aReader.close();
      }
      if (bReader != null) {
        bReader.close();
      }
    }
  }

  /**
   * Invokes {@link #readRAM(java.io.File)}, but decorates the result with a
   * {@link java.io.ByteArrayInputStream}.
   *

   * This means: The complete content of the given File has been loaded before
   * using the returned InputStream. There are no IO-delays afterwards but
   * OutOfMemoryErrors may occur.
   *

   *
   * @param f
   *          the file to cache.
   *
   * @return an input stream backed by the file read into memory.
   *
   * @throws java.io.IOException
   *           if something goes wrong.
   */
  public static InputStream readCache(final File f) throws IOException {
    return new ByteArrayInputStream(FileUtil.readRAM(f));
  }

  /**
   * Reads the content of the given File into an array.
   *

   * This method currently does not check for maximum length and might cause a
   * java.lang.OutOfMemoryError. It is only intended for
   * performance-measurements of data-based algorithms that want to exclude
   * I/O-usage.
   *

   *
   * @param f
   *          the file to read.
   *
   * @throws java.io.IOException
   *           if something goes wrong.
   *
   * @return the contents of the given file.
   *
   */
  public static byte[] readRAM(final File f) throws IOException {
    final int total = (int) f.length();
    final byte[] ret = new byte[total];
    final InputStream in = new FileInputStream(f);
    try {
      int offset = 0;
      int read = 0;
      do {
        read = in.read(ret, offset, total - read);
        if (read > 0) {
          offset += read;
        }
      } while ((read != -1) && (offset != total));
      return ret;
    } finally {
      in.close();
    }
  }

  /**
   * Removes the duplicate line breaks in the given file.
   *

   *
   * Be careful with big files: In order to avoid having to write a tmpfile
   * (cannot read and directly write to the same file) a StringBuffer is used
   * for manipulation. Big files will cost all RAM and terminate VM hard.
   *

   *
   * @param f
   *          the file to remove duplicate line breaks in.
   */
  public static void removeDuplicateLineBreaks(final File f) {
    final String sep = StringUtil.getNewLine();
    if (!f.exists()) {
      System.err.println("FileUtil.removeDuplicateLineBreak(File f): " + f.getAbsolutePath()
          + " does not exist!");
    } else {
      if (f.isDirectory()) {
        System.err.println("FileUtil.removeDuplicateLineBreak(File f): " + f.getAbsolutePath()
            + " is a directory!");
      } else {
        // real file
        FileInputStream inStream = null;
        BufferedInputStream in = null;
        FileWriter out = null;
        try {
          inStream = new FileInputStream(f);
          in = new BufferedInputStream(inStream, 1024);
          StringBuffer result = new StringBuffer();
          int tmpread;
          while ((tmpread = in.read()) != -1) {
            result.append((char) tmpread);
          }
          String tmpstring;
          final StringTokenizer toke = new StringTokenizer(result.toString(), sep, true);
          result = new StringBuffer();
          int breaks = 0;
          while (toke.hasMoreTokens()) {
            tmpstring = toke.nextToken().trim();
            if (tmpstring.equals("") && (breaks > 0)) {
              breaks++;
              // if(breaks<=2)result.append(sep);
              continue;
            }
            if (tmpstring.equals("")) {
              tmpstring = sep;
              breaks++;
            } else {
              breaks = 0;
            }
            result.append(tmpstring);
          }
          // delete original file and write it new from tmpfile.
          f.delete();
          f.createNewFile();
          out = new FileWriter(f);
          out.write(result.toString());
        } catch (final FileNotFoundException e) {
          // does never happen.
        } catch (final IOException g) {
          g.printStackTrace(System.err);
        } finally {
          if (in != null) {
            try {
              in.close();
            } catch (final IOException e) {
              e.printStackTrace();
            }
          }
          if (out != null) {
            try {
              out.flush();
              out.close();
            } catch (final IOException e) {
              e.printStackTrace();
            }
          }
        }
      }
    }
  }

  /** Needed for localization. */
  private final ResourceBundle m_bundle;

  /**
   * Utility class constructor.
   *

   */
  private FileUtil() {
    this.m_bundle = ResourceBundle.getBundle("messages");
  }

  /**
   * @see Object#equals(Object)
   */
  @Override
  public boolean equals(final Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (this.getClass() != obj.getClass()) {
      return false;
    }
    final FileUtil other = (FileUtil) obj;
    if (this.m_bundle == null) {
      if (other.m_bundle != null) {
        return false;
      }
    } else if (!this.m_bundle.equals(other.m_bundle)) {
      return false;
    }
    return true;
  }

  /**
   * Returns the formatted file size to Bytes, KB, MB or GB depending on the
   * given value.
   *

   *
   * @param filesize
   *          in bytes
   *
   * @param locale
   *          the locale to translate the result to (e.g. in France they us
   *
   * @return the formatted filesize to Bytes, KB, MB or GB depending on the
   *         given value.
   */
  public String formatFilesize(final long filesize, final Locale locale) {

    String result;
    final long filesizeNormal = Math.abs(filesize);

    if (Math.abs(filesize) < 1024) {
      result = MessageFormat.format(this.m_bundle.getString("GUI_FILEUTIL_FILESIZE_BYTES_1"),
          new Object[] {new Long(filesizeNormal) });
    } else if (filesizeNormal < 1048576) {
      // 1048576 = 1024.0 * 1024.0
      result = MessageFormat.format(this.m_bundle.getString("GUI_FILEUTIL_FILESIZE_KBYTES_1"),
          new Object[] {new Double(filesizeNormal / 1024.0) });
    } else if (filesizeNormal < 1073741824) {
      // 1024.0^3 = 1073741824
      result = MessageFormat.format(this.m_bundle.getString("GUI_FILEUTIL_FILESIZE_MBYTES_1"),
          new Object[] {new Double(filesize / 1048576.0) });
    } else {
      result = MessageFormat.format(this.m_bundle.getString("GUI_FILEUTIL_FILESIZE_GBYTES_1"),
          new Object[] {new Double(filesizeNormal / 1073741824.0) });
    }
    return result;
  }

  /**
   * @see Object#hashCode()
   */
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((this.m_bundle == null) ? 0 : this.m_bundle.hashCode());
    return result;
  }
}