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

com.github.mygreen.supercsv.localization.MessageInterpolator Maven / Gradle / Ivy

Go to download

CSVのJavaライブラリであるSuperCSVに、アノテーション機能を追加したライブラリです。

There is a newer version: 2.3
Show newest version
package com.github.mygreen.supercsv.localization;

import java.util.Formatter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

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

import com.github.mygreen.supercsv.expression.CustomFunctions;
import com.github.mygreen.supercsv.expression.ExpressionEvaluationException;
import com.github.mygreen.supercsv.expression.ExpressionLanguage;
import com.github.mygreen.supercsv.expression.ExpressionLanguageJEXLImpl;
import com.github.mygreen.supercsv.util.StackUtils;

/**
 * 名前付き変数のメッセージをフォーマットするクラス。
 * 

{...}の場合、変数を単純に置換する。 *

${...}の場合、EL式を利用し処理する。 *

文字'$', '{', '}'は特殊文字のため、\でエスケープを行う。 *

ELのパーサは、{@link ExpressionLanguage}の実装クラスで切り替え可能。 *

{@link MessageResolver}を指定した場合、メッセージ中の変数{...}をメッセージ定義コードとして解決する。 * ただし、メッセージ変数で指定されている変数が優先される。 * * @since 2.0 * @author T.TSUCHIE * */ public class MessageInterpolator { private static final Logger logger = LoggerFactory.getLogger(MessageInterpolator.class); private ExpressionLanguage expressionLanguage; /** * デフォルトのコンストラクタ *

式言語の処理実装として、JEXLの{@link ExpressionLanguageJEXLImpl} が設定されます。 *
さらに、関数として{@link CustomFunctions}が登録されており、接頭語 {@literal f:}で呼び出し可能です。 *

* */ public MessageInterpolator() { // EL式中で使用可能な関数の登録 ExpressionLanguageJEXLImpl el = new ExpressionLanguageJEXLImpl(); Map funcs = new HashMap<>(); funcs.put("f", CustomFunctions.class); el.getJexlEngine().setFunctions(funcs); setExpressionLanguage(el); } /** * 式言語の実装を指定するコンストラクタ * @param expressionLanguage EL式を評価する実装。 */ public MessageInterpolator(final ExpressionLanguage expressionLanguage) { Objects.requireNonNull(expressionLanguage, "expressionLanguage should not be null."); this.expressionLanguage = expressionLanguage; } /** * メッセージを引数varsで指定した変数で補完する。 * * @param message 対象のメッセージ。 * @param vars メッセージ中の変数に対する値のマップ。 * @return 補完したメッセージ。 */ public String interpolate(final String message, final Map vars) { return interpolate(message, vars, false); } /** * メッセージを引数varsで指定した変数で補完する。 * * @param message 対象のメッセージ。 * @param vars メッセージ中の変数に対する値のマップ。 * @param recursive 変換したメッセージに対しても再帰的に処理するかどうか * @return 補完したメッセージ。 */ public String interpolate(final String message, final Map vars, boolean recursive) { return parse(message, vars, recursive, null); } /** * メッセージを引数varsで指定した変数で補完する。 *

{@link MessageResolver}を指定した場合、メッセージ中の変数をメッセージコードとして解決します。 * * @param message 対象のメッセージ。 * @param vars メッセージ中の変数に対する値のマップ。 * @param recursive 変換したメッセージに対しても再帰的に処理するかどうか * @param messageResolver メッセージを解決するクラス。nullの場合、指定しないと同じ意味になります。 * @return 補完したメッセージ。 */ public String interpolate(final String message, final Map vars, boolean recursive, final MessageResolver messageResolver) { return parse(message, vars, recursive, messageResolver); } /** * メッセージをパースし、変数に値を差し込み、EL式を評価する。 * @param message 対象のメッセージ。 * @param vars メッセージ中の変数に対する値のマップ。 * @param messageResolver メッセージを解決するクラス。nullの場合、指定しないと同じ意味になります。 * @return 補完したメッセージ。 */ protected String parse(final String message, final Map vars, boolean recursive, final MessageResolver messageResolver) { // 評価したメッセージを格納するバッファ。 final StringBuilder sb = new StringBuilder(message.length()); /* * 変数とEL式を解析する際に使用する、スタック変数。 * 式の開始が現れたらスタックに積み、式の終了が現れたらスタックから全てを取り出す。 * スタックに積まれるのは、1つ文の変数またはEL式。 */ final LinkedList stack = new LinkedList(); final int length = message.length(); for(int i=0; i < length; i++) { final char c = message.charAt(i); if(StackUtils.equalsTopElement(stack, "\\")) { // 直前の文字がエスケープ文字の場合、エスケープ文字として結合する。 String escapedChar = StackUtils.popup(stack) + c; if(!stack.isEmpty()) { // 取り出した後もスタックがある場合は、式の途中であるため、再度スタックに積む。 stack.push(escapedChar); } else { // 取り出した後にスタックがない場合は、エスケープを解除して通常の文字として積む。 sb.append(c); } } else if(c == '\\') { // エスケープ文字の場合はスタックに積む。 stack.push(String.valueOf(c)); } else if(c == '$') { stack.push(String.valueOf(c)); } else if(c == '{') { if(!stack.isEmpty() && !StackUtils.equalsAnyBottomElement(stack, new String[]{"$", "{"})) { // スタックの先頭が式の開始形式でない場合 throw new MessageParseException(message, "expression not start with '{' or '$'"); } else { stack.push(String.valueOf(c)); } } else if(c == '}') { if(StackUtils.equalsAnyBottomElement(stack, new String[]{"{", "$"})) { // 式の終わりの場合は、式を取り出し評価する。 String expression = StackUtils.popupAndConcat(stack) + c; // エスケープを解除する expression = removeEscapeChar(expression, '\\'); String result = evaluate(expression, vars, recursive, messageResolver); sb.append(result); } else { sb.append(c); } } else { if(stack.isEmpty()) { sb.append(c); } else { stack.push(String.valueOf(c)); } } } if(!stack.isEmpty()) { String val = StackUtils.popupAndConcat(stack); val = removeEscapeChar(val, '\\'); sb.append(val); } return sb.toString(); } private String evaluate(final String expression, final Map values, final boolean recursive, final MessageResolver messageResolver) { if(expression.startsWith("{")) { // 変数の置換の場合 final String varName = expression.substring(1, expression.length()-1); if(values.containsKey(varName)) { // 該当するキーが存在する場合 final Object value = values.get(varName); final String eval = (value == null) ? "" : value.toString(); if(!eval.isEmpty() && recursive) { return parse(eval, values, recursive, messageResolver); } else { return eval; } } else if(messageResolver != null) { // メッセージコードをとして解決をする。 final Optional eval = messageResolver.getMessage(varName); if(!eval.isPresent()) { // 該当するキーが存在しない場合は、値をそのまま返す。 return String.format("{%s}", varName); } if(recursive) { return parse(eval.get(), values, recursive, messageResolver); } else { return eval.get(); } } else { // 該当するキーが存在しない場合は、値をそのまま返す。 return expression.toString(); } } else if(expression.startsWith("${")) { // EL式で処理する final String expr = expression.substring(2, expression.length()-1); final String eval = evaluateExpression(expr, values); if(recursive) { return parse(eval, values, recursive, messageResolver); } else { return eval; } } throw new MessageParseException(expression, "not support expression."); } /** * EL式を評価する。 * @param expression EL式 * @param values EL式中の変数。 * @return 評価した式。 * @throws ExpressionEvaluationException */ protected String evaluateExpression(final String expression, final Map values) throws ExpressionEvaluationException { final Map context = new LinkedHashMap(); context.putAll(values); // フォーマッターの追加 context.computeIfAbsent("formatter", key -> new Formatter()); final String value = expressionLanguage.evaluate(expression, context).toString(); if(logger.isTraceEnabled()) { logger.trace("evaluate expression language: expression='{}' ===> value='{}'", expression, value); } return value; } /** * エスケープ文字を除去した文字列を取得する。 * @param str * @param escapeChar * @return */ private String removeEscapeChar(final String str, final char escapeChar) { if(str == null || str.isEmpty()) { return str; } final String escapeStr = String.valueOf(escapeChar); StringBuilder sb = new StringBuilder(); final LinkedList stack = new LinkedList<>(); final int length = str.length(); for(int i=0; i < length; i++) { final char c = str.charAt(i); if(StackUtils.equalsTopElement(stack, escapeStr)) { // スタックの一番上がエスケープ文字の場合 StackUtils.popup(stack); sb.append(c); } else if(c == escapeChar) { // スタックに積む stack.push(String.valueOf(c)); } else { sb.append(c); } } if(!stack.isEmpty()) { sb.append(StackUtils.popupAndConcat(stack)); } return sb.toString(); } /** * EL式を解析する実装クラスを取得する。 * @return */ public ExpressionLanguage getExpressionLanguage() { return expressionLanguage; } /** * EL式を解析する実装クラスを設定する。 * @param expressionLanguage EL式の解析するクラスの実装。 */ public void setExpressionLanguage(ExpressionLanguage expressionLanguage) { this.expressionLanguage = expressionLanguage; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy