/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */
package goja.plugins.shiro;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import com.jfinal.config.Routes;
import com.jfinal.core.ActionKey;
import com.jfinal.core.Controller;
import com.jfinal.plugin.IPlugin;

import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

import static goja.core.StringPool.SLASH;

/**
 * Shiro插件，启动时加载所有Shiro访问控制注解。
 *
 * @author dafei
 */
@SuppressWarnings("unchecked")
public class ShiroPlugin implements IPlugin {

    /**
     * Shiro的几种访问控制注解
     */
    private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES = new Class[]{
            RequiresPermissions.class, RequiresRoles.class, RequiresUser.class,
            RequiresGuest.class, RequiresAuthentication.class};

    /**
     * 路由设定
     */
    private final Routes routes;

    /**
     * 构造函数
     *
     * @param routes 路由设定
     */
    public ShiroPlugin(Routes routes) {
        this.routes = routes;
    }

    /**
     * 停止插件
     */
    @Override
    public boolean stop() {
        return true;
    }

    /**
     * 启动插件
     */
    @Override
    public boolean start() {
        Set<String> excludedMethodName = buildExcludedMethodName();
        ConcurrentMap<String, AuthzHandler> authzMaps = Maps.newConcurrentMap();
        //逐个访问所有注册的Controller，解析Controller及action上的所有Shiro注解。
        //并依据这些注解，actionKey提前构建好权限检查处理器。
        final List<Routes.Route> routeItemList = routes.getRouteItemList();
        for (Routes.Route route : routeItemList) {
            final Class<? extends Controller> controllerClass = route.getControllerClass();

            String controllerKey = route.getControllerKey();

            // 获取Controller的所有Shiro注解。
            List<Annotation> controllerAnnotations = getAuthzAnnotations(controllerClass);
            // 逐个遍历方法。
            Method[] methods = controllerClass.getMethods();
            for (Method method : methods) {
                //排除掉Controller基类的所有方法，并且只关注没有参数的Action方法。
                if (!excludedMethodName.contains(method.getName())
                        && method.getParameterTypes().length == 0) {
                    //若该方法上存在ClearShiro注解，则对该action不进行访问控制检查。
                    if (isClearShiroAnnotationPresent(method)) {
                        continue;
                    }
                    //获取方法的所有Shiro注解。
                    List<Annotation> methodAnnotations = getAuthzAnnotations(method);
                    //依据Controller的注解和方法的注解来生成访问控制处理器。
                    AuthzHandler authzHandler = createAuthzHandler(controllerAnnotations, methodAnnotations);
                    //生成访问控制处理器成功。
                    if (authzHandler != null) {
                        //构建ActionKey，参考ActionMapping中实现
                        String actionKey = createActionKey(controllerClass, method, controllerKey);
                        //添加映射
                        authzMaps.put(actionKey, authzHandler);
                    }
                }
            }
        }
        //注入到ShiroKit类中。ShiroKit类以单例模式运行。
        ShiroKit.init(authzMaps);
        return true;
    }

    /**
     * 从Controller方法中构建出需要排除的方法列表
     *
     * @return 排除的方法列表
     */
    private Set<String> buildExcludedMethodName() {
        Set<String> excludedMethodName = new HashSet<>();
        Method[] methods = Controller.class.getMethods();
        for (Method m : methods) {
            if (m.getParameterTypes().length == 0) {
                excludedMethodName.add(m.getName());
            }
        }
        return excludedMethodName;
    }

    /**
     * 依据Controller的注解和方法的注解来生成访问控制处理器。
     *
     * @param controllerAnnotations Controller的注解
     * @param methodAnnotations     方法的注解
     * @return 访问控制处理器
     */
    private AuthzHandler createAuthzHandler(
            List<Annotation> controllerAnnotations,
            List<Annotation> methodAnnotations) {

        //没有注解
        if (controllerAnnotations.size() == 0 && methodAnnotations.size() == 0) {
            return null;
        }
        //至少有一个注解
        List<AuthzHandler> authzHandlers = Lists.newArrayListWithCapacity(5);
        for (int index = 0; index < 5; index++) {
            authzHandlers.add(null);
        }

        // 逐个扫描注解，若是相应的注解则在相应的位置赋值。
        scanAnnotation(authzHandlers, controllerAnnotations);
        // 逐个扫描注解，若是相应的注解则在相应的位置赋值。函数的注解优先级高于Controller
        scanAnnotation(authzHandlers, methodAnnotations);

        // 去除空值
        List<AuthzHandler> finalAuthzHandlers = Lists.newArrayList();
        for (AuthzHandler a : authzHandlers) {
            if (a != null) {
                finalAuthzHandlers.add(a);
            }
        }
        authzHandlers = null;
        // 存在多个，则构建组合AuthzHandler
        if (finalAuthzHandlers.size() > 1) {
            return new CompositeAuthzHandler(finalAuthzHandlers);
        }
        // 一个的话直接返回
        return finalAuthzHandlers.get(0);
    }

    /**
     * 逐个扫描注解，若是相应的注解则在相应的位置赋值。 注解的处理是有顺序的，依次为RequiresRoles，RequiresPermissions，
     * RequiresAuthentication，RequiresUser，RequiresGuest
     */
    private void scanAnnotation(List<AuthzHandler> authzArray,
                                List<Annotation> annotations) {
        if (null == annotations || 0 == annotations.size()) {
            return;
        }
        for (Annotation a : annotations) {
            if (a instanceof RequiresRoles) {
                authzArray.set(0, new RoleAuthzHandler(a));
            } else if (a instanceof RequiresPermissions) {
                authzArray.set(1, new PermissionAuthzHandler(a));
            } else if (a instanceof RequiresAuthentication) {
                authzArray.set(2, AuthenticatedAuthzHandler.me());
            } else if (a instanceof RequiresUser) {
                authzArray.set(3, UserAuthzHandler.me());
            } else if (a instanceof RequiresGuest) {
                authzArray.set(4, GuestAuthzHandler.me());
            }
        }
    }

    /**
     * 构建actionkey，参考ActionMapping中的实现。
     */
    private String createActionKey(Class<? extends Controller> controllerClass,
                                   Method method, String controllerKey) {
        String methodName = method.getName();
        String actionKey;

        ActionKey ak = method.getAnnotation(ActionKey.class);
        if (ak != null) {
            actionKey = ak.value().trim();
            if (Strings.isNullOrEmpty(actionKey)) {
                throw new IllegalArgumentException(controllerClass.getName()
                        + "."
                        + methodName
                        + "(): The argument of ActionKey can not be blank.");
            }
            if (!actionKey.startsWith(SLASH)) {
                actionKey = SLASH + actionKey;
            }
        } else if (methodName.equals("index")) {
            actionKey = controllerKey;
        } else {
            actionKey =
                    controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
        }
        return actionKey;
    }

    /**
     * 返回该方法的所有访问控制注解
     */
    private List<Annotation> getAuthzAnnotations(Method method) {
        List<Annotation> annotations = Lists.newArrayList();
        for (Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES) {
            Annotation a = method.getAnnotation(annClass);
            if (a != null) {
                annotations.add(a);
            }
        }
        return annotations;
    }

    /**
     * 返回该Controller的所有访问控制注解
     */
    private List<Annotation> getAuthzAnnotations(
            Class<? extends Controller> targetClass) {
        List<Annotation> annotations = Lists.newArrayList();
        for (Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES) {
            Annotation a = targetClass.getAnnotation(annClass);
            if (a != null) {
                annotations.add(a);
            }
        }
        return annotations;
    }

    /**
     * 该方法上是否有ClearShiro注解
     */
    private boolean isClearShiroAnnotationPresent(Method method) {
        Annotation a = method.getAnnotation(ClearShiro.class);
        return a != null;
    }
}


package goja.security.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;

/**
 * <p> 执行登录处理的协助类 </p>
 *
 * @author sogYF
 * @version 1.0
 * @since JDK 1.6
 */
public abstract class LoginActionKit {

    /**
     * 触发Shiro的登录请求.
     *
     * @param username   用户名称
     * @param password   登录密码
     * @param rememberMe 是否记住我
     * @throws AuthenticationException 授权失败的异常
     */
    public static void login(String username, String password, boolean rememberMe)
            throws AuthenticationException {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
        final Subject subject = SecurityUtils.getSubject();
        subject.login(token);
    }

    /**
     * 触发Shiro的登录请求.默认的不记住我.
     *
     * @param username 用户名称
     * @param password 登录密码
     * @throws AuthenticationException 授权失败的异常
     */
    public static void login(String username, String password) throws AuthenticationException {
        login(username, password, false);
    }
}


/*
 * Copyright (C) 2011-2013 Eiichiro Uchiumi. All Rights Reserved.
 *
 * 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.eiichiro.bootleg;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eiichiro.reverb.lang.UncheckedException;

/**
 * {@code Forward} is a {@link Response} implementation to forward HTTP request
 * to another resource.
 *
 * @author Eiichiro Uchiumi
 */
public class Forward implements Response {

	private final String path;

	/**
	 * Constructs {@code Forward} instance to forward to the specified path.
	 *
	 * @param path The path to be forwarded. The specified path must be
	 * relative.
	 */
	public Forward(String path) {
		this.path = path;
	}

	/** Unsupported operation. {@code UnsupportedOperationException} is thrown. */
	public void status(int status) {
		throw new UnsupportedOperationException(
				"'public void status(int status)' is not supoprted in ["
						+ getClass().getName() + "]");
	}

	/** Unsupported operation. {@code UnsupportedOperationException} is thrown. */
	public void mediaType(String mediaType) {
		throw new UnsupportedOperationException(
				"'public void mediaType(String mediaType)' is not supoprted in ["
						+ getClass().getName() + "]");
	}

	/** Unsupported operation. {@code UnsupportedOperationException} is thrown. */
	public void entity(Object entity) {
		throw new UnsupportedOperationException(
				"'public void entity(Object entity)' is not supoprted in ["
						+ getClass().getName() + "]");
	}

	/**
	 * Forwards HTTP request to the specified path.
	 *
	 * @param context HTTP request processing context.
	 */
	public void to(WebContext context) {
		HttpServletRequest request = context.request();
		HttpServletResponse response = context.response();

		try {
			request.getRequestDispatcher(path).forward(request, response);
		} catch (ServletException e) {
			throw new UncheckedException(e);
		} catch (IOException e) {
			throw new UncheckedException(e);
		}
	}

}


/**
 *
 */
package goja.mvc.upload.filerenamepolicy;

import com.oreilly.servlet.multipart.FileRenamePolicy;

import java.io.File;

import goja.core.StringPool;

/**
 * @author BruceZCQ
 */
public abstract class FileRenamePolicyWrapper implements FileRenamePolicy {

    private String saveDirectory;

    @Override
    public File rename(File f) {
        if (null == f) {
            return null;
        }
        String name = f.getName();
        String body = "";
        String ext = "";
        int dot = name.lastIndexOf(".");
        if (dot != -1) {
            body = name.substring(0, dot);
            ext = name.substring(dot);
        }
        return this.nameProcess(f, body, ext);
    }

    /**
     * 文件名字处理
     *
     * @param f    文件
     * @param name 原名称
     * @param ext  文件扩展名
     */
    public abstract File nameProcess(File f, String name, String ext);

    public String getSaveDirectory() {
        return saveDirectory;
    }

    protected void setSaveDirectory(String saveDirectory) {
        this.saveDirectory = saveDirectory;
    }

    /**
     * Add File Separator
     */
    public String appendFileSeparator(String path) {
        if (null == path) {
            return File.separator;
        }
        // add "/" prefix
        if (!path.startsWith(StringPool.SLASH) && !path.startsWith(StringPool.BACK_SLASH)) {
            path = File.separator + path;
        }

        // add "/" postfix
        if (!path.endsWith(StringPool.SLASH) && !path.endsWith(StringPool.BACK_SLASH)) {
            path = path + File.separator;
        }
        return path;
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.mvc.kits;

import com.google.common.base.Strings;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import goja.core.StringPool;

/**
 *
 Http Request Kit class.

 *
 * @author sagyf yang
 * @version 1.0 2014-06-01 20:47
 * @since JDK 1.6
 */
public abstract class Requests {

    /**
     * 获取客户端浏览器信息
     *
     * @return string
     */
    public static String browserInfo(HttpServletRequest req) {
        String browserInfo = "other";
        String ua = req.getHeader("User-Agent").toLowerCase();
        String s;
        String version;
        String msieP = "msie ([\\d.]+)";
        String ieheighP = "rv:([\\d.]+)";
        String firefoxP = "firefox\\/([\\d.]+)";
        String chromeP = "chrome\\/([\\d.]+)";
        String operaP = "opr.([\\d.]+)";
        String safariP = "version\\/([\\d.]+).*safari";

        Pattern pattern = Pattern.compile(msieP);
        Matcher mat = pattern.matcher(ua);
        if (mat.find()) {
            s = mat.group();
            if (s != null) {
                version = s.split(" ")[1];
                browserInfo = "ie " + version.substring(0, version.indexOf("."));
                return browserInfo;
            }
        }

        pattern = Pattern.compile(firefoxP);
        mat = pattern.matcher(ua);
        if (mat.find()) {
            s = mat.group();
            if (s != null) {
                version = s.split("/")[1];
                browserInfo = "firefox " + version.substring(0, version.indexOf("."));
                return browserInfo;
            }
        }

        pattern = Pattern.compile(ieheighP);
        mat = pattern.matcher(ua);
        if (mat.find()) {
            s = mat.group();
            if (s != null) {
                version = s.split(":")[1];
                browserInfo = "ie " + version.substring(0, version.indexOf("."));
                return browserInfo;
            }
        }

        pattern = Pattern.compile(operaP);
        mat = pattern.matcher(ua);
        if (mat.find()) {
            s = mat.group();
            if (s != null) {
                version = s.split("/")[1];
                browserInfo = "opera " + version.substring(0, version.indexOf("."));
                return browserInfo;
            }
        }

        pattern = Pattern.compile(chromeP);
        mat = pattern.matcher(ua);
        if (mat.find()) {
            s = mat.group();
            if (s != null) {
                version = s.split("/")[1];
                browserInfo = "chrome " + version.substring(0, version.indexOf("."));
                return browserInfo;
            }
        }

        pattern = Pattern.compile(safariP);
        mat = pattern.matcher(ua);
        if (mat.find()) {
            s = mat.group();
            if (s != null) {
                version = s.split("/")[1].split(" ")[0];
                browserInfo = "safari " + version.substring(0, version.indexOf("."));
                return browserInfo;
            }
        }
        return browserInfo;
    }

    /**
     * 获取客户端操作系统信息
     *
     * @param req http request.
     * @return string
     */
    public static String clientOS(HttpServletRequest req) {
        String userAgent = req.getHeader("User-Agent");
        String cos = "unknow os";
        Pattern p = Pattern.compile(".*(Windows NT 6\\.2).*");
        Matcher m = p.matcher(userAgent);
        if (m.find()) {
            cos = "Win 8";
            return cos;
        }

        p = Pattern.compile(".*(Windows NT 6\\.1).*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "Win 7";
            return cos;
        }

        p = Pattern.compile(".*(Windows NT 5\\.1|Windows XP).*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "WinXP";
            return cos;
        }

        p = Pattern.compile(".*(Windows NT 5\\.2).*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "Win2003";
            return cos;
        }

        p = Pattern.compile(".*(Win2000|Windows 2000|Windows NT 5\\.0).*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "Win2000";
            return cos;
        }

        p = Pattern.compile(".*(Mac|apple|MacOS8).*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "MAC";
            return cos;
        }

        p = Pattern.compile(".*(WinNT|Windows NT).*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "WinNT";
            return cos;
        }

        p = Pattern.compile(".*Linux.*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "Linux";
            return cos;
        }

        p = Pattern.compile(".*(68k|68000).*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "Mac68k";
            return cos;
        }

        p = Pattern
                .compile(".*(9x 4.90|Win9(5|8)|Windows 9(5|8)|95/NT|Win32|32bit).*");
        m = p.matcher(userAgent);
        if (m.find()) {
            cos = "Win9x";
            return cos;
        }

        return cos;
    }

    public static List<String> acceptLanguage(HttpServletRequest request) {

        final Pattern qpattern = Pattern.compile("q=([0-9\\.]+)");
        final String acceptLanguage = request.getHeader("accept-language");
        if (Strings.isNullOrEmpty(acceptLanguage)) {
            return Collections.emptyList();
        }
        List<String> languages = Arrays.asList(acceptLanguage.split(","));
        Collections.sort(languages, new Comparator<String>() {

            public int compare(String lang1, String lang2) {
                double q1 = 1.0;
                double q2 = 1.0;
                Matcher m1 = qpattern.matcher(lang1);
                Matcher m2 = qpattern.matcher(lang2);
                if (m1.find()) {
                    q1 = Double.parseDouble(m1.group(1));
                }
                if (m2.find()) {
                    q2 = Double.parseDouble(m2.group(1));
                }
                return (int) (q2 - q1);
            }
        });
        List<String> result = new ArrayList<String>(10);
        for (String lang : languages) {
            result.add(lang.trim().split(";")[0]);
        }
        return result;
    }

    /**
     * 获取客户端IP地址，此方法用在proxy环境中
     *
     * @param request 当前请求
     * @return 客户端ip，如果获取失败，则返回 127.0.0.1
     */
    public static String remoteIP(HttpServletRequest request) {
        String ipAddress;
        ipAddress = request.getHeader("x-forwarded-for");
        if ((ipAddress == null) || (ipAddress.length() == 0)
                || ("unknown".equalsIgnoreCase(ipAddress))) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if ((ipAddress == null) || (ipAddress.length() == 0)
                || ("unknown".equalsIgnoreCase(ipAddress))) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if ((ipAddress == null) || (ipAddress.length() == 0)
                || ("unknown".equalsIgnoreCase(ipAddress))) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1")) {
                InetAddress inet;
                try {
                    inet = InetAddress.getLocalHost();
                    ipAddress = inet.getHostAddress();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
            }
        }
        if ((ipAddress != null) && (ipAddress.length() > 15)) {
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }

    /**
     * 判断是否为搜索引擎
     *
     * @param request 当前请求
     * @return true 表示当前请求为搜索引擎发过来的请求。
     */
    public static boolean robot(HttpServletRequest request) {
        String ua = request.getHeader("user-agent");
        return !StringUtils.isBlank(ua) && ((ua.contains("Baiduspider")
                || ua.contains("Googlebot")
                || ua.contains("sogou")
                || ua.contains("sina")
                || ua.contains("iaskspider")
                || ua.contains("ia_archiver")
                || ua.contains("Sosospider")
                || ua.contains("YoudaoBot")
                || ua.contains("yahoo")
                || ua.contains("yodao")
                || ua.contains("MSNBot")
                || ua.contains("spider")
                || ua.contains("Twiceler")
                || ua.contains("Sosoimagespider")
                || ua.contains("naver.com/robots")
                || ua.contains("Nutch")
                || ua.contains("spider")));
    }

    /**
     * 获取COOKIE
     *
     * @param name    cookie 名称
     * @param request 当前请求
     * @return cookie 信息
     */
    public static Cookie getCookie(HttpServletRequest request, String name) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return null;
        for (Cookie ck : cookies) {
            if (StringUtils.equalsIgnoreCase(name, ck.getName())) {
                return ck;
            }
        }
        return null;
    }

    /**
     * 获取COOKIE
     *
     * @param request 当前请求
     * @param name    cokie名称
     * @return 字符串形式的cookie值
     */
    public static String getCookieValue(HttpServletRequest request, String name) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return null;
        for (Cookie ck : cookies) {
            if (StringUtils.equalsIgnoreCase(name, ck.getName())) {
                return ck.getValue();
            }
        }
        return null;
    }

    /**
     * 设置COOKIE
     *
     * @param name   cookie's name.
     * @param value  cooke value.
     * @param maxAge age time.
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response,
                                 String name, String value, int maxAge) {
        setCookie(request, response, name, value, maxAge, true);
    }

    /**
     * 设置COOKIE
     *
     * @param name   cookie's name.
     * @param value  cooke value.
     * @param maxAge age time.
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response,
                                 String name,
                                 String value, int maxAge, boolean all_sub_domain) {
        Cookie cookie = new Cookie(name, value);
        cookie.setMaxAge(maxAge);
        if (all_sub_domain) {
            String serverName = request.getServerName();
            String domain = domainOfServerName(serverName);
            if (domain != null && domain.indexOf('.') != -1) {
                cookie.setDomain('.' + domain);
            }
        }
        cookie.setPath(StringPool.SLASH);
        response.addCookie(cookie);
    }

    public static void deleteCookie(HttpServletRequest request,
                                    HttpServletResponse response, String name, boolean all_sub_domain) {
        setCookie(request, response, name, "", 0, all_sub_domain);
    }

    /**
     * 获取用户访问URL中的根域名 例如: www.dlog.cn -> dlog.cn
     *
     * @param host 访问地址
     * @return 根域名
     */
    public static String domainOfServerName(String host) {
        if (isIPAddr(host)) {
            return null;
        }
        String[] names = StringUtils.split(host, '.');
        if (names == null) return null;
        int len = names.length;
        if (len == 1) return null;
        if (len == 3) {
            return makeup(names[len - 2], names[len - 1]);
        }
        if (len > 3) {
            String dp = names[len - 2];
            if (dp.equalsIgnoreCase("com")
                    || dp.equalsIgnoreCase("gov")
                    || dp.equalsIgnoreCase("net")
                    || dp.equalsIgnoreCase("edu")
                    || dp.equalsIgnoreCase("org")) {
                return makeup(names[len - 3], names[len - 2], names[len - 1]);
            } else {
                return makeup(names[len - 2], names[len - 1]);
            }
        }
        return host;
    }

    /**
     * 判断字符串是否是一个IP地址
     *
     * @param addr 字符串
     * @return true 表示ip地址，否则反之
     */
    public static boolean isIPAddr(String addr) {
        if (StringUtils.isEmpty(addr)) {
            return false;
        }
        String[] ips = StringUtils.split(addr, '.');
        if (ips == null || ips.length != 4) {
            return false;
        }
        try {
            int ipa = Integer.parseInt(ips[0]);
            int ipb = Integer.parseInt(ips[1]);
            int ipc = Integer.parseInt(ips[2]);
            int ipd = Integer.parseInt(ips[3]);
            return ipa >= 0 && ipa <= 255 && ipb >= 0 && ipb <= 255 && ipc >= 0
                    && ipc <= 255 && ipd >= 0 && ipd <= 255;
        } catch (Exception ignored) {
        }
        return false;
    }

    private static String makeup(String... ps) {
        StringBuilder s = new StringBuilder();
        for (int idx = 0; idx < ps.length; idx++) {
            if (idx > 0) {
                s.append('.');
            }
            s.append(ps[idx]);
        }
        return s.toString();
    }

    /**
     * 获取HTTP端口
     *
     * @param request 当前请求
     * @return http端口
     */
    public static int httpPort(HttpServletRequest request) {
        try {
            return new URL(request.getRequestURL().toString()).getPort();
        } catch (MalformedURLException excp) {
            return 80;
        }
    }

    /**
     * 获取浏览器提交的整形参数
     *
     * @param request      当前请求
     * @param param        参数
     * @param defaultValue 默认值
     * @return 值
     */
    public static int param(HttpServletRequest request, String param, int defaultValue) {
        return NumberUtils.toInt(request.getParameter(param), defaultValue);
    }

    /**
     * 获取浏览器提交的字符串参
     *
     * @param request      当前请求
     * @param param        参数
     * @param defaultValue 默认值
     * @return 值
     */
    public static String param(HttpServletRequest request, String param, String defaultValue) {
        String value = request.getParameter(param);
        return (StringUtils.isEmpty(value)) ? defaultValue : value;
    }

    /**
     * Get shaping parameters submitted by the browser
     *
     * @param request      Request
     * @param param        Param
     * @param defaultValue Default Value
     * @return Param Values.
     */
    public static long param(HttpServletRequest request, String param, long defaultValue) {
        return NumberUtils.toLong(request.getParameter(param), defaultValue);
    }

    public static long[] paramValues(HttpServletRequest req, String name) {
        String[] values = req.getParameterValues(name);
        if (values == null) return null;
        return (long[]) ConvertUtils.convert(values, long.class);
    }

    /**
     * 判断当前Request是否位Ajax请求，通过消息头来进行判断.
     *
     * @param request 当前请求对象
     * @return 如果返回true则，表示当前的Request是由Ajax请求，否则反之
     */
    public static boolean ajax(HttpServletRequest request) {
        String x_requested = request.getHeader("x-requested-with");
        return !Strings.isNullOrEmpty(x_requested) && "XMLHttpRequest".equals(x_requested);
    }

    /**
     * 去html
     *
     * @param src HTMl
     * @return 去掉HTML后的字符串
     */
    public static String replaceTagHTML(String src) {
        String regex = "\\<(.+?)\\>";
        return StringUtils.isNotEmpty(src) ? src.replaceAll(regex, "") : "";
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.logging;

import org.slf4j.LoggerFactory;

/**
 * <p> Main logger for the application. </p>
 *
 * @author sagyf yang
 * @version 1.0 2014-06-01 18:00
 * @since JDK 1.6
 */
public class Logger {
    /**
     * Will record and display the caller method.
     */
    protected static boolean recordCaller = false;
    /**
     * The application logger (japp).
     */
    protected static org.slf4j.Logger slf4j;

    /**
     * true if logger is configured manually (log4j-config file supplied by application)
     */
    protected static boolean configuredManually = false;

    protected Logger() {
    }

    /**
     * @return true if log4j.debug / jul.fine logging is enabled
     */
    public static boolean isDebugEnabled() {

        return slf4j.isDebugEnabled();
    }

    /**
     * Log with DEBUG level
     *
     * @param message The message pattern
     * @param args    Pattern arguments
     */
    public static void debug(String message, Object... args) {

        if (recordCaller) {
            LoggerFactory.getLogger(getCallerClassName()).debug(message, args);
        } else {
            slf4j.debug(message, args);
        }
    }

    /**
     * Log with DEBUG level
     *
     * @param e       the exception to log
     * @param message The message pattern
     * @param args    Pattern arguments
     */
    public static void debug(Throwable e, String message, Object... args) {

        if (recordCaller) {
            LoggerFactory.getLogger(getCallerClassName()).debug(message, args, e);
        } else {
            slf4j.debug(message, args, e);
        }
    }

    /**
     * Log with INFO level
     *
     * @param message The message pattern
     * @param args    Pattern arguments
     */

    public static void info(String message, Object... args) {

        if (recordCaller) {
            LoggerFactory.getLogger(getCallerClassName()).info(message, args);
        } else {
            slf4j.info(message, args);
        }
    }

    /**
     * Log with INFO level
     *
     * @param e       the exception to log
     * @param message The message pattern
     * @param args    Pattern arguments
     */
    public static void info(Throwable e, String message, Object... args) {

        if (recordCaller) {
            LoggerFactory.getLogger(getCallerClassName()).info(message, args, e);
        } else {
            slf4j.info(message, args, e);
        }
    }

    /**
     * Log with WARN level
     *
     * @param message The message pattern
     * @param args    Pattern arguments
     */
    public static void warn(String message, Object... args) {

        try {
            if (recordCaller) {
                LoggerFactory.getLogger(getCallerClassName()).warn(message, args);
            } else {
                slf4j.warn(message, args);
            }
        } catch (Throwable ex) {
            slf4j.error("Oops. Error in Logger !", ex);
        }
    }

    /**
     * Log with WARN level
     *
     * @param e       the exception to log
     * @param message The message pattern
     * @param args    Pattern arguments
     */
    public static void warn(Throwable e, String message, Object... args) {

        try {
            if (recordCaller) {
                LoggerFactory.getLogger(getCallerClassName()).warn(message, args, e);
            } else {
                slf4j.warn(message, args, e);
            }
        } catch (Throwable ex) {
            slf4j.error("Oops. Error in Logger !", ex);
        }
    }

    public static boolean isErrorEnabled() {

        try {
            if (recordCaller) {
                return LoggerFactory.getLogger(getCallerClassName()).isErrorEnabled();
            } else {
                return slf4j.isErrorEnabled();
            }
        } catch (Throwable ex) {
            slf4j.error("Oops. Error in Logger !", ex);
            return false;
        }
    }

    /**
     * Log with ERROR level
     *
     * @param message The message pattern
     * @param args    Pattern arguments
     */
    public static void error(String message, Object... args) {

        try {
            if (recordCaller) {
                LoggerFactory.getLogger(getCallerClassName()).error(message, args);
            } else {
                slf4j.error(message, args);
            }
        } catch (Throwable ex) {
            slf4j.error("Oops. Error in Logger !", ex);
        }
    }

    /**
     * Log with ERROR level
     *
     * @param e       the exception to log
     * @param message The message pattern
     * @param args    Pattern arguments
     */
    public static void error(Throwable e, String message, Object... args) {

        if (recordCaller) {
            LoggerFactory.getLogger(getCallerClassName()).error(message, args, e);
        } else {
            slf4j.error(message, args, e);
        }
    }

    public static boolean isWarnEnabled() {

        try {
            if (recordCaller) {
                return LoggerFactory.getLogger(getCallerClassName()).isWarnEnabled();
            } else {
                return slf4j.isWarnEnabled();
            }
        } catch (Throwable ex) {
            slf4j.error("Oops. Error in Logger !", ex);
            return false;
        }
    }

    /**
     * @return the className of the class actually logging the message
     */
    static String getCallerClassName() {
        final int level = 5;
        return getCallerClassName(level);
    }

    /**
     * @return the className of the class actually logging the message
     */
    static String getCallerClassName(final int level) {
        CallInfo ci = getCallerInformations(level);
        return ci.className;
    }

    /**
     * Examine stack trace to get caller
     *
     * @param level method stack depth
     * @return who called the logger
     */
    static CallInfo getCallerInformations(int level) {
        StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
        StackTraceElement caller = callStack[level];
        return new CallInfo(caller.getClassName(), caller.getMethodName());
    }

    /**
     * Info about the logger caller
     */
    static class CallInfo {

        public String className;
        public String methodName;

        public CallInfo() {
        }

        public CallInfo(String className, String methodName) {
            this.className = className;
            this.methodName = methodName;
        }
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.rapid.qrcode;

import com.google.zxing.EncodeHintType;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import java.awt.*;
import java.util.Hashtable;

/**
 *
 .

 *
 * @author sagyf yang
 * @version 1.0 2014-08-27 15:28
 * @since JDK 1.6
 */
public class QRCodeFormat {

    /**
     * 图片大小
     */
    private int size;

    /**
     * 内容编码格式
     */
    private String encode;

    /**
     * 错误修正等级 (Error Collection Level)
     */
    private ErrorCorrectionLevel errorCorrectionLevel;

    /**
     * 前景色
     */
    private Color foreGroundColor;

    /**
     * 背景色
     */
    private Color backGroundColor;

    /**
     * 图片的文件格式
     */
    private String imageFormat;

    /**
     * 图片的外边距大小 (Quiet Zone)
     */
    private int margin;

    /**
     * 提供给编码器额外的参数
     */
    private Hashtable<EncodeHintType, Object> hints;

    private QRCodeFormat() {
        this.size = 256;
        this.encode = "UTF-8";
        this.errorCorrectionLevel = ErrorCorrectionLevel.M;
        this.foreGroundColor = Color.BLACK;
        this.backGroundColor = Color.WHITE;
        this.imageFormat = "png";
        this.margin = 0;
        this.hints = new Hashtable<EncodeHintType, Object>();
    }

    /**
     * 创建一个带有默认值的 QRCode 生成器的格式。默认值如下
     *

     *

图片大小: 256px

内容编码格式: UTF-8

错误修正等级: Level M (有15% 的内容可被修正)

     *
前景色: 黑色

背景色: 白色

输出图片的文件格式: png

图片空白区域大小: 0个单位

     *
     * @return QRCode 生成器格式
     */
    public static QRCodeFormat NEW() {
        return new QRCodeFormat();
    }

    /**
     * 返回图片的大小。
     *
     * @return 图片的大小
     */
    public int getSize() {
        return this.size;
    }

    /**
     * 设置图片的大小。图片的大小等于实际内容与外边距的值（建议设置成偶数值）。
     *
     * @param size 图片的大小
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setSize(int size) {
        this.size = size;
        return this;
    }

    /**
     * 返回内容编码格式。
     *
     * @return 内容编码格式
     */
    public String getEncode() {
        return encode;
    }

    /**
     * 设置内容编码格式。
     *
     * @param encode 内容编码格式
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setEncode(String encode) {
        this.encode = encode;
        return this;
    }

    /**
     * 返回错误修正等级。
     *
     * @return 错误修正等级
     */
    public ErrorCorrectionLevel getErrorCorrectionLevel() {
        return errorCorrectionLevel;
    }

    /**
     * 设置错误修正等级。其定义如下
     *

     *

L: 有 7% 的内容可被修正

M: 有15% 的内容可被修正

Q: 有 25% 的内容可被修正

H: 有 30%
     * 的内容可被修正

     *
     * @param errorCorrectionLevel 错误修正等级
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setErrorCorrectionLevel(char errorCorrectionLevel) {
        switch (Character.toUpperCase(errorCorrectionLevel)) {
            case 'L':
                this.errorCorrectionLevel = ErrorCorrectionLevel.L;
                break;
            case 'M':
                this.errorCorrectionLevel = ErrorCorrectionLevel.M;
                break;
            case 'Q':
                this.errorCorrectionLevel = ErrorCorrectionLevel.Q;
                break;
            case 'H':
                this.errorCorrectionLevel = ErrorCorrectionLevel.H;
                break;
            default:
                this.errorCorrectionLevel = ErrorCorrectionLevel.M;
        }

        return this;
    }

    /**
     * 返回前景色。
     *
     * @return 前景色
     */
    public Color getForeGroundColor() {
        return foreGroundColor;
    }

    /**
     * 设置前景色。
     *
     * @param foreGroundColor 前景色的值
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setForeGroundColor(Color foreGroundColor) {
        this.foreGroundColor = foreGroundColor;
        return this;
    }

    /**
     * 设置前景色。值为十六进制的颜色值（与 CSS 定义颜色的值相同，不支持简写），可以忽略「#」符号。
     *
     * @param foreGroundColor 前景色的值
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setForeGroundColor(String foreGroundColor) {
        try {
            this.foreGroundColor = getColor(foreGroundColor);
        } catch (NumberFormatException e) {
            this.foreGroundColor = Color.BLACK;
        }
        return this;
    }

    /**
     * 返回背景色。
     *
     * @return 背景色
     */
    public Color getBackGroundColor() {
        return backGroundColor;
    }

    /**
     * 设置背景色。
     *
     * @param backGroundColor 前景色的值
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setBackGroundColor(Color backGroundColor) {
        this.backGroundColor = backGroundColor;
        return this;
    }

    /**
     * 设置背景色。值为十六进制的颜色值（与 CSS 定义颜色的值相同，不支持简写），可以忽略「#」符号。
     *
     * @param backGroundColor 前景色的值
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setBackGroundColor(String backGroundColor) {
        try {
            this.backGroundColor = getColor(backGroundColor);
        } catch (NumberFormatException e) {
            this.backGroundColor = Color.WHITE;
        }
        return this;
    }

    /**
     * 返回图片的文件格式。
     *
     * @return 图片的文件格式
     */
    public String getImageFormat() {
        return imageFormat.toUpperCase();
    }

    /**
     * 设置图片的文件格式 。
     *
     * @param imageFormat 图片的文件格式
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setImageFormat(String imageFormat) {
        this.imageFormat = imageFormat;
        return this;
    }

    /**
     * 返回图片的外边距大小。
     *
     * @return 图片的外边距大小
     */
    public int getMargin() {
        return margin;
    }

    /**
     * 设置图片的外边距大小 。
     *
     * @param margin 图片的外边距大小
     * @return QRCode生成器的格式
     */
    public QRCodeFormat setMargin(int margin) {
        this.margin = margin;
        return this;
    }

    /**
     * 返回提供给编码器额外的参数。
     *
     * @return 提供给编码器额外的参数
     */
    public Hashtable<EncodeHintType, ?> getHints() {
        hints.clear();
        hints.put(EncodeHintType.ERROR_CORRECTION, getErrorCorrectionLevel());
        hints.put(EncodeHintType.CHARACTER_SET, getEncode());
        hints.put(EncodeHintType.MARGIN, getMargin());
        return hints;
    }

    private Color getColor(String hexString) {
        if (hexString.charAt(0) == '#') {
            return new Color(Long.decode(hexString).intValue());
        } else {
            return new Color(Long.decode("0xFF" + hexString).intValue());
        }
    }
}


/*
 * Copyright (C) 2011-2013 Eiichiro Uchiumi. All Rights Reserved.
 *
 * 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.eiichiro.bootleg.json;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.http.HttpServletResponse;

import org.eiichiro.bootleg.AbstractResponse;
import org.eiichiro.bootleg.MediaType;
import org.eiichiro.bootleg.Types;
import org.eiichiro.bootleg.WebContext;
import org.eiichiro.reverb.lang.UncheckedException;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * {@code JSONResponse} is a JSON-based implementation of {@code Response}.
 * If the generated/negotiated MIME media type is 'application/json', Bootleg
 * uses this implementation.
 * Web endpoint method invocation result is deserialized to JSON with
 * Google Gson library.
 * UTF-8 is used for the character encoding (MIME charset) at any time.
 *
 * @author Eiichiro Uchiumi
 */
public class JSONResponse extends AbstractResponse {

	public JSONResponse() {
		mediaType = MediaType.APPLICATION_JSON;
	}

	/**
	 * Writes {@code JSONResponse} to the current {@code WebContext}.
	 *
	 * @param context The current {@code WebContext}.
	 */
	public void to(WebContext context) {
		if (entity == null) {
			return;
		}

		HttpServletResponse response = context.response();

		try {
			response.setCharacterEncoding("UTF-8");
			response.setHeader("Content-Type", mediaType);

			if (status > 0) {
				response.setStatus(status);
			}

			if (entity instanceof String) {
				response.getWriter().write((String) entity);
				return;
			}

			GsonBuilder builder = new GsonBuilder();
			Set<Class<?>> classes = new HashSet<Class<?>>();
			parse(entity.getClass(), classes);

			for (Class<?> clazz : classes) {
				builder.registerTypeAdapter(clazz, new ValueTypeJsonSerializer<Object>());
			}

			// XXX: Any options?
			Gson gson = builder.create();
			response.getWriter().write(gson.toJson(entity));
		} catch (IOException e) {
			throw new UncheckedException(e);
		}
	}

	private void parse(Class<?> clazz, Set<Class<?>> values) {
		if (Types.isArray(clazz) || Types.isCollection(clazz)) {
			parse(Types.getElementType(clazz), values);
		} else if (Types.isUserDefinedValueType(clazz)
				|| Types.isCoreValueType(clazz)) {
			if (!values.contains(clazz)) {
				values.add(clazz);
			}

			return;
		}

		for (Field field : clazz.getDeclaredFields()) {
			parse(field.getType(), values);
		}
	}

}


package io.github.oliviercailloux.swt_tools;

import java.util.function.Function;

import org.eclipse.jface.viewers.ColumnViewer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *

 * A {@link TextEditingSupport} that further restricts its content to values
 * that can be converted to integers.
 *

 *

 * The underlying control does not constrain the user while typing, only when
 * validating. Thus the user is free to type invalid characters (such as
 * letters). The reason for not using verify listeners (that constrain the user
 * while typing) is that, IMHO, it constrains the user without really helping
 * her.
 *

 *
It prevents copy/paste when the string copied includes an invalid
 * character (example: the user wants to paste the string “12455,32” and intends
 * to delete the last part);

 *
if user types erroneously a mistaken character, then hits backspace by
 * reflex, the backspace will eat the wrong character;

 *
the verify listener will allow pasting “-4244” next to “3411” (which
 * produces an invalid integer) but will refuse pasting “3411-4244”, a behavior
 * that is difficult for the user to understand.

 *

 *

 *
 * @author Olivier Cailloux
 *
 * @param <E>
 *            the type of elements returned by the underlying column viewer.
 */
public abstract class IntEditingSupport<E> extends TextEditingSupport<E> {

	@SuppressWarnings("unused")
	private static final Logger LOGGER = LoggerFactory.getLogger(IntEditingSupport.class);

	public IntEditingSupport(ColumnViewer viewer, Class<E> classOfElements) {
		super(viewer, classOfElements);
		/** Here we forbid empty strings, strings equal to "-", … */
		setFirstLevelValidator(v -> !v.matches("[-]?[0-9]+") ? "Integer required." : null);
	}

	/**
	 *

	 * Get the value to set to the editor.
	 *

	 *

	 * This method is simply a better typed equivalent to {@link #getValue(Object)}.
	 *

	 *
	 * @param element
	 *            the model element
	 * @return the value shown.
	 */
	public abstract int getIntValue(E element);

	@Override
	public String getValueTyped(E element) {
		final int intValue = getIntValue(element);
		return Integer.toString(intValue);
	}

	/**
	 *

	 * Sets the input validator for this cell editor.
	 *

	 *

	 * The validator is given the (integer) value to be validated, and must return a
	 * string indicating whether the given value is valid; null means
	 * valid, and non-null means invalid, with the result being the
	 * error message to display to the end user.
	 *

	 *

	 * This is simply a better-typed version of {@link #setValidator(Function)}.
	 *

	 *
	 * @param valueToErrorMessage
	 *            the input validator, or null if none
	 */
	public void setIntegerValidator(Function<Integer, String> valueToErrorMessage) {
		setValidator(valueToErrorMessage.compose(v -> Integer.valueOf(v)));
	}

	/**
	 *

	 * Sets the new value on the given element. Note that implementers need to
	 * ensure that getViewer().update(element, null) or similar methods
	 * are called, either directly or through some kind of listener mechanism on the
	 * implementer's model, to cause the new value to appear in the viewer.
	 *

	 *
	 *

	 * This method is simply a better typed equivalent to
	 * {@link #setValue(Object, Object)}.
	 *

	 *
	 * @param element
	 *            the model element
	 * @param value
	 *            the new value
	 */
	public abstract void setIntValue(E element, int value);

	@Override
	public void setValueTyped(E element, String value) {
		assert value != null;
		assert !value.isEmpty();
		final int intValue = Integer.valueOf(value).intValue();
		setIntValue(element, intValue);
	}

}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.mvc.csv;

import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Record;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import static goja.core.StringPool.COMMA;
import static goja.core.StringPool.EMPTY;
import static goja.core.StringPool.NEWLINE;
import static goja.core.StringPool.QUOTE;
import static goja.core.date.DateFormatter.DP_YYYY_MM_DD_HH_MM;

/**
 * 该类是把数据转化成csv字符串做了简要的封装 List headers是显示数据每列的属性，建议使用字符 List data数据，单个元素格式可以为Array，list，map，model，record
 * List columns 表示需要显示的数据，如果data是Array与list，输入希望显示列的下标即可 如果data是map，model，record，输入希望显示列的key值即可。
 */
public class CsvUtil {

    private static UserSettings userSettings = new UserSettings();

    private CsvUtil() {
    }

    /**
     * 将文本头与数据共同转成csv字符串
     *
     * @param headers 列属性
     * @param data    数据
     * @param columns 需要显示列的key值
     * @return csv字符串
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static String createCSV(List headers, List data, List columns) {
        StringBuffer strOut = new StringBuffer("");
        if (null != headers && !headers.isEmpty()) { // 如果文本不为空则添加到csv字符串中
            listToCSV(strOut, headers);
        }
        if (null == data || data.isEmpty()) {
            return strOut.toString();
        }

        Iterator itr = data.iterator();
        while (itr.hasNext()) {
            Object obj = itr.next(); // 将数据添加到csv字符串
            Class cls = obj.getClass();
            if (cls != null && cls.isArray()) {
                Object[] objs = (Object[]) obj;
                for (Object obj1 : objs) {
                    createCol(strOut, obj1);
                    strOut.append(COMMA);
                }
                strOut = strOut.deleteCharAt(strOut.length() - 1); // 去点多余逗号
                strOut.append(NEWLINE);
            } else if (obj instanceof List) {
                List objlist = (List) obj;
                if (null == columns || columns.isEmpty()) { // 如果没有限制，默认全部显示
                    listToCSV(strOut, objlist);
                } else {
                    for (Object column : columns) {
                        createCol(strOut, objlist.get((Integer) column));
                        strOut.append(COMMA);
                    }
                    strOut = strOut.deleteCharAt(strOut.length() - 1);
                    strOut.append(NEWLINE);
                }
            } else if (obj instanceof Map) {
                Map objmap = (Map) obj;
                if (null == columns || columns.isEmpty()) { // 如果没有限制，默认全部显示
                    Set keyset = objmap.keySet();
                    for (Object key : keyset) {
                        createCol(strOut, objmap.get(key));
                        strOut.append(COMMA);
                    }
                    strOut = strOut.deleteCharAt(strOut.length() - 1);
                    strOut.append(NEWLINE);
                } else {
                    for (Object column : columns) {
                        createCol(strOut, objmap.get(column));
                        strOut.append(COMMA);
                    }
                    strOut = strOut.deleteCharAt(strOut.length() - 1);
                    strOut.append(NEWLINE);
                }
            } else if (obj instanceof Model) {
                Model objmodel = (Model) obj;
                if (null == columns || columns.isEmpty()) { // 如果没有限制，默认全部显示
                    Set<Entry<String, Object>> entries = objmodel._getAttrsEntrySet();
                    for (Entry entry : entries) {
                        createCol(strOut, entry.getValue());
                        strOut.append(COMMA);
                    }
                    strOut = strOut.deleteCharAt(strOut.length() - 1);
                    strOut.append(NEWLINE);
                } else {
                    for (Object column : columns) {
                        createCol(strOut, objmodel.get(column + EMPTY));
                        strOut.append(COMMA);
                    }
                    strOut = strOut.deleteCharAt(strOut.length() - 1);
                    strOut.append(NEWLINE);
                }
            } else if (obj instanceof Record) {
                Record objrecord = (Record) obj;
                Map<String, Object> map = objrecord.getColumns();
                if (null == columns || columns.isEmpty()) { // 如果没有限制，默认全部显示
                    Set<String> keys = map.keySet();
                    for (String key : keys) {
                        createCol(strOut, objrecord.get(key));
                        strOut.append(COMMA);
                    }
                    strOut = strOut.deleteCharAt(strOut.length() - 1);
                    strOut.append(NEWLINE);
                } else {
                    for (Object column : columns) {
                        createCol(strOut, objrecord.get(column + EMPTY));
                        strOut.append(COMMA);
                    }
                    strOut = strOut.deleteCharAt(strOut.length() - 1);
                    strOut.append(NEWLINE);
                }
            } else {
                while (itr.hasNext()) {
                    Object objs = itr.next();
                    if (objs != null) {
                        createCol(strOut, objs);
                        strOut.append(NEWLINE);
                    }
                }
            }
            obj = null;
        }
        return strOut.toString();
    }

    /**
     * 把单纯的集合转化成csv字符串
     *
     * @param strOut StringBuffer
     * @param list   数据
     */
    public static void listToCSV(StringBuffer strOut, List<?> list) {
        if (null != list && !list.isEmpty()) { // 如果文本不为空则添加到csv字符串中
            for (Object aList : list) {
                createCol(strOut, aList);
                strOut.append(COMMA);
            }
            strOut = strOut.deleteCharAt(strOut.length() - 1);
            strOut.append(NEWLINE);
        }
    }

    // 把单个元素转化
    public static void createCol(StringBuffer strOut, Object obj) {
        if (obj != null) {
            strOut.append(QUOTE);
            String content;

            if (obj instanceof Boolean) {
                content = obj.toString();
            } else if (obj instanceof Calendar) {
                content = obj.toString();
            } else if (obj instanceof Timestamp) {
                content = DP_YYYY_MM_DD_HH_MM.format(new Date(((Timestamp) obj).getTime()));
            } else if (obj instanceof Date) {
                content = DP_YYYY_MM_DD_HH_MM.format((Date) obj);
            } else {
                content = write(String.valueOf(obj));
            }
            strOut.append(content);
            strOut.append(QUOTE);
        } else {
            strOut.append("\" \" ");
        }
    }

    public static String write(String content) {

        boolean textQualify = userSettings.forceQualifier;

        if (content.length() > 0) {
            content = content.trim();
        }

        if (!textQualify
                && userSettings.useTextQualifier
                && (content.indexOf(userSettings.textQualifier) > -1
                || content.indexOf(userSettings.delimiter) > -1
                || (content.indexOf(Letters.LF) > -1 || content.indexOf(Letters.CR) > -1)
                || (content.indexOf(userSettings.recordDelimiter) > -1)
                || (content.length() > 0 && content.charAt(0) == userSettings.comment)
                || (content.length() == 0))) {
            textQualify = true;
        }

        if (userSettings.useTextQualifier && !textQualify && content.length() > 0) {
            char firstLetter = content.charAt(0);

            if (firstLetter == Letters.SPACE || firstLetter == Letters.TAB) {
                textQualify = true;
            }

            if (!textQualify && content.length() > 1) {
                char lastLetter = content.charAt(content.length() - 1);

                if (lastLetter == Letters.SPACE || lastLetter == Letters.TAB) {
                    textQualify = true;
                }
            }
        }

        if (textQualify) {

            if (userSettings.escapeMode == UserSettings.ESCAPE_MODE_BACKSLASH) {
                content =
                        replace(content, "" + Letters.BACKSLASH, "" + Letters.BACKSLASH + Letters.BACKSLASH);
                content = replace(content, "" + userSettings.textQualifier, "" + Letters.BACKSLASH
                        + userSettings.textQualifier);
            } else {
                content = replace(content, "" + userSettings.textQualifier, "" + userSettings.textQualifier
                        + userSettings.textQualifier);
            }
        } else if (userSettings.escapeMode == UserSettings.ESCAPE_MODE_BACKSLASH) {
            content =
                    replace(content, "" + Letters.BACKSLASH, "" + Letters.BACKSLASH + Letters.BACKSLASH);
            content = replace(content, "" + userSettings.delimiter,
                    "" + Letters.BACKSLASH + userSettings.delimiter);

            content = replace(content, "" + Letters.CR, "" + Letters.BACKSLASH + Letters.CR);
            content = replace(content, "" + Letters.LF, "" + Letters.BACKSLASH + Letters.LF);
        }
        return content;
    }

    // 特殊字符的转换 "\t" -> "\\t"
    public static String replace(String original, String pattern, String replace) {
        final int len = pattern.length();
        int found = original.indexOf(pattern);
        if (found > -1) {
            StringBuilder sb = new StringBuilder();
            int start = 0;
            while (found != -1) {
                sb.append(original.substring(start, found));
                sb.append(replace);
                start = found + len;
                found = original.indexOf(pattern, start);
            }
            sb.append(original.substring(start));
            return sb.toString();
        } else {
            return original;
        }
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.core.kits.lang;

import goja.core.StringPool;

import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;

/**
 *

 * .
 *

 *
 * @author sagyf yang
 * @version 1.0 2014-01-31 17:51
 * @since JDK 1.6
 */
public class CharKit {

    // ---------------------------------------------------------------- simple

    /**
     * Converts (signed) byte to (unsigned) char.
     */
    public static char toChar(byte b) {
        return (char) (b & 0xFF);
    }

    /**
     * Converts char array into byte array by stripping the high byte of each character.
     */
    public static byte[] toSimpleByteArray(char[] carr) {
        byte[] barr = new byte[carr.length];
        for (int i = 0; i < carr.length; i++) {
            barr[i] = (byte) carr[i];
        }
        return barr;
    }

    /**
     * Converts char sequence into byte array.
     *
     * @see #toSimpleByteArray(char[])
     */
    public static byte[] toSimpleByteArray(CharSequence charSequence) {
        byte[] barr = new byte[charSequence.length()];
        for (int i = 0; i < barr.length; i++) {
            barr[i] = (byte) charSequence.charAt(i);
        }
        return barr;
    }

    /**
     * Converts byte array to char array by simply extending bytes to chars.
     */
    public static char[] toSimpleCharArray(byte[] barr) {
        char[] carr = new char[barr.length];
        for (int i = 0; i < barr.length; i++) {
            carr[i] = (char) (barr[i] & 0xFF);
        }
        return carr;
    }

    // ---------------------------------------------------------------- ascii

    /**
     * Returns ASCII value of a char. In case of overload, 0x3F is returned.
     */
    public static int toAscii(char c) {
        if (c <= 0xFF) {
            return c;
        } else {
            return 0x3F;
        }
    }

    /**
     * Converts char array into {@link #toAscii(char) ASCII} array.
     */
    public static byte[] toAsciiByteArray(char[] carr) {
        byte[] barr = new byte[carr.length];
        for (int i = 0; i < carr.length; i++) {
            barr[i] = (byte) ((int) (carr[i] <= 0xFF ? carr[i] : 0x3F));
        }
        return barr;
    }

    /**
     * Converts char sequence into ASCII byte array.
     */
    public static byte[] toAsciiByteArray(CharSequence charSequence) {
        byte[] barr = new byte[charSequence.length()];
        for (int i = 0; i < barr.length; i++) {
            char c = charSequence.charAt(i);
            barr[i] = (byte) ((int) (c <= 0xFF ? c : 0x3F));
        }
        return barr;
    }

    // ---------------------------------------------------------------- raw arrays

    /**
     * Converts char array into byte array by replacing each character with two bytes.
     */
    public static byte[] toRawByteArray(char[] carr) {
        byte[] barr = new byte[carr.length << 1];
        for (int i = 0, bpos = 0; i < carr.length; i++) {
            char c = carr[i];
            barr[bpos++] = (byte) ((c & 0xFF00) >> 8);
            barr[bpos++] = (byte) (c & 0x00FF);
        }
        return barr;
    }

    public static char[] toRawCharArray(byte[] barr) {
        int carrLen = barr.length >> 1;
        if (carrLen << 1 < barr.length) {
            carrLen++;
        }
        char[] carr = new char[carrLen];
        int i = 0, j = 0;
        while (i < barr.length) {
            char c = (char) (barr[i] << 8);
            i++;

            if (i != barr.length) {
                c += barr[i] & 0xFF;
                i++;
            }
            carr[j++] = c;
        }
        return carr;
    }

    // ---------------------------------------------------------------- encoding

    /**
     * Converts char array to byte array using default Jodd encoding.
     */
    public static byte[] toByteArray(char[] carr) throws UnsupportedEncodingException {
        return new String(carr).getBytes(StringPool.UTF_8);
    }

    /**
     * Converts char array to byte array using provided encoding.
     */
    public static byte[] toByteArray(char[] carr, String charset) throws UnsupportedEncodingException {
        return new String(carr).getBytes(charset);
    }

    /**
     * Converts byte array of default Jodd encoding to char array.
     */
    public static char[] toCharArray(byte[] barr) throws UnsupportedEncodingException {
        return new String(barr, StringPool.UTF_8).toCharArray();
    }

    /**
     * Converts byte array of specific encoding to char array.
     */
    public static char[] toCharArray(byte[] barr, String charset) throws UnsupportedEncodingException {
        return new String(barr, charset).toCharArray();
    }

    // ---------------------------------------------------------------- find

    /**
     * Match if one character equals to any of the given character.
     *
     * @return true if characters match any character from given array,
     * otherwise false
     */
    public static boolean equalsOne(char c, char[] match) {
        for (char aMatch : match) {
            if (c == aMatch) {
                return true;
            }
        }
        return false;
    }

    /**
     * Finds index of the first character in given array the matches any from the
     * given set of characters.
     *
     * @return index of matched character or -1
     */
    public static int findFirstEqual(char[] source, int index, char[] match) {
        for (int i = index; i < source.length; i++) {
            if (equalsOne(source[i], match) == true) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Finds index of the first character in given array the matches any from the
     * given set of characters.
     *
     * @return index of matched character or -1
     */
    public static int findFirstEqual(char[] source, int index, char match) {
        for (int i = index; i < source.length; i++) {
            if (source[i] == match) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Finds index of the first character in given array the differs from the
     * given set of characters.
     *
     * @return index of matched character or -1
     */
    public static int findFirstDiff(char[] source, int index, char[] match) {
        for (int i = index; i < source.length; i++) {
            if (equalsOne(source[i], match) == false) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Finds index of the first character in given array the differs from the
     * given set of characters.
     *
     * @return index of matched character or -1
     */
    public static int findFirstDiff(char[] source, int index, char match) {
        for (int i = index; i < source.length; i++) {
            if (source[i] != match) {
                return i;
            }
        }
        return -1;
    }

    // ---------------------------------------------------------------- is

    /**
     * Returns true if character is a white space (<= ' ').
     * White space definition is taken from String class (see: trim()).
     */
    public static boolean isWhitespace(char c) {
        return c <= ' ';
    }

    /**
     * Returns true if specified character is lowercase ASCII.
     * If user uses only ASCIIs, it is much much faster.
     */
    public static boolean isLowercaseAlpha(char c) {
        return (c >= 'a') && (c <= 'z');
    }

    /**
     * Returns true if specified character is uppercase ASCII.
     * If user uses only ASCIIs, it is much much faster.
     */
    public static boolean isUppercaseAlpha(char c) {
        return (c >= 'A') && (c <= 'Z');
    }

    public static boolean isAlphaOrDigit(char c) {
        return isDigit(c) || isAlpha(c);
    }

    public static boolean isWordChar(char c) {
        return isDigit(c) || isAlpha(c) || (c == '_');
    }

    public static boolean isPropertyNameChar(char c) {
        return isDigit(c) || isAlpha(c) || (c == '_') || (c == '.') || (c == '[') || (c == ']');
    }

    // ---------------------------------------------------------------- RFC

    /**
     * Indicates whether the given character is in the {@code ALPHA} set.
     *
     * @see RFC 3986, appendix A
     */
    public static boolean isAlpha(char c) {
        return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
    }

    /**
     * Indicates whether the given character is in the {@code DIGIT} set.
     *
     * @see RFC 3986, appendix A
     */
    public static boolean isDigit(char c) {
        return c >= '0' && c <= '9';
    }

    /**
     * Indicates whether the given character is in the gen-delims set.
     *
     * @see RFC 3986, appendix A
     */
    public static boolean isGenericDelimiter(int c) {
        return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@';
    }

    /**
     * Indicates whether the given character is in the sub-delims set.
     *
     * @see RFC 3986, appendix A
     */
    protected static boolean isSubDelimiter(int c) {
        return c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '+' ||
                c == ',' || c == ';' || c == '=';
    }

    /**
     * Indicates whether the given character is in the reserved set.
     *
     * @see RFC 3986, appendix A
     */
    protected static boolean isReserved(char c) {
        return isGenericDelimiter(c) || isReserved(c);
    }

    /**
     * Indicates whether the given character is in the unreserved set.
     *
     * @see RFC 3986, appendix A
     */
    protected static boolean isUnreserved(char c) {
        return isAlpha(c) || isDigit(c) || c == '-' || c == '.' || c == '_' || c == '~';
    }

    /**
     * Indicates whether the given character is in the pchar set.
     *
     * @see RFC 3986, appendix A
     */
    protected static boolean isPchar(char c) {
        return isUnreserved(c) || isSubDelimiter(c) || c == ':' || c == '@';
    }

    // ---------------------------------------------------------------- conversions

    /**
     * Uppers lowercase ASCII char.
     */
    public static char toUpperAscii(char c) {
        if (isLowercaseAlpha(c)) {
            c -= (char) 0x20;
        }
        return c;
    }

    /**
     * Lowers uppercase ASCII char.
     */
    public static char toLowerAscii(char c) {
        if (isUppercaseAlpha(c)) {
            c += (char) 0x20;
        }
        return c;
    }

    /**
     * @see #indexOfChars(String, String, int)
     */
    public static int indexOfChars(String string, String chars) {
        return indexOfChars(string, chars, 0);
    }

    /**
     * Returns the very first index of any char from provided string, starting from specified index offset.
     * Returns index of founded char, or -1 if nothing found.
     */
    public static int indexOfChars(String string, String chars, int startindex) {
        int stringLen = string.length();
        int charsLen = chars.length();
        if (startindex < 0) {
            startindex = 0;
        }
        for (int i = startindex; i < stringLen; i++) {
            char c = string.charAt(i);
            for (int j = 0; j < charsLen; j++) {
                if (c == chars.charAt(j)) {
                    return i;
                }
            }
        }
        return -1;
    }

    public static int indexOfChars(String string, char[] chars) {
        return indexOfChars(string, chars, 0);
    }

    /**
     * Returns the very first index of any char from provided string, starting from specified index offset.
     * Returns index of founded char, or -1 if nothing found.
     */
    public static int indexOfChars(String string, char[] chars, int startindex) {
        int stringLen = string.length();
        for (int i = startindex; i < stringLen; i++) {
            char c = string.charAt(i);
            for (char aChar : chars) {
                if (c == aChar) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * Returns if string starts with given character.
     */
    public static boolean startsWithChar(String s, char c) {
        return s.length() != 0 && s.charAt(0) == c;
    }

    /**
     * Returns true if string contains only digits.
     */
    public static boolean containsOnlyDigits(String string) {
        int size = string.length();
        for (int i = 0; i < size; i++) {
            char c = string.charAt(i);
            if (!CharKit.isDigit(c)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns true if string {@link #containsOnlyDigits(String) contains only digits}
     * or signs plus or minus.
     */
    public static boolean containsOnlyDigitsAndSigns(String string) {
        int size = string.length();
        for (int i = 0; i < size; i++) {
            char c = string.charAt(i);
            if ((!CharKit.isDigit(c)) && (c != '-') && (c != '+')) {
                return false;
            }
        }
        return true;
    }

    /**
     * Splits a string in several parts (tokens) that are separated by delimiter
     * characters. Delimiter may contains any number of character and it is
     * always surrounded by two strings.
     *
     * @param src source to examine
     * @param d   string with delimiter characters
     * @return array of tokens
     */
    public static String[] splitc(String src, String d) {
        if ((d.length() == 0) || (src.length() == 0)) {
            return new String[]{src};
        }
        char[] delimiters = d.toCharArray();
        char[] srcc = src.toCharArray();

        int maxparts = srcc.length + 1;
        int[] start = new int[maxparts];
        int[] end = new int[maxparts];

        int count = 0;

        start[0] = 0;
        int s = 0, e;
        if (CharKit.equalsOne(srcc[0], delimiters)) {    // string starts with delimiter
            end[0] = 0;
            count++;
            s = CharKit.findFirstDiff(srcc, 1, delimiters);
            if (s == -1) {                            // nothing after delimiters
                return new String[]{StringUtils.EMPTY, StringUtils.EMPTY};
            }
            start[1] = s;                            // new start
        }
        while (true) {
            // find new end
            e = CharKit.findFirstEqual(srcc, s, delimiters);
            if (e == -1) {
                end[count] = srcc.length;
                break;
            }
            end[count] = e;

            // find new start
            count++;
            s = CharKit.findFirstDiff(srcc, e, delimiters);
            if (s == -1) {
                start[count] = end[count] = srcc.length;
                break;
            }
            start[count] = s;
        }
        count++;
        String[] result = new String[count];
        for (int i = 0; i < count; i++) {
            result[i] = src.substring(start[i], end[i]);
        }
        return result;
    }

    /**
     * Splits a string in several parts (tokens) that are separated by single delimiter
     * characters. Delimiter is always surrounded by two strings.
     *
     * @param src       source to examine
     * @param delimiter delimiter character
     * @return array of tokens
     */
    public static String[] splitc(String src, char delimiter) {
        if (src.length() == 0) {
            return new String[]{StringUtils.EMPTY};
        }
        char[] srcc = src.toCharArray();

        int maxparts = srcc.length + 1;
        int[] start = new int[maxparts];
        int[] end = new int[maxparts];

        int count = 0;

        start[0] = 0;
        int s = 0, e;
        if (srcc[0] == delimiter) {    // string starts with delimiter
            end[0] = 0;
            count++;
            s = CharKit.findFirstDiff(srcc, 1, delimiter);
            if (s == -1) {                            // nothing after delimiters
                return new String[]{StringUtils.EMPTY, StringUtils.EMPTY};
            }
            start[1] = s;                            // new start
        }
        while (true) {
            // find new end
            e = CharKit.findFirstEqual(srcc, s, delimiter);
            if (e == -1) {
                end[count] = srcc.length;
                break;
            }
            end[count] = e;

            // find new start
            count++;
            s = CharKit.findFirstDiff(srcc, e, delimiter);
            if (s == -1) {
                start[count] = end[count] = srcc.length;
                break;
            }
            start[count] = s;
        }
        count++;
        String[] result = new String[count];
        for (int i = 0; i < count; i++) {
            result[i] = src.substring(start[i], end[i]);
        }
        return result;
    }

    /**
     * Returns if string ends with provided character.
     */
    public static boolean endsWithChar(String s, char c) {
        return s.length() != 0 && s.charAt(s.length() - 1) == c;
    }

}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.core.kits.lang;

import com.google.common.collect.Maps;

import goja.core.StringPool;

import org.apache.commons.lang3.StringUtils;

import java.text.NumberFormat;
import java.util.Locale;
import java.util.Map;

/**
 *

 * .
 *

 *
 * @author sagyf yang
 * @version 1.0 2014-01-31 18:17
 * @since JDK 1.6
 */
@SuppressWarnings("UnusedDeclaration")
public abstract class LocaleKit {

    protected static Map<String, LocaleData> locales = Maps.newHashMap();

    // ---------------------------------------------------------------- locale cache

    /**
     * Lookups for locale data and creates new if it doesn't exist.
     */
    protected static LocaleData lookupLocaleData(String code) {
        LocaleData localeData = locales.get(code);
        if (localeData == null) {
            String[] data = decodeLocaleCode(code);
            localeData = new LocaleData(new Locale(data[0], data[1], data[2]));
            locales.put(code, localeData);
        }
        return localeData;
    }

    protected static LocaleData lookupLocaleData(Locale locale) {
        return lookupLocaleData(resolveLocaleCode(locale));
    }

    /**
     * Returns Locale from cache.
     */
    public static Locale getLocale(String language, String country, String variant) {
        LocaleData localeData = lookupLocaleData(resolveLocaleCode(language, country, variant));
        return localeData.locale;
    }

    // ---------------------------------------------------------------- get cached locales

    /**
     * Returns Locale from cache.
     */
    public static Locale getLocale(String language, String country) {
        return getLocale(language, country, null);
    }

    /**
     * Returns Locale from cache where Locale may be specified also using language code.
     * Converts a locale string like "en", "en_US" or "en_US_win" to new Java locale object.
     */
    public static Locale getLocale(String languageCode) {
        LocaleData localeData = lookupLocaleData(languageCode);
        return localeData.locale;
    }

    /**
     * Transforms locale data to locale code. null values are allowed.
     */
    public static String resolveLocaleCode(String lang, String country, String variant) {
        StringBuilder code = new StringBuilder(lang);
        if (!StringUtils.isEmpty(country)) {
            code.append('_').append(country);
            if (!StringUtils.isEmpty(variant)) {
                code.append('_').append(variant);
            }
        }
        return code.toString();
    }

    // ---------------------------------------------------------------- convert

    /**
     * Resolves locale code from locale.
     */
    public static String resolveLocaleCode(Locale locale) {
        return resolveLocaleCode(locale.getLanguage(), locale.getCountry(), locale.getVariant());
    }

    /**
     * Decodes locale code in string array that can be used for Locale constructor.
     */
    public static String[] decodeLocaleCode(String localeCode) {
        String result[] = new String[3];
        String[] data = CharKit.splitc(localeCode, '_');
        result[0] = data[0];
        result[1] = result[2] = StringPool.EMPTY;
        if (data.length >= 2) {
            result[1] = data[1];
            if (data.length >= 3) {
                result[2] = data[2];
            }
        }
        return result;
    }

    /**
     * Returns cached NumberFormat instance for specified locale.
     */
    public static NumberFormat getNumberFormat(Locale locale) {
        LocaleData localeData = lookupLocaleData(locale);
        NumberFormat nf = localeData.numberFormat;
        if (nf == null) {
            nf = NumberFormat.getInstance(locale);
            localeData.numberFormat = nf;
        }
        return nf;
    }

    // ---------------------------------------------------------------- locale elements

    /**
     * Holds all per-Locale data.
     */
    static class LocaleData {
        final Locale locale;
        NumberFormat numberFormat;

        LocaleData(Locale locale) {
            this.locale = locale;
        }
    }
}


/*
 * Copyright (C) 2011-2013 Eiichiro Uchiumi. All Rights Reserved.
 *
 * 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.eiichiro.bootleg.xml;

import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

import org.eiichiro.bootleg.AbstractResponse;
import org.eiichiro.bootleg.MediaType;
import org.eiichiro.bootleg.WebContext;
import org.eiichiro.reverb.lang.UncheckedException;

/**
 * {@code XMLResponse} is a XML-based implementation of {@code Response}.
 * If the generated/negotiated MIME media type is 'application/xml', default
 * configuration of Bootleg uses this implementation.
 * Web endpoint method invocation result is deserialized to XML with JAXB (Java
 * Architecture for XML Binding). UTF-8 is used for the character encoding
 * (MIME charset) at any time.
 *
 * @author Eiichiro Uchiumi
 */
public class XMLResponse extends AbstractResponse {

	public XMLResponse() {
		mediaType = MediaType.APPLICATION_XML;
	}

	/**
	 * Writes {@code XMLResponse} to the current {@code WebContext}.
	 *
	 * @param context The current {@code WebContext}.
	 */
	public void to(WebContext context) {
		if (entity == null) {
			return;
		}

		HttpServletResponse response = context.response();

		try {
			response.setCharacterEncoding("UTF-8");
			response.setHeader("Content-Type", mediaType);

			if (status > 0) {
				response.setStatus(status);
			}

			if (entity instanceof String) {
				response.getWriter().write((String) entity);
				return;
			}

			Marshaller marshaller = JAXBContext.newInstance(entity.getClass()).createMarshaller();
			marshaller.setAdapter(new ValueTypeXmlAdapter<Object>());
			marshaller.marshal(entity, response.getWriter());
		} catch (Exception e) {
			throw new UncheckedException(e);
		}
	}

}


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

package com.cloudbees.sdk.extensibility;

import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.BindingAnnotation;
import com.google.inject.Injector;
import com.google.inject.Key;

import javax.inject.Named;
import javax.inject.Qualifier;
import java.lang.annotation.Annotation;

/**
 * Responsible for producing {@link Binding}s inside {@link Injector}
 * from a discovered extension.
 *
 * @author Kohsuke Kawaguchi
 * @see ExtensionPoint
 */
public abstract class ExtensionLoaderModule<T> extends AbstractModule {
    /**
     * The type of the extension implementation discovered.
     */
    protected Class<? extends T> impl;
    /**
     * The type of the extension point.
     */
    protected Class<T> extensionPoint;

    /**
     * Called by {@link ExtensionFinder} to initialize this module.
     */
    public void init(Class<? extends T> impl, Class<T> extensionPoint) {
        this.impl = impl;
        this.extensionPoint = extensionPoint;
    }

    /**
     * The default implementation of {@link ExtensionLoaderModule}.
     *
     * If the discovered implementation has any {@linkplain BindingAnnotation binding annotation},
     * that is used as the key. This allows an extension point that supports named lookup, such as:
     *
     * <pre>
     * @Retention(RUNTIME)
     * @Target(TYPE)
     * @Indexed
     * @BindingAnnotation
     * @ExtensionImplementation
     * public @interface CLICommand {
     *     String value();
     * }
     *
     * @CLICommand("acme")
     * public class AcmeCommand extends Command { ... }
     *
     * // then somewhere else
     * Command cmd = injector.getBinding(Key.get(Command.class,AnnotationLiteral.of(CLICommand.class,"acme"))
     * </pre>
     *
     * If no binding annotation is present, this implementation creates a unique binding
     * annotation, so that at least it can be looked up via {@link ExtensionPointList}.
     */
    static class Default<T> extends ExtensionLoaderModule<T> {
        @Override
        protected void configure() {
            Annotation qa = findQualifierAnnotation(impl);
            if (qa==null)
               // this is just to make it unique among others that implement the same contract
                qa = AnnotationLiteral.of(Named.class,impl.getName());
            binder().withSource(impl).bind(Key.get(extensionPoint, qa)).to(impl);
            bind(impl);
        }

        private <T> Annotation findQualifierAnnotation(Class<? extends T> impl) {
            for (Annotation a : impl.getAnnotations()) {
                Class<? extends Annotation> at = a.annotationType();
                if (at.isAnnotationPresent(Qualifier.class)
                 || at.isAnnotationPresent(BindingAnnotation.class))
                    return a;
            }
            return null;
        }
    }
}


package goja.core.libs;

import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Map;

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

/**
 * <p> </p>
 *
 * @author sog
 * @version 1.0
 * @since JDK 1.6
 */
public class Freemarkers {

    private static Logger log = LoggerFactory.getLogger(Freemarkers.class);

    /**
     * 渲染模板
     */
    public static String render(String templateContent, Map<String, Object> paramMap) {
        StringWriter writer = new StringWriter();
        try {
            Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
            cfg.setTemplateLoader(new StringTemplateLoader(templateContent));
            cfg.setDefaultEncoding("UTF-8");

            Template template = cfg.getTemplate("");

            template.process(paramMap, writer);
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage());
        } catch (TemplateException e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return writer.toString();
    }

    static class StringTemplateLoader implements TemplateLoader {

        private String template;

        public StringTemplateLoader(String template) {
            this.template = template;
            if (template == null) {
                this.template = "";
            }
        }

        public void closeTemplateSource(Object templateSource) throws IOException {
            ((StringReader) templateSource).close();
        }

        public Object findTemplateSource(String name) throws IOException {
            return new StringReader(template);
        }

        public long getLastModified(Object templateSource) {
            return 0;
        }

        public Reader getReader(Object templateSource, String encoding) throws IOException {
            return (Reader) templateSource;
        }
    }

}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.job;

import goja.Invoker;
import goja.core.libs.Promise;
import goja.core.libs.Time;

import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * A job is an asynchronously executed unit of work
 *
 * @param <V> The job result type (if any)
 */
public class Job<V> extends Invoker.Invocation implements Callable<V> {

    public static final  String           invocationType = "GojaJob";
    private static final org.slf4j.Logger logger         = LoggerFactory.getLogger(Job.class);
    protected ExecutorService executor;

    protected long    lastRun  = 0;
    protected boolean wasError = false;

    protected Throwable lastException = null;

    Date nextPlannedExecution = null;

    @Override
    public Invoker.InvocationContext getInvocationContext() {
        return new Invoker.InvocationContext(invocationType, this.getClass().getAnnotations());
    }

    /**
     * Here you do the job
     */
    public void doJob() throws Exception {
    }

    /**
     * Here you do the job and return a result
     */
    public V doJobWithResult() throws Exception {
        doJob();
        return null;
    }

    @Override
    public void execute() throws Exception {
    }

    /**
     * Start this job now (well ASAP)
     *
     * @return the job completion
     */
    public Promise<V> now() {
        final Promise<V> smartFuture = new Promise<V>();
        JobsPlugin.executor.submit(getJobCallingCallable(smartFuture));
        return smartFuture;
    }

    /**
     * Start this job in several seconds
     *
     * @return the job completion
     */
    public Promise<V> in(String delay) {
        return in(Time.parseDuration(delay));
    }

    /**
     * Start this job in several seconds
     *
     * @return the job completion
     */
    public Promise<V> in(int seconds) {
        final Promise<V> smartFuture = new Promise<V>();

        JobsPlugin.executor.schedule(getJobCallingCallable(smartFuture), seconds, TimeUnit.SECONDS);

        return smartFuture;
    }

    private Callable<V> getJobCallingCallable(final Promise<V> smartFuture) {
        return new Callable<V>() {
            public V call() throws Exception {
                try {
                    V result = Job.this.call();
                    if (smartFuture != null) {
                        smartFuture.invoke(result);
                    }
                    return result;
                } catch (Exception e) {
                    if (smartFuture != null) {
                        smartFuture.invokeWithException(e);
                    }
                    return null;
                }
            }
        };
    }

    /**
     * Run this job every n seconds
     */
    public void every(String delay) {
        every(Time.parseDuration(delay));
    }

    /**
     * Run this job every n seconds
     */
    public void every(int seconds) {
        JobsPlugin.executor.scheduleWithFixedDelay(this, seconds, seconds, TimeUnit.SECONDS);
    }

    // Customize Invocation
    @Override
    public void onException(Throwable e) {
        wasError = true;
        lastException = e;
        try {
            super.onException(e);
        } catch (Throwable ex) {
            logger.error("Error during job execution (%s)", this, ex);
        }
    }

    @Override
    public void run() {
        call();
    }

    @Override
    public V call() {
        try {
            if (init()) {
                before();
                V result;

                lastException = null;
                lastRun = System.currentTimeMillis();
                result = doJobWithResult();
                wasError = false;
                after();
                return result;
            }
        } catch (Throwable e) {
            onException(e);
        } finally {
            _finally();
        }
        return null;
    }

    @Override
    public void _finally() {
        super._finally();
        if (executor == JobsPlugin.executor) {
            JobsPlugin.scheduleForCRON(this);
        }
    }

    @Override
    public String toString() {
        return this.getClass().getName();
    }

    public boolean isWasError() {
        return wasError;
    }

    public void setWasError(boolean wasError) {
        this.wasError = wasError;
    }

    public Throwable getLastException() {
        return lastException;
    }

    public void setLastException(Throwable lastException) {
        this.lastException = lastException;
    }

    public Date getNextPlannedExecution() {
        return nextPlannedExecution;
    }

    public void setNextPlannedExecution(Date nextPlannedExecution) {
        this.nextPlannedExecution = nextPlannedExecution;
    }
}


package com.truward.brikar.server.util;

import org.eclipse.jetty.util.resource.Resource;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Paths;

/**
 * @author Alexander Shabanov
 */
public final class JettyResourceUtil {
  private JettyResourceUtil() {} // hidden

  public static Resource createResource(String resourcePath) throws IOException {
    if (resourcePath.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
      return Resource.newClassPathResource(resourcePath.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length()));
    } else if (resourcePath.startsWith(ResourceUtils.FILE_URL_PREFIX)) {
      return Resource.newResource(new File(resourcePath.substring(ResourceUtils.FILE_URL_PREFIX.length())));
    } else if (resourcePath.startsWith("/")) {
      return Resource.newResource(new File(resourcePath));
    }

    try {
      if (Files.exists(Paths.get(resourcePath))) {
        return Resource.newResource(new File(resourcePath));
      }

      // path seem to be valid but nothing exists there
      throw new IOException("Missing file at resourcePath=" + resourcePath);
    } catch (InvalidPathException e) {
      throw new IOException("Invalid path or unknown protocol in resourcePath=" + resourcePath, e);
    }
  }
}