public class SqlBuilder {

    private DruidSqlParser sqlParser;

//    private ParamParser paramParser;

    private ParamBinder paramBinder;

    private SqlEntity entity;

    public SqlBuilder(){
        sqlParser = new DruidSqlParser();
        paramBinder = new ParamBinder();
        entity = new SqlEntity();
    }

    public SqlBuilder buildSql(String sql){
        sqlParser.parse(sql);
        entity.setSql(sql);
        entity.setPreparedSql(sqlParser.getPreparedSql());
        entity.setType(sqlParser.getType());
        return this;
    }

    public SqlBuilder buildParams(Object object,String name ){
        entity.addParam(paramBinder.reflect(object,name));
        return this;
    }
    public SqlBuilder buildParams(List objects, List<String> names){
        entity.addParam(paramBinder.reflect(objects,names));
        return this;
    }

    public SqlBuilder buildParams(Map<String,Object> params){
        entity.addParam(paramBinder.reflect(params));
        return this;
    }
    public SqlBuilder buildParams(Object object){
        entity.addParam(paramBinder.reflect(object,object.getClass()));
        return this;
    }

    /**
     * 重构SqlEntity,整理出需要的参数，将不在sql解析结果的参数抛弃掉
     * 这个方法要在sql解析后执行
     * @return
     */
    public SqlBuilder buildParams(){
        Map<String,Object> mapper = paramBinder.getMapper();
        List<String> paramNames = sqlParser.getSqlParamNames();
        entity.clear();
        for (String name:paramNames){
            for (Map.Entry<String,Object> entry:mapper.entrySet()){
                if (entry.getKey().equals(name)){
                    entity.addParam(name,entry.getValue());
                }
            }
        }
        return this;
    }

    public SqlEntity buildEntity(String bind){
        entity.setBinder(bind);
        return entity;
    }

    public SqlEntity buildEntity(SqlEntity entity){
        buildSql(entity.getSql());
        buildParams(entity.getParams());
        buildParams();
        buildEntity(entity.getBinder());
        return this.entity;
    }

    public SqlEntity getSqlEntity(){
        return entity;
    }

    public SqlType getType(){
        return entity.getType();
    }

    public String getPreparedSql(){
        return entity.getPreparedSql();
    }

    public Map<String,Object> getParameters(){
        return entity.getParams();
    }

    public void setType(SqlType type) {
        entity.setType(type);
    }

    public void setPreparedSql(String preparedSql) {
        entity.setPreparedSql(preparedSql);
    }

    public void setParams(List<String> params) {
        sqlParser.setSqlParamNames(params);
    }

    @Override
    public String toString() {
        return sqlParser.getOriginSql()+" \n "+sqlParser.getSqlParamNames()+" \n "+entity.getParams();
    }


    public static void main(String[] args) {
        Map<String,String> map = new LinkedHashMap<>();
        map.put("daaf","b");
        map.put("1","a");
        map.put("4","d");
        map.put("3","c");
        map.put("6","f");
        map.put("5","e");
        for (Map.Entry<String,String> entry:map.entrySet()){
            System.out.println("key:"+entry.getKey()+",value:"+entry.getValue());
        }
    }
} public class SqlMapCache implements Cache<String,SqlEntity> {

    private volatile Cache<String,SqlEntity> cache;

    public SqlMapCache(Cache<String,SqlEntity> cache) {
        this.cache = cache;
    }

//    public SqlEntity getSqlEntity(String namespace, String id){
//        Map<String,SqlEntity> statement = get(namespace);
//        if(statement == null){
//            return new SqlEntity();
//        }
//        return statement.get(id);
//    }

    public SqlEntity getSqlEntity(String namespace, String id,Object[] params){
        String key = hashEntity(namespace,id,params);
        return get(key);
    }
    public SqlEntity getSqlEntity(String namespace, String id,Collection params){
        return getSqlEntity(namespace,id,params.toArray());
    }

    public void addSqlEntity(SqlEntity sqlEntity){
        String key = null;
        if (sqlEntity.getParams() != null){
            Collection col = sqlEntity.getParams().values();
            key = hashEntity(sqlEntity.getPrefix()
                    ,sqlEntity.getSuffix(),col.toArray());
        }else{
            key = hashEntity(sqlEntity.getPrefix()
                    ,sqlEntity.getSuffix(),new Object[0]);
        }
        set(key,sqlEntity);
    }

    public boolean isHit(String namespace,String id,Object[] params){
        String key = hashEntity(namespace,id,params);
        return isHit(key);
    }
    public boolean isHit(String namespace,String id,Collection params){
        String key = hashEntity(namespace,id,params.toArray());
        return isHit(key);
    }

    @Override
    public SqlEntity get(String key) {
        return cache.get(key);
    }

    @Override
    public void set(String key, SqlEntity value) {
        cache.set(key,value);
    }

    @Override
    public SqlEntity remove(String key) {
        return cache.remove(key);
    }

    @Override
    public boolean isHit(String key) {
        return cache.isHit(key);
    }

    private String hashEntity(String namespace,String id,Object[] params){
        StringBuffer keyBuffer = new StringBuffer();
        keyBuffer.append(CodecUtils.sha1(namespace))
                .append(CodecUtils.sha1Obj(id))
                .append(CodecUtils.sha1Obj(params));
        return CodecUtils.sha1(keyBuffer.toString());
    }

    private String hashBinder(String namespace){
        return CodecUtils.sha1(namespace);
    }

    private String hashParams(Collection params){
        return CodecUtils.sha1Obj(params);
    }
    private String hashParams(Object[] params){
        return CodecUtils.sha1Obj(params);
    }
    @Override
    public void clear() {
        cache.clear();
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        String[] array = list.toArray(new String[list.size()]);
        System.out.println(array.length);
    }
} public class SqlMapCacheFactory extends AbstractCacheFactory<SqlMapCache>{
    private static SqlMapCacheFactory sqlMapCacheFactory = new SqlMapCacheFactory();

    private volatile SqlMapCache sqlMapCache;

    public static SqlMapCacheFactory getInstance() {
        synchronized (SqlMapCacheFactory.class){
            if (sqlMapCacheFactory == null){
                synchronized (SqlMapCacheFactory.class){
                    sqlMapCacheFactory = new SqlMapCacheFactory();
                }
            }
        }
        return sqlMapCacheFactory;
    }

    private SqlMapCacheFactory() {
    }

    @Override
    public SqlMapCache createCache(CacheEntity entity) {
        if (sqlMapCache == null){
            if (entity != null &&entity.isEnable()){
                switch (entity.getMode()){
                    case FIFO:
                        sqlMapCache = new SqlMapCache(new FIFOCache<>(entity.getSize()));
                        return sqlMapCache;
                    case LRU:
                        sqlMapCache = new SqlMapCache(new LRUCache<>(entity.getSize()));
                        return sqlMapCache;
                    case TIMEOUT:
                        sqlMapCache = new SqlMapCache(new TimeOutCache<>(entity.getSize()));
                        return sqlMapCache;
                    default:
                        sqlMapCache = new SqlMapCache(new DefaultCache<>());
                        return sqlMapCache;
                }
            }
            sqlMapCache = new SqlMapCache(new DefaultCache<>());
        }
        return sqlMapCache;
    }
} public class SqlEntity {

    private String sql;

    private String preparedSql;

    private Map<String, Object> params;

    private SqlType type;

    //sql语句的key,通常是 mapper接口名@方法名
    private String binder;

    public SqlEntity() {
        params = new LinkedHashMap<>();
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public String getPreparedSql() {
        return preparedSql;
    }

    public void setPreparedSql(String preparedSql) {
        this.preparedSql = preparedSql;
    }

    public SqlType getType() {
        return type;
    }

    public void setType(SqlType type) {
        this.type = type;
    }

    public Map<String, Object> getParams() {
        return params;
    }

    public void setParams(Map<String, Object> params) {
        this.params = params;
    }

    public void addParam(String name, Object value) {
        params.put(name,value);
    }
    public void addParam(Map<String,Object> params) {
        this.params.putAll(params);
    }

    public void delParam(String name){
        params.remove(name);
    }

    public void clear(){
        params.clear();
    }

    public String getBinder() {
        return binder;
    }

    public void setBinder(String binder) {
        this.binder = binder;
    }

    public String getPrefix() {
        String[] segements = binder.split(BIND_SEPARATOR);
        return segements[0];
    }

    public String getSuffix() {
        String[] segements = binder.split(BIND_SEPARATOR);
        return segements[1];
    }

    @Override
    public String toString() {
        return "SqlEntity{" +
                "params=" + params +
                ", binder='" + binder + '\'' +
                '}';
    }
} public class JSContext {

    public static final String CONTEXT = "ctx";

    public static final String WHERE = "where";
    public static final String WHERE_CONTENT = " where 1 = 1 ";
    //默认namespace为空，需要用户自己设置
    public static final String NAMESPACE = "namespace";

    public Map<String,Map<String ,Object>> defaultVars;

    public JSContext(){
        defaultVars = new HashMap<>();
        init();
    }

    private void init(){
        Map<String,Object> vars = new HashMap<>();
        vars.put(WHERE,WHERE_CONTENT);
        vars.put(NAMESPACE,"");
        defaultVars.put(CONTEXT,vars);
    }

    public Map<String,Object> getCtx(){
        return defaultVars.get(CONTEXT);
    }
} public class Configuration {

    public static final String JADE = "jade";
    public static final String INTERFACE = "interface";
    public static final String PACKAGE = "package";
    public static final String MAPPER = "mapper";
    public static final String LOCATION = "location";
    public static final String DATASOURCECLASS = "class";
    public static final String DATASOURCE = "datasource";
    public static final String CACHE = "sqlMapCache";
    public static final String ENABLE = "enable";
    public static final String MODE = "mode";
    public static final String SIZE = "size";
    public static final String LEVEL = "level";

    //存放datasource内部配置db信息用
    public static final String LIST = "list";

    public static final String CLASSPATH = "classpath:";
    public static final String JS = ".js";
    public static final String JSON = ".json";

    private volatile Map<String, Map<String, String>> mappedSqls = new HashMap<>();

    private volatile Map<String,Object> configMap;

    //配置文件所在路径
    private String configFilePath;

    private DataSource dataSource;

    private List<Reader> sqlMapperReaders;
    private List<File> sqlMapperFiles;
    private SqlMapCache sqlMapCache;
    private TemplateCache templateCache;
    public Configuration(String configPath,Map<String,Object> content) {
        this.configFilePath = configPath;
        this.configMap = content;
        this.sqlMapCache = SqlMapCacheFactory.getInstance()
                .createCache(getCacheEntity());
        this.templateCache = TemplateCacheFactory.getInstance()
                .createCache(getCacheEntity());
        sqlMapperReaders = new ArrayList<>();
        sqlMapperFiles = new ArrayList<>();
        initMapper();
        initDataSource();
        initMapperTemplate();
    }

    /**
     * 从配置文件路径读取mapper配置文件信息（注意只能是精确到目录不能是文件）
     * mapper未配置时，不加载该配置
     * 支持配置路径如下：
     * 1.classpath:mapper/                              (走classpath)
     * 2./opt/web/webapp/admin/WEB-INF/classes/mapper/  (走绝对路径)
     * 3.mapper/                                        (走当前配置文件的相对路径,
     *                                                  如果配置文件在resources/config,
     *                                                  则mapper目录就是resources/config/mapper/)
     */
    private void initMapper(){
        try {
            String basePath = getMapperLocation();
            if (StringUtils.isEmpty(basePath)){
                return;
            }
            File baseDir = new File(basePath);
            List<File> mapperFiles = null;
            if (basePath.startsWith(CLASSPATH)){
                //去掉classpath:关键字
                basePath = basePath.replace(CLASSPATH,"");
                //连接classpath目录和用户输入目录
                basePath = ResourcesUtil.getResources(basePath);
                //遍历相对路径下的所有目录中的文件
                mapperFiles = FileUtils.listFileRecursive(basePath);
                for (File file : mapperFiles){
                    String filename = file.getPath();
                    if (!filename.endsWith(JS)&&!filename.endsWith(JSON)){
                        continue;
                    }
                    System.out.println("config filename:"+filename);
                    //获得具体文件在classpath下的相对路径
                    String relativePath = filename.substring(MapperScanner.CLASSPATH.length(),filename.length());
                    Reader reader = new InputStreamReader(
                            getClass().getClassLoader().getResourceAsStream(relativePath));
                    if (filename.endsWith(JS)){
                        sqlMapperReaders.add(reader);
                        continue;
                    }else if (filename.endsWith(JSON)){
                        sqlMapperFiles.add(file);
                    }
                }
                return ;
            }else if(baseDir.isAbsolute()){
                mapperFiles = FileUtils.listFileRecursive(baseDir);
            }else{
                //prefixPath 就是最底层目录路径
                String prefixPath = FileUtils.getBottomDir(configFilePath);
                basePath = prefixPath + basePath;
                mapperFiles = FileUtils.listFileRecursive(basePath);
            }
            for (File file:mapperFiles){
                if (!file.getPath().endsWith(JS)&&!file.getPath().endsWith(JSON)){
                    continue;
                }
                FileReader reader = new FileReader(file);
                if (file.getName().endsWith(JS)){
                    sqlMapperReaders.add(reader);
                    continue;
                }else if (file.getName().endsWith(JSON)){
                    sqlMapperFiles.add(file);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 规则同mapper
     */
    private void initDataSource(){
        Map<String,Object> mapper = (Map<String, Object>) configMap.get(DATASOURCE);
        Properties prop = (Properties) mapper.get(LIST);
        String classname = String.valueOf(mapper.get(DATASOURCECLASS));
        try {
            if (prop == null){
                String baseFilePath = String.valueOf(mapper.get(LOCATION));
                File baseFile = new File(baseFilePath);
                if (baseFilePath.startsWith(CLASSPATH)){
                    //去掉classpath:关键字
                    baseFilePath = baseFilePath.replace(CLASSPATH,"");
                    //连接classpath目录和用户输入目录
                    baseFilePath = ResourcesUtil.getResources(baseFilePath);
                }else if(!baseFile.isAbsolute()){
                    //prefixPath 就是最底层目录路径
                    String prefixPath = FileUtils.getBottomDir(configFilePath);
                    baseFilePath = prefixPath + baseFilePath;
                }
                prop.load(new FileInputStream(baseFilePath));
            }


            //引用外部的数据库连接池
            PoolConfig config = PoolConfig.config(prop);
            if (StringUtils.isNotEmpty(classname)&&!classname.equals(DBCPool.class.getName())){
                Object dataSource = ReflectUtils.newInstance(classname);
                ReflectUtils.setter(dataSource, ConnConfig.DRIVERNAME,prop.get(ConnConfig.DRIVERNAME));
                ReflectUtils.setter(dataSource, ConnConfig.URL,prop.get(ConnConfig.URL));
                ReflectUtils.setter(dataSource, ConnConfig.PASSWORD,prop.get(ConnConfig.PASSWORD));
                ReflectUtils.setter(dataSource, ConnConfig.USERNAME,prop.get(ConnConfig.USERNAME));
                ReflectUtils.setter(dataSource, PoolConfig.INITIAL_SIZE, config.getMaxIdle());
                ReflectUtils.setter(dataSource, PoolConfig.MINIDLE, config.getMinIdle());
                ReflectUtils.setter(dataSource, PoolConfig.MAXACTIVE, config.getMaxActive());
                ReflectUtils.setter(dataSource, PoolConfig.MAXWAIT, config.getMaxWait());
                this.dataSource = (DataSource) dataSource;
                return ;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.dataSource = DBCPoolFactory.newConfigedDBCPool(prop);
    }

    private void initMapperTemplate(){
        for (File file:sqlMapperFiles){
            try {
                String content = FileUtils.cat(file);
                Map<String,Object> jsonMap = JsonUtils.json2Map(content);
                for (String namespace:jsonMap.keySet()){
                    templateCache.set(namespace,content);
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public String getPacakge(){
        Map<String,Object> pkg = (Map<String, Object>) configMap.get(INTERFACE);
        if (pkg == null){
            return null;
        }
        return pkg.get(PACKAGE)+"";
    }

    public String getMapperLocation(){
        Map<String,Object> mapper = (Map<String, Object>) configMap.get(MAPPER);
        if (mapper == null){
            return null;
        }
        return String.valueOf(mapper.get(LOCATION));
    }

    /**
     * 支持其他第三方连接池（配置文件信息和本框架不同）
     * @return
     */
    public DataSource getDataSource(){
        return this.dataSource;
    }

    /**
     * 之前add过后不会不命中
     * @param namespace
     * @param id
     * @param params
     * @return
     */
    public SqlEntity getMappedSql(String namespace, String id,Collection params) {
        return sqlMapCache.getSqlEntity(namespace,id,params.toArray());
    }

    /**
     * 从所有js文件中挑选符合namespace的那个文件，得到sql实体，并保存
     * @param sqlEntity
     */
    public void parseMappedSql(SqlEntity sqlEntity){
        if (sqlMapCache.isHit(sqlEntity.getPrefix()
                ,sqlEntity.getSuffix(),sqlEntity.getParams().values())){
            return ;
        }
//        String content = templateCache.get(sqlEntity.getPrefix());
//        TemplateParser parser = new TemplateParser(content,this);
//        parser.parse(sqlEntity);
        for (Reader reader:sqlMapperReaders){
            JSParser parser = new JSParser(reader,this);
            parser.parse(sqlEntity);
        }
    }

    private boolean enableCache(){
        Map<String,Object> cache = (Map<String, Object>) configMap.get(CACHE);
        if (cache==null){
            return false;
        }
        Object value = cache.get(ENABLE);
        if (value !=null){
            if (StringUtils.isEmpty(String.valueOf(value))){
                return false;
            }
        }
        return Boolean.valueOf(String.valueOf(value));
    }

    /**
     * 缓存大小为0表示不使用缓存
     * @return
     */
    private int getCacheSize(){
        Map<String,Object> cache = (Map<String, Object>) configMap.get(CACHE);
        if (cache==null){
            return 0;
        }
        Object value = cache.get(SIZE);
        if (value !=null){
            if (StringUtils.isEmpty(String.valueOf(value))){
                return 0;
            }
        }
        int size = Integer.parseInt(String.valueOf(value));
        return size<=0?0:size;
    }

    private CacheMode getCacheMode(){
        Map<String,Object> cache = (Map<String, Object>) configMap.get(CACHE);
        if (cache==null){
            return null;
        }
        Object value = cache.get(MODE);
        if (value !=null){
            if (StringUtils.isEmpty(String.valueOf(value))){
                return null;
            }
        }
        return CacheMode.valueOf(String.valueOf(value).toUpperCase());
    }

    public CacheEntity getCacheEntity(){
        CacheEntity entity = new CacheEntity(
                getCacheSize(),getCacheMode(),enableCache());
        return entity;
    }
} public class CodecUtils {

    public static String hash(byte[] bytes,String encName){
        MessageDigest md = null;
        String strDes = null;
        try {
            if (encName == null || encName.isEmpty()) {
                encName = "MD5";
            }
            md = MessageDigest.getInstance(encName);
            md.update(bytes);
            strDes = bytes2Hex(md.digest()); //to HexString
        } catch (NoSuchAlgorithmException e) {
            System.out.println("Invalid algorithm.");
            return null;
        }
        return strDes;
    }

    public static String sha1(byte[] bytes){
        return hash(bytes,SHA1.name);
    }

    public static String md5(byte[] bytes){
        return hash(bytes, MD5.name);
    }
    public static String md5(String key){
        return hash(key,MD5.name);
    }

    public static String sha1(String key){
        return hash(key,SHA1.name);
    }

    public static String sha1Obj(Object obj){
        if (obj == null){
            return "";
        }
        if (obj instanceof Map){
            Map map = (Map) obj;
            Set<Map.Entry> entrySet = map.entrySet();
            if (CollectionUtils.isEmpty(entrySet)){
                return "";
            }
            StringBuffer buffer = new StringBuffer();
            for (Map.Entry entry : entrySet){
                buffer.append(sha1Obj(entry.getKey()));
                buffer.append(sha1Obj(entry.getValue()));
            }
            return sha1(buffer.toString());
        }
        if (Collection.class.isAssignableFrom(obj.getClass())){
            Collection col = (Collection) obj;
            if (CollectionUtils.isEmpty(col)){
                return "";
            }
            StringBuffer buffer = new StringBuffer();
            for (Object o:col){
                buffer.append(sha1Obj(o));
            }
            return sha1(buffer.toString());
        }
        if (obj.getClass().isArray()){
            Object[] objs = (Object[]) obj;
            if (objs.length == 0){
                return"";
            }
            StringBuffer buffer = new StringBuffer();
            for (Object o:objs){
                buffer.append(sha1Obj(o));
            }
            return sha1(buffer.toString());
        }
        return sha1(String.valueOf(obj.hashCode()));
    }

    public static String hash(String strSrc, String encName) {
        return hash(strSrc.getBytes(),encName);
    }

    public static long hash(String key) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance(SHA1.name);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        md5.update(key.getBytes());
        byte[] bKey = md5.digest();
        long res = ((long) (bKey[3] & 0xFF) << 24) | ((long) (bKey[2] & 0xFF) << 16) | ((long) (bKey[1] & 0xFF) << 8)
                | (long) (bKey[0] & 0xFF);
        return res & 0xffffffffL;
    }

    public static byte[] hmacSHA1(byte[] data, String key) {
        SecretKey secretKey;
        byte[] bytes = null;
        try {
            secretKey = new SecretKeySpec(decodeBase64(key), HMACSHA1.name);
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
            bytes = mac.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bytes;
    }

    public static String encodeBase64(byte[] key) {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    /**
     * BASE64 解密
     * @param key 需要解密的字符串
     * @return 字节数组
     * @throws Exception
     */
    public static byte[] decodeBase64(String key) {
        try {
            return (new BASE64Decoder()).decodeBuffer(key);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String bytes2Hex(byte[] bts) {
        StringBuffer des = new StringBuffer();
        String tmp = null;
        for (int i = 0; i < bts.length; i++) {
            tmp = (Integer.toHexString(bts[i] & 0xFF));
            if (tmp.length() == 1) {
                des.append("0");
            }
            des.append(tmp);
        }
        return des.toString();

    }

    public enum HashType{
        SHA1("SHA-1"),MD5("MD5"),SHA256("SHA-256"),HMACSHA1("HmacSHA1");
        public String name;

        HashType(String name) {
            this.name = name;
        }
    }

    public static void main(String[] args) {
        List<String> param = new ArrayList<>();
        param.add("1");
        param.add("2");
        param.add("3");
        System.out.println("collection:"+sha1Obj(param));
        System.out.println("array:"+sha1Obj(param.toArray()));
    }
} public class StringUtils {

    private static final Random RANDOM = new Random();

    /**
     * 字符串是否是空
     *
     * @param src
     * @return
     */
    public static boolean isEmpty(final String src) {
        return src == null || src.length() == 0;
    }
    public static boolean isNotEmpty(final String src) {
        return !isEmpty(src);
    }

    /**
     * 字符串是否是空白的
     *
     * @param src
     * @return
     */
    public static boolean isBlank(final String src) {
        return trimToEmpty(src).length() == 0;
    }

    /**
     * 字符串头部添加省略符
     *
     * @param src
     * @param begin
     * @param ellipsisLength
     * @return
     */
    public static String headAbbreviate(final String src, int begin, int ellipsisLength) {
        final int length = src.length();
        if (src == null || src.isEmpty() || begin > length - ellipsisLength) {
            return src;
        }
        StringBuffer buffer = new StringBuffer();
        String ellipsis = "...";
        if (ellipsisLength > 3) {
            for (int index = 0; index < ellipsisLength - 3; index++) {
                buffer.append(ellipsis.concat("."));
            }
        }
        String newStr = src.substring(0, begin) + buffer.toString();
        return newStr;
    }

    /**
     * 字符串尾部添加省略符
     *
     * @param end
     * @param ellipsisLength
     * @param src
     * @return
     */
    public static String tailAbbreviate(final String src ,int end, int ellipsisLength) {
        final int length = src.length();
        if (src == null || src.isEmpty() || length - end < ellipsisLength) {
            return src;
        }
        StringBuffer buffer = new StringBuffer();
        String ellipsis = "...";
        if (ellipsisLength > 3) {
            for (int index = 0; index < ellipsisLength - 3; index++) {
                buffer.append(ellipsis.concat("."));
            }
        }
        String newStr = buffer.toString() + src.substring(length - end, length);
        return newStr;
    }

    /**
     * 字符串首字母大写
     *
     * @param str
     * @return
     */
    public static String capitalize(final String str) {
        int length = str.length();
        if (str == null || length == 0) {
            return str;
        }
        char first = str.charAt(0);
        if (Character.isTitleCase(first)) {
            return str;
        }
        return String.valueOf(Character.toTitleCase(first))
                .concat(str.substring(1));
    }

    /**
     * 给字符串追加内容
     *
     * @param src
     * @param plugin
     * @return
     */
    public static String append(final String src, String plugin) {
        return src.concat(plugin);
    }

    /**
     * 给字符串前面添加内容
     *
     * @param src
     * @param plugin
     * @return
     */
    public static String preAppend(final String src, String plugin) {
        return plugin.concat(src);
    }

    /**
     * 用指定的标识符将字符串居中
     *
     * @param src
     * @param pad
     * @param symbol
     * @return
     */
    public static String center(final String src, int pad, String symbol) {
        if (src == null || src.isEmpty() || pad <= 0) {
            return src;
        }
        String symbolDump = "";
        for (int index = 0; index < pad; index++) {
            symbolDump += symbol;
        }
        return append(preAppend(src, symbolDump), symbolDump);
    }

    /**
     * 将字符串居中
     *
     * @param src
     * @param pad
     * @return
     */
    public static String center(final String src, int pad) {
        return center(src, pad, " ");
    }

    /**
     * 用指定的标签组给字符串打标签
     *
     * @param src
     * @param frontSymbol
     * @param backSymbol
     * @return
     */
    public static String surround(final String src, final String frontSymbol, final String backSymbol) {
        if (src == null || src.isEmpty()) {
            return src;
        }
        return append(preAppend(src, frontSymbol), backSymbol);
    }

    /**
     * 去除字符串中所有空白符
     *
     * @param src 字符串不能为null
     * @return
     */
    public static String trimAll(final String src) {
        String[] slices = src.split(" ");
        StringBuffer buffer = new StringBuffer();
        for (String str : slices) {
            buffer.append(str);
        }
        return buffer.toString();
    }

    /**
     * 去除字符串中所有的空白符（包括null）
     *
     * @param src
     * @return
     */
    public static String trimToEmpty(final String src) {
        return src == null ? "" : trimAll(src);
    }

    /**
     * 在源字符串指定位置插入字符串
     *
     * @param src
     * @param plugin
     * @param index
     * @return
     */
    public static String insert(final String src, String plugin, int index) {
        if (index == 0) {
            return preAppend(src, plugin);
        }
        if (index == src.length()) {
            System.out.println("append!!");
            return append(src, plugin);
        }
        String midstr = src.substring(0, index - 1);
        StringBuffer buffer = new StringBuffer(midstr);
        return buffer.append(plugin).append(
                src.substring(index - 1, src.length())).toString();
    }

    /**
     * @param str1
     * @param str2
     * @return
     */
    public static String difference(final String str1, String str2) {
        if (str1 == null) {
            return str2;
        }
        if (str2 == null) {
            return str1;
        }
        final int at = indexOfDifference(str1, str2);
        if (at == -1) {
            return "";
        }
        String diff = str2.substring(at);
        if (diff.isEmpty()) {
            diff = difference(str2, str1);
        }
        return diff;
    }

    /**
     * 两个字符串开始出现不同的临界点
     *
     * @param src1
     * @param src2
     * @return
     */
    private static int indexOfDifference(final String src1, final String src2) {
        if (src1 == src2) {
            return -1;
        }
        if (src1 == null || src2 == null) {
            return 0;
        }
        int i;
        for (i = 0; i < src1.length() && i < src2.length(); ++i) {
            if (src1.charAt(i) != src2.charAt(i)) {
                break;
            }
        }
        if (i < src2.length() || i < src1.length()) {
            return i;
        }
        return -1;
    }

    /**
     * 字符串翻转
     *
     * @param str
     * @return
     */
    public static String reverse(final String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return new StringBuffer(str).reverse().toString();
    }

    /**
     * 是否是回文
     *
     * @param src
     * @return
     */
    public static boolean isPalindrom(final String src) {
        return new StringBuffer(src).reverse()
                .toString().equals(src);
    }

    /**
     * 是否是回文（忽略大小写）
     *
     * @param src
     * @return
     */
    public static boolean isPalindromIgnoreCase(final String src) {
        return new StringBuffer(src).reverse()
                .toString().equalsIgnoreCase(src);
    }

    /**
     * 配合kmp算法获取下一个数组
     *
     * @param t
     * @return
     */
    private static int[] next(char[] t) {
        int[] next = new int[t.length];
        next[0] = -1;
        int i = 0;
        int j = -1;
        while (i < t.length - 1) {
            if (j == -1 || t[i] == t[j]) {
                i++;
                j++;
                if (t[i] != t[j]) {
                    next[i] = j;
                } else {
                    next[i] = next[j];
                }
            } else {
                j = next[j];
            }
        }
        return next;
    }

    /**
     * KMP匹配字符串
     *
     * @param s 主串
     * @param t 模式串
     * @return 若匹配成功，返回下标，否则返回-1
     */
    public static int kmp(String s, String t) {
        int[] next = next(t.toCharArray());
        int i = 0;
        int j = 0;
        while (i <= s.length() - 1 && j <= t.length() - 1) {
            if (j == -1 || s.charAt(i) == t.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        if (j < t.length()) {
            return -1;
        } else
            return i - t.length(); // 返回模式串在主串中的头下标
    }

    /**
     * 用分隔符把字符串数组拼接成按分隔符分割的字符串
     *
     * @param srcs
     * @param separator
     * @param edge      是否把分隔符添加到边界
     * @return
     */
    public static String join(final String[] srcs, String separator, boolean edge) {
        StringBuffer buffer = new StringBuffer();
        int index = 0;
        for (String src : srcs) {
            if (edge && index == 0) {
                buffer.append(separator);
            }
            buffer.append(src);
            if (index != srcs.length - 1 ||
                    (index == srcs.length - 1 && edge))
                buffer.append(separator);
            index++;
        }
        return buffer.toString();
    }

    /**
     * 用分隔符把字符串数组拼接成按分隔符分割的字符串
     *
     * @param srcs
     * @param separator
     * @return
     */
    public static String join(final String[] srcs, String separator) {
        return join(srcs, separator, false);
    }

    /**
     * 关键字打标签
     *
     * @param src
     * @param key
     * @param frontTag
     * @param backTag
     * @return
     */
    public static String tagKeyWord(final String src, String key, String frontTag, String backTag) {
        StringBuffer buffer = new StringBuffer();
        int begin = 0;
        int end = 0;
        String copy = src;
        while (true) {
            begin = kmp(copy, key);
            if (begin == -1) {
                buffer.append(copy);
                break;
            }
            end = begin + key.length();
            String frontSegment = src.substring(0, begin);
            copy = src.substring(end);
            buffer.append(frontSegment)
                    .append(frontTag)
                    .append(key)
                    .append(backTag);
        }
        return buffer.toString();
    }

    /**
     * 按模式将字符串剥去出来，仅限开头结尾被包裹的字符串
     * @param src
     * @param beginToken
     * @param endToken
     * @return
     */
    public static String strip(final String src,final String beginToken,final String endToken){
        int beginIndex = beginToken.length();
        int endIndex = endToken.length();
        if (!src.contains(beginToken)){
            return src;
        }
        return src.substring(beginIndex,src.length() - endIndex);
    }

    public static String strip(final String src,final String beginToken){
        int beginIndex = beginToken.length();
        return src.substring(beginIndex,src.length() - beginIndex);
    }


    private static String random(int count, int start, int end, final boolean letters, final boolean numbers,
                                 final char[] chars, final Random random) {
        if (count == 0) {
            return "";
        } else if (count < 0) {
            throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
        }
        if (chars != null && chars.length == 0) {
            throw new IllegalArgumentException("The chars array must not be empty");
        }

        if (start == 0 && end == 0) {
            if (chars != null) {
                end = chars.length;
            } else {
                if (!letters && !numbers) {
                    end = Integer.MAX_VALUE;
                } else {
                    end = 'z' + 1;
                    start = ' ';
                }
            }
        } else {
            if (end <= start) {
                throw new IllegalArgumentException("Parameter end (" + end + ") must be greater than start (" + start + ")");
            }
        }

        final char[] buffer = new char[count];
        final int gap = end - start;

        while (count-- != 0) {
            char ch;
            if (chars == null) {
                ch = (char) (random.nextInt(gap) + start);
            } else {
                ch = chars[random.nextInt(gap) + start];
            }
            if (letters && Character.isLetter(ch)
                    || numbers && Character.isDigit(ch)
                    || !letters && !numbers) {
                if (ch >= 56320 && ch <= 57343) {
                    if (count == 0) {
                        count++;
                    } else {
                        // low surrogate, insert high surrogate after putting it in
                        buffer[count] = ch;
                        count--;
                        buffer[count] = (char) (55296 + random.nextInt(128));
                    }
                } else if (ch >= 55296 && ch <= 56191) {
                    if (count == 0) {
                        count++;
                    } else {
                        // high surrogate, insert low surrogate before putting it in
                        buffer[count] = (char) (56320 + random.nextInt(128));
                        count--;
                        buffer[count] = ch;
                    }
                } else if (ch >= 56192 && ch <= 56319) {
                    // private high surrogate, no effing clue, so skip it
                    count++;
                } else {
                    buffer[count] = ch;
                }
            } else {
                count++;
            }
        }
        return new String(buffer);
    }

    private static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers) {
        return random(count, start, end, letters, numbers, null, RANDOM);
    }

    private static String random(final int count, final boolean letters, final boolean numbers) {
        return random(count, 0, 0, letters, numbers);
    }

    public static String randomString(final int count) {
        return random(count, 32, 127, false, false);
    }

    public static String randomNumeric(final int count) {
        return random(count, false, true);
    }

    public static String uuid() {
        return UUID.randomUUID().toString();
    }

    public static void main(String[] args) {
//        System.out.println(tagKeyWord("this book told about javaweb programming","java","<br>","</br>"));
//        System.out.println(kmp("this book told about java programming","java"));
//        String src1 = "this book told about java programming";
//        String src2 = "this book told about C++ programming";
//        String key = "java";
//        String frontSegement = src.substring(0,21);;
//        System.out.println(frontSegement);
//        System.out.println(src.replace("java","c++"));
//        String[] languages = new String[]{"java", "c++", "python"};
//        System.out.println(join(languages, File.separator));
//        System.out.println(indexOfDifference(src1,src2));
//        System.out.println(capitalize(src2));
        String sql = "select * from user where age = #user.age# and curse_id = #user.curseId#";
        System.out.println(strip("$user.id ","$",""));
//        System.out.println(sql.substring(0,31));
//        System.out.println(sql.indexOf("##"));

    }
} public static final String CONTEXT = "ctx"; public static final String NAMESPACE = "namespace";
import org.easyarch.slardar.build.SqlBuilder;
import org.easyarch.slardar.cache.SqlMapCache;
import org.easyarch.slardar.cache.factory.SqlMapCacheFactory;
import org.easyarch.slardar.entity.SqlEntity;
import org.easyarch.slardar.parser.script.JSContext;
import org.easyarch.slardar.session.Configuration;
import org.easyarch.slardar.utils.CodecUtils;
import org.easyarch.slardar.utils.StringUtils;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import static org.easyarch.slardar.parser.script.JSContext.CONTEXT;
import static org.easyarch.slardar.parser.script.JSContext.NAMESPACE;
package org.easyarch.slardar.parser;




/**
 * Description :
 * Created by xingtianyu on 17-1-27
 * 下午6:38
 * description:
 */

public class JSParser extends ParserAdapter<SqlEntity> {

    public static final String JAVASCRIPT = "javascript";

    private ScriptEngineManager engineManager ;

    private SqlBuilder sqlBuilder;

    private Reader reader;

    private JSContext ctx;

    private static Map<String,SqlEntity> sqlValues = new HashMap<>();
    private static Map<String,ScriptEngine> jsFunctions = new HashMap<>();
    private SqlMapCache cache;
    public JSParser(Reader reader,Configuration configuration){
        engineManager = new ScriptEngineManager();
        this.sqlBuilder = new SqlBuilder();
        this.reader = reader;
        ctx = new JSContext();
        cache = SqlMapCacheFactory.getInstance().createCache(
                configuration.getCacheEntity());
    }

    @Override
    public void parse(SqlEntity entity) {
        String entityHash = hashEntity(entity);
        SqlEntity se = sqlValues.get(entityHash);
        if (se != null){
            return ;
        }
        try {
            ScriptEngine engine = jsFunctions.get(entity.getPrefix());
            jsFunctions.put(entityHash,engine);
            String namespace = null;
            if (engine == null){
                engine = engineManager.getEngineByName(JAVASCRIPT);
                engine.put(JSContext.CONTEXT,ctx.getCtx());
                engine.eval(reader);
                jsFunctions.put(entity.getPrefix(),engine);
            }
            namespace = String.valueOf(((Map<String,Object>)engine.get(CONTEXT)).get(NAMESPACE));
            if (StringUtils.isEmpty(namespace)){