Package com.alibaba.citrus.service.requestcontext.locale.impl

Source Code of com.alibaba.citrus.service.requestcontext.locale.impl.SetLocaleRequestContextImpl

/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* 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 com.alibaba.citrus.service.requestcontext.locale.impl;

import static com.alibaba.citrus.util.StringUtil.*;

import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.alibaba.citrus.service.requestcontext.RequestContext;
import com.alibaba.citrus.service.requestcontext.locale.SetLocaleRequestContext;
import com.alibaba.citrus.service.requestcontext.support.AbstractRequestContextWrapper;
import com.alibaba.citrus.service.requestcontext.support.AbstractRequestWrapper;
import com.alibaba.citrus.service.requestcontext.support.AbstractResponseWrapper;
import com.alibaba.citrus.util.i18n.LocaleInfo;
import com.alibaba.citrus.util.i18n.LocaleUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* <code>SetLocaleRequestContext</code>的实现。
*
* @author Michael Zhou
*/
public class SetLocaleRequestContextImpl extends AbstractRequestContextWrapper implements SetLocaleRequestContext {
    private final static Logger log = LoggerFactory.getLogger(SetLocaleRequestContext.class);
    private Pattern              inputCharsetPattern;
    private Pattern              outputCharsetPattern;
    private SetLocaleOverrider[] overriders;
    private Locale               defaultLocale;
    private String               defaultCharset;
    private String               sessionKey;
    private String               paramKey;
    private Locale               locale;

    /**
     * 包装一个<code>RequestContext</code>对象。
     *
     * @param wrappedContext 被包装的<code>RequestContext</code>
     */
    public SetLocaleRequestContextImpl(RequestContext wrappedContext) {
        super(wrappedContext);
        setRequest(new RequestWrapper(wrappedContext.getRequest()));
        setResponse(new ResponseWrapper(wrappedContext.getResponse()));
    }

    public void setInputCharsetPattern(Pattern inputCharsetPattern) {
        this.inputCharsetPattern = inputCharsetPattern;
    }

    public void setOutputCharsetPattern(Pattern outputCharsetPattern) {
        this.outputCharsetPattern = outputCharsetPattern;
    }

    public void setOverriders(SetLocaleOverrider[] overriders) {
        this.overriders = overriders;
    }

    public void setDefaultLocale(Locale defaultLocale) {
        this.defaultLocale = defaultLocale;
    }

    public void setDefaultCharset(String defaultCharset) {
        this.defaultCharset = defaultCharset;
    }

    public void setSessionKey(String sessionKey) {
        this.sessionKey = sessionKey;
    }

    public void setParamKey(String paramKey) {
        this.paramKey = paramKey;
    }

    /**
     * 取得content type。
     *
     * @return content type,包括charset的定义
     */
    public String getResponseContentType() {
        return ((ResponseWrapper) getResponse()).getContentType();
    }

    /**
     * 设置content type。 如果content type不包含charset,并且
     * <code>getCharacterEncoding</code>被设置,则加上charset标记。
     * <p>
     * 如果<code>appendCharset</code><code>false</code>,则content
     * type中将不包含charset标记。
     * </p>
     *
     * @param contentType   content type
     * @param appendCharset 输出字符集
     */
    public void setResponseContentType(String contentType, boolean appendCharset) {
        ((ResponseWrapper) getResponse()).setContentType(contentType, appendCharset);
    }

    /**
     * 设置response输出字符集。注意,此方法必须在第一次<code>getWriter</code>之前执行。
     *
     * @param charset 输出字符集,如果charset为<code>null</code>
     *                ,则从contentType中删除charset标记
     */
    public void setResponseCharacterEncoding(String charset) {
        ((ResponseWrapper) getResponse()).setCharacterEncoding(charset);
    }

    /** 设置locale、输入charset、输出charset。 */
    @Override
    public void prepare() {
        // 首先从session中取得localeInfo,如果不存在,则取得默认值。
        LocaleInfo localeInfo = getLocaleInfoFromSessionOrGetTheDefaultValue();

        // 匹配request uri
        SetLocaleOverrider overrider = getMatchedOverrider();

        // 将input charset设置到request中,以便进一步解析request parameters。
        setInputCharsetToRequest(localeInfo.getCharset().name(), overrider);

        // 现在已经可以安全地调用getParameter()方法,以解析参数了,因为input charset已经被设置。
        // 从parameter中取locale信息,如果存在,则设置到cookie中。
        if (PARAMETER_SET_TO_DEFAULT_VALUE.equalsIgnoreCase(getRequest().getParameter(paramKey))) {
            localeInfo = resetLocaleInfoInSession();
        } else {
            String outputCharset = getOutputCharsetFromQueryString();

            if (outputCharset == null) {
                outputCharset = getOutputCharsetOverridden(overrider);
            }

            // 如果parameter中指明了locale,则取得并保存之
            LocaleInfo paramLocale = getLocaleInfoFromParameter();

            if (paramLocale != null) {
                getRequest().getSession().setAttribute(sessionKey, paramLocale.toString());

                // 用parameter中的locale信息覆盖session中的localeInfo信息。
                localeInfo = paramLocale;
            }

            if (outputCharset != null) {
                localeInfo = new LocaleInfo(localeInfo.getLocale(), outputCharset);
            }
        }

        // 设置用于输出的locale信息。
        getResponse().setLocale(localeInfo.getLocale());
        setResponseCharacterEncoding(localeInfo.getCharset().name());
        log.debug("Set OUTPUT locale:charset to " + localeInfo);

        // 设置thread context中的locale信息。
        LocaleUtil.setContext(localeInfo.getLocale(), localeInfo.getCharset().name());
        log.debug("Set THREAD CONTEXT locale:charset to " + localeInfo);

        this.locale = localeInfo.getLocale();
    }

    /** 找出request uri匹配的overrider。 */
    private SetLocaleOverrider getMatchedOverrider() {
        if (overriders != null) {
            String uri = getRequest().getRequestURI();

            for (SetLocaleOverrider overrider : overriders) {
                if (overrider.getRequestUriPattern().matcher(uri).find()) {
                    return overrider;
                }
            }
        }

        return null;
    }

    /**
     * 从当前请求的session中取得用户的locale设置。如果session未设置,则取默认值。
     *
     * @return 当前session中的locale设置或默认值
     */
    private LocaleInfo getLocaleInfoFromSessionOrGetTheDefaultValue() {
        HttpSession session = getRequest().getSession(false); // 如果session不存在,也不用创建。
        String localeName = session == null ? null : (String) getRequest().getSession().getAttribute(sessionKey);
        LocaleInfo localeInfo;

        if (isEmpty(localeName)) {
            localeInfo = new LocaleInfo(defaultLocale, defaultCharset);
        } else {
            localeInfo = LocaleInfo.parse(localeName);

            if (!LocaleUtil.isLocaleSupported(localeInfo.getLocale())
                || !LocaleUtil.isCharsetSupported(localeInfo.getCharset().name())) {
                log.warn("Invalid locale " + localeInfo + " from session");

                localeInfo = new LocaleInfo(defaultLocale, defaultCharset);
            }
        }

        return localeInfo;
    }

    /**
     * 设置input charset。
     * 假如query string中指定了input charset,则采用之。
     * 否则使用参数所指定的值作为input charset。
     *
     * @param inputCharset 默认input charset,或是从session中所取得的input charset
     * @param overrider    如果有,则从中取得input charset
     */
    private void setInputCharsetToRequest(String inputCharset, SetLocaleOverrider overrider) {
        try {
            String charset = getInputCharsetFromQueryString();

            if (charset == null) {
                charset = getInputCharsetOverridden(overrider);
            }

            if (charset != null) {
                inputCharset = charset;
            }

            getRequest().setCharacterEncoding(inputCharset);

            log.debug("Set INPUT charset to " + inputCharset);
        } catch (UnsupportedEncodingException e) {
            try {
                getRequest().setCharacterEncoding(CHARSET_DEFAULT);

                log.warn("Unknown charset {}.  Set INPUT charset to {}", inputCharset, CHARSET_DEFAULT);
            } catch (UnsupportedEncodingException ee) {
                log.error("Failed to set INPUT charset to {}", CHARSET_DEFAULT);
            }
        }
    }

    /** 试图从queryString中取得inputCharset。 */
    private String getInputCharsetFromQueryString() {
        String inputCharsetQS = null;
        String queryString = getRequest().getQueryString();

        if (queryString != null) {
            Matcher matcher = inputCharsetPattern.matcher(queryString);

            if (matcher.find()) {
                String charset = null;

                if (matcher.groupCount() >= 2) {
                    charset = matcher.group(2);
                }

                if (LocaleUtil.isCharsetSupported(charset)) {
                    inputCharsetQS = charset;
                } else {
                    log.warn("Specified input charset is not supported: " + charset);
                }
            }
        }

        return inputCharsetQS;
    }

    private String getInputCharsetOverridden(SetLocaleOverrider overrider) {
        String inputCharsetOverridden = null;

        if (overrider != null) {
            String charset = overrider.getInputCharset();

            if (charset != null) {
                if (LocaleUtil.isCharsetSupported(charset)) {
                    inputCharsetOverridden = charset;
                } else {
                    log.warn("Specified overridden input charset is not supported: " + charset);
                }
            }
        }

        return inputCharsetOverridden;
    }

    /**
     * 恢复session中所保存的localeInfo信息。
     *
     * @return 默认的localeInfo
     */
    private LocaleInfo resetLocaleInfoInSession() {
        HttpSession session = getRequest().getSession(false); // 如果session不存在,也不用创建

        if (session != null) {
            session.removeAttribute(sessionKey);
        }

        LocaleInfo localeInfo = new LocaleInfo(defaultLocale, defaultCharset);

        log.debug("Reset OUTPUT locale:charset to " + localeInfo);

        return localeInfo;
    }

    /** 试图从queryString中取得outputCharset。 */
    private String getOutputCharsetFromQueryString() {
        String queryString = getRequest().getQueryString();
        String outputCharsetQS = null;

        if (queryString != null) {
            Matcher matcher = outputCharsetPattern.matcher(queryString);

            if (matcher.find()) {
                String charset = null;

                if (matcher.groupCount() >= 2) {
                    charset = matcher.group(2);
                }

                if (LocaleUtil.isCharsetSupported(charset)) {
                    outputCharsetQS = charset;
                } else {
                    log.warn("Specified output charset is not supported: " + charset);
                }
            }
        }

        return outputCharsetQS;
    }

    private String getOutputCharsetOverridden(SetLocaleOverrider overrider) {
        String outputCharsetOverridden = null;

        if (overrider != null) {
            String charset = overrider.getOutputCharset();

            if (charset != null) {
                if (LocaleUtil.isCharsetSupported(charset)) {
                    outputCharsetOverridden = charset;
                } else {
                    log.warn("Specified overridden output charset is not supported: " + charset);
                }
            }
        }

        return outputCharsetOverridden;
    }

    /**
     * 从当前请求的参数中取得用户的locale设置。如果参数未设置,则返回<code>null</code>
     *
     * @return 当前request parameters中的locale设置
     */
    private LocaleInfo getLocaleInfoFromParameter() {
        String localeName = getRequest().getParameter(paramKey);
        LocaleInfo localeInfo = null;

        if (!isEmpty(localeName)) {
            localeInfo = LocaleInfo.parse(localeName);

            if (!LocaleUtil.isLocaleSupported(localeInfo.getLocale())
                || !LocaleUtil.isCharsetSupported(localeInfo.getCharset().name())) {
                log.warn("Invalid locale " + localeInfo + " from request parameters");

                localeInfo = new LocaleInfo(defaultLocale, defaultCharset);
            }
        }

        return localeInfo;
    }

    /** 包装request。 */
    private class RequestWrapper extends AbstractRequestWrapper {
        public RequestWrapper(HttpServletRequest request) {
            super(SetLocaleRequestContextImpl.this, request);
        }

        @Override
        public Locale getLocale() {
            return locale == null ? super.getLocale() : locale;
        }
    }

    /** 包装response。 */
    private class ResponseWrapper extends AbstractResponseWrapper {
        private String contentType;
        private String charset;

        public ResponseWrapper(HttpServletResponse response) {
            super(SetLocaleRequestContextImpl.this, response);
        }

        /**
         * 取得content type。
         *
         * @return content type,包括charset的定义
         */
        @Override
        public String getContentType() {
            return contentType;
        }

        /**
         * 设置content type。 如果content type不包含charset,并且
         * <code>getCharacterEncoding</code>被设置,则加上charset标记。
         *
         * @param contentType content type
         */
        @Override
        public void setContentType(String contentType) {
            setContentType(contentType, true);
        }

        /**
         * 设置content type。 如果content type不包含charset,并且
         * <code>getCharacterEncoding</code>被设置,则加上charset标记。
         * <p>
         * 如果<code>appendCharset</code><code>false</code>,则content
         * type中将不包含charset标记。
         * </p>
         *
         * @param contentType   content type
         * @param appendCharset 输出字符集
         */
        public void setContentType(String contentType, boolean appendCharset) {
            // 取得指定contentType中的"; charset="部分。
            String charset = trimToNull(substringAfterLast(contentType, "charset="));

            // 如果未指定charset,则从this.charset中取,这是由setCharacterEncoding方法所设置的。
            if (charset == null) {
                charset = this.charset;
            }

            // 除去contentType中的charset部分。
            this.contentType = trimToNull(substringBefore(contentType, ";"));

            // 调用setCharacterEncoding方法加上charset。
            setCharacterEncoding(appendCharset ? charset : null);
        }

        /** 取得response的输出字符集。 */
        @Override
        public String getCharacterEncoding() {
            return super.getCharacterEncoding();
        }

        /**
         * 设置response输出字符集。注意,此方法必须在第一次<code>getWriter</code>之前执行。
         *
         * @param charset 输出字符集,如果charset为<code>null</code>
         *                ,则从contentType中删除charset标记
         */
        @Override
        public void setCharacterEncoding(String charset) {
            this.charset = charset;

            if (contentType != null) {
                contentType = trimToNull(substringBefore(contentType, ";"));

                if (charset != null) {
                    contentType += "; charset=" + charset;
                }

                log.debug("Set content type to " + contentType);

                super.setContentType(contentType);
            } else {
                // 假如没有设置contentType,确保charset仍然被设置。
                // 适用于Servlet API 2.4及更新版。
                try {
                    super.setCharacterEncoding(charset);
                } catch (NoSuchMethodError e) {
                }
            }
        }
    }
}
TOP

Related Classes of com.alibaba.citrus.service.requestcontext.locale.impl.SetLocaleRequestContextImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.