/**
 * Copyright 2013-2014 Guoqiang Chen, Shanghai, China. All rights reserved.
 *
 * Email: subchen@gmail.com
 * URL: http://subchen.github.io/
 *
 * 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 jetbrick.dao.dialect;

import java.util.*;
import jetbrick.dao.dialect.supports.*;
import jetbrick.lang.StringUtils;

public abstract class SqlDialect {
    private static final Map<String, SqlDialect> dialect_map = new HashMap<String, SqlDialect>();
    private static final Set<String> iso_reservedWords = new HashSet<String>(512);
    protected final Set<String> reservedWords = new HashSet<String>(256);

    static {
        MySqlDialect mysql = new MySqlDialect();
        dialect_map.put(MySqlDialect.NAME.toLowerCase(), mysql);
        dialect_map.put(mysql.getName().toLowerCase(), mysql);

        H2Dialect h2 = new H2Dialect();
        dialect_map.put(H2Dialect.NAME.toLowerCase(), h2);
        dialect_map.put(h2.getName().toLowerCase(), h2);

        SqlServerDialect sqlserver = new SqlServerDialect();
        dialect_map.put(SqlServerDialect.NAME.toLowerCase(), sqlserver);
        dialect_map.put(sqlserver.getName().toLowerCase(), sqlserver);

        OracleDialect oracle = new OracleDialect();
        dialect_map.put(OracleDialect.NAME.toLowerCase(), oracle);
        dialect_map.put(oracle.getName().toLowerCase(), oracle);

        init_sql92_reservedWords();
        init_sql99_reservedWords();
    }

    public static SqlDialect getDialect(String name) {
        SqlDialect dialect = (name == null) ? null : dialect_map.get(name.toLowerCase());
        if (dialect == null) {
            throw new IllegalStateException("Unsupport database, dialect.name = " + name);
        }
        return dialect;
    }

    public SqlDialect() {
        initializeReservedWords();
    }

    public String getName() {
        String name = getClass().getSimpleName().toLowerCase();
        return StringUtils.remove(name, "dialect");
    }

    protected abstract void initializeReservedWords();

    /**
     * 返回对应的 Hibernate 的 Dialect
     */
    public abstract String getHibernateDialect();

    /**
     * 数据库是否支持 Sequence
     */
    public boolean supportsSequences() {
        return false;
    }

    /**
     * 是否支持在添加字段的时候，指定字段位置
     */
    public boolean supportsColumnPosition() {
        return false;
    }

    /**
     * 哪些数据库字段运行指定长度.
     * @param type 具体的数据库字段类型
     */
    public boolean supportsColumnLength(String type) {
        Set<String> columnSet = new HashSet<String>();
        columnSet.add("char");
        columnSet.add("nchar");
        columnSet.add("varchar");
        columnSet.add("nvarchar");
        columnSet.add("varchar2");
        columnSet.add("nvarchar2");
        columnSet.add("number");
        columnSet.add("numeric");
        columnSet.add("dec");
        columnSet.add("decimal");
        return columnSet.contains(type.toLowerCase());
    }

    /**
     * 哪些数据库字段运行指定精度.
     * @param type 具体的数据库字段类型
     */
    public boolean supportsColumnScale(String type) {
        Set<String> columnSet = new HashSet<String>();
        columnSet.add("number");
        columnSet.add("numeric");
        columnSet.add("dec");
        columnSet.add("decimal");
        return columnSet.contains(type.toLowerCase());
    }

    /**
     * 将数据库无关的 SubStyleType 转换成具体数据库的字段名称。特殊的字段类型由子类实现。
     * @param type  数据库无关的字段名称，参考 {@link SubStyleType}
     */
    public String asSqlType(String type, Integer length, Integer scale) {
        if (SubStyleType.UID.equals(type)) {
            return "bigint";
        } else if (SubStyleType.UUID.equals(type)) {
            return "char(16)";
        } else if (SubStyleType.ENUM.equals(type)) {
            return "integer";
        } else if (SubStyleType.INT.equals(type)) {
            return "integer";
        } else if (SubStyleType.LONG.equals(type)) {
            return "bigint";
        } else if (SubStyleType.BIGINT.equals(type)) {
            return "decimal(38, 0)";
        } else if (SubStyleType.DOUBLE.equals(type)) {
            return "double";
        } else if (SubStyleType.DECIMAL.equals(type)) {
            return new SqlType("decimal", length, scale).toString();
        } else if (SubStyleType.CHAR.equals(type)) {
            return new SqlType("char", length, null).toString();
        } else if (SubStyleType.VARCHAR.equals(type)) {
            return new SqlType("varchar", length, null).toString();
        } else if (SubStyleType.TEXT.equals(type)) {
            return new SqlType("longvarchar", Integer.MAX_VALUE, null).toString();
        } else if (SubStyleType.BOOLEAN.equals(type)) {
            return "tinyint(1)";
        } else if (SubStyleType.DATETIME_STRING.equals(type)) {
            return "char(19)";
        } else if (SubStyleType.DATE_STRING.equals(type)) {
            return "char(10)";
        } else if (SubStyleType.TIME_STRING.equals(type)) {
            return "char(8)";
        } else if (SubStyleType.DATETIME.equals(type)) {
            return "timestamp";
        } else if (SubStyleType.TIMESTAMP.equals(type)) {
            return "timestamp";
        } else if (SubStyleType.DATE.equals(type)) {
            return "date";
        } else if (SubStyleType.TIME.equals(type)) {
            return "time";
        } else if (SubStyleType.CLOB.equals(type)) {
            return "clob";
        } else if (SubStyleType.BLOB.equals(type)) {
            return "blob";
        } else if (SubStyleType.INPUTSTREAM.equals(type)) {
            return "longvarbinary";
        } else {
            return new SqlType(type, length, scale).toString();
        }
    }

    /**
     * 将字段名/表名与SQL保留字冲突的名称进行 Wrapper
     */
    public String getIdentifier(String name) {
        String upperCaseName = name.toUpperCase();
        if (iso_reservedWords.contains(upperCaseName)) {
            return getQuotedIdentifier(name);
        }
        if (reservedWords.contains(upperCaseName)) {
            return getQuotedIdentifier(name);
        }
        return name;
    }

    /**
     * 将字段名/表名进行 Wrapper
     */
    protected String getQuotedIdentifier(String name) {
        return name;
    }

    /**
     * SQL 语句中的字符串值，默认编码单引号(')为双单引号(')
     */
    protected String escapeSqlValue(String value) {
        return StringUtils.replace(value, "'", "''");
    }

    /**
     * 批量运行多个SQL语句之间的分隔符。
     */
    public String getSqlStatmentSeparator() {
        return "";
    }

    public abstract String sql_table_drop(String table);

    public abstract String sql_table_rename(String oldName, String newName);

    public abstract String sql_column_add(String table, String column_definition, String column_position);

    public abstract String sql_column_modify(String table, String column_definition, String column_position);

    public abstract String sql_column_drop(String table, String column);

    /**
     * 生成分页sql
     * @param sql 原始sql
     * @param offset 开始位置，从0开始 （= (Page-1)*PageSize）
     * @param limit 返回的限制大小（= 分页大小 PageSize）
     * @return 如果不支持，返回 null
     */
    public String sql_pagelist(String sql, int offset, int limit) {
        return null;
    }

    private static void init_sql92_reservedWords() {
        iso_reservedWords.add("ABSOLUTE");
        iso_reservedWords.add("ACTION");
        iso_reservedWords.add("ADD");
        iso_reservedWords.add("ALL");
        iso_reservedWords.add("ALLOCATE");
        iso_reservedWords.add("ALTER");
        iso_reservedWords.add("AND");
        iso_reservedWords.add("ANY");
        iso_reservedWords.add("ARE");
        iso_reservedWords.add("AS");
        iso_reservedWords.add("ASC");
        iso_reservedWords.add("ASSERTION");
        iso_reservedWords.add("AT");
        iso_reservedWords.add("AUTHORIZATION");
        iso_reservedWords.add("AVG");
        iso_reservedWords.add("BEGIN");
        iso_reservedWords.add("BETWEEN");
        iso_reservedWords.add("BIT");
        iso_reservedWords.add("BIT_LENGTH");
        iso_reservedWords.add("BOTH");
        iso_reservedWords.add("BY");
        iso_reservedWords.add("CASCADE");
        iso_reservedWords.add("CASCADED");
        iso_reservedWords.add("CASE");
        iso_reservedWords.add("CAST");
        iso_reservedWords.add("CATALOG");
        iso_reservedWords.add("CHAR");
        iso_reservedWords.add("CHARACTER");
        iso_reservedWords.add("CHARACTER_LENGTH");
        iso_reservedWords.add("CHAR_LENGTH");
        iso_reservedWords.add("CHECK");
        iso_reservedWords.add("CLOSE");
        iso_reservedWords.add("COALESCE");
        iso_reservedWords.add("COLLATE");
        iso_reservedWords.add("COLLATION");
        iso_reservedWords.add("COLUMN");
        iso_reservedWords.add("COMMIT");
        iso_reservedWords.add("CONNECT");
        iso_reservedWords.add("CONNECTION");
        iso_reservedWords.add("CONSTRAINT");
        iso_reservedWords.add("CONSTRAINTS");
        iso_reservedWords.add("CONTINUE");
        iso_reservedWords.add("CONVERT");
        iso_reservedWords.add("CORRESPONDING");
        iso_reservedWords.add("COUNT");
        iso_reservedWords.add("CREATE");
        iso_reservedWords.add("CROSS");
        iso_reservedWords.add("CURRENT");
        iso_reservedWords.add("CURRENT_DATE");
        iso_reservedWords.add("CURRENT_TIME");
        iso_reservedWords.add("CURRENT_TIMESTAMP");
        iso_reservedWords.add("CURRENT_USER");
        iso_reservedWords.add("CURSOR");
        iso_reservedWords.add("DATE");
        iso_reservedWords.add("DAY");
        iso_reservedWords.add("DEALLOCATE");
        iso_reservedWords.add("DEC");
        iso_reservedWords.add("DECIMAL");
        iso_reservedWords.add("DECLARE");
        iso_reservedWords.add("DEFAULT");
        iso_reservedWords.add("DEFERRABLE");
        iso_reservedWords.add("DEFERRED");
        iso_reservedWords.add("DELETE");
        iso_reservedWords.add("DESC");
        iso_reservedWords.add("DESCRIBE");
        iso_reservedWords.add("DESCRIPTOR");
        iso_reservedWords.add("DIAGNOSTICS");
        iso_reservedWords.add("DISCONNECT");
        iso_reservedWords.add("DISTINCT");
        iso_reservedWords.add("DOMAIN");
        iso_reservedWords.add("DOUBLE");
        iso_reservedWords.add("DROP");
        iso_reservedWords.add("ELSE");
        iso_reservedWords.add("END");
        iso_reservedWords.add("END-EXEC");
        iso_reservedWords.add("ESCAPE");
        iso_reservedWords.add("EXCEPT");
        iso_reservedWords.add("EXCEPTION");
        iso_reservedWords.add("EXEC");
        iso_reservedWords.add("EXECUTE");
        iso_reservedWords.add("EXISTS");
        iso_reservedWords.add("EXTERNAL");
        iso_reservedWords.add("EXTRACT");
        iso_reservedWords.add("FALSE");
        iso_reservedWords.add("FETCH");
        iso_reservedWords.add("FIRST");
        iso_reservedWords.add("FLOAT");
        iso_reservedWords.add("FOR");
        iso_reservedWords.add("FOREIGN");
        iso_reservedWords.add("FOUND");
        iso_reservedWords.add("FROM");
        iso_reservedWords.add("FULL");
        iso_reservedWords.add("GET");
        iso_reservedWords.add("GLOBAL");
        iso_reservedWords.add("GO");
        iso_reservedWords.add("GOTO");
        iso_reservedWords.add("GRANT");
        iso_reservedWords.add("GROUP");
        iso_reservedWords.add("HAVING");
        iso_reservedWords.add("HOUR");
        iso_reservedWords.add("IDENTITY");
        iso_reservedWords.add("IMMEDIATE");
        iso_reservedWords.add("IN");
        iso_reservedWords.add("INDICATOR");
        iso_reservedWords.add("INITIALLY");
        iso_reservedWords.add("INNER");
        iso_reservedWords.add("INPUT");
        iso_reservedWords.add("INSENSITIVE");
        iso_reservedWords.add("INSERT");
        iso_reservedWords.add("INT");
        iso_reservedWords.add("INTEGER");
        iso_reservedWords.add("INTERSECT");
        iso_reservedWords.add("INTERVAL");
        iso_reservedWords.add("INTO");
        iso_reservedWords.add("IS");
        iso_reservedWords.add("ISOLATION");
        iso_reservedWords.add("JOIN");
        iso_reservedWords.add("KEY");
        iso_reservedWords.add("LANGUAGE");
        iso_reservedWords.add("LAST");
        iso_reservedWords.add("LEADING");
        iso_reservedWords.add("LEFT");
        iso_reservedWords.add("LEVEL");
        iso_reservedWords.add("LIKE");
        iso_reservedWords.add("LOCAL");
        iso_reservedWords.add("LOWER");
        iso_reservedWords.add("MATCH");
        iso_reservedWords.add("MAX");
        iso_reservedWords.add("MIN");
        iso_reservedWords.add("MINUTE");
        iso_reservedWords.add("MODULE");
        iso_reservedWords.add("MONTH");
        iso_reservedWords.add("NAMES");
        iso_reservedWords.add("NATIONAL");
        iso_reservedWords.add("NATURAL");
        iso_reservedWords.add("NCHAR");
        iso_reservedWords.add("NEXT");
        iso_reservedWords.add("NO");
        iso_reservedWords.add("NOT");
        iso_reservedWords.add("NULL");
        iso_reservedWords.add("NULLIF");
        iso_reservedWords.add("NUMERIC");
        iso_reservedWords.add("OCTET_LENGTH");
        iso_reservedWords.add("OF");
        iso_reservedWords.add("ON");
        iso_reservedWords.add("ONLY");
        iso_reservedWords.add("OPEN");
        iso_reservedWords.add("OPTION");
        iso_reservedWords.add("OR");
        iso_reservedWords.add("ORDER");
        iso_reservedWords.add("OUTER");
        iso_reservedWords.add("OUTPUT");
        iso_reservedWords.add("OVERLAPS");
        iso_reservedWords.add("PAD");
        iso_reservedWords.add("PARTIAL");
        iso_reservedWords.add("POSITION");
        iso_reservedWords.add("PRECISION");
        iso_reservedWords.add("PREPARE");
        iso_reservedWords.add("PRESERVE");
        iso_reservedWords.add("PRIMARY");
        iso_reservedWords.add("PRIOR");
        iso_reservedWords.add("PRIVILEGES");
        iso_reservedWords.add("PROCEDURE");
        iso_reservedWords.add("PUBLIC");
        iso_reservedWords.add("READ");
        iso_reservedWords.add("REAL");
        iso_reservedWords.add("REFERENCES");
        iso_reservedWords.add("RELATIVE");
        iso_reservedWords.add("RESTRICT");
        iso_reservedWords.add("REVOKE");
        iso_reservedWords.add("RIGHT");
        iso_reservedWords.add("ROLLBACK");
        iso_reservedWords.add("ROWS");
        iso_reservedWords.add("SCHEMA");
        iso_reservedWords.add("SCROLL");
        iso_reservedWords.add("SECOND");
        iso_reservedWords.add("SECTION");
        iso_reservedWords.add("SELECT");
        iso_reservedWords.add("SESSION");
        iso_reservedWords.add("SESSION_USER");
        iso_reservedWords.add("SET");
        iso_reservedWords.add("SIZE");
        iso_reservedWords.add("SMALLINT");
        iso_reservedWords.add("SOME");
        iso_reservedWords.add("SPACE");
        iso_reservedWords.add("SQL");
        iso_reservedWords.add("SQLCODE");
        iso_reservedWords.add("SQLERROR");
        iso_reservedWords.add("SQLSTATE");
        iso_reservedWords.add("SUBSTRING");
        iso_reservedWords.add("SUM");
        iso_reservedWords.add("SYSTEM_USER");
        iso_reservedWords.add("TABLE");
        iso_reservedWords.add("TEMPORARY");
        iso_reservedWords.add("THEN");
        iso_reservedWords.add("TIME");
        iso_reservedWords.add("DATETIME");
        iso_reservedWords.add("TIMEZONE_HOUR");
        iso_reservedWords.add("TIMEZONE_MINUTE");
        iso_reservedWords.add("TO");
        iso_reservedWords.add("TRAILING");
        iso_reservedWords.add("TRANSACTION");
        iso_reservedWords.add("TRANSLATE");
        iso_reservedWords.add("TRANSLATION");
        iso_reservedWords.add("TRIM");
        iso_reservedWords.add("TRUE");
        iso_reservedWords.add("UNION");
        iso_reservedWords.add("UNIQUE");
        iso_reservedWords.add("UNKNOWN");
        iso_reservedWords.add("UPDATE");
        iso_reservedWords.add("UPPER");
        iso_reservedWords.add("USAGE");
        iso_reservedWords.add("USER");
        iso_reservedWords.add("USING");
        iso_reservedWords.add("VALUE");
        iso_reservedWords.add("VALUES");
        iso_reservedWords.add("VARCHAR");
        iso_reservedWords.add("VARYING");
        iso_reservedWords.add("VIEW");
        iso_reservedWords.add("WHEN");
        iso_reservedWords.add("WHENEVER");
        iso_reservedWords.add("WHERE");
        iso_reservedWords.add("WITH");
        iso_reservedWords.add("WORK");
        iso_reservedWords.add("WRITE");
        iso_reservedWords.add("YEAR");
        iso_reservedWords.add("ZONE");
    }

    private static void init_sql99_reservedWords() {
        iso_reservedWords.add("ADMIN");
        iso_reservedWords.add("AFTER");
        iso_reservedWords.add("AGGREGATE");
        iso_reservedWords.add("ALIAS");
        iso_reservedWords.add("ARRAY");
        iso_reservedWords.add("BEFORE");
        iso_reservedWords.add("BINARY");
        iso_reservedWords.add("BLOB");
        iso_reservedWords.add("BOOLEAN");
        iso_reservedWords.add("BREADTH");
        iso_reservedWords.add("CALL");
        iso_reservedWords.add("CLASS");
        iso_reservedWords.add("CLOB");
        iso_reservedWords.add("COMPLETION");
        iso_reservedWords.add("CONSTRUCTOR");
        iso_reservedWords.add("CUBE");
        iso_reservedWords.add("CURRENT_PATH");
        iso_reservedWords.add("CURRENT_ROLE");
        iso_reservedWords.add("CYCLE");
        iso_reservedWords.add("DATA");
        iso_reservedWords.add("DEPTH");
        iso_reservedWords.add("DEREF");
        iso_reservedWords.add("DESTROY");
        iso_reservedWords.add("DESTRUCTOR");
        iso_reservedWords.add("DETERMINISTIC");
        iso_reservedWords.add("DICTIONARY");
        iso_reservedWords.add("DYNAMIC");
        iso_reservedWords.add("EACH");
        iso_reservedWords.add("EQUALS");
        iso_reservedWords.add("EVERY");
        iso_reservedWords.add("FREE");
        iso_reservedWords.add("FUNCTION");
        iso_reservedWords.add("GENERAL");
        iso_reservedWords.add("GROUPING");
        iso_reservedWords.add("HOST");
        iso_reservedWords.add("IGNORE");
        iso_reservedWords.add("INITIALIZE");
        iso_reservedWords.add("INOUT");
        iso_reservedWords.add("ITERATE");
        iso_reservedWords.add("LARGE");
        iso_reservedWords.add("LATERAL");
        iso_reservedWords.add("LESS");
        iso_reservedWords.add("LIMIT");
        iso_reservedWords.add("LOCALTIME");
        iso_reservedWords.add("LOCALTIMESTAMP");
        iso_reservedWords.add("LOCATOR");
        iso_reservedWords.add("MAP");
        iso_reservedWords.add("MODIFIES");
        iso_reservedWords.add("MODIFY");
        iso_reservedWords.add("NCLOB");
        iso_reservedWords.add("NEW");
        iso_reservedWords.add("NONE");
        iso_reservedWords.add("OBJECT");
        iso_reservedWords.add("OFF");
        iso_reservedWords.add("OLD");
        iso_reservedWords.add("OPERATION");
        iso_reservedWords.add("ORDINALITY");
        iso_reservedWords.add("OUT");
        iso_reservedWords.add("PARAMETER");
        iso_reservedWords.add("PARAMETERS");
        iso_reservedWords.add("PATH");
        iso_reservedWords.add("POSTFIX");
        iso_reservedWords.add("PREFIX");
        iso_reservedWords.add("PREORDER");
        iso_reservedWords.add("READS");
        iso_reservedWords.add("RECURSIVE");
        iso_reservedWords.add("REF");
        iso_reservedWords.add("REFERENCING");
        iso_reservedWords.add("RESULT");
        iso_reservedWords.add("RETURN");
        iso_reservedWords.add("RETURNS");
        iso_reservedWords.add("ROLE");
        iso_reservedWords.add("ROLLUP");
        iso_reservedWords.add("ROUTINE");
        iso_reservedWords.add("ROW");
        iso_reservedWords.add("SAVEPOINT");
        iso_reservedWords.add("SCOPE");
        iso_reservedWords.add("SEARCH");
        iso_reservedWords.add("SEQUENCE");
        iso_reservedWords.add("SETS");
        iso_reservedWords.add("SPECIFIC");
        iso_reservedWords.add("SPECIFICTYPE");
        iso_reservedWords.add("SQLEXCEPTION");
        iso_reservedWords.add("SQLWARNING");
        iso_reservedWords.add("START");
        iso_reservedWords.add("STATE");
        iso_reservedWords.add("STATEMENT");
        iso_reservedWords.add("STATIC");
        iso_reservedWords.add("STRUCTURE");
        iso_reservedWords.add("TERMINATE");
        iso_reservedWords.add("THAN");
        iso_reservedWords.add("TREAT");
        iso_reservedWords.add("TRIGGER");
        iso_reservedWords.add("UNDER");
        iso_reservedWords.add("UNNEST");
        iso_reservedWords.add("VARIABLE");
        iso_reservedWords.add("WITHOUT");
    }

}


/**
 * Copyright 2013-2014 Guoqiang Chen, Shanghai, China. All rights reserved.
 *
 * Email: subchen@gmail.com
 * URL: http://subchen.github.io/
 *
 * 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 jetbrick.reflect;

import java.util.List;
import jetbrick.beans.ClassUtils;

public final class ExecutableUtils {

    /**
     * 查找完全匹配的方法或者构造函数.
     *
     * @param executables - 带查找的 list
     * @param name - 方法名称(如果查找构造函数，那么name可以是null)
     * @param parameterTypes － 方法或者构造函数参数(完全匹配)
     * @return 找到的方法或者构造函数，找不到返回 null.
     */
    public static <T extends Executable> T getExecutable(List<T> executables, String name, Class<?>... parameterTypes) {
        for (T info : executables) {
            if (name == null || info.getName().equals(name)) {
                Class<?>[] types = info.getParameterTypes();
                if (parameterTypes.length == types.length) {
                    boolean match = true;
                    for (int i = 0; i < parameterTypes.length; i++) {
                        if (types[i] != parameterTypes[i]) {
                            match = false;
                            break;
                        }
                    }
                    if (match) {
                        return info;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 查找最佳匹配的方法或者构造函数。
     *
     * @param executables - 带查找的 list
     * @param name - 方法名称(如果查找构造函数，那么name可以是null)
     * @param parameterTypes － 方法或者构造函数参数(支持类型转换)
     * @return 找到的方法或者构造函数，找不到返回 null.
     */
    public static <T extends Executable> T searchExecutable(List<T> executables, String name, Class<?>... parameterTypes) {
        T best = null;
        Class<?>[] bestParametersTypes = null;

        for (T execute : executables) {
            if (name != null && !execute.getName().equals(name)) continue;

            Class<?>[] types = execute.getParameterTypes();
            if (isParameterTypesCompatible(types, parameterTypes, execute.isVarArgs(), false)) {
                // 可能有多个方法与实际参数类型兼容。采用就近兼容原则。
                if (best == null) {
                    best = execute;
                    bestParametersTypes = types;
                } else if (best.isVarArgs() && (!execute.isVarArgs())) {
                    best = execute; // 不可变参数的函数优先
                    bestParametersTypes = types;
                } else if ((!best.isVarArgs()) && execute.isVarArgs()) {
                    // no change
                } else {
                    if (isParameterTypesCompatible(bestParametersTypes, types, best.isVarArgs(), execute.isVarArgs())) {
                        best = execute;
                        bestParametersTypes = types;
                    }
                }
            }
        }
        return best;
    }

    /**
     * 判断参数列表是否兼容, 支持可变参数.
     *
     * @param lhs - left operand
     * @param rhs - right operand
     * @param lhsVarArgs － lhs 是否是可变参数
     * @param rhsVarArgs － rhs 是否是可变参数
     * @return 是否兼容
     */
    public static boolean isParameterTypesCompatible(Class<?>[] lhs, Class<?>[] rhs, boolean lhsVarArgs, boolean rhsVarArgs) {
        if (lhs == null) {
            return rhs == null || rhs.length == 0;
        }
        if (rhs == null) {
            return lhs.length == 0;
        }

        if (lhsVarArgs && rhsVarArgs) {
            if (lhs.length != rhs.length) {
                return false;
            }
            //校验前面的固定参数
            for (int i = 0; i < lhs.length - 1; i++) {
                if (!ClassUtils.isAssignable(lhs[i], rhs[i])) {
                    return false;
                }
            }
            // 校验最后一个可变参数
            Class<?> c1 = lhs[lhs.length - 1].getComponentType();
            Class<?> c2 = rhs[rhs.length - 1].getComponentType();
            if (!ClassUtils.isAssignable(c1, c2)) {
                return false;
            }
        } else if (lhsVarArgs) {
            if (lhs.length - 1 > rhs.length) {
                return false;
            }
            //校验前面的固定参数
            for (int i = 0; i < lhs.length - 1; i++) {
                if (!ClassUtils.isAssignable(lhs[i], rhs[i])) {
                    return false;
                }
            }
            // 校验最后一个可变参数
            Class<?> varType = lhs[lhs.length - 1].getComponentType();
            for (int i = lhs.length - 1; i < rhs.length; i++) {
                if (!ClassUtils.isAssignable(varType, rhs[i])) {
                    return false;
                }
            }
        } else {
            if (lhs.length != rhs.length) {
                return false;
            }
            for (int i = 0; i < lhs.length; i++) {
                if (!ClassUtils.isAssignable(lhs[i], rhs[i])) {
                    return false;
                }
            }
        }
        return true;
    }
}


/*
 * Copyright  2002-2005 WYMIWYG (http://wymiwyg.org)
 *
 *  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 org.wymiwyg.commons.util.io;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;

/**
 * @author reto
 *
 */
public class IndentWriter extends Writer {

	private Writer base;
	private char[] separatorChars;
	boolean identBeforeNextChar = true;

	/**
	 * @param base
	 */
	public IndentWriter(Writer base) {
		this.base = base;
		String lineSeparator = System.getProperty("line.separator");
		this.separatorChars = lineSeparator.toCharArray();
	}

	/* (non-Javadoc)
	 * @see java.io.Writer#close()
	 */
	public void close() throws IOException {
		base.close();

	}

	/* (non-Javadoc)
	 * @see java.io.Writer#flush()
	 */
	public void flush() throws IOException {
		base.flush();

	}

	/* (non-Javadoc)
	 * @see java.io.Writer#write(char[], int, int)
	 */
	public void write(char[] cbuf, int off, int len) throws IOException {
		int posInSeparatorChars = 0;
		for (int i = 0; i < len; i++) {
			if (identBeforeNextChar) {
				base.write('\t');
				identBeforeNextChar = false;
			}
			base.write(cbuf[i]);
			if (cbuf[i] == separatorChars[posInSeparatorChars]) {
				posInSeparatorChars++;
				if (posInSeparatorChars == separatorChars.length) {
					identBeforeNextChar = true;
					posInSeparatorChars = 0;
				}
			} else {
				posInSeparatorChars = 0;
			}
		}

	}

}


/**
 * Copyright 2013-2014 Guoqiang Chen, Shanghai, China. All rights reserved.
 *
 * Email: subchen@gmail.com
 * URL: http://subchen.github.io/
 *
 * 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 jetbrick.io;

import java.io.File;

public class PathUtils {

    /**
     * Returns normalized path (or simply the path if
     * it is already in normalized form). Normalized path does not contain any
     * empty or "." segments or ".." segments preceded by other segment than
     * "..".
     *
     * @param path path to normalize
     * @return normalize path
     */
    public static String normalize(final String path) {
        if (path == null) {
            return null;
        }
        if (path.indexOf('.') == -1 && path.indexOf('/') == -1) {
            return path;
        }

        boolean wasNormalized = true;

        // 1. count number of nonempty segments in path
        // Note that this step is not really necessary because we could simply
        // estimate the number of segments as path.length() and do the empty
        // segment check in step two ;-).
        int numSegments = 0;
        int lastChar = path.length() - 1;
        for (int src = lastChar; src >= 0;) {
            int slash = path.lastIndexOf('/', src);
            if (slash != -1) {
                // empty segment? (two adjacent slashes?)
                if (slash == src) {
                    if (src != lastChar) { // ignore the first slash occurence
                        // (when numSegments == 0)
                        wasNormalized = false;
                    }
                } else {
                    numSegments++;
                }
            } else {
                numSegments++;
            }
            src = slash - 1;
        }

        // 2. split path to segments skipping empty segments
        int[] segments = new int[numSegments];
        char[] chars = new char[path.length()];
        path.getChars(0, chars.length, chars, 0);
        numSegments = 0;
        for (int src = 0; src < chars.length;) {
            // skip empty segments
            while (src < chars.length && chars[src] == '/') {
                src++;
            }
            if (src < chars.length) {
                // note the segment start
                segments[numSegments++] = src;
                // seek to the end of the segment
                while (src < chars.length && chars[src] != '/') {
                    src++;
                }
            }
        }
        // assert (numSegments == segments.length);

        // 3. scan segments and remove all "." segments and "foo",".." segment pairs
        final int DELETED = -1;
        for (int segment = 0; segment < numSegments; segment++) {
            int src = segments[segment];
            if (chars[src++] == '.') {
                if (src == chars.length || chars[src] == '/') { // "." or"./"
                    // delete the "." segment
                    segments[segment] = DELETED;
                    wasNormalized = false;
                } else { // ".something"
                    if (chars[src++] == '.' && (src == chars.length || chars[src] == '/')) { // ".." or "../"
                        // we have the ".." segment scan backwards for segment to delete together with ".."
                        for (int toDelete = segment - 1; toDelete >= 0; toDelete--) {
                            if (segments[toDelete] != DELETED) {
                                if (chars[segments[toDelete]] != '.') {
                                    // delete the two segments
                                    segments[toDelete] = DELETED;
                                    segments[segment] = DELETED;
                                    wasNormalized = false;
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }

        // 4. join the result, if necessary
        if (wasNormalized) { // already normalized? nothing to do...
            return path;
        } else {
            // join the resulting normalized path, retain the leading and ending slash
            int dst = (chars[0] == '/') ? 1 : 0;
            for (int segment = 0; segment < numSegments; segment++) {
                int segmentStart = segments[segment];
                if (segmentStart != DELETED) {
                    // if we remembered segment lengths in step 2, we could use
                    // System.arraycopy method now but we had to allocate one
                    // more array
                    for (int src = segmentStart; src < chars.length; src++) {
                        char ch = chars[src];
                        chars[dst++] = ch;
                        if (ch == '/') {
                            break;
                        }
                    }
                }
            }
            return new String(chars, 0, dst);
        }
    }

    /**
     * 组合路径.
     */
    public static String concat(final String parent, final String child) {
        if (parent == null) {
            return normalize(child);
        }
        if (child == null) {
            return normalize(parent);
        }
        return normalize(parent + '/' + child);
    }

    /**
     * 计算相对路径.
     */
    public static String getRelativePath(final String path, final String relativePath) {
        if (relativePath.startsWith("/")) {
            return normalize(relativePath);
        }
        int separatorIndex = path.lastIndexOf('/');
        if (separatorIndex != -1) {
            String newPath = path.substring(0, separatorIndex + 1);
            return normalize(newPath + relativePath);
        } else {
            return normalize(relativePath);
        }
    }

    /**
     * 转为 Unix 样式的路径.
     */
    public static String separatorsToUnix(String path) {
        if (path == null || path.indexOf('\\') == -1) {
            return path;
        }
        return path.replace('\\', '/');
    }

    /**
     * 转为 Windows 样式的路径.
     */
    public static String separatorsToWindows(String path) {
        if (path == null || path.indexOf('/') == -1) {
            return path;
        }
        return path.replace('/', '\\');
    }

    /**
     * 转为系统默认样式的路径.
     */
    public static String separatorsToSystem(String path) {
        if (path == null) {
            return null;
        }
        if (File.separatorChar == '\\') {
            return separatorsToWindows(path);
        }
        return separatorsToUnix(path);
    }
}


/**
 * Copyright 2013-2014 Guoqiang Chen, Shanghai, China. All rights reserved.
 *
 * Email: subchen@gmail.com
 * URL: http://subchen.github.io/
 *
 * 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 jetbrick.reflect;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import jetbrick.lang.concurrent.ConcurrentInitializer;
import jetbrick.lang.concurrent.LazyInitializer;
import jetbrick.reflect.Filters.FieldFilter;
import jetbrick.reflect.Filters.MethodFilter;
import jetbrick.reflect.asm.ASMAccessor;
import jetbrick.reflect.asm.ASMFactory;

/**
 * 代表一个 Class.
 *
 * @author Guoqiang Chen
 */
public final class KlassInfo {
    private static final ConcurrentHashMap<Class<?>, KlassInfo> pool = new ConcurrentHashMap<Class<?>, KlassInfo>(128);

    /**
     * 将 Class 对象转成 KlassInfo 对象 (有缓存).
     *
     * @param clazz - 原始对象
     * @return KlassInfo 对象
     */
    public static KlassInfo create(final Class<?> clazz) {
        KlassInfo klass = pool.get(clazz);
        if (klass == null) {
            klass = new KlassInfo(clazz);
            KlassInfo old = pool.putIfAbsent(clazz, klass);
            if (old != null) {
                klass = old;
            }
        }
        return klass;
    }

    private final Class<?> clazz;

    private KlassInfo(Class<?> type) {
        this.clazz = type;
    }

    @SuppressWarnings("unchecked")
    public <T> Class<T> getType() {
        return (Class<T>) clazz;
    }

    public String getName() {
        return clazz.getName();
    }

    public String getSimpleName() {
        return clazz.getSimpleName();
    }

    public KlassInfo getSuperKlass() {
        Class<?> superKlass = clazz.getSuperclass();
        return (superKlass == null) ? null : KlassInfo.create(superKlass);
    }

    public List<KlassInfo> getInterfaces() {
        Class<?>[] interfaces = clazz.getInterfaces();
        if (interfaces.length == 0) {
            return Collections.emptyList();
        }
        List<KlassInfo> results = new ArrayList<KlassInfo>(interfaces.length);
        for (Class<?> intf : interfaces) {
            results.add(KlassInfo.create(intf));
        }
        return results;
    }

    // ------------------------------------------------------------------
    public Annotation[] getAnnotations() {
        return clazz.getAnnotations();
    }

    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        return clazz.getAnnotation(annotationClass);
    }

    public <T extends Annotation> boolean isAnnotationPresent(Class<T> annotationClass) {
        return clazz.isAnnotationPresent(annotationClass);
    }

    // ------------------------------------------------------------------
    public int getModifiers() {
        return clazz.getModifiers();
    }

    public boolean isInterface() {
        return Modifier.isInterface(getModifiers());
    }

    public boolean isAbstract() {
        return Modifier.isAbstract(getModifiers());
    }

    public boolean isStatic() {
        return Modifier.isStatic(getModifiers());
    }

    public boolean isPrivate() {
        return Modifier.isPrivate(getModifiers());
    }

    public boolean isProtected() {
        return Modifier.isProtected(getModifiers());
    }

    public boolean isPublic() {
        return Modifier.isPublic(getModifiers());
    }

    // ------------------------------------------------------------------
    private final ConcurrentInitializer<List<ConstructorInfo>> declaredConstructorsGetter = new LazyInitializer<List<ConstructorInfo>>() {
        @Override
        protected List<ConstructorInfo> initialize() {
            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
            if (constructors.length == 0) {
                return Collections.emptyList();
            } else {
                ArrayList<ConstructorInfo> results = new ArrayList<ConstructorInfo>(constructors.length);
                for (int i = 0; i < constructors.length; i++) {
                    results.add(new ConstructorInfo(KlassInfo.this, constructors[i], i));
                }
                results.trimToSize();
                return Collections.unmodifiableList(results);
            }
        }
    };

    public List<ConstructorInfo> getDeclaredConstructors() {
        return declaredConstructorsGetter.get();
    }

    /**
     * 根据目标参数类型，查找完全匹配的构造函数.
     *
     * @param parameterTypes - 匹配的构造函数参数
     * @return 找到的构造函数对象，找不到返回 null.
     */
    public ConstructorInfo getDeclaredConstructor(Class<?>... parameterTypes) {
        return ExecutableUtils.getExecutable(declaredConstructorsGetter.get(), null, parameterTypes);
    }

    /**
     * 根据目标参数类型，查找最佳匹配的构造函数.
     *
     * @param parameterTypes - 匹配的构造函数参数
     * @return 找到的构造函数对象，找不到返回 null.
     */
    public ConstructorInfo searchDeclaredConstructor(Class<?>... parameterTypes) {
        ConstructorInfo constructor = ExecutableUtils.getExecutable(declaredConstructorsGetter.get(), null, parameterTypes);
        if (constructor == null) {
            constructor = ExecutableUtils.searchExecutable(declaredConstructorsGetter.get(), null, parameterTypes);
        }
        return constructor;
    }

    public ConstructorInfo getDeclaredConstructor(Constructor<?> constructor) {
        for (ConstructorInfo info : declaredConstructorsGetter.get()) {
            if (info.getConstructor() == constructor) {
                return info;
            }
        }
        return null;
    }

    public ConstructorInfo getDefaultConstructor() {
        for (ConstructorInfo info : declaredConstructorsGetter.get()) {
            if (info.isDefault()) {
                return info;
            }
        }
        return null;
    }

    // ------------------------------------------------------------------
    private final ConcurrentInitializer<List<MethodInfo>> declaredMethodsGetter = new LazyInitializer<List<MethodInfo>>() {
        @Override
        protected List<MethodInfo> initialize() {
            Method[] methods = clazz.getDeclaredMethods();
            if (methods.length == 0) {
                return Collections.emptyList();
            } else {
                ArrayList<MethodInfo> results = new ArrayList<MethodInfo>(methods.length);
                for (int i = 0; i < methods.length; i++) {
                    results.add(new MethodInfo(KlassInfo.this, methods[i], i));
                }
                results.trimToSize();
                return Collections.unmodifiableList(results);
            }
        }
    };

    public List<MethodInfo> getDeclaredMethods() {
        return declaredMethodsGetter.get();
    }

    public List<MethodInfo> getDeclaredMethods(MethodFilter filter) {
        List<MethodInfo> methods = declaredMethodsGetter.get();
        if (methods.size() == 0) {
            return methods;
        }
        List<MethodInfo> results = new ArrayList<MethodInfo>(methods.size());
        for (MethodInfo method : methods) {
            if (filter.accept(method)) {
                results.add(method);
            }
        }
        return results;
    }

    public MethodInfo getDeclaredMethod(String name, Class<?>... parameterTypes) {
        return ExecutableUtils.getExecutable(declaredMethodsGetter.get(), name, parameterTypes);
    }

    public MethodInfo getDeclaredMethod(Method method) {
        for (MethodInfo info : declaredMethodsGetter.get()) {
            if (info.getMethod() == method) {
                return info;
            }
        }
        return null;
    }

    private final ConcurrentInitializer<List<MethodInfo>> methodsGetter = new LazyInitializer<List<MethodInfo>>() {
        @Override
        protected List<MethodInfo> initialize() {
            KlassInfo klass = KlassInfo.this;
            List<MethodInfo> declaredMethods = klass.getDeclaredMethods();

            ArrayList<MethodInfo> results = new ArrayList<MethodInfo>(declaredMethods.size() + 16);
            results.addAll(declaredMethods);

            if (klass.isInterface()) {
                for (KlassInfo parent : klass.getInterfaces()) {
                    for (MethodInfo method : parent.getDeclaredMethods()) {
                        results.add(method);
                    }
                }
            } else {
                KlassInfo parent = klass.getSuperKlass();
                while (parent != null) {
                    for (MethodInfo method : parent.getDeclaredMethods()) {
                        // 只加入 super class 中 非private, 非 static 的, 非 abstract 的方法 (这些方法一般是被认为可继承的)
                        if (!method.isPrivate() && !method.isStatic() && !method.isAbstract()) {
                            results.add(method);
                        }
                    }
                    parent = parent.getSuperKlass();
                }
            }
            results.trimToSize();
            return Collections.unmodifiableList(results);
        }
    };

    public List<MethodInfo> getMethods() {
        return methodsGetter.get();
    }

    public List<MethodInfo> getMethods(MethodFilter filter) {
        List<MethodInfo> methods = methodsGetter.get();
        if (methods.size() == 0) {
            return methods;
        }
        List<MethodInfo> results = new ArrayList<MethodInfo>(methods.size());
        for (MethodInfo method : methods) {
            if (filter.accept(method)) {
                results.add(method);
            }
        }
        return results;
    }

    /**
     * 根据目标参数类型，查找完全匹配的方法.
     *
     * @param parameterTypes - 匹配的方法参数
     * @return 找到的方法对象，找不到返回 null.
     */
    public MethodInfo getMethod(String name, Class<?>... parameterTypes) {
        return ExecutableUtils.getExecutable(methodsGetter.get(), name, parameterTypes);
    }

    /**
     * 根据目标参数类型，查找最佳匹配的方法.
     *
     * @param parameterTypes - 匹配的方法参数
     * @return 找到的方法对象，找不到返回 null.
     */
    public MethodInfo searchMethod(String name, Class<?>... parameterTypes) {
        MethodInfo method = ExecutableUtils.getExecutable(methodsGetter.get(), name, parameterTypes);
        if (method == null) {
            method = ExecutableUtils.searchExecutable(methodsGetter.get(), name, parameterTypes);
        }
        return method;
    }

    // ------------------------------------------------------------------
    private final ConcurrentInitializer<List<FieldInfo>> declaredFieldsGetter = new LazyInitializer<List<FieldInfo>>() {
        @Override
        protected List<FieldInfo> initialize() {
            Field[] fields = clazz.getDeclaredFields();
            if (fields.length == 0) {
                return Collections.emptyList();
            }
            ArrayList<FieldInfo> results = new ArrayList<FieldInfo>(fields.length);
            for (int i = 0; i < fields.length; i++) {
                results.add(new FieldInfo(KlassInfo.this, fields[i], i));
            }
            results.trimToSize();
            return Collections.unmodifiableList(results);
        }
    };

    public List<FieldInfo> getDeclaredFields() {
        return declaredFieldsGetter.get();
    }

    public List<FieldInfo> getDeclaredFields(FieldFilter filter) {
        List<FieldInfo> fields = declaredFieldsGetter.get();
        if (fields.size() == 0) {
            return fields;
        }
        List<FieldInfo> results = new ArrayList<FieldInfo>(fields.size());
        for (FieldInfo field : fields) {
            if (filter.accept(field)) {
                results.add(field);
            }
        }
        return results;
    }

    public FieldInfo getDeclaredField(String name) {
        for (FieldInfo field : declaredFieldsGetter.get()) {
            if (field.getName().equals(name)) {
                return field;
            }
        }
        return null;
    }

    public FieldInfo getDeclaredField(Field field) {
        for (FieldInfo info : declaredFieldsGetter.get()) {
            if (info.getField() == field) {
                return info;
            }
        }
        return null;
    }

    private final ConcurrentInitializer<List<FieldInfo>> fieldsGetter = new LazyInitializer<List<FieldInfo>>() {
        @Override
        protected List<FieldInfo> initialize() {
            KlassInfo klass = KlassInfo.this;
            List<FieldInfo> declaredFields = klass.getDeclaredFields();

            ArrayList<FieldInfo> results = new ArrayList<FieldInfo>(declaredFields.size() + 8);
            results.addAll(declaredFields);

            if (klass.isInterface()) {
                for (KlassInfo parent : klass.getInterfaces()) {
                    for (FieldInfo field : parent.getDeclaredFields()) {
                        // 只加入 super interface 中 非private， 非 static 的字段 (这些字段一般是被认为可继承的)
                        if (!field.isPrivate() && !field.isStatic()) {
                            results.add(field);
                        }
                    }
                }
            } else {
                KlassInfo parent = klass.getSuperKlass();
                while (parent != null) {
                    for (FieldInfo field : parent.getDeclaredFields()) {
                        // 只加入 super class 中 非private， 非 static 的字段 (这些字段一般是被认为可继承的)
                        if (!field.isPrivate() && !field.isStatic()) {
                            results.add(field);
                        }
                    }
                    parent = parent.getSuperKlass();
                }
            }
            results.trimToSize();
            return Collections.unmodifiableList(results);
        }
    };

    public List<FieldInfo> getFields() {
        return fieldsGetter.get();
    }

    public List<FieldInfo> getFields(FieldFilter filter) {
        List<FieldInfo> fields = fieldsGetter.get();
        if (fields.size() == 0) {
            return fields;
        }
        List<FieldInfo> results = new ArrayList<FieldInfo>(fields.size());
        for (FieldInfo field : fields) {
            if (filter.accept(field)) {
                results.add(field);
            }
        }
        return results;
    }

    public FieldInfo getField(String name) {
        for (FieldInfo field : fieldsGetter.get()) {
            if (field.getName().equals(name)) {
                return field;
            }
        }
        return null;
    }

    // ------------------------------------------------------------------
    private final ConcurrentInitializer<List<PropertyInfo>> propertiesGetter = new LazyInitializer<List<PropertyInfo>>() {
        @Override
        protected List<PropertyInfo> initialize() {
            List<MethodInfo> methods = methodsGetter.get();
            Map<String, PropertyInfo> map = new HashMap<String, PropertyInfo>(methods.size());
            for (MethodInfo method : methods) {
                if (method.isStatic()) continue;
                if (method.isAbstract()) continue;
                if (!method.isPublic()) continue;

                if (method.isReadMethod()) {
                    String name = method.getPropertyName();
                    PropertyInfo propertyInfo = map.get(name);
                    if (propertyInfo == null) {
                        propertyInfo = new PropertyInfo(KlassInfo.this, name);
                        map.put(name, propertyInfo);
                    }
                    propertyInfo.setGetter(method);
                } else if (method.isWriteMethod()) {
                    String name = method.getPropertyName();
                    PropertyInfo propertyInfo = map.get(name);
                    if (propertyInfo == null) {
                        propertyInfo = new PropertyInfo(KlassInfo.this, name);
                        map.put(name, propertyInfo);
                    }
                    propertyInfo.setSetter(method);
                }
            }
            if (map.size() == 0) {
                return Collections.emptyList();
            }
            return Collections.unmodifiableList(new ArrayList<PropertyInfo>(map.values()));
        }
    };

    public List<PropertyInfo> getProperties() {
        return propertiesGetter.get();
    }

    public PropertyInfo getProperty(String name) {
        for (PropertyInfo prop : propertiesGetter.get()) {
            if (prop.getName().equals(name)) {
                return prop;
            }
        }
        return null;
    }

    // ------------------------------------------------------------------
    private ASMAccessor asmAccessor; // ASM 生成的类的实例
    private int asmCallNumber = 0; //  反射调用计数器，超过阈值，则使用 ASM 字节码增强技术

    protected ASMAccessor getASMAccessor() {
        if (asmAccessor == null) {
            if (asmCallNumber >= ASMFactory.getThreshold()) {
                asmAccessor = ASMFactory.generateAccessor(this);
            } else {
                asmCallNumber++;
            }
        }
        return asmAccessor;
    }

    /**
     * 调用默认的构造函数生成对象实例.
     *
     * @return 生成的对象实例.
     * @throws IllegalStateException - 如果找不到默认的构造函数或者没有访问权限，那么抛出该异常.
     */
    public Object newInstance() throws IllegalStateException {
        ASMAccessor accessor = getASMAccessor();
        if (accessor == null) {
            ConstructorInfo ctor = getDefaultConstructor();
            if (ctor != null) {
                return ctor.newInstance();
            }
            throw new IllegalStateException("No default constructor");
        } else {
            return accessor.newInstance();
        }
    }

    // ------------------------------------------------------------------
    @Override
    public String toString() {
        return clazz.toString();
    }
}


// Copyright (c) 2014 Erick Bourgeois, All Rights Reserved

package ca.jeb.protobuf;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ca.jeb.common.infra.JException;
import ca.jeb.common.infra.JReflectionUtils;
import ca.jeb.common.infra.JStringUtils;
import ca.jeb.protobuf.converter.NullConverter;
import ca.jeb.protobuf.internal.ProtobufSerializerUtils;

import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.Message;
import com.google.protobuf.Message.Builder;

/**
 * This class' has two methods for serialization:
 *

 *
toProtobuf: serialize to a Protobuf
 *
fromProtobuf: serialize from a Protobuf object
 *

 * This class can be used as Java bean and thus a singleton.
 *
 * @author Erick Bourgeois
 */
public class ProtobufSerializer implements IProtobufSerializer
{
  private static final Logger LOGGER = LoggerFactory.getLogger(ProtobufSerializer.class);

  /**
   * @see ca.jeb.protobuf.IProtobufSerializer#toProtobuf(java.lang.Object)
   */
  @Override
  @SuppressWarnings({"unchecked", "rawtypes"})
  public Message toProtobuf(Object pojo) throws ProtobufException
  {
    try
    {
      final Class<?> fromClazz = (Class<?>)pojo.getClass();
      final Class<? extends GeneratedMessage> protoClazz = ProtobufSerializerUtils.getProtobufClassFromPojoAnno(fromClazz);
      if (protoClazz == null)
      {
        throw new ProtobufAnnotationException("Doesn't seem like " + fromClazz + " is ProtobufEntity");
      }

      final Map<Field, ProtobufAttribute> protoBufFields = ProtobufSerializerUtils.getAllProtbufFields(fromClazz);
      if (protoBufFields.isEmpty())
      {
        return null;
      }

      final Method newBuilderMethod = protoClazz.getMethod("newBuilder");
      final Builder protoObjBuilder = (Builder)newBuilderMethod.invoke(null);
      for (Entry<Field, ProtobufAttribute> entry : protoBufFields.entrySet())
      {
        final Field field = entry.getKey();
        final ProtobufAttribute gpbAnnotation = entry.getValue();
        final String fieldName = field.getName();

        // 1. Determine validity of value
        Object value = getPojoFieldValue(pojo, gpbAnnotation, field);
        // If value is null and it is not required, skip, as the default for Protobuf values is null
        if (value == null)
        {
          continue;
        }

        // 2. Call recursively if this is a ProtobufEntity
        value = serializeToProtobufEntity(value);

        // 3. Handle POJO Collections/Lists
        if (value instanceof Collection)
        {
          value = convertCollectionToProtobufs((Collection<Object>)value);
          if (((Collection)value).isEmpty())
          {
            continue;
          }
        }

        // 4. Determine the setter name
        final String setter = ProtobufSerializerUtils.getProtobufSetter(gpbAnnotation, field, value);

        // 5. Finally, set the value on the Builder
        setProtobufFieldValue(gpbAnnotation, protoObjBuilder, setter, value);
      }

      return protoObjBuilder.build();

    }
    catch (Exception e)
    {
      throw new ProtobufException("Could not generate Protobuf object for " + pojo.getClass() + ": " + e, e);
    }
  }

  /**
   * @see ca.jeb.protobuf.IProtobufSerializer#fromProtobuf(Message, Class)
   */
  @Override
  public Object fromProtobuf(Message protobuf, Class<?> pojoClazz) throws ProtobufException
  {
    try
    {
      final Class<? extends GeneratedMessage> protoClazz = ProtobufSerializerUtils.getProtobufClassFromPojoAnno(pojoClazz);
      if (protoClazz == null)
      {
        throw new ProtobufAnnotationException("Doesn't seem like " + pojoClazz + " is ProtobufEntity");
      }

      final Map<Field, ProtobufAttribute> protobufFields = ProtobufSerializerUtils.getAllProtbufFields(pojoClazz);
      if (protobufFields.isEmpty())
      {
        throw new ProtobufException("No protoBuf fields have been annotated on the class " + pojoClazz + ", thus cannot continue.");
      }

      Object pojo = pojoClazz.newInstance();

      for (Entry<Field, ProtobufAttribute> entry : protobufFields.entrySet())
      {
        final Field field = entry.getKey();
        final ProtobufAttribute protobufAttribute = entry.getValue();
        final String setter = ProtobufSerializerUtils.getPojoSetter(protobufAttribute, field);

        Object protobufValue = getProtobufFieldValue(protobuf, protobufAttribute, field);
        if (protobufValue == null)
        {
          continue;
        }

        setPojoFieldValue(pojo, setter, protobufValue, protobufAttribute);
      }

      return pojo;
    }
    catch (Exception e)
    {
      throw new ProtobufException("Could not generate POJO of type " + pojoClazz + " from Protobuf object " + protobuf.getClass() + ": "
              + e, e);
    }
  }

  /**
   * Returns the field value from the supplied pojo object. If a pojoGetter</code has been set in the
   * {@link ProtobufAttribute}, then use that, otherwise, try getting the field value directly.
   *
   * @param pojo - POJO Object
   * @param protobufAttribute
   * @param field
   * @return Object - the resulting object after calling the POJO's getter
   * @throws ProtobufAnnotationException
   */
  private static final Object getPojoFieldValue(Object pojo, ProtobufAttribute protobufAttribute, Field field)
          throws ProtobufAnnotationException
  {
    final String getter = protobufAttribute.pojoGetter();

    Object value = null;
    if (!getter.isEmpty())
    {
      try
      {
        return JReflectionUtils.runMethod(pojo, getter);
      }
      catch (Exception e)
      {
        throw new ProtobufAnnotationException("Could not get a value for field " + field.getName() + " using configured getter of "
                + getter, e);
      }
    }

    try
    {
      value = JReflectionUtils.runGetter(pojo, field);
    }
    catch (Exception ee)
    {
      throw new ProtobufAnnotationException("Could not execute getter " + getter + " on class " + pojo.getClass().getCanonicalName() + ": "
              + ee, ee);
    }

    if (value == null && protobufAttribute.required())
    {
      throw new ProtobufAnnotationException("Required field " + field.getName() + " on class " + pojo.getClass().getCanonicalName()
              + " is null");
    }

    return value;
  }

  @SuppressWarnings("rawtypes")
  private static final Object getProtobufFieldValue(Message protoBuf, ProtobufAttribute protobufAttribute, Field field) throws JException,
          InstantiationException, IllegalAccessException
  {
    final String fieldName = field.getName();
    final String upperCaseFirstFieldName = JStringUtils.upperCaseFirst(fieldName);
    final String getter = ProtobufSerializerUtils.getProtobufGetter(protobufAttribute, field);
    // This is used to determine if the Protobuf message has populated this value

    Boolean isCollection = Boolean.FALSE;
    if (Collection.class.isAssignableFrom(field.getType()))
    {
      isCollection = Boolean.TRUE;
    }
    if (!isCollection)
    {
      // no need to continue if this field in the protoBuf is not set
      final String haser = StringUtils.replaceOnce(getter, "get", "has");

      final boolean fieldIsSet = (boolean)JReflectionUtils.runMethod(protoBuf, haser, (Object[])null);
      if (!fieldIsSet)
      {
        return null;
      }
    }

    // Go ahead and fun the getter
    Object protobufValue = JReflectionUtils.runMethod(protoBuf, getter, (Object[])null);
    if (isCollection && ((Collection)protobufValue).isEmpty())
    {
      return null;
    }

    // If the field itself is a ProtbufEntity, serialize that!
    if (protobufValue instanceof GeneratedMessage && ProtobufSerializerUtils.isProtbufEntity(field.getType()))
    {
      protobufValue = serializeFromProtobufEntity((Message)protobufValue, field.getType());
    }

    if (protobufValue instanceof Collection)
    {
      protobufValue = convertCollectionFromProtobufs(field, (Collection<?>)protobufValue);
      if (((Collection)protobufValue).isEmpty())
      {
        return null;
      }
    }

    return protobufValue;
  }

  /**
   * Create a new serializer and serializes the supplied object/attribute.
   *
   * @param pojo - the POJO object
   * @return Object - the resulting GPB object
   * @throws JException
   */
  private static final Object serializeToProtobufEntity(Object pojo) throws JException
  {
    final ProtobufEntity protoBufEntity = ProtobufSerializerUtils.getProtobufEntity(pojo.getClass());

    if (protoBufEntity == null)
    {
      return pojo;
    }

    return new ProtobufSerializer().toProtobuf(pojo);
  }

  /**
   * Create a new serializer and (de)serializes the supplied Protobu/attribute to a POJO of type pojoClazz.
   *
   * @param protoBuf
   * @param pojoClazz
   * @return
   * @throws JException
   */
  private static final Object serializeFromProtobufEntity(Message protoBuf, Class<?> pojoClazz) throws JException
  {
    final ProtobufEntity protoBufEntity = ProtobufSerializerUtils.getProtobufEntity(pojoClazz);

    if (protoBufEntity == null)
    {
      return protoBuf;
    }

    return new ProtobufSerializer().fromProtobuf(protoBuf, pojoClazz);
  }

  /**
   * Loops through the collection of objects and serializes them, iff they have ProtobufEntity annotations.
   *
   * @param value - Collection<Object>
   * @return Object - Collection of serialized objects.
   * @throws JException
   */
  private static final Object convertCollectionToProtobufs(Collection<Object> collectionOfNonProtobufs) throws JException
  {
    if (collectionOfNonProtobufs.isEmpty())
    {
      return collectionOfNonProtobufs;
    }
    final Object first = collectionOfNonProtobufs.toArray()[0];
    if (!ProtobufSerializerUtils.isProtbufEntity(first))
    {
      return collectionOfNonProtobufs;
    }

    final Collection<Object> newCollectionValues;

    /**
     * Maintain the Collection type of value at this stage (if it is a Set), and if conversion is required to a
     * different Collection type, that will be handled by a converter later on
     */
    if (collectionOfNonProtobufs instanceof Set)
    {
      newCollectionValues = new HashSet<>();
    }
    else
    {
      newCollectionValues = new ArrayList<>();
    }

    for (Object iProtobufGenObj : collectionOfNonProtobufs)
    {
      newCollectionValues.add(serializeToProtobufEntity(iProtobufGenObj));
    }

    return newCollectionValues;
  }

  private static Object convertCollectionFromProtobufs(Field field, Collection<?> collectionOfProtobufs) throws JException,
          InstantiationException, IllegalAccessException
  {
    if (collectionOfProtobufs.isEmpty())
    {
      return collectionOfProtobufs;
    }

    final ParameterizedType listType = (ParameterizedType)field.getGenericType();
    final Class<?> collectionClazzType = (Class<?>)listType.getActualTypeArguments()[0];
    final ProtobufEntity protoBufEntityAnno = ProtobufSerializerUtils.getProtobufEntity(collectionClazzType);

    final Object first = collectionOfProtobufs.toArray()[0];
    if (!(first instanceof GeneratedMessage) && protoBufEntityAnno == null)
    {
      return collectionOfProtobufs;
    }

    final Collection<Object> newCollectionOfValues = new ArrayList<>();
    for (Object protobufValue : collectionOfProtobufs)
    {
      if (!(protobufValue instanceof GeneratedMessage))
      {
        throw new ProtobufException("Collection contains an object of type " + protobufValue.getClass()
                + " which is not an instanceof GeneratedMessage, can not (de)serialize this");
      }
      newCollectionOfValues.add(serializeFromProtobufEntity((Message)protobufValue, collectionClazzType));
    }

    return newCollectionOfValues;
  }

  /**
   * This method does the actual "set" on the Protobuf builder. If the user specified a converter,
   * then use that right before we actually try and set the value.
   *
   * @param protobufAttribute - ProtobufAttribute
   * @param protoObjBuilder - G.Builder
   * @param setter - String
   * @param fieldValue - POJO's filed Object
   * @throws NoSuchMethodException
   * @throws SecurityException
   * @throws ProtobufAnnotationException
   * @throws InstantiationException
   * @throws IllegalAccessException
   * @throws IllegalArgumentException
   * @throws InvocationTargetException
   */
  private static final void setProtobufFieldValue(ProtobufAttribute protobufAttribute, Builder protoObjBuilder, String setter,
          Object fieldValue) throws NoSuchMethodException, SecurityException, ProtobufAnnotationException, InstantiationException,
          IllegalAccessException, IllegalArgumentException, InvocationTargetException
  {
    Class<? extends Object> fieldValueClass = fieldValue.getClass();
    Class<? extends Object> gpbClass = fieldValueClass;

    final Class<? extends IProtobufConverter> converterClazz = protobufAttribute.converter();
    if (converterClazz != NullConverter.class)
    {
      final IProtobufConverter protoBufConverter = (IProtobufConverter)converterClazz.newInstance();
      fieldValue = protoBufConverter.convertToProtobuf(fieldValue);
      gpbClass = fieldValue.getClass();
      fieldValueClass = gpbClass;
    }

    // Need to convert the argument class from non-primitives to primitives, as Protobuf uses these.
    gpbClass = ProtobufSerializerUtils.getProtobufClass(fieldValue, gpbClass);

    final Method gpbMethod = protoObjBuilder.getClass().getDeclaredMethod(setter, gpbClass);
    gpbMethod.invoke(protoObjBuilder, fieldValue);
  }

  /**
   * This method does the actual "set" on the POJO instance. If the user specified a converter,
   * then use that right before we actually try and set the value.
   *
   * @param pojo - the instance of the POJO
   * @param setter - the setter method name
   * @param protobufValue - the Protobuf value, note this could be anything from another ProtobufEntity to a primitive, like boolean
   * @param protobufAttribute - the ProtobufAttribute annotation
   * @throws InstantiationException
   * @throws IllegalAccessException
   * @throws JException
   */
  private static final void setPojoFieldValue(Object pojo, String setter, Object protobufValue, ProtobufAttribute protobufAttribute)
          throws InstantiationException, IllegalAccessException, JException
  {
    /**
     * convertCollectionFromProtoBufs() above returns an ArrayList, and we may have a converter to convert to a Set,
     * so we are performing the conversion there
     */
    final Class<? extends IProtobufConverter> fromProtoBufConverter = protobufAttribute.converter();
    if (fromProtoBufConverter != NullConverter.class)
    {
      final IProtobufConverter converter = fromProtoBufConverter.newInstance();
      protobufValue = converter.convertFromProtobuf(protobufValue);
    }

    Class<? extends Object> argClazz = protobufValue.getClass();

    JReflectionUtils.runSetter(pojo, setter, protobufValue, argClazz);
  }
}


/*
 * FileExtensionFilter.java
 *
 * Created on April 9, 2000, 5:09 AM

 * Copyright © 2012 jbundle.org. All rights reserved.
 */
package org.jbundle.jbackup.util;

import java.io.*;

/**
 * The FileExtensionFilter filters files in a directory depending on their extension.
 *
 * @author  Don Corley
 * @version 1.0.0
 */
public class FileExtensionFilter extends Object
	implements FileFilter
{
	protected String[] m_rgstrIncludeExtensions = null;
	protected String[] m_rgstrExcludeExtensions = null;

	/**
	  * Creates new FileExtensionFilter
	  */
	public FileExtensionFilter(String[] rgstrIncludeExtensions, String[] rgstrExcludeExtensions)
	{
		super();
		m_rgstrIncludeExtensions = rgstrIncludeExtensions;
		m_rgstrExcludeExtensions = rgstrExcludeExtensions;
	}
	/** Tests whether or not the specified abstract pathname should be
	 * included in a pathname list.
	 *
	 * @param  pathname  The abstract pathname to be tested
	 * @return  true if and only if pathname
	 *          should be included
	 */
	public boolean accept(File pathname)
	{
		String name = pathname.getName();
		int iLastDot = name.lastIndexOf('.');
		String strExtension = "";
		if ((iLastDot != -1)
			&& (iLastDot != name.length() -1 ))
				strExtension = name.substring(iLastDot + 1);
		if (m_rgstrIncludeExtensions != null)
		{
			for (int i = 0; i < m_rgstrIncludeExtensions.length; i++)
			{
				if (m_rgstrIncludeExtensions[i].equalsIgnoreCase(strExtension))
					return true;	// Accept
			}
			return false;	// Not in included - return
		}
		if (m_rgstrExcludeExtensions != null)
		{
			for (int i = 0; i < m_rgstrExcludeExtensions.length; i++)
			{
				if (m_rgstrExcludeExtensions[i].equalsIgnoreCase(strExtension))
					return false;	// Don't accept
			}
		}
		return true;	// Accept this file
	}
}


/*
 * #%L
 * This file is part of a universal JAAS library, providing a plaintext password
 * validator.
 * %%
 * Copyright (C) 2014 - 2016 Michael Beiter <michael@beiter.org>
 * %%
 * All rights reserved.
 * .
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holder nor the names of the
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 * .
 * .
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */
package org.beiter.michael.authn.jaas.loginmodules.password.validators.plaintext;

import org.beiter.michael.authn.jaas.common.CommonProperties;
import org.beiter.michael.authn.jaas.loginmodules.password.common.PasswordValidator;

import java.util.Arrays;

/**
 * This implementation of a password validator can validate a password against a plain-text password string.
 *

 * Obviously, this is insecure and should not be used in production. Instead use an implementation that
 * is based, for instance, on an iterated password hash function instead!
 */
public class PlainTextPasswordValidator
        implements PasswordValidator {

    /**
     * {@inheritDoc}
     */
    @Override
    public void init(final CommonProperties  properties) {

        // nothing to do here - this implementation does not take any properties
    }

    /**
     * {@inheritDoc}
     */
    @Override
    // It would be pretty dumb to use varargs for the credential...
    @SuppressWarnings("PMD.UseVarargs")
    public final boolean validate(final char[] providedPassword, final char[] storedCredential) {

        if (providedPassword == null || storedCredential == null) {
            return false;
        } else {
            return Arrays.equals(providedPassword, storedCredential);
        }
    }
}


/**
 * Copyright 2013-2014 Guoqiang Chen, Shanghai, China. All rights reserved.
 *
 * Email: subchen@gmail.com
 * URL: http://subchen.github.io/
 *
 * 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 jetbrick.dao.jdbclog;

import java.lang.reflect.*;
import java.sql.ResultSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * ResultSet Wrapper to add logging
 */
public final class JdbcLogResultSet extends JdbcLogSupport implements InvocationHandler {
    private static final Logger log = LoggerFactory.getLogger(JdbcLogResultSet.class);
    private final ResultSet rs;

    /**
     * Creates a logging version of a ResultSet
     *
     * @param rs   - the ResultSet to proxy
     * @return - the ResultSet with logging
     */
    public static ResultSet getInstance(ResultSet rs) {
        InvocationHandler handler = new JdbcLogResultSet(rs);
        ClassLoader cl = ResultSet.class.getClassLoader();
        return (ResultSet) Proxy.newProxyInstance(cl, new Class[] { ResultSet.class }, handler);
    }

    private JdbcLogResultSet(ResultSet rs) {
        this.rs = rs;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
        try {
            Object value = method.invoke(rs, params);

            String methodName = method.getName();
            if (GET_METHODS.contains(methodName)) {
                String columnName;
                if (params[0] instanceof String) {
                    columnName = (String) params[0];
                } else {
                    Integer index = (Integer) params[0];
                    columnName = rs.getMetaData().getColumnName(index);
                }
                if (rs.wasNull()) {
                    value = null;
                }
                addParam(columnName, value);
            } else if (MOVE_METHODS.contains(methodName)) {
                if (isParamNotEmpty() && log.isDebugEnabled()) {
                    log.debug("#{} ResultSet.Get(): {}", id, getParamNameList());
                    log.debug("#{} Parameters: {}", id, getParamValueList());
                    log.debug("#{} Types: {}", id, getParamTypeList());
                }
                resetParamsInfo();
            } else if (UPDATE_METHODS.contains(methodName)) {
                String columnName;
                if (params[0] instanceof String) {
                    columnName = (String) params[0];
                } else {
                    Integer index = (Integer) params[0];
                    columnName = rs.getMetaData().getColumnName(index);
                }
                addParam(columnName, value);
            } else if ("updateRow".equals(methodName) || "cancelRowUpdates".equals(methodName)) {
                if (log.isDebugEnabled()) {
                    log.debug("#{} ResultSet.{}(): {}", id, methodName, getParamNameList());
                    log.debug("#{} Parameters: {}", id, getParamValueList());
                    log.debug("#{} Types: {}", id, getParamTypeList());
                }
                resetParamsInfo();
            } else if ("insertRow".equals(methodName)) {
                log.debug("#{} ResultSet.insertRow()", id);
                resetParamsInfo();
            }

            return value;
        } catch (Exception e) {
            log.error("#{} <ERROR> in {}", id, toString(method, params));
            throw unwrapThrowable(e);
        }
    }
}


package com.w3asel.cubesensors.api.v1.json;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

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

import com.w3asel.cubesensors.api.v1.State;

/**
 * Utility class to convert from the arrays returned by the API to {@link State} objects.
 *
 * @author Joe
 */
public class StateParser {
	private static final Logger LOGGER = LoggerFactory.getLogger(StateParser.class);

	private enum ExpectedLabels {
		time,
		temp,
		pressure,
		humidity,
		voc,
		light,
		noise,
		noisedba,
		battery,
		shake,
		cable,
		voc_resistance,
		rssi
	}

	/**
	 * Produces a mapping of label to index for use in parsing state values to the appropriate slot in the state object.
	 */
	private static Map<ExpectedLabels, Integer> mapLabels(final List<String> labels) {
		final Map<ExpectedLabels, Integer> map = new EnumMap<>(ExpectedLabels.class);

		final List<ExpectedLabels> unusedLabels = new ArrayList<>(Arrays.asList(ExpectedLabels.values()));
		for (int index = 0; index < labels.size(); index++) {
			final String next = labels.get(index);
			ExpectedLabels labelValue;
			try {
				labelValue = ExpectedLabels.valueOf(next);

				unusedLabels.remove(labelValue);
				if (map.containsKey(labelValue)) {
					LOGGER.warn("Duplicate state label: {} ({})", next, labels);
				}
				map.put(labelValue, index);
			} catch (final IllegalArgumentException e) {
				LOGGER.warn("Unexpected state label: {}", next);
			}
		}
		for (final ExpectedLabels label : unusedLabels) {
			LOGGER.warn("Unused label: {}", label);
		}

		return map;
	}

	/**
	 * Uses the provided label-index mapping to extract state values and create a new State object.
	 */
	private static State extractValues(final List<Object> values, final Map<ExpectedLabels, Integer> map) {
		final ZonedDateTime time = ZonedDateTime.parse((String) values.get(map.get(ExpectedLabels.time)));
		final int temp = safeInt((Integer) values.get(map.get(ExpectedLabels.temp)));
		final int pressure = safeInt((Integer) values.get(map.get(ExpectedLabels.pressure)));
		final int humidity = safeInt((Integer) values.get(map.get(ExpectedLabels.humidity)));
		final int voc = safeInt((Integer) values.get(map.get(ExpectedLabels.voc)));
		final int light = safeInt((Integer) values.get(map.get(ExpectedLabels.light)));
		final int noise = safeInt((Integer) values.get(map.get(ExpectedLabels.noise)));
		final int noisedba = safeInt((Integer) values.get(map.get(ExpectedLabels.noisedba)));
		final int battery = safeInt((Integer) values.get(map.get(ExpectedLabels.battery)));
		final boolean shake = safeBoolean((Boolean) values.get(map.get(ExpectedLabels.shake)));
		final boolean cable = safeBoolean((Boolean) values.get(map.get(ExpectedLabels.cable)));
		final int vocResistance = safeInt((Integer) values.get(map.get(ExpectedLabels.voc_resistance)));
		final int rssi = safeInt((Integer) values.get(map.get(ExpectedLabels.rssi)));
		return new State(time, temp, pressure, humidity, voc, light, noise, noisedba, battery, shake, cable, vocResistance, rssi);
	}

	private static int safeInt(Integer i) {
		return i == null ? 0 : i;
	}

	private static boolean safeBoolean(Boolean b) {
		return b == null ? false : b;
	}

	/**
	 * @param labels
	 *            the list of labels returned by the query
	 * @param values
	 *            the list of lists of values to map to those labels
	 * @return the decoded states
	 */
	public static List<State> parseState(final List<String> labels, final List<List<Object>> values) {
		final Map<ExpectedLabels, Integer> map = mapLabels(labels);

		final List<State> states = new ArrayList<>(values.size());

		for (final List<Object> state : values) {
			states.add(extractValues(state, map));
		}

		return states;
	}
}


/**
 * Copyright 2013-2014 Guoqiang Chen, Shanghai, China. All rights reserved.
 *
 * Email: subchen@gmail.com
 * URL: http://subchen.github.io/
 *
 * 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 jetbrick.reflect;

import java.lang.reflect.*;
import jetbrick.lang.ArrayUtils;

/**
 * 根据类型定义，获取泛型信息.
 *
 * @author Guoqiang Chen
 */
public final class TypeResolverUtils {

    /**
     * Returns raw class for given type. Use this method with both
     * regular and generic types.
     *
     * @param type - the type to convert
     * @return the closest class representing the given type
     * @see #getRawType(java.lang.reflect.Type, Class)
     */
    public static Class<?> getRawType(Type type) {
        return getRawType(type, null);
    }

    /**
     * Returns raw class for given type when implementation class is known
     * and it makes difference.
     *
     * @param type - given type
     * @param implClass - implementation clas
     * @return raw class
     * @see #resolveVariable(java.lang.reflect.TypeVariable, Class)
     */
    public static Class<?> getRawType(Type type, Class<?> implClass) {
        if (type instanceof Class) {
            return (Class<?>) type;
        }
        if (type instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) type;
            return getRawType(pType.getRawType(), implClass);
        }
        if (type instanceof WildcardType) {
            WildcardType wType = (WildcardType) type;

            Type[] lowerTypes = wType.getLowerBounds();
            if (lowerTypes.length > 0) {
                return getRawType(lowerTypes[0], implClass);
            }

            Type[] upperTypes = wType.getUpperBounds();
            if (upperTypes.length != 0) {
                return getRawType(upperTypes[0], implClass);
            }

            return Object.class;
        }
        if (type instanceof GenericArrayType) {
            Type genericComponentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> rawType = getRawType(genericComponentType, implClass);

            return Array.newInstance(rawType, 0).getClass();
        }
        if (type instanceof TypeVariable) {
            TypeVariable<?> varType = (TypeVariable<?>) type;
            if (implClass != null) {
                Type resolvedType = resolveVariable(varType, implClass);
                if (resolvedType != null) {
                    return getRawType(resolvedType, null);
                }
            }
            Type[] boundsTypes = varType.getBounds();
            if (boundsTypes.length == 0) {
                return Object.class;
            }
            return getRawType(boundsTypes[0], implClass);
        }
        return null;
    }

    /**
     * Resolves TypeVariable with given implementation class.
     *
     * @param variable - variable
     * @param implClass - implementation class
     * @return resolved type
     */
    public static Type resolveVariable(TypeVariable<?> variable, Class<?> implClass) {
        Class<?> rawType = getRawType(implClass, null);

        int index = ArrayUtils.indexOf(rawType.getTypeParameters(), variable);
        if (index >= 0) {
            return variable;
        }

        Class<?>[] interfaces = rawType.getInterfaces();
        Type[] genericInterfaces = rawType.getGenericInterfaces();

        for (int i = 0; i <= interfaces.length; i++) {
            Class<?> rawInterface;
            if (i < interfaces.length) {
                rawInterface = interfaces[i];
            } else {
                rawInterface = rawType.getSuperclass();
                if (rawInterface == null) {
                    continue;
                }
            }
            Type resolved = resolveVariable(variable, rawInterface);
            if (resolved instanceof Class || resolved instanceof ParameterizedType) {
                return resolved;
            }

            if (resolved instanceof TypeVariable) {
                TypeVariable<?> typeVariable = (TypeVariable<?>) resolved;
                index = ArrayUtils.indexOf(rawInterface.getTypeParameters(), typeVariable);
                if (index < 0) {
                    throw new IllegalArgumentException("Can't resolve type variable:" + typeVariable);
                }
                Type type = i < genericInterfaces.length ? genericInterfaces[i] : rawType.getGenericSuperclass();
                if (type instanceof Class) {
                    return Object.class;
                }
                if (type instanceof ParameterizedType) {
                    return ((ParameterizedType) type).getActualTypeArguments()[index];
                }
                throw new IllegalArgumentException("Unsupported type: " + type);
            }
        }
        return null;
    }

    public static Class<?> getComponentType(Type type) {
        return getComponentType(type, null, -1);
    }

    public static Class<?> getComponentType(Type type, Class<?> implClass) {
        return getComponentType(type, implClass, -1);
    }

    public static Class<?> getComponentType(Type type, int index) {
        return getComponentType(type, null, index);
    }

    /**
     * Returns the component type of the given type.
     * Returns null if given type does not have a single
     * component type. For example the following types all have the
     * component-type MyClass:
     *

     *
MyClass[]

     *
List<MyClass>

     *
Foo<? extends MyClass>

     *
Bar<? super MyClass>

     *
<T extends MyClass> T[]

     *

     *
     * Index represents the index of component type, when class supports more then one.
     * For example, Map<A, B> has 2 component types. If index is 0 or positive,
     * than it represents order of component type. If the value is negative, then it represents
     * component type counted from the end! Therefore, the default value of -1
     * always returns the last component type.
     *
     * @param type - type
     * @param implClass - implementation class
     * @param index - index of component type
     * @return component type
     */
    public static Class<?> getComponentType(Type type, Class<?> implClass, int index) {
        if (type instanceof Class) {
            Class<?> clazz = (Class<?>) type;
            if (clazz.isArray()) {
                return clazz.getComponentType();
            }
        } else if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            Type[] generics = pt.getActualTypeArguments();
            if (index < 0) {
                index = generics.length + index;
            }
            if (index < generics.length) {
                return getRawType(generics[index], implClass);
            }
        } else if (type instanceof GenericArrayType) {
            GenericArrayType gat = (GenericArrayType) type;
            return getRawType(gat.getGenericComponentType(), implClass);
        }
        return null;
    }

    /**
     * @see #getComponentType(java.lang.reflect.Type)
     */
    public static Class<?> getGenericSupertype(Class<?> type) {
        return getComponentType(type.getGenericSuperclass());
    }

    /**
     * Returns generic supertype for given class and 0-based index.
     * @see #getComponentType(java.lang.reflect.Type, int)
     */
    public static Class<?> getGenericSupertype(Class<?> type, int index) {
        return getComponentType(type.getGenericSuperclass(), null, index);
    }

}


/*
 * Created on Nov 5, 2003
 *
 *
 * ====================================================================
 *
 * The WYMIWYG Software License, Version 1.0
 *
 * Copyright (c) 2002-2003 WYMIWYG
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by WYMIWYG."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The name "WYMIWYG" or "WYMIWYG.org" must not be used to endorse
 *    or promote products derived from this software without prior written
 *    permission. For written permission, please contact wymiwyg@wymiwyg.org.
 *
 * 5. Products derived from this software may not be called
 *    "WYMIWYG" nor may "WYMIWYG" appear in their names
 *    without prior written permission of WYMIWYG.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL WYMIWYG OR ITS CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of WYMIWYG.  For more
 * information on WYMIWYG, please see http://www.WYMIWYG.org/.
 *
 * This licensed is based on The Apache Software License, Version 1.1,
 * see http://www.apache.org/.
 */

package org.wymiwyg.commons.util.dirbrowser;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author reto
 */
public class MultiPathNode implements PathNode {
	private static final Log log = LogFactory.getLog(MultiPathNode.class);

	private final PathNode[] nodes;

	/**
	 *
	 */
	public MultiPathNode(PathNode... nodes) {
		int i = 0;
		for (PathNode n : nodes) {
			if (n == null) {
				throw new IllegalArgumentException("Pathnode at position "+i+" is null");
			}
			i++;
		}
		this.nodes = nodes;
	}

	/**
	 * @see org.wymiwyg.commons.util.dirbrowser.PathNode#getSubPath(java.lang.String)
	 */
	public PathNode getSubPath(String requestPath) {
		List<PathNode> subNodeList = new ArrayList<PathNode>();
		for (int i = 0; i < nodes.length; i++) {
			PathNode subNode = nodes[i].getSubPath(requestPath);
			if (subNode != null) {
				subNodeList.add(subNode);
			}
		}
		PathNode[] subNodes = new PathNode[subNodeList.size()];
		subNodes = subNodeList.toArray(subNodes);
		return new MultiPathNode(subNodes);
	}

	/**
	 * @see org.wymiwyg.commons.util.dirbrowser.PathNode#isDirectory()
	 */
	public boolean isDirectory() {
		for (int i = 0; i < nodes.length; i++) {
			if (nodes[i].isDirectory()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @see org.wymiwyg.commons.util.dirbrowser.PathNode#list(org.wymiwyg.commons.util.dirbrowser.PathNameFilter)
	 */
	public String[] list(PathNameFilter filter) {
		Set resultList = new HashSet();
		for (int i = 0; i < nodes.length; i++) {
			String[] subList = nodes[i].list(filter);
			if (subList != null) {
				resultList.addAll(Arrays.asList(subList));
			}
		}
		return (String[]) resultList.toArray(new String[resultList.size()]);
	}
	/**
	 * @see org.wymiwyg.commons.util.dirbrowser.PathNode#list()
	 */
	public String[] list() {
		Set resultList = new HashSet();
		for (int i = 0; i < nodes.length; i++) {
			String[] subList = nodes[i].list();
			if (subList != null) {
				resultList.addAll(Arrays.asList(subList));
			}
		}
		return (String[]) resultList.toArray(new String[resultList.size()]);
	}

	/**
	 * @see org.wymiwyg.commons.util.dirbrowser.PathNode#getInputStream()
	 */
	public InputStream getInputStream() throws IOException {
		InputStream result = null;
		for (int i = 0; i < nodes.length; i++) {
			try {
				result = nodes[i].getInputStream();
			} catch (Exception e) {
				// nothing
			}
			if (result != null) {
				return result;
			}
		}
		return null;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("MultiPathNode of ");
		for (int i = 0; i < nodes.length; i++) {
			buffer.append(nodes[i]);
		}
		return buffer.toString();
	}

	/**
	 * @see org.wymiwyg.commons.util.dirbrowser.PathNode#getLength()
	 */
	public long getLength() {
		InputStream result = null;
		for (int i = 0; i < nodes.length; i++) {
			try {
				result = nodes[i].getInputStream();
			} catch (Exception e) {
				// nothing
			}

			if (result != null) {
				try {
					return nodes[i].getLength();
				} finally {
					try {
						result.close();
					} catch (IOException e1) {
						log.error("cloding inputstream", e1);
					}
				}
			}
		}
		return -1;
	}

	/**
	 * @return the longest common subpath of the nodes this MultiPathNOde consists of
	 */
	public String getPath() {
		String currentLongest = nodes[0].getPath();
		for (int i = 1; i < nodes.length; i++) {
			currentLongest = getLongestCommonSuffix(currentLongest, nodes[i].getPath());
		}
		return currentLongest;
	}

	private String getLongestCommonSuffix(String a, String b) {
		List<String> commonSections = new ArrayList<String>();
		String[] aSects = a.split("/");
		String[] bSects = b.split("/");
		for (int i = 1; i <= a.length(); i++) {
			if ((i > aSects.length) ||
				(i > bSects.length)) {
				break;
			}
			final String section = aSects[aSects.length - i];
			if (section.equals(bSects[bSects.length-i])) {
				commonSections.add(0, section);
			} else {
				break;
			}
		}
		StringBuffer buffer = new StringBuffer();
		boolean first = true;
		for (String string : commonSections) {
			if (!first) {
				buffer.append('/');
			} else {
				first = false;
			}
			buffer.append(string);
		}
		return buffer.toString();
	}

	/* (non-Javadoc)
	 * @see org.wymiwyg.commons.util.dirbrowser.PathNode#getLastModified()
	 */
	public Date getLastModified() {
		Date lastModified = null;
		for (int i = 0; i < nodes.length; i++) {
			PathNode node = nodes[i];
			Date currentLastModified = node.getLastModified();
			if (currentLastModified != null) {
				if ((lastModified == null) || (currentLastModified.after(lastModified))) {
					lastModified = currentLastModified;
				}
			}
		}
		return lastModified;
	}

	public boolean exists() {
		for (PathNode pathNode : nodes) {
			if (pathNode.exists()) {
				return true;
			}
		}
		return false;
	}
}