jef.tools.IOUtils.java Source code

Java tutorial

Introduction

Here is the source code for jef.tools.IOUtils.java

Source

/*
 * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
 *
 * 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 jef.tools;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.Multimap;

import jef.codegen.support.OverWrittenMode;
import jef.common.BigDataBuffer;
import jef.common.JefSerializable;
import jef.common.SimpleException;
import jef.common.log.LogUtil;
import jef.jre5support.ProcessUtil;
import jef.tools.TextFileCallback.Dealwith;
import jef.tools.collection.CollectionUtils;
import jef.tools.io.Charsets;
import jef.tools.io.UnicodeReader;

public class IOUtils {
    private static final int DEFAULT_BUFFER_SIZE = 4096;
    private static final File[] EMPTY = new File[0];
    private static final Logger log = LoggerFactory.getLogger(IOUtils.class);

    /**
     * ?
     * 
     * @param input
     *            ??
     * @deprecated {@link #closeQuietly}Apache commons-io??????
     */
    public static void close(Closeable input) {
        closeQuietly(input);
    }

    /**
     * ?
     * 
     * @param input
     *            ??
     */
    public static void closeQuietly(Closeable input) {
        if (input != null) {
            try {
                input.close();
            } catch (IOException e) {
                LogUtil.exception(e);
            }
        }
    }

    /**
     * /?
     * 
     * @param file
     *            File??
     * @return size of the directory
     */
    public static long getLength(File file) {
        if (file.exists()) {
            if (file.isFile()) {
                return file.length();
            } else {
                long total = 0;
                for (File child : file.listFiles()) {
                    total += getLength(child);
                }
                return total;
            }
        } else {
            return 0;
        }
    }

    /**
     * ?????(linux?)<br>
     * pid???PID????<br>
     * 
     * ??????
     * 
     * @param ??
     * @return 
     */
    public synchronized static File createTempDirectory(String name) {
        String tempPath = System.getProperty("java.io.tmpdir");
        File f = new File(tempPath, name + "." + ProcessUtil.getPid());
        try {
            if (f.isDirectory()) {
                deleteAllChildren(f);
            } else if (f.isFile()) {
                f.delete();
            }
            f.mkdirs();
            return f;
        } catch (Exception e) {
            String user = System.getProperty("user.name");
            throw new SimpleException(
                    "Current user[" + user + "] doesn't have any permission to access folder " + e.getMessage());
        }
    }

    /**
     * ??= :?properties
     * <p>
     * ?JDK{@link java.util.Properties}Properties??
     * <ol>
     * <li>??PropertiesMap&lt;Object,Object&gt;????</li>
     * <li>PropertiesHashtablegetProperty(null)</li>
     * <li>Properties?????</li>
     * <li>Properties?InputStream??</li>
     * </ol>
     * .properties??JDK{@link java.util.Properties}
     * <s>??java.util.Properties</s>
     * 
     * @param in
     *            ???
     * @return ?
     */
    public static Map<String, String> loadProperties(URL in) {
        return loadProperties(in, false);
    }

    public static Map<String, String> loadProperties(URL in, Boolean supportSection) {
        Map<String, String> result = new LinkedHashMap<String, String>();
        loadProperties(getReader(in, null), result, supportSection);
        return result;
    }

    /**
     * ??= :?properties
     * <p>
     * ?JDK{@link java.util.Properties}Properties??
     * <ol>
     * <li>??PropertiesMap&lt;Object,Object&gt;????</li>
     * <li>PropertiesHashtablegetProperty(null)</li>
     * <li>Properties?????</li>
     * <li>Properties?InputStream??</li>
     * </ol>
     * .properties??JDK{@link java.util.Properties}
     * <s>??java.util.Properties</s>
     * 
     * @param in
     *            ?????????
     * @return ?
     */
    public static Map<String, String> loadProperties(Reader in) {
        return loadProperties(in, false);
    }

    /**
     * ??= :?properties
     * <p>
     * ?JDK{@link java.util.Properties}Properties??
     * <ol>
     * <li>??PropertiesMap&lt;Object,Object&gt;????</li>
     * <li>PropertiesHashtablegetProperty(null)</li>
     * <li>Properties?????</li>
     * <li>Properties?InputStream??</li>
     * </ol>
     * .properties??JDK{@link java.util.Properties}
     * <s>??java.util.Properties</s>
     * 
     * @param in
     *            ?????????
     * @param supportSection
     *            ?window?properties??INIINI?[section]
     *            ??????????? ? {@code ??|???}?
     * 
     * 
     * @return ?
     */
    public static Map<String, String> loadProperties(Reader in, Boolean supportSection) {
        Map<String, String> result = new LinkedHashMap<String, String>();
        loadProperties(in, result, supportSection);
        return result;
    }

    /**
     * Map???properties
     * 
     * @param writer
     *            ??
     * @param map
     *            ???
     * @param closeWriter
     *            true???false????
     */
    public static void storeProperties(Writer writer, Map<String, String> map, boolean closeWriter) {
        storeProperties(writer, map, closeWriter, false, 0);

    }

    /**
     * Map???properties
     * 
     * @param writer
     *            ??
     * @param map
     *            ???
     * @param closeWriter
     *            true???false????
     * @param sectionSupport
     *            ?
     * @param ?1KV??
     *            0KEY?value?? -1 KV???
     * 
     */
    public static void storeProperties(Writer writer, Map<String, String> map, boolean closeWriter,
            Boolean sectionSupport, int saveConvert) {

        if (sectionSupport == null) {
            int limit = 3;
            sectionSupport = true;
            for (Entry<String, String> entry : map.entrySet()) {
                limit--;
                String key = entry.getKey();
                if (key.indexOf('|') == -1) {
                    sectionSupport = false;
                    break;
                }
                if (limit < 0) {
                    break;
                }
            }
        }
        try {

            if (sectionSupport) {
                Multimap<String, Map.Entry<String, String>> sections = CollectionUtils.group(map.entrySet(),
                        new Function<Map.Entry<String, String>, String>() {
                            public String apply(Entry<String, String> input) {
                                int sectionLen = input.getKey().indexOf('|');
                                return sectionLen == -1 ? "" : input.getKey().substring(0, sectionLen);
                            }
                        });
                boolean hasNoSecLine = false;
                for (Map.Entry<String, String> entry : sections.removeAll("")) {
                    writer.write(saveConvert(entry.getKey(), true, saveConvert));
                    writer.write('=');
                    writer.write(saveConvert(entry.getValue(), false, saveConvert));
                    writer.write(StringUtils.CRLF_STR);
                    hasNoSecLine = true;
                }
                if (!sections.isEmpty()) {
                    if (hasNoSecLine)
                        writer.write(StringUtils.CRLF_STR);
                    for (String section : sections.keySet()) {
                        writer.write("[" + section + "]\r\n");
                        for (Map.Entry<String, String> entry : sections.get(section)) {
                            writer.write(
                                    saveConvert(entry.getKey().substring(section.length() + 1), true, saveConvert));
                            writer.write('=');
                            writer.write(saveConvert(entry.getValue(), false, saveConvert));
                            writer.write(StringUtils.CRLF_STR);
                        }
                        writer.write(StringUtils.CRLF_STR);
                    }
                }
            } else {
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    writer.write(saveConvert(entry.getKey(), true, saveConvert));
                    writer.write('=');
                    writer.write(saveConvert(entry.getValue(), false, saveConvert));
                    writer.write(StringUtils.CRLF_STR);
                }
            }
            writer.flush();
        } catch (IOException e1) {
            LogUtil.exception(e1);
        } finally {
            if (closeWriter)
                closeQuietly(writer);
        }
    }

    /**
     * <br>
     * ? filetrue
     * 
     * @param f
     *            ?
     * @return true????false.
     */
    public static boolean deleteAllChildren(File f) {
        Assert.notNull(f);
        if (!f.exists())
            return true;
        if (f.isDirectory()) {
            for (File sub : listFolders(f)) {
                if (!deleteTree(sub, true))
                    return false;
            }
            for (File sub : listFiles(f)) {
                if (!sub.delete())
                    return false;
            }
        }
        return true;
    }

    /**
     * 
     * 
     * @param f
     *            ?
     * @param includeSub
     *            false,??false
     * @return ?true,?false ?true
     */
    public static boolean deleteTree(File f, boolean includeSub) {
        Assert.notNull(f);
        if (!f.exists())
            return true;
        if (includeSub && f.isDirectory()) {
            for (File sub : listFolders(f)) {
                if (!deleteTree(sub, true))
                    return false;
            }
            for (File sub : listFiles(f)) {
                if (!sub.delete())
                    return false;
            }
        }
        return f.delete();
    }

    /**
     * ()
     * 
     * @param root
     *            ??
     * @param folderFilter
     *            ???
     * @return 
     */
    public static File[] listFoldersRecursive(File root, final FileFilter folderFilter) {
        List<File> files = new ArrayList<File>();
        for (File folder : listFolders(root)) {
            if (folderFilter == null || folderFilter.accept(folder)) {
                files.add(folder);
                files.addAll(Arrays.asList(listFoldersRecursive(folder, folderFilter)));
            }
        }
        return files.toArray(new File[files.size()]);
    }

    /**
     * ()?????? <br>
     * ?
     * 
     * @param root
     *            ??
     * @param extnames
     *            ??????.?????
     * @return ??
     */
    public static File[] listFilesRecursive(File root, final String... extnames) {
        List<File> files = new ArrayList<File>();
        for (File folder : listFolders(root)) {
            files.addAll(Arrays.asList(listFilesRecursive(folder, extnames)));
        }
        files.addAll(Arrays.asList(listFiles(root, extnames)));
        return files.toArray(new File[files.size()]);
    }

    /**
     * ()??? <br>
     * ?
     * 
     * @param root
     *            ??
     * @param filter
     *            
     * @param folderFilter
     *            ?????
     * @return 
     */
    public static File[] listFilesRecursive(File root, final FileFilter fileFilter, final FileFilter folderFilter) {
        List<File> files = new ArrayList<File>();
        if (root.exists()) {
            for (File f : root.listFiles()) {
                if (f.isDirectory()) {
                    if (folderFilter != null && !folderFilter.accept(f)) {
                        continue;
                    }
                    files.addAll(Arrays.asList(listFilesRecursive(f, fileFilter, folderFilter)));
                } else {
                    if (fileFilter != null && !fileFilter.accept(f)) {
                        continue;
                    }
                    files.add(f);
                }
            }
        }
        return files.toArray(new File[files.size()]);
    }

    /**
     * ()?????
     * <p>
     * ??*??? windows?
     * 
     * @param root
     *            ??
     * @param pattern
     *            ??*???
     * @return ??
     */
    public static File[] listFilesRecursiveLike(File root, final String pattern) {
        return listFilesRecursive(root, new FileFilter() {
            public boolean accept(File f) {
                if (StringUtils.matches(f.getName(), pattern, true)) {
                    return true;
                }
                return false;
            }
        }, null);
    }

    /**
     * ???
     * 
     * @param file
     *            ??
     * @param extnames
     *            ?????'.'?
     * @return ?(????)<strong>?</strong>
     */
    public static File[] listFiles(File file, final String... extnames) {
        File[] r = file.listFiles(new FileFilter() {
            public boolean accept(File f) {
                boolean isAll = extnames.length == 0;
                if (f.isFile() && (isAll || ArrayUtils.contains(extnames, getExtName(f.getName())))) {
                    return true;
                }
                return false;
            }
        });
        return r == null ? EMPTY : r;
    }

    /**
     * ?????
     * 
     * @param root
     *            ??
     * @param pattern
     *            ??*,?,+??
     * @return ????(????)?
     */
    public static File[] listFilesLike(File root, final String pattern) {
        File[] r = root.listFiles(new FileFilter() {
            public boolean accept(File f) {
                if (f.isFile() && StringUtils.matches(f.getName(), pattern, true)) {
                    return true;
                }
                return false;
            }
        });
        return r == null ? EMPTY : r;
    }

    /**
     * 
     * 
     * @param root
     *            
     * @return 
     */
    public static File[] listFolders(File root) {
        File[] r = root.listFiles(new FileFilter() {
            public boolean accept(File f) {
                if (f.isDirectory()) {
                    return true;
                }
                return false;
            }
        });
        return r == null ? EMPTY : r;
    }

    /**
     * ?
     * 
     * @param root
     *            ??
     * @param pattern
     *            ??*,?,+??
     * @return ??(????)?
     */
    public static File[] listFoldersLike(File root, final String pattern) {
        File[] r = root.listFiles(new FileFilter() {
            public boolean accept(File f) {
                if (f.isDirectory() && StringUtils.matches(f.getName(), pattern, true)) {
                    return true;
                }
                return false;
            }
        });
        return r == null ? EMPTY : r;
    }

    /**
     * ????
     * 
     * @param root
     *            ??
     * @param extnames
     *            ??????.?
     * @return ???.xxx??
     */
    public static File[] listFilesAndFolders(File root, final String... extnames) {
        File[] r = root.listFiles(new FileFilter() {
            public boolean accept(File f) {
                boolean isAll = extnames.length == 0;
                if (f.isDirectory()) {
                    return true;
                }
                if (isAll || ArrayUtils.contains(extnames, getExtName(f.getName()))) {
                    return true;
                }
                return false;
            }
        });
        return r == null ? EMPTY : r;
    }

    /**
     * ??
     * 
     * @param root
     *            ??
     * @param pattern
     *            ?
     * @return ?? (????)
     */
    public static File[] listFilesAndFoldersLike(File root, final String pattern) {
        File[] r = root.listFiles(new FileFilter() {
            public boolean accept(File f) {
                return StringUtils.matches(f.getName(), pattern, true);
            }
        });
        return r == null ? EMPTY : r;
    }

    /**
     * File,????? <br>
     * ??<br>
     * ?? report.txt?report.txt "report(1).txt"
     * "report(1).txt""report(2).txt" 
     * 
     * @param file
     *            
     * @return ????
     */
    public static File escapeExistFile(File file) {
        if (!file.exists())
            return file;
        int pos = file.getName().lastIndexOf(".");
        String path = file.getParent();
        if (StringUtils.isEmpty(path)) {
            throw new IllegalArgumentException(file.getAbsolutePath() + " has no valid parent folder.");
        }
        String baseFilename = null;
        String extName = null;
        if (pos > -1) {
            baseFilename = file.getName().substring(0, pos);
            extName = file.getName().substring(pos + 1);
        } else {
            baseFilename = file.getName();
        }
        int n = 1;
        while (file.exists()) {
            file = new File(path + "/" + baseFilename + "(" + n + ")" + ((extName == null) ? "" : "." + extName));
            n++;
        }
        return file;
    }

    /**
     * ????????\/????
     * 
     * @param fileName
     * @return
     */
    public static String getExtName(String fileName) {
        int dashIndex1 = fileName.lastIndexOf('/');
        int dashIndex2 = fileName.lastIndexOf('\\');
        int dash = Math.max(dashIndex1, dashIndex2);// ???

        int pos = fileName.lastIndexOf(".");
        if (pos > -1 && pos > dash) {
            return fileName.substring(pos + 1).toLowerCase();
        } else {
            return "";
        }
    }

    /**
     * ?????????\/???? ????
     * 
     * @param fileName
     * @return
     */
    public static String removeExt(String fileName) {
        int dashIndex1 = fileName.lastIndexOf('/');
        int dashIndex2 = fileName.lastIndexOf('\\');
        int dash = Math.max(dashIndex1, dashIndex2);// ???
        int pos = fileName.lastIndexOf('.');
        if (pos > -1 && pos > dash) {
            return fileName.substring(0, pos);
        }
        return fileName;
    }

    /**
     * ?
     * 
     * @param path
     * @return ?true?false
     */
    public static boolean isFile(String path) {
        File f = new File(path);
        return f.exists() && f.isFile();
    }

    /**
     * ?
     * 
     * @param path
     * @return true?false
     */
    public static boolean isFolder(String path) {
        File f = new File(path);
        return f.exists() && f.isDirectory();
    }

    /**
     * JDK6???\\uHHHH??????
     * 
     * @param source
     * @param target
     * @param charset
     * @throws IOException
     */
    public static void fromHexUnicodeString(File source, File target, Charset charset) throws IOException {
        Reader r = getReader(source, (Charset) null);
        Writer w = getWriter(target, charset, false);
        StringUtils.fromHexUnicodeString(r, w);
        r.close();
        w.close();
    }

    /**
     * JDK6???\\uHHHH?????
     * 
     * @param source
     * @param target
     * @throws IOException
     */
    public static void toHexUnicodeString(File source, File target, String sourceCharset) throws IOException {
        Reader r = getReader(source, sourceCharset);
        Writer w = getWriter(target, null, false);
        StringUtils.toHexUnicodeString(r, w, "\\u");
        r.close();
        w.close();
    }

    /**
     * / 
     * 
     * @param path
     */
    public static void createFolder(String path) {
        createFolder(new File(path));
    }

    public static void createFolder(File file) {
        if (file.exists() && file.isFile()) {
            throw new RuntimeException("Duplicate name file exist. can't create directory " + file.getPath());
        } else if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * / ?? ?
     * 
     * @param file
     *            ?
     */
    public static void ensureParentFolder(File file) {
        File f = file.getParentFile();
        if (f != null && !f.exists()) {
            f.mkdirs();
        } else if (f != null && f.isFile()) {
            throw new RuntimeException(f.getAbsolutePath() + " is a exist file, can't create directory.");
        }
    }

    /**
     * reader??? ??
     * 
     * @param reader
     * @param appear
     * @return
     * @throws IOException
     */
    public static String readUntill(Reader reader, char... appear) throws IOException {
        StringBuilder sb = new StringBuilder();
        while (true) {
            int i = reader.read();
            if (i < 0)
                return sb.length() == 0 ? null : sb.toString();
            sb.append((char) i);
            for (int ind = 0; ind < appear.length; ind++) {
                if (appear[ind] == i) {
                    return sb.toString();
                }
            }
        }
    }

    /**
     * reader??? ???
     * 
     * @param reader
     * @param appear
     * @return
     * @throws IOException
     */
    public static String readTill(Reader reader, char... appear) throws IOException {
        StringBuilder sb = new StringBuilder();
        while (true) {
            int i = reader.read();
            if (i < 0)
                return sb.length() == 0 ? null : sb.toString();
            for (int ind = 0; ind < appear.length; ind++) {
                if (appear[ind] == i) {
                    return sb.toString();
                }
            }
            sb.append((char) i);
        }
    }

    /**
     * ?
     * 
     * @param in
     * @param charset
     * @param num
     * @return
     * @throws IOException
     */
    public static String[] readLine(URL in, Charset charset, int... num) throws IOException {
        BufferedReader is = getReader(in, charset);
        try {
            String line = null;
            if (num.length == 0)
                num = new int[] { -1 };
            boolean isAll = num[0] == -1;
            List<String> result = new ArrayList<String>(isAll ? 20 : num.length);
            int n = 0;
            while ((line = is.readLine()) != null) {
                n++;
                if (isAll || ArrayUtils.contains(num, n)) {
                    result.add(line);
                }
                if (!isAll && n >= num[num.length - 1])
                    break;
            }
            return result.toArray(new String[result.size()]);
        } finally {
            closeQuietly(is);
        }
    }

    /**
     * ?? 
     * 
     * @param inName
     *            ?
     * @param num
     *            ?,??(????1)
     * @return
     * @throws IOException
     */
    public static String[] readLine(File inName, Charset charset, int... num) throws IOException {
        return readLine(inName.toURI().toURL(), charset, num);
    }

    /**
     * ?
     * 
     * @author Administrator
     * 
     */
    @FunctionalInterface
    public interface LineFilter {
        /**
         * 
         * 
         * @param line
         *            
         * @param num
         *            ?
         * @return ?null??
         */
        String filter(String line, int num);
    }

    public static String[] readLine(URL inName, Charset charset, LineFilter filter) throws IOException {
        if (inName == null) {
            return new String[0];
        }
        BufferedReader is = getReader(inName, charset);
        try {
            String line = null;
            List<String> result = new ArrayList<String>();
            int n = 0;
            while ((line = is.readLine()) != null) {
                if (filter != null) {
                    String ll = filter.filter(line, n++);
                    if (ll != null) {
                        result.add(ll);
                    }
                } else {
                    result.add(line);
                }
            }

            return result.toArray(new String[result.size()]);
        } finally {
            is.close();
        }

    }

    /**
     * ??
     * 
     * @param inName
     * @param filter
     * @return
     * @throws IOException
     */
    public static String[] readLine(URL inName, String charset, LineFilter filter) throws IOException {
        return readLine(inName, Charsets.forName(charset), filter);
    }

    /**
     * ?String
     * 
     * @param inName
     * @param charset
     * @param filter
     * @return
     * @throws IOException
     */
    public static String readLinesAsString(File inName, String charset, LineFilter filter) throws IOException {
        BufferedReader is = getReader(inName, charset);
        String line = null;
        StringBuilder sb = new StringBuilder();
        try {
            int n = 0;
            while ((line = is.readLine()) != null) {
                if (filter == null) {
                    if (sb.length() > 0)
                        sb.append('\n');
                    sb.append(line);
                } else {
                    String ll = filter.filter(line, n++);
                    if (ll != null) {
                        if (sb.length() > 0)
                            sb.append('\n');
                        sb.append(line);
                    }
                }
            }
            return sb.toString();
        } finally {
            is.close();
        }
    }

    /**
     * <BR>
     * getAbsolutePath ?? getAbsolutePath()? C:/TEMP/../book.exe
     * ,? C:/book.exe getCanonicalPath()?? <BR>
     * ??? ??
     * 
     * @param file
     */
    public static String getPath(File file) {
        Assert.notNull(file);
        try {
            return file.getCanonicalPath();
        } catch (IOException e) {
            return file.getAbsolutePath();
        }
    }

    /*
     * Copies the contents of the given {@link InputStream} to the given {@link
     * OutputStream}.
     * 
     * @param pIn The input stream, which is being read. It is guaranteed, that
     * {@link InputStream#close()} is called on the stream.
     * InputStram????Stream??
     * ?close ???InputStream?????
     * (reset????Stream??)
     * ?????(?)
     * 
     * @param pOut ??null,??
     * 
     * @param pClose True guarantees, that {@link OutputStream#close()} is
     * called on the stream. False indicates, that only {@link
     * OutputStream#flush()} should be called finally.
     * 
     * @param pBuffer Temporary buffer, which is to be used for copying data.
     * 
     * @return Number of bytes, which have been copied.
     * 
     * @throws IOException An I/O error occurred.
     */
    private static long copy(InputStream in, OutputStream out, boolean inClose, boolean outClose, byte[] pBuffer)
            throws IOException {
        if (in == null)
            throw new NullPointerException();
        long total = 0;
        try {
            int res;
            while ((res = in.read(pBuffer)) != -1) {
                if (out != null) {
                    out.write(pBuffer, 0, res);
                }
                total += res;
            }
            if (out != null)
                out.flush();
        } finally {
            if (outClose)
                closeQuietly(out);
            if (inClose)
                closeQuietly(in);
        }
        return total;
    }

    /*
     * ??READERWriter?
     */
    private static long copy(Reader in, Writer out, boolean inClose, boolean outClose, char[] pBuffer)
            throws IOException {
        if (in == null)
            throw new NullPointerException();
        long total = 0;
        try {
            int res;
            while ((res = in.read(pBuffer)) != -1) {
                if (out != null) {
                    out.write(pBuffer, 0, res);
                }
                total += res;
            }
            if (out != null)
                out.flush();
        } finally {
            if (outClose && out != null)
                closeQuietly(out);
            if (inClose)
                closeQuietly(in);
        }
        return total;
    }

    /**
     * ??
     * 
     * @param in
     *            
     * @param out
     *            
     * @param inClose
     *            ?
     * @param outClose
     *            ??
     * @return
     * @throws IOException
     */
    public static long copy(InputStream in, OutputStream out, boolean inClose, boolean outClose)
            throws IOException {
        return copy(in, out, inClose, outClose, new byte[DEFAULT_BUFFER_SIZE]);
    }

    /**
     * ??
     * 
     * @param in
     *            
     * @param out
     *            
     * @param inClose
     *            ?
     * @param outClose
     *            ?
     * @return
     * @throws IOException
     */
    public static long copy(Reader in, Writer out, boolean inClose, boolean outClose) throws IOException {
        return copy(in, out, inClose, outClose, new char[DEFAULT_BUFFER_SIZE]);
    }

    /**
     * ??
     * 
     * @param in
     *            
     * @param out
     *            
     * @param pClose
     *            ??
     * @return ?
     * @throws IOException
     */
    public static long copy(Reader in, Writer out, boolean pClose) throws IOException {
        return copy(in, out, true, pClose, new char[DEFAULT_BUFFER_SIZE]);
    }

    /**
     * ??
     * 
     * @param in
     *            
     * @param out
     *            
     * @param closeOutStream
     *            ?? (?)
     * @return
     * @throws IOException
     */
    public static long copy(InputStream in, OutputStream out, boolean closeOutStream) throws IOException {
        return copy(in, out, true, closeOutStream, new byte[DEFAULT_BUFFER_SIZE]);
    }

    /**
     * Reader?charArray
     * 
     * @param reader
     *            
     * @return
     * @throws IOException
     */
    public static char[] asCharArray(Reader reader) throws IOException {
        CharArrayWriter cw = new CharArrayWriter(256);
        char[] buf = new char[1024];
        int n;
        try {
            while ((n = reader.read(buf)) > -1) {
                cw.write(buf, 0, n);
            }
        } finally {
            reader.close();
        }
        return cw.toCharArray();
    }

    /**
     * Reader?
     * 
     * @param reader
     * @return
     * @throws IOException
     */
    public static String asString(Reader reader) throws IOException {
        return asString(reader, true);
    }

    /**
     * Reader?
     * 
     * @param reader
     * @param close
     *            reader
     * @return
     * @throws IOException
     */
    public static String asString(Reader reader, boolean close) throws IOException {
        if (reader == null)
            return null;
        StringBuilder sb = new StringBuilder(128);
        char[] buf = new char[1024];
        int n;
        try {
            while ((n = reader.read(buf)) > -1) {
                sb.append(buf, 0, n);
            }
        } finally {
            if (close)
                reader.close();
        }
        return sb.toString();
    }

    public static String asString(File pStream, String charset) throws IOException {
        return asString(getReader(pStream, charset));
    }

    /**
     * ???
     * 
     * @param url
     *            ??
     * @param charset
     *            ??null
     * @return 
     * @throws IOException
     *             IO?
     **/
    public static String asString(URL url, String charset) throws IOException {
        if (url == null)
            return null;
        return asString(url.openStream(), charset, true);
    }

    /**
     * ?String .(?)
     * 
     * ????String <code>
     *    IOUtils.asString(ClassLoader.getSystemResourceAsStream(filename))
     * </code>
     */
    public static String asString(InputStream pStream) throws IOException {
        return asString(pStream, null, true);
    }

    /**
     * ?String
     * 
     * @param pStream
     *            The input stream to read.
     * @param pEncoding
     *            The character encoding, typically "UTF-8".
     * @param close
     *            close the in stream?
     */
    public static String asString(InputStream pStream, String pEncoding, boolean close) throws IOException {
        if (pStream == null)
            return null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
        copy(pStream, baos, close);
        if (pEncoding == null) {
            return baos.toString();
        } else {
            return baos.toString(pEncoding);
        }
    }

    /**
     * URL??byte[]
     * 
     * @param url
     *            ??
     * @return 
     * @throws IOException
     *             IO?
     */
    public static byte[] toByteArray(URL url) throws IOException {
        return toByteArray(url.openStream());
    }

    /**
     * ?(??)
     * 
     * @param file
     *            
     * @return 
     * @throws IOException
     *             IO?
     */
    public static byte[] toByteArray(File file) throws IOException {
        InputStream in = new FileInputStream(file);
        try {
            byte[] result = toByteArray(in, (int) file.length());
            return result;
        } finally {
            in.close();
        }
    }

    /**
     * ?(??)
     * 
     * @param file
     * @return
     * @throws IOException
     * @deprecated use {@linkp #toByteArray(File)}
     */
    public static byte[] asByteArray(File file) throws IOException {
        return toByteArray(file);
    }

    /**
     * ????????
     * 
     * @param in
     * @return
     * @throws IOException
     */
    public static byte[] toByteArray(InputStream in) throws IOException {
        try {
            byte[] msg = toByteArray(in, -1);
            return msg;
        } finally {
            in.close();
        }
    }

    /**
     * @deprecated use {@link #toByteArray(InputStream)}
     * @param in
     * @return
     * @throws IOException
     */
    public static byte[] asByteArray(InputStream in) throws IOException {
        return toByteArray(in);
    }

    /**
     * ???
     * 
     * @param bytes
     * @return
     */
    public static InputStream asInputStream(byte[] bytes) {
        return new ByteArrayInputStream(bytes);
    }

    /**
     * ????????
     * 
     * @param in
     * @param length
     * 
     * @return
     * @throws IOException
     * @deprecated
     */
    public static byte[] toByteArray_v2(InputStream in, int length) throws IOException {
        ByteArrayOutputStream out;
        if (length > 0) {
            out = new ByteArrayOutputStream(length);
        } else {
            out = new ByteArrayOutputStream(1024);
        }
        byte[] pBuffer = new byte[DEFAULT_BUFFER_SIZE];
        int left = (length > 0) ? length : Integer.MAX_VALUE;// 
        while (left > 0) {
            int n;
            if (left < DEFAULT_BUFFER_SIZE) {
                n = in.read(pBuffer, 0, left);
            } else {
                n = in.read(pBuffer);
            }
            if (n == -1)
                break;
            left -= n;
            out.write(pBuffer, 0, n);
        }
        out.close();
        byte[] message = out.toByteArray();
        return message;
    }

    /**
     * asByteArray????5%10% ??
     * 
     * @deprecated
     * @param in
     * @param length
     * @return
     * @throws IOException
     */
    public static byte[] toByteArray_old(InputStream in, int length) throws IOException {
        ByteArrayOutputStream out;
        if (length > 0) {
            out = new ByteArrayOutputStream(length);
        } else {
            out = new ByteArrayOutputStream(1024);
        }
        byte[] pBuffer = new byte[DEFAULT_BUFFER_SIZE];
        int count = 0;
        while (count < length || length < 0) {
            if (length < 0 || length - count > DEFAULT_BUFFER_SIZE) {
                int n = in.read(pBuffer);
                if (n == -1)
                    break;
                count += n;
                out.write(pBuffer, 0, n);
            } else {
                int n = in.read(pBuffer, 0, length - count);
                if (n == -1)
                    break;
                count += n;
                out.write(pBuffer, 0, n);
            }
        }
        out.close();
        byte[] message = out.toByteArray();
        return message;
    }

    /**
     * JDK InputStream.read(byte b[], int off, int len) 
     * ?????????
     * 
     * @return
     */
    public static int readBytes(InputStream in, byte[] data, int offset, int length) throws IOException {
        if (length < 0)
            throw new IOException(
                    "This method just for reading bytes of a expected length from stream.The param  length must >=0");
        if (length == 0)
            return 0;
        if (offset + length > data.length) {
            throw new IOException("the container byte[] does not enough for the expected length.");
        }
        int left = length;
        int off = offset;
        while (left > 0) {
            int n;
            n = in.read(data, off, left);
            if (n == -1)
                break;
            left -= n;
            off += n;
        }
        return off - offset;
    }

    /**
     * ?
     * 
     * @param packages
     * @return
     */
    public static byte[] mergeBytes(List<byte[]> packages) {
        int len = 0;
        for (byte[] aPackage : packages) {
            len += aPackage.length;
        }
        byte[] result = new byte[len];
        int pos = 0;
        for (byte[] aPackage : packages) {
            for (int j = 0; j < aPackage.length; j++) {
                result[pos++] = aPackage[j];
            }
        }
        return result;
    }

    /**
     * ?????? ??120M??60M125ms,v2156ms
     * 
     * @param in
     * @param length
     *            ??-1????-1?2G?2G??
     * @return
     * @throws IOException
     */
    public static byte[] toByteArray(InputStream in, int length) throws IOException {
        ByteArrayOutputStream out;
        if (length > 0) {
            out = new ByteArrayOutputStream(length);
        } else {
            out = new ByteArrayOutputStream(1024);
        }
        int buf = DEFAULT_BUFFER_SIZE;
        byte[] pBuffer = new byte[buf];
        int left = (length > 0) ? length : Integer.MAX_VALUE;// 
        while (left >= buf) {
            int n = in.read(pBuffer);
            if (n == -1) {
                left = 0;
                break;
            }
            left -= n;
            out.write(pBuffer, 0, n);
        }
        while (left > 0) {
            int n = in.read(pBuffer, 0, left);
            if (n == -1) {
                break;
            }
            left -= n;
            out.write(pBuffer, 0, n);
        }
        out.close();// ByteArrayOut??closeclose???
        byte[] message = out.toByteArray();
        return message;
    }

    /**
     * @deprecated use {@link #toByteArray(InputStream, int)}
     * @param in
     * @param length
     * @return
     * @throws IOException
     */
    public static byte[] asByteArray(InputStream in, int length) throws IOException {
        return toByteArray(in, length);
    }

    /**
     * ??BigDataBuffer
     * 
     * @param in
     *            ?
     * @param limit
     *            ?
     * @return
     * @throws IOException
     */
    public static BigDataBuffer asBigDataBuffer(InputStream in, long limit) throws IOException {
        BigDataBuffer out = new BigDataBuffer();
        byte[] pBuffer = new byte[DEFAULT_BUFFER_SIZE];
        long size = 0;
        try {
            while (size < limit || limit < 0) {
                int n = processDataRead(pBuffer, in, out, limit, size);
                if (n == 0) {
                    // 
                } else if (n == -1) {
                    break; // ?
                } else {
                    size += n;// 
                }
            }
            return out;
        } finally {
            in.close();
        }
    }

    /*
     * @param pBuffer byte[] Bufferr???
     * 
     * @param in ???
     * 
     * @param out ??Bufferbyte[]
     * 
     * @param limit ??
     * 
     * @param current ??
     * 
     * @return ?
     * 
     * @throws IOException
     */
    private static int processDataRead(byte[] pBuffer, InputStream in, OutputStream out, long limit, long current)
            throws IOException {
        int n = 0;
        if (limit < 0 || limit - current > DEFAULT_BUFFER_SIZE) {
            n = in.read(pBuffer);
        } else {
            int left = (int) (limit - current);
            n = in.read(pBuffer, 0, left);
        }
        if (n < 0) {
            return -1; // ?
        } else {// ?,?
            out.write(pBuffer, 0, n);
            return n;
        }
    }

    /**
     * ??
     * 
     * @param is
     * @return
     * @throws IOException
     */
    public static File saveAsTempFile(InputStream is) throws IOException {
        File f = File.createTempFile("~tmp", ".io");
        saveAsFile(f, is);
        return f;
    }

    /**
     * ????
     * 
     * @param reader
     * @return
     * @throws IOException
     */
    public static File saveAsTempFile(Reader reader) throws IOException {
        File f = File.createTempFile("~tmp", ".io");
        saveAsFile(f, null, reader);
        return f;
    }

    /**
     * ??
     * 
     * @param is
     * @param file
     * @throws IOException
     */
    public static void saveAsFile(File file, InputStream... iss) throws IOException {
        ensureParentFolder(file);
        BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
        try {
            for (InputStream is : iss) {
                copy(is, os, false);
            }
        } finally {
            if (os != null) {
                os.flush();
                os.close();
            }
        }
    }

    /**
     * reader?
     * 
     * @param reader
     * @param file
     * @throws IOException
     */
    public static void saveAsFile(File file, Charset charset, Reader... readers) throws IOException {
        BufferedWriter os = getWriter(file, charset == null ? null : charset, false);
        try {
            for (Reader reader : readers) {
                copy(reader, os, true, false, new char[2048]);
            }
        } finally {
            closeQuietly(os);
        }
    }

    /**
     * 
     * 
     * @param text
     * @param file
     * @param append
     * @throws IOException
     */
    public static void saveAsFile(File file, Charset charset, String... texts) throws IOException {
        BufferedWriter os = getWriter(file, charset == null ? null : charset, false);
        try {
            for (String text : texts) {
                os.write(text);
            }
        } finally {
            if (os != null) {
                os.flush();
                os.close();
            }
        }
    }

    public static void saveAsFile(File file, String... texts) throws IOException {
        saveAsFile(file, null, texts);
    }

    /**
     * ??
     * 
     * @param data
     * @param file
     * @throws IOException
     */
    public static void saveAsFile(File file, boolean append, byte[] data) throws IOException {
        ensureParentFolder(file);
        OutputStream out = new FileOutputStream(file, append);
        try {
            out.write(data);
        } finally {
            if (out != null) {
                out.flush();
                out.close();
            }
        }
    }

    /**
     * ??
     * 
     * @param file
     * @param data
     * @throws IOException
     */
    public static void saveAsFile(File file, byte[] data) throws IOException {
        saveAsFile(file, false, data);
    }

    /**
     * ?size??? Channel?
     * 
     * @param file
     * @param output
     * @param size
     * @return ?
     */
    public static int cut(File file, String output, int size) {
        try {
            if (!file.exists())
                return -1;
            // ?
            FileInputStream fin = new FileInputStream(file);
            FileChannel fc1 = fin.getChannel();
            int cnt = 0;// ??
            int nth = 1;
            long position = 0;// ?
            long len = file.length();
            while (position < len) {
                String name = output + "/" + file.getName() + "@" + nth;
                FileOutputStream fou = new FileOutputStream(name);
                FileChannel fc2 = fou.getChannel();
                cnt = (int) fc1.transferTo(position, size, fc2);
                fou.close();
                fc2.close();
                nth++;
                position += cnt;
            }
            fin.close();
            fc1.close();
            return nth - 1;
        } catch (IOException e) {
            LogUtil.exception(e);
            return -1;
        }
    }

    /**
     * ? ?nio?
     * 
     * @param list
     * @param path
     * @param srcFileName
     * @return
     */
    public static boolean combine(Collection<String> list, String path, String srcFileName) {
        File outputFile = new File(path + "/" + srcFileName);
        outputFile = escapeExistFile(outputFile);
        try {
            FileOutputStream fou = new FileOutputStream(outputFile);
            FileChannel fco = fou.getChannel();
            long position = 0;
            for (String i : list) {// ????
                File file = new File(i);// 
                if (!file.exists())
                    return false;
                FileInputStream fin = new FileInputStream(file);
                FileChannel fci = fin.getChannel();
                long len = file.length();
                fco.transferFrom(fci, position, len);// ??
                position += len;
                closeQuietly(fin);
                closeQuietly(fci);
            }
            closeQuietly(fou);
            closeQuietly(fco);
            return true;
        } catch (Exception ee) {
            LogUtil.exception(ee);
            return false;
        }
    }

    /**
     * ??<br>
     * ????<br>
     * ???
     * 
     * @param file
     *            ?
     * @param newFile
     *            
     * @return true??false?
     */
    public static boolean copyFile(File file, File newFile) {
        return copyFile(file, newFile, CopyStrategy.ALLWAYS_OVERWRITE);
    }

    /**
     * ??<br>
     * ??<br>
     * ????
     * <p>
     * ? / ?
     * <p>
     *  copyFile(new File("c:\temp"),new File("d:\temproot"));
     * //c:\tempd:\temproot ??c:\temp?d:'temproot
     * <p>
     * copyFile(new File("c:\temp\io.sys"),new File("d:\temproot"));
     * d:\temproot???d:\temproot
     * 
     * @param source
     *            ?
     * @param newFile
     *            
     * @param strategy
     *            ??????
     * @return true??false?
     * @see CopyStrategy
     */
    public static boolean copyFile(File source, File newFile, CopyStrategy strategy) {
        if (newFile == null)
            return false;
        if (!source.exists())
            return false;
        if (source.isDirectory()) {// ?
            if (!strategy.processFolder(source, newFile)) {
                return false;
            }
            if (newFile.exists()) {
                if (!newFile.isDirectory()) {
                    throw new IllegalArgumentException(
                            "the target " + newFile.getPath() + " has exist, and is not folder.");
                }
            }
            newFile.mkdirs();
            for (File f : source.listFiles()) {
                File target = strategy.getTargetFile(f, newFile);
                if (target != null) {
                    log.debug("Coping [{}] to [{}]", f, target);
                    copyFile(f, target, strategy);
                }
            }
            if (strategy.isMove()) {
                if (source.list().length == 0) {
                    source.delete();
                }
            }
            return true;
        } else {// ?
            if (newFile.isDirectory()) {
                if (strategy.allowFileIntoFolder()) {
                    return copyFile(source, new File(newFile, source.getName()), strategy);
                } else {
                    throw new IllegalArgumentException(
                            "the target " + newFile.getPath() + " has exist, and is a folder.");
                }
            }
            if (newFile.exists() && !strategy.canOverWritten(source, newFile)) {
                return false;
            }
            if (strategy.isMove()) {
                if (newFile.exists() && !newFile.delete()) {// 
                    return false;
                }
                return move(source, newFile);
            } else {
                FileChannel in = null;
                FileChannel out = null;
                boolean flag = false;
                try {
                    in = new FileInputStream(source).getChannel();
                    out = new FileOutputStream(newFile).getChannel();
                    in.transferTo(0, source.length(), out);
                    flag = true;
                } catch (IOException e) {
                    LogUtil.exception(e);
                } finally {
                    closeQuietly(out);
                    closeQuietly(in);
                }
                return flag;
            }
        }
    }

    /**
     * ???????<br>
     * 
     * @param tmpFile
     *            ??
     * @param path
     *            ??()?
     * @return ??
     * @throws IOException
     *             ??
     * @deprecated Please use {@link #copyIntoFolder(File, File)};
     */
    public static File copyToFolder(File file, String path) throws IOException {
        return copyIntoFolder(file, new File(path));
    }

    /**
     * ???????<br>
     * 
     * @param tmpFile
     *            ??
     * @param path
     *            ??()?
     * @return ??
     * @throws IOException
     *             ??
     */
    public static File copyIntoFolder(File source, File dir) {
        File target = new File(dir, source.getName());
        copyFile(source, target);
        return target;
    }

    /**
     * 
     * 
     * @param file
     *            
     * @param folder
     *            
     * @param autoEscape
     *            ?????
     * @return
     */
    public static boolean moveToFolder(File file, File folder, boolean autoEscape) {
        if (folder.exists() && folder.isFile()) {
            throw new IllegalArgumentException("Target is a file.(" + folder.getAbsolutePath() + ")");
        }
        if (!folder.exists())
            folder.mkdirs();
        File target = new File(folder, file.getName());
        if (target.exists()) {
            if (autoEscape) {
                target = escapeExistFile(target);
            } else {
                return false;
            }
        }
        if (file.equals(target)) {
            return true;
        }
        return move(file, target);
    }

    /**
     * ()???
     * 
     * @param file
     *            ??
     * @param newName
     *            ?????
     * @param overwite
     *            ??????
     * @return ??????file?null
     */
    public static File rename(File file, String newName, boolean overwite) {
        File target = new File(file.getParentFile(), newName);
        if (target.exists()) {
            if (overwite) {
                if (!target.delete())
                    return null;
            } else {
                return null;
            }
        }
        return file.renameTo(target) ? target : null;
    }

    /**
     * 
     * 
     * @param oldFile
     * @param newFile
     * @return
     */
    public static boolean move(File oldFile, File newFile) {
        Assert.notNull(oldFile, "source file is null!");
        Assert.notNull(newFile, "target file is null!");
        Assert.isTrue(oldFile.exists(), "source file doesn't exist.");
        Assert.isFalse(newFile.exists(), "target file already exist!");
        ensureParentFolder(newFile);
        return oldFile.renameTo(newFile);
    }

    /**
     * ? from ? to
     * 
     * @param f
     * @param from
     * @param to
     * @throws IOException
     */
    public static int converFileEncode(File f, final String from, final String to, String... extPatterns)
            throws IOException {
        TextFileCallback c = new TextFileCallback(from, to, Dealwith.REPLACE);
        int n = 0;
        if (f.isDirectory()) {
            for (File sub : f.listFiles()) {
                n += converFileEncode(sub, from, to, extPatterns);
            }
        } else {
            if (extPatterns.length == 0 || ArrayUtils.contains(extPatterns, getExtName(f.getName()))) {
                processFile(f, c);
                n++;
            }
        }
        return n;
    }

    /**
     * 
     * 
     * @author jiyi
     * 
     */
    public static abstract class FileFilterEx implements FileFilter {
        /**
         * ??? ???accept??true??
         * 
         * @return true?false?
         */
        protected boolean breakFolder(File root) {
            return false;
        };
    }

    /**
     * ???
     * 
     * @Company: Asiainfo-Linkage Technologies(China),Inc. Hangzhou
     * @author Administrator
     * @Date 2011-6-15
     */
    public abstract static class FolderCallback {
        protected abstract void process(File source);

        protected boolean breakProcess() {
            return false;
        }
    }

    /**
     * ?
     * 
     * @param root
     * @param call
     * @return
     * @throws IOException
     */
    public static int processFolders(File root, FolderCallback call) throws IOException {
        int n = 0;
        for (File f : listFolders(root)) {
            call.process(f);
            n += processFolders(f, call);
            if (call.breakProcess())
                break;
        }
        return n;
    }

    /**
     * ?(???)
     * 
     * @param f
     *            
     * @param sourceCharset
     *            ??
     * @param call
     *            ?
     * @param extPatterns
     *            ??
     * @throws IOException
     */
    public static int processFiles(File f, TextFileCallback call, String... extPatterns) throws IOException {
        int n = 0;
        if (f.isDirectory()) {
            for (File sub : f.listFiles()) {
                n += processFiles(sub, call, extPatterns);
            }
        } else {
            if (extPatterns.length == 0 || ArrayUtils.contains(extPatterns, getExtName(f.getName()))) {
                processFile(f, call);
                n++;
            }
        }
        return n;
    }

    /**
     * ?(???)
     * 
     * @param f
     * @param sourceCharset
     * @param call
     * @throws IOException
     */
    public static int processFiles(File f, BinaryFileCallback call, String... extPatterns) throws IOException {
        int n = 0;
        if (f.isDirectory()) {
            for (File sub : f.listFiles()) {
                n += processFiles(sub, call, extPatterns);
            }
        } else {
            if (extPatterns.length == 0 || ArrayUtils.contains(extPatterns, getExtName(f.getName()))) {
                processFile(f, call);
                n++;
            }
        }
        return n;
    }

    /**
     * ?
     * 
     * @param f
     * @param call
     * @return
     * @throws IOException
     */
    public static File processFile(File f, BinaryFileCallback call) throws IOException {
        InputStream reader = new FileInputStream(f);
        FileOutputStream w = null;
        File target = call.getTarget(f);
        if (target != null) {
            ensureParentFolder(target);
            w = new FileOutputStream(target);
        }
        call.beforeProcess(w, f, target);
        byte[] cache = new byte[call.getMaxBufferSize()];
        int next = call.nextBufferSize();
        int len = 0;
        while ((len = reader.read(cache, 0, next)) > -1) {
            byte[] txt = call.process(ArrayUtils.subArray(cache, len));
            if (w != null) {
                if (txt != null) {
                    w.write(txt);
                }
            }
            if (call.breakProcess())
                break;
        }
        reader.close();
        call.afterProcess(w);
        if (w != null)
            w.close();

        if (call.isSuccess()) {
            if (call.replaceSource(f)) {
                if (f.delete()) {
                    File n = new File(f.getPath());
                    target.renameTo(n);
                    return n;
                }
            } else if (call.deleteSource(f)) {
                f.delete();
            }
            return target;
        } else {
            if (target != null) {
                target.delete();
            }
            return null;
        }
    }

    /**
     * ?
     * 
     * @param f
     *            
     * @param sourceCharset
     *            ?
     * @param call
     *            ?
     * @throws IOException
     */
    public static File processFile(File f, TextFileCallback call) throws IOException {
        if (!call.accept(f)) {
            return null;
        }
        Charset sourceCharset = call.sourceCharset(f);
        BufferedReader reader = getReader(f, sourceCharset);
        call.sourceFile = f;
        Charset charSet = call.targetCharset();
        BufferedWriter w = null;
        File target = call.getTarget(f);
        if (target != null) {
            w = getWriter(target, charSet == null ? sourceCharset : charSet, false);
        }
        String line;
        call.beforeProcess(f, target, w);
        while ((line = reader.readLine()) != null) {
            String txt = null;
            try {
                txt = call.processLine(line);
            } catch (Throwable e) {
                LogUtil.exception(e);
                call.lastException = e;
            }
            if (w != null) {
                if (txt != null) {
                    w.write(txt);
                    int newLines = call.wrapLine();
                    for (int li = 0; li < newLines; li++) {
                        w.write("\r\n");
                    }
                }
            }
            if (call.breakProcess())
                break;
        }
        reader.close();
        call.afterProcess(f, target, w);
        if (w != null)
            w.close();

        if (call.isSuccess() && target != null) {
            Dealwith deal = call.dealwithSourceOnSuccess(f);
            if (deal == Dealwith.REPLACE) {
                if (f.delete()) {
                    File n = new File(f.getPath());
                    target.renameTo(n);
                    return n;
                }
            } else if (deal == Dealwith.DELETE) {
                f.delete();
            } else if (deal == Dealwith.BACKUP_REPLACE) {
                File backupfile = new File(f.getParentFile(), f.getName() + ".bak");
                backupfile = escapeExistFile(backupfile);
                if (f.renameTo(backupfile)) {
                    File n = new File(f.getPath());
                    target.renameTo(n);
                    return n;
                }
            }
            return target;
        } else {
            if (target != null) {
                target.delete();
            }
            return null;
        }
    }

    /**
     * 
     * 
     * @param file
     * @param charset
     * @param lines
     * @throws IOException
     */
    public static void appendToTextFile(File file, String charset, String... lines) throws IOException {
        Assert.isTrue(file.exists(), "The file which you want to append is not exist!" + file.getAbsolutePath());
        FileOutputStream out = new FileOutputStream(file, true);
        String line;
        for (int i = 0; i < lines.length - 1; i++) {
            line = lines[i];
            if (line != null) {
                out.write(charset == null ? line.getBytes() : line.getBytes(charset));
                out.write(StringUtils.CRLF);
            }
        }
        line = lines[lines.length - 1];
        if (line != null)
            out.write(charset == null ? line.getBytes() : line.getBytes(charset));
        out.close();
    }

    /**
     * URLreader
     * 
     * @param file
     * @param charSet
     * @return
     * @throws IOException
     */
    public static BufferedReader getReader(URL file, Charset charSet) {
        if (file == null)
            return null;
        try {
            UnicodeReader isr = new UnicodeReader(file.openStream(), charSet);
            return new BufferedReader(isr);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ?reader, ?BOMutf-8, unicodecharset?null.
     * ??BOMunicode??
     * 
     * @param source
     * @param charSet
     * @return
     * @throws IOException
     */
    public static BufferedReader getReader(File file, String charSet) throws IOException {
        return getReader(file, Charsets.forName(charSet));
    }

    /**
     * Charset
     * 
     * @param file
     * @param charSet
     * @return
     * @throws IOException
     */
    public static BufferedReader getReader(File file, Charset charSet) throws IOException {
        if (file == null)
            return null;
        InputStream is = new FileInputStream(file);
        return new BufferedReader(new UnicodeReader(is, charSet));
    }

    /**
     * Reader
     * 
     * @param is
     * @param charSet
     * @return
     * @throws IOException
     */
    public static BufferedReader getReader(InputStream is, String charSet) {
        if (is == null)
            return null;
        UnicodeReader isr = new UnicodeReader(is, Charsets.forName(charSet));
        return new BufferedReader(isr);
    }

    /**
     * class?
     * 
     * @param source
     *            class
     * @param fileName
     *            
     * @param charSet
     *            ?
     * @return BufferedReader ?null
     */
    public static BufferedReader getReader(Class<?> source, String fileName, String charSet) {
        InputStream is = source.getResourceAsStream(fileName);
        if (is == null) {
            is = source.getClassLoader().getResourceAsStream(toClassLoaderResourcePath(source, fileName));
        }
        if (is == null)
            return null;
        UnicodeReader isr = new UnicodeReader(is, Charsets.forName(charSet));
        return new BufferedReader(isr);
    }

    /**
     * ClassLoader resource?/c?
     * 
     * @param fileName
     * @return
     */
    public static String toClassLoaderResourcePath(Class<?> c, String fileName) {
        if (fileName.startsWith("/"))
            return fileName.substring(1);
        String path = c.getPackage().getName().replace('.', '/');
        return path.concat("/").concat(fileName);
    }

    /**
     * ?url?
     * 
     * @param fileName
     * @return
     */
    public static String toClassResourcePath(String fileName) {
        if (fileName.startsWith("/"))
            return fileName;
        return "/".concat(fileName);
    }

    /**
     * URL?BufferedInputStream??
     * 
     * @param url
     * @return BufferedInputStream
     */
    public static BufferedInputStream getInputStream(URL url) {
        try {
            URLConnection conn = url.openConnection();
            return new BufferedInputStream(conn.getInputStream());
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }

    }

    /**
     * URL
     * 
     * @param url
     *            ??URLfile://???
     */
    public static File urlToFile(URL url) {
        Assert.notNull(url);
        String type = url.getProtocol().toLowerCase();
        if (!"file".equals(type)) {
            throw new UnsupportedOperationException(
                    "Can't convert URL of protocol " + url.getProtocol() + " to a File.");
        }
        try {
            return new File(URLDecoder.decode(url.getPath(), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // Never Happens
            return new File(url.getPath());
        }
    }

    /**
     * URL?
     * 
     * @param url
     * @return
     */
    public static File[] urlToFile(URL[] url) {
        File[] result = new File[url.length];
        for (int i = 0; i < url.length; i++) {
            result[i] = urlToFile(url[i]);
        }
        return result;
    }

    /**
     * ?
     * 
     * @Title: getInputStream
     */
    public static BufferedInputStream getInputStream(File file) {
        try {
            return new BufferedInputStream(new FileInputStream(file));
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * ?
     * 
     * @param target
     * @param charSet
     * @param append
     * @return
     * @throws IOException
     */
    public static BufferedWriter getWriter(File target, Charset charSet, boolean append) {
        ensureParentFolder(target);
        try {
            OutputStream os = new FileOutputStream(target, append);
            if (charSet == null)
                charSet = Charset.defaultCharset();
            OutputStreamWriter osw = new OutputStreamWriter(os, charSet);
            return new BufferedWriter(osw);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * ?
     * 
     * @param target
     * @param charSet
     * @return
     */
    public static BufferedWriter getWriter(File target, String charSet) {
        return getWriter(target, Charsets.forName(charSet));
    }

    /**
     * ?
     * 
     * @param target
     * @param charSet
     * @return
     * @throws IOException
     */
    public static BufferedWriter getWriter(File target, Charset charSet) {
        return getWriter(target, charSet, false);
    }

    /**
     * OutputStream?BufferedWriter
     * 
     * @param out
     * @param charSet
     * @return
     */
    public static BufferedWriter getWriter(OutputStream out, Charset charSet) {
        if (charSet == null)
            charSet = Charset.defaultCharset();
        OutputStreamWriter osw;
        osw = new OutputStreamWriter(out, charSet);
        return new BufferedWriter(osw);
    }

    /**
     * ?
     * 
     * @param file
     * @return
     */
    public static BufferedOutputStream getOutputStream(File file) {
        return getOutputStream(file, OverWrittenMode.YES);
    }

    /**
     * ?
     * 
     * @param file
     * @param mode
     * @return
     */
    public static BufferedOutputStream getOutputStream(File file, OverWrittenMode mode) {
        if (file.exists()) {
            if (mode == OverWrittenMode.NO) {
                return null;
            } else if (mode == OverWrittenMode.ESCAPE_NAME || mode == OverWrittenMode.AUTO) {
                file = IOUtils.escapeExistFile(file);
            } else if (mode == OverWrittenMode.YES && file.isDirectory()) {
                throw new IllegalArgumentException("the folder " + file.getAbsolutePath() + " is already exists");
            }
        }
        ensureParentFolder(file);
        try {
            return new BufferedOutputStream(new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            LogUtil.exception(e);
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * ?
     * 
     * @param obj
     * @return
     */
    public static byte[] serialize(Serializable obj) {
        ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
        saveObject(obj, out);
        return out.toByteArray();
    }

    /**
     * ??
     * 
     * @param data
     * @return
     */
    public static Object deserialize(byte[] data) {
        return loadObject(new ByteArrayInputStream(data));
    }

    /**
     * ???
     * 
     * @param obj
     * @param output
     * @return
     */
    public static boolean saveObject(Serializable obj, OutputStream output) {
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(output);
            out.writeObject(obj);
            return true;
        } catch (IOException ex) {
            LogUtil.exception(ex);
            return false;
        } finally {
            closeQuietly(out);
        }
    }

    /**
     * ???
     * 
     * @param obj
     * @return
     */
    public static byte[] saveObject(Serializable obj) {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream(2048);
        try {
            ObjectOutputStream out = new ObjectOutputStream(bytes);
            out.writeObject(obj);
            closeQuietly(out);
            return bytes.toByteArray();
        } catch (IOException ex) {
            LogUtil.exception(ex);
            throw new RuntimeException(ex.getMessage());
        }
    }

    /**
     * ????
     * 
     * @param obj
     * @param file
     * @return
     * @throws IOException
     */
    public static boolean saveObject(Serializable obj, File file) {
        try {
            return saveObject(obj, new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            LogUtil.exception(e);
            return false;
        }
    }

    /**
     * ????
     * 
     * @param aaa
     * @param filePath
     * @return
     */
    public static boolean saveObject(Serializable aaa, String filePath) {
        return saveObject(aaa, new File(filePath));
    }

    /**
     * ???
     * 
     * @param objFile
     * @return
     */
    public static Object loadObject(InputStream inn) {
        try {
            ObjectInputStream in = (inn instanceof ObjectInputStream) ? (ObjectInputStream) inn
                    : new ObjectInputStream(inn);
            Object obj = in.readObject();
            if (obj instanceof JefSerializable) {
                ((JefSerializable) obj).init();
            }
            return obj;
        } catch (ClassNotFoundException ex) {
            LogUtil.exception(ex);
        } catch (IOException ex) {
            LogUtil.exception(ex);
        } finally {
            IOUtils.closeQuietly(inn);
        }
        return null;
    }

    public static Object loadObject(byte[] objFile) {
        return loadObject(new ByteArrayInputStream(objFile));
    }

    /**
     * ???
     * 
     * @param objFile
     * @return
     */
    public static Object loadObject(File file) {
        if (!file.exists())
            return null;
        try {
            return loadObject(new FileInputStream(file));
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * folder?file
     * 
     * @param file
     * @param folder
     * @return
     */
    public static String getRelativepath(String file, String folder) {
        String[] f1 = StringUtils.split(file.replace('/', '\\'), '\\');
        String[] f2 = StringUtils.split(folder.replace('/', '\\'), '\\');
        int breakCount = -1;
        for (int i = 0; i < f1.length; i++) {
            String str = f1[i];
            if (i < f2.length && str.equals(f2[i])) {
                breakCount = i + 1;
            } else {
                break;
            }
        }
        if (breakCount == -1)
            return file;
        StringBuilder sb = new StringBuilder();
        sb.append(StringUtils.repeat(".." + File.separator, f2.length - breakCount));
        for (int i = breakCount; i < f1.length; i++) {
            sb.append(f1[i]).append(File.separatorChar);
        }
        sb.setLength(sb.length() - 1);
        return sb.toString();
    }

    /**
     * folder?file
     * 
     * @param file
     * @param folder
     */
    public static String getRelativepath(File file, File folder) {
        String s1 = getPath(file);
        String s2 = getPath(folder);
        return getRelativepath(s1, s2);
    }

    /**
     * ByteBufferbyte[]
     * 
     * @param bf
     *            ByteBuffer
     * @return 
     */
    public static byte[] toByteArray(ByteBuffer bf) {
        if (bf.position() != 0) {
            throw new RuntimeException("This method only allow you to read a ByteBuffer from start");
        }
        if (bf.limit() == bf.capacity()) {
            return bf.array();
        }
        byte[] bb = new byte[bf.limit()];
        bf.get(bb);
        return bb;
    }

    /**
     * ??
     * 
     * @Title: findFile
     * @param root
     * @param filter
     *            ?
     * @return File 
     */
    public static File findFile(File root, FileFilterEx filter) {
        if (root == null || !root.exists())
            return null;
        boolean breakThisFolder = false;
        for (File f : root.listFiles()) {
            if (!breakThisFolder) {
                if (filter.accept(f)) {
                    return f;
                }
                breakThisFolder = filter.breakFolder(root);
            }
            if (f.isDirectory()) {
                File result = findFile(f, filter);
                if (result != null)
                    return result;
            }
        }
        return null;
    }

    /**
     * ?
     * 
     * @param root
     *            ??
     * @param filter
     *            
     * @return ?
     */
    public static Collection<File> findFiles(File root, FileFilterEx filter) {
        if (root == null || !root.exists())
            return null;
        List<File> result = new ArrayList<File>();
        boolean breakThisFolder = false;
        for (File f : root.listFiles()) {
            if (!breakThisFolder) {
                if (filter.accept(f)) {
                    result.add(f);
                }
                breakThisFolder = filter.breakFolder(root);
            }
            if (f.isDirectory()) {
                result.addAll(findFiles(f, filter));
            }
        }
        return result;
    }

    /**
     * ??
     * 
     * @param root
     *            ??
     * @param name
     *            ????
     * @param acceptFolder
     *            ??
     * @return File 
     */
    public static File findFile(File root, final String name, final boolean acceptFolder) {
        return findFile(root, new FileFilterEx() {
            public boolean accept(File pathname) {
                if (!acceptFolder && pathname.isDirectory())
                    return false;
                return pathname.getName().equals(name);
            }
        });
    }

    // ?GB18030?UTF?????
    // ??chardet?
    public static String get_charset(File file) {
        String charset = "GB18030";
        byte[] first3Bytes = new byte[3];
        try {
            boolean checked = false;
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
            bis.mark(0);
            int read = bis.read(first3Bytes, 0, 3);
            if (read == -1) {
                bis.close();
                return charset;
            }

            if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) { // ??unicode
                charset = "UTF-16LE";
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {// ??unicode
                charset = "UTF-16BE";
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB
                    && first3Bytes[2] == (byte) 0xBF) {// UTF-8
                charset = "UTF-8";
                checked = true;
            }
            if (checked) {
                bis.close();
                return charset;
            }
            bis.reset();
            // int pos = 0;
            while ((read = bis.read()) != -1) {// ??
                // pos++;
                if (read >= 0xF0) // 0xF0GBK
                    break;
                if (0x80 <= read && read <= 0xBF) // ?BFGBK
                    break;
                if (0xC0 <= read && read <= 0xDF) { // c0-df?
                    read = bis.read();
                    if (0x80 <= read && read <= 0xBF) // ? (0xC0 - 0xDF) -
                        // (0x80 -
                        // 0xBF),?GBK?
                        continue;
                    else
                        break;
                } else if (0xE0 <= read && read <= 0xEF) {// ??
                    read = bis.read();
                    if (0x80 <= read && read <= 0xBF) {
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) {
                            charset = "UTF-8";
                            break;
                        } else
                            break;
                    } else
                        break;
                }
            }
            bis.close();
        } catch (Exception e) {
            LogUtil.exception(e);
        }
        return charset;
    }

    /**
     * /? <br>
     * ?/???
     * 
     * @param origin
     *            ??
     * @param target
     *            ?
     * @return
     */
    public static boolean equals(File origin, File target) {
        boolean isFile = origin.isFile();
        if (isFile != target.isFile()) {
            return false;
        }
        if (isFile) {
            // 
            return FileComparator.LENGTH_SKIP.equals(origin, target);
        } else if (origin.isDirectory() && target.isDirectory()) {
            File[] ss = origin.listFiles();
            File[] ts = target.listFiles();
            if (ss.length != ts.length) {
                return false;
            }
            // 
            for (File file : ss) {
                File newTarget = new File(target, file.getName());
                boolean flag = equals(file, newTarget);
                if (!flag) {
                    return false;
                }
            }
            return true;
        } else {
            return false;
        }
    }

    /*
     * Read in a "logical line" from an InputStream/Reader, skip all comment and
     * blank lines and filter out those leading whitespace characters ( , and )
     * from the beginning of a "natural line". Method returns the char length of
     * the "logical line" and stores the line in "lineBuf".
     */
    static final class LineReader {
        private char[] inCharBuf;
        private char[] lineBuf = new char[1024];
        private int inLimit = 0;
        private int inOff = 0;
        private Reader reader;

        public LineReader(Reader reader) {
            this.reader = reader;
            inCharBuf = new char[8192];
        }

        int readLine() throws IOException {
            int len = 0;
            char c = 0;

            boolean skipWhiteSpace = true;
            boolean isCommentLine = false;
            boolean isNewLine = true;
            boolean appendedLineBegin = false;
            boolean precedingBackslash = false;
            boolean skipLF = false;

            while (true) {
                if (inOff >= inLimit) {
                    inLimit = reader.read(inCharBuf);
                    inOff = 0;
                    if (inLimit <= 0) {
                        if (len == 0 || isCommentLine) {
                            return -1;
                        }
                        return len;
                    }
                }
                c = inCharBuf[inOff++];

                if (skipLF) {
                    skipLF = false;
                    if (c == '\n') {
                        continue;
                    }
                }
                if (skipWhiteSpace) {
                    if (c == ' ' || c == '\t' || c == '\f') {
                        continue;
                    }
                    if (!appendedLineBegin && (c == '\r' || c == '\n')) {
                        continue;
                    }
                    skipWhiteSpace = false;
                    appendedLineBegin = false;
                }
                if (isNewLine) {
                    isNewLine = false;
                    if (c == '#' || c == '!') {
                        isCommentLine = true;
                        continue;
                    }
                }

                if (c != '\n' && c != '\r') {
                    lineBuf[len++] = c;
                    if (len == lineBuf.length) {
                        int newLength = lineBuf.length * 2;
                        if (newLength < 0) {
                            newLength = Integer.MAX_VALUE;
                        }
                        char[] buf = new char[newLength];
                        System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
                        lineBuf = buf;
                    }
                    // flip the preceding backslash flag
                    if (c == '\\') {
                        precedingBackslash = !precedingBackslash;
                    } else {
                        precedingBackslash = false;
                    }
                } else {
                    // reached EOL
                    if (isCommentLine || len == 0) {
                        isCommentLine = false;
                        isNewLine = true;
                        skipWhiteSpace = true;
                        len = 0;
                        continue;
                    }
                    if (inOff >= inLimit) {
                        inLimit = reader.read(inCharBuf);
                        inOff = 0;
                        if (inLimit <= 0) {
                            return len;
                        }
                    }
                    if (precedingBackslash) {
                        len -= 1;
                        // skip the leading whitespace characters in following
                        // line
                        skipWhiteSpace = true;
                        appendedLineBegin = true;
                        precedingBackslash = false;
                        if (c == '\r') {
                            skipLF = true;
                        }
                    } else {
                        return len;
                    }
                }
            }
        }
    }

    /*
     * Jiyi 2015-9-14windowsini? ? | ??key?
     * 
     * @param lr
     * 
     * @param map
     * 
     * @throws IOException
     */
    private static void load0(LineReader lr, Map<String, String> map, Boolean supportSection) throws IOException {
        char[] convtBuf = new char[1024];
        int limit;
        int keyLen;
        int valueStart;
        char c;
        boolean hasSep;
        boolean precedingBackslash;
        String currentSection = null;

        while ((limit = lr.readLine()) >= 0) {
            c = 0;
            keyLen = 0;
            valueStart = limit;
            hasSep = false;

            precedingBackslash = false;
            while (keyLen < limit) {
                c = lr.lineBuf[keyLen];
                // need check if escaped.
                if ((c == '=' || c == ':') && !precedingBackslash) {
                    valueStart = keyLen + 1;
                    hasSep = true;
                    break;
                } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
                    valueStart = keyLen + 1;
                    break;
                }
                if (c == '\\') {
                    precedingBackslash = !precedingBackslash;
                } else {
                    precedingBackslash = false;
                }
                keyLen++;
            }
            while (valueStart < limit) {
                c = lr.lineBuf[valueStart];
                if (c != ' ' && c != '\t' && c != '\f') {
                    if (!hasSep && (c == '=' || c == ':')) {
                        hasSep = true;
                    } else {
                        break;
                    }
                }
                valueStart++;
            }
            String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
            String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
            if (supportSection == null) {
                supportSection = isSection(key);
            }
            if (supportSection && value.length() == 0 && isSection(key)) {
                currentSection = key.length() > 2 ? key.substring(1, key.length() - 1) : null;
            } else {
                if (currentSection == null) {
                    map.put(key, value);
                } else {
                    map.put(currentSection + "|" + key, value);
                }
            }
        }
    }

    private static boolean isSection(String key) {
        if (key == null || key.length() < 2) {
            return false;
        }
        return key.charAt(0) == '[' && key.charAt(key.length() - 1) == ']';
    }

    /*
     * Converts encoded &#92;uxxxx to unicode chars and changes special saved
     * chars to their original forms
     */
    private static String loadConvert(char[] in, int off, int len, char[] convtBuf) {
        if (convtBuf.length < len) {
            int newLen = len * 2;
            if (newLen < 0) {
                newLen = Integer.MAX_VALUE;
            }
            convtBuf = new char[newLen];
        }
        char aChar;
        char[] out = convtBuf;
        int outLen = 0;
        int end = off + len;

        while (off < end) {
            aChar = in[off++];
            if (aChar == '\\') {
                aChar = in[off++];
                if (aChar == 'u') {
                    // Read the xxxx
                    int value = 0;
                    for (int i = 0; i < 4; i++) {
                        aChar = in[off++];
                        switch (aChar) {
                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9':
                            value = (value << 4) + aChar - '0';
                            break;
                        case 'a':
                        case 'b':
                        case 'c':
                        case 'd':
                        case 'e':
                        case 'f':
                            value = (value << 4) + 10 + aChar - 'a';
                            break;
                        case 'A':
                        case 'B':
                        case 'C':
                        case 'D':
                        case 'E':
                        case 'F':
                            value = (value << 4) + 10 + aChar - 'A';
                            break;
                        default:
                            throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
                        }
                    }
                    out[outLen++] = (char) value;
                } else {
                    if (aChar == 't')
                        aChar = '\t';
                    else if (aChar == 'r')
                        aChar = '\r';
                    else if (aChar == 'n')
                        aChar = '\n';
                    else if (aChar == 'f')
                        aChar = '\f';
                    out[outLen++] = aChar;
                }
            } else {
                out[outLen++] = (char) aChar;
            }
        }
        return new String(out, 0, outLen);
    }

    private static String saveConvert(String theString, boolean isKey, int option) {
        if (isKey && option < 0) {
            return theString;
        } else if (isKey == false && option < 1) {
            return theString;
        }
        int len = theString.length();
        int bufLen = len * 2;
        if (bufLen < 0) {
            bufLen = Integer.MAX_VALUE;
        }
        StringBuilder outBuffer = new StringBuilder(bufLen);

        for (int x = 0; x < len; x++) {
            char aChar = theString.charAt(x);
            // Handle common case first, selecting largest block that
            // avoids the specials below
            if ((aChar > 61) && (aChar < 127)) {
                if (aChar == '\\') {
                    outBuffer.append('\\');
                    outBuffer.append('\\');
                    continue;
                }
                outBuffer.append(aChar);
                continue;
            }
            switch (aChar) {
            case ' ':
                if (x == 0 || isKey)
                    outBuffer.append('\\');
                outBuffer.append(' ');
                break;
            case '\t':
                outBuffer.append('\\');
                outBuffer.append('t');
                break;
            case '\n':
                outBuffer.append('\\');
                outBuffer.append('n');
                break;
            case '\r':
                outBuffer.append('\\');
                outBuffer.append('r');
                break;
            case '\f':
                outBuffer.append('\\');
                outBuffer.append('f');
                break;
            case '=': // Fall through
            case ':': // Fall through
            case '#': // Fall through
            case '!':
                outBuffer.append('\\');
                outBuffer.append(aChar);
                break;
            default:
                outBuffer.append(aChar);
            }
        }
        return outBuffer.toString();
    }

    /*
     * ,properties?
     */
    static final void loadProperties(Reader in, Map<String, String> map, Boolean supportSecion) {
        if (in == null)
            return;
        try {
            load0(new LineReader(in), map, supportSecion);
        } catch (Exception e1) {
            LogUtil.exception(e1);
        } finally {
            closeQuietly(in);
        }
    }

    public static BufferedReader getReader(File file) throws IOException {
        return getReader(file, (Charset) null);
    }
}