All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nablarch.fw.web.handler.ContentDispositionRawValue Maven / Gradle / Ivy

package nablarch.fw.web.handler;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import nablarch.fw.web.download.encorder.DownloadFileNameEncoder;
import nablarch.fw.web.download.encorder.UrlDownloadFileNameEncoder;

/**
 * エンコードされていない生のContent-Dispositionヘッダ値を表すクラス。
 * 
 * @author Taichi Uragami
 *
 */
class ContentDispositionRawValue {

    /** filenameキー */
    private static final String FILENAME_PARAM_NAME = normalizeKey("filename");
    /** filename*キー */
    private static final String FILENAME_EXT_PARAM_NAME = normalizeKey("filename*");
    /** disposition-typeを抽出するための正規表現 */
    private static final Pattern TYPE_PATTERN = Pattern.compile("\\s*([^;\\s]+)");
    /** パラメーターを抽出するための正規表現 */
    private static final Pattern PARAM_PATTERN = Pattern
            .compile("\\s*;\\s*([^=\\s]+)\\s*=\\s*([^;\\s]+)");
    /** UTF-8でURLエンコードをする{@link DownloadFileNameEncoder} */
    private static final UrlDownloadFileNameEncoder UTF8_URL_ENCODER = new UrlDownloadFileNameEncoder();

    /**
     * inline、attachementなどのdisposition-type
     * 
     * @see https://tools.ietf.org/html/rfc6266#section-4.2
     */
    private final String type;
    /** パラメーターのリスト */
    private final List params;
    /** filenameパラメーター。nullの場合もある。 */
    private final Param filenameParam;
    /** filename*パラメーターを保持している場合はtrue */
    private final boolean containsFilenameExtParam;

    /**
     * コンストラクタ。
     * 
     * @param rawValue エンコードされていない生のContent-Dispositionヘッダ値
     */
    public ContentDispositionRawValue(final String rawValue) {
        final Matcher typeMatcher = TYPE_PATTERN.matcher(rawValue);
        typeMatcher.find();
        this.type = typeMatcher.group(1);

        int index = typeMatcher.group().length();

        final LinkedHashMap paramMap = new LinkedHashMap();
        final Matcher paramMatcher = PARAM_PATTERN.matcher(rawValue);
        while (paramMatcher.find(index)) {
            final String key = paramMatcher.group(1);
            final String value = paramMatcher.group(2);
            final Param param = new Param(key, value);
            param.putTo(paramMap);
            index += paramMatcher.group().length();
        }
        this.params = new ArrayList(paramMap.values());

        this.filenameParam = paramMap.get(FILENAME_PARAM_NAME);
        this.containsFilenameExtParam = paramMap.containsKey(FILENAME_EXT_PARAM_NAME);
    }

    /**
     *エンコードする必要があるかどうかを返す。
     * 
     * @return エンコードする必要があればtrue
     */
    public boolean needsToBeEncoded() {
        return filenameParam != null && filenameParam.quoted;
    }

    /**
     * エンコードされていないファイル名を取得する。
     * 
     * @return エンコードされていないファイル名
     */
    public String getRawFileName() {
        return filenameParam.value;
    }

    /**
     * エンコード済みのContent-Dispositionヘッダ値を組み立てる。
     * 
     * @param encodedFileName エンコード済みのファイル名
     * @return エンコード済みのContent-Dispositionヘッダ値
     */
    public String buildEncodedValue(final String encodedFileName) {
        final StringBuilder buf = new StringBuilder();
        buf.append(type);
        for (Param param : params) {
            param.appendTo(buf, encodedFileName);
        }
        return buf.toString();
    }

    /**
     * パラメーターのキーを正規化する。
     * 
     * 

* パラメーターのキーはcase insensitiveなので検索のために正規化する。 *

* * @param key 正規化する前のキー * @return 正規化したキー */ private static String normalizeKey(String key) { return key.toLowerCase(); } /** * Content-Dispositionヘッダの値が含んでいるパラメーターを表すクラス。 * */ private class Param { /** コンストラクタで受け取ったキー */ final String originalKey; /** * キーに{@link ContentDispositionRawValue#normalizeKey(String)}を適用したもの。 * キーの比較には{@link #originalKey}ではなくこちらを使用する。 */ final String normalizedKey; /** 値。ダブルクォートで囲まれていた場合はダブルクォートが取り除かれた状態 */ final String value; /** 値がダブルクォートで囲まれていたか */ final boolean quoted; /** * コンストラクタ。 * * @param key キー * @param value 値 */ Param(final String key, final String value) { this.originalKey = key; this.normalizedKey = normalizeKey(key); if (value.charAt(0) == '"') { this.value = value.substring(1, value.length() - 1); this.quoted = true; } else { this.value = value; this.quoted = false; } } /** * StringBuilderへパラメーターを追加する。 * *

* 自身がfilenameパラメーターなら引数で受け取ったencodedFileNameを値として追加する。 * また、自身がfilenameパラメーターかつ明示的にfilename*パラメーターが設定されていない場合、 * filenameパラメーターに設定された値をもとにしてfilename*パラメーターを追加する。 *

* * @param buf ここにパラメーターが追加される * @param encodedFileName エンコード済みのファイル名 */ void appendTo(final StringBuilder buf, final String encodedFileName) { final boolean isFilenameParam = normalizedKey.equals(FILENAME_PARAM_NAME); if (isFilenameParam && containsFilenameExtParam == false) { buf.append("; ") .append(FILENAME_EXT_PARAM_NAME) .append('=') .append("UTF-8''").append(UTF8_URL_ENCODER.encode(value)); } buf.append("; ").append(originalKey).append('='); if (quoted) { buf.append('"'); } buf.append(isFilenameParam ? encodedFileName : value); if (quoted) { buf.append('"'); } } /** * 引数で渡された{@link LinkedHashMap}に自身を加える。 * * @param paramMap ここに自身を加える */ void putTo(final LinkedHashMap paramMap) { paramMap.put(normalizedKey, this); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy