-1

私は単純な MessageRenderer をやっています。

それは仕様です:

  • コンテキストに基づいてメッセージをレンダリングします (これは、すべてのキーと値のペアのパラメーターを含むマップです)
  • 次のような単純なレンダリングをサポートします: あなたのユーザー名は << ユーザー名 >> です。コンテキスト内のユーザー名が barcelona であると仮定すると、結果は Your username is Barcelona になります。
  • 関数のようなオブジェクトをサポートしました。例: 現在の時刻は << now() >>, now(): 現在の日時の文字列を返すオブジェクトです。結果は次のようになります: 現在の時刻は 2011-05-30 です
  • 関数の各パラメーターはテンプレート化することもできます: 現在の時刻は << now( << date_format >> ) >> です。このテンプレートは、コンテキストから取得したキー「date_format」の値であるフォーマットを使用して、現在の日時の文字列を返します。Context の date_format が dd/MM/yyyy であると仮定すると、結果は次のようになります: 現在の時刻は 30/05/2011
  • 関数の各パラメーターは、別のメソッド呼び出しでテンプレート化することもできます: Time is << now_locale ( << getLocale() >> )。getLocale() が関数オブジェクトであり、ロケールが en_US であり、結果が次のようになるとします: Time is 2011/05/30 11:20:34 PM
  • テンプレートはネストできます。例: あなたのユーザー名は << << ユーザー名 >> >> です。つまり、キーのユーザー名の値は param1、キーのパラメーター 1 の値は barcelona であるため、最終的な結果は次のようになります。ユーザー名は Barcelona です。

私のクラスとインターフェース:

RenderContext.java

public interface RenderContext {
    public String getParameter(String key);
}

MessageRenderer.java

public interface MessageRenderer {
      public String render(String s, RenderContext... context);    
}

MethodExpressionEvaluator.java

// Using this class to implements the method evaluation, such as now(), now_locale()
public interface MethodExpressionEvaluator {
      public String evaluate(String[] methodParams, RenderContext... context);
}

AbstractMessageRenderer.java

public abstract class AbstractMessageRenderer implements MessageRenderer {

public static final String DEFAULT_NULL = "###";
public static final String PLACEHOLDER_START_TOKEN = "<<";
public static final String PLACEHOLDER_END_TOKEN = ">>";

protected int lenPlaceholderStartToken = 0;
protected int lenPlaceholderEndToken = 0;
protected String nullToken;
protected String placeholderStartToken;
protected String placeholderEndToken;
protected boolean escape = true;

public AbstractMessageRenderer() {
    placeholderStartToken = PLACEHOLDER_START_TOKEN;
    placeholderEndToken = PLACEHOLDER_END_TOKEN;
    lenPlaceholderStartToken = placeholderStartToken.length();
    lenPlaceholderEndToken = placeholderEndToken.length();
    nullToken = DEFAULT_NULL;
}

public String getNullToken() {
    return nullToken;
}

public void setNullToken(String defaultNull) {
    this.nullToken = defaultNull;
}

public String getPlaceholderStartToken() {
    return placeholderStartToken;
}

public void setPlaceholderStartToken(String placeholderStartToken) {
    this.placeholderStartToken = placeholderStartToken;
    lenPlaceholderStartToken = placeholderStartToken.length();
}

public String getPlaceholderEndToken() {
    return placeholderEndToken;
}

public void setPlaceholderEndToken(String placeholderEndToken) {
    this.placeholderEndToken = placeholderEndToken;
    lenPlaceholderEndToken = placeholderEndToken.length();
}

public boolean isEscape() {
    return escape;
}

public boolean getEscape() {
    return escape;
}

public void setEscape(boolean escape) {
    this.escape = escape;
}

public String getParam(String key, RenderContext... context) {

    if(context != null)
    {
        for(RenderContext param:context)
        {
            if(param != null)
            {
                String value = param.getParameter(key);

                if(!StringUtil.isEmpty(value))
                {
                    return value;
                }
            }
        }
    }

    return nullToken;
}

public String render(String s, RenderContext... context) {

    // handle trivial cases of empty template or no placeholders
    if (s == null)
    {
        Log4j.app.debug("Message is null in template. Cannot render null message.");
        return nullToken;
    }

    if (context == null)
    {
        Log4j.app.debug("RenderContext is null. Cannot render message with null RenderContext.");
        return nullToken;
    }

    if (s.indexOf(placeholderStartToken) < 0)
    {
        return s;
    }

    String msg = nullToken;

    try
    {
        // private int renderTemplate(Renderable r, String src, StringBuffer dst, String nil, int i, String[] marks, StringBuffer end,boolean escapes)
        msg = doRender(s, context);
    }
    catch (Exception e)
    {
        Log4j.app.error("Exception in rendering template: " + e.getMessage(), e);
        return nullToken;
    }

       return msg;
    }

protected abstract String doRender(String s, RenderContext... context);

}

MethodExpressionRenderer.java

public class MethodExpressionRenderer extends AbstractMessageRenderer {

    private boolean inSingleQuote = false;
    private boolean inDoubleQuote=false;
    private int placeholders;
    private Stack<String> methodStack;    
    private String[] endTokens;
    private String marker;
    private List<String> methodParams;
    private String prefix = "&";

    public MethodExpressionRenderer() {
        super();
        methodStack = new Stack<String>();
        marker = ",";
        endTokens = new String[] { placeholderEndToken, marker, "(", ")" };
        methodParams = new ArrayList<String>();
    }

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getMarker() {
        return marker;
    }

    public void setMarker(String marker) {
        this.marker = marker;
        endTokens = new String[] { placeholderEndToken, marker };
    }

    @Override
    public void setPlaceholderEndToken(String placeholderEndToken) {
        super.setPlaceholderEndToken(placeholderEndToken);
        endTokens = new String[] { placeholderEndToken, marker };
    }

    protected String doRender(String s, RenderContext... context) {

        StringBuffer sb = new StringBuffer();

        try
        {            
            renderTemplate(s, sb, nullToken, 0, endTokens, null, context);
        } 
        catch (Exception e)
        {
            Log4j.app.error("Exception in rendering method expression message emplate: " + e.getMessage(), e);
            return nullToken;
        }

        return sb.toString();
    }

    private int renderTemplate(String src, StringBuffer dst, String nil, int i, String[] marks, StringBuffer end, RenderContext... context) {

        int len = src.length();

        while (i < len)
        {
            char c = src.charAt(i);

            if (escape)
            {
                if (c=='\\')
                {
                    i++;
                    char ch = src.charAt(i);

                    if(inSingleQuote)
                    {
                        if(ch=='\'')
                        {
                            inSingleQuote=false;
                        }
                    }
                    else if(inDoubleQuote)
                    {
                        if(ch=='"')
                        {
                            inDoubleQuote=false;
                        }
                    }
                    else
                    {
                        if(ch=='\'')
                        {
                            inSingleQuote=true;
                        }
                        else if(ch=='"')
                        {
                            inDoubleQuote=true;
                        }
                    }

                    dst.append(ch);
                    i++;
                    continue;
                }
            }

            if(inSingleQuote)
            {
                if(c=='\'')
                {
                    inSingleQuote=false;
                }
            }
            else if(inDoubleQuote)
            {
                if(c=='"')
                {
                    inDoubleQuote=false;
                }
            }
            else
            {
                if(c=='\'')
                {
                    inSingleQuote=true;
                }
                else if(c=='"')
                {
                    inDoubleQuote=true;
                }
            }

            // check for end marker
            if (marks != null && !inSingleQuote && !inDoubleQuote)
            {
                for (int m = 0; m < marks.length; m++)
                {
                    // If one of markers found
                    if (src.regionMatches(i, marks[m], 0, marks[m].length()))
                    {
                        // return marker if required
                        if (end != null)
                        {
                            end.append(marks[m]);
                        }

                        return i+marks[m].length();
                    }
                }
            }

            // check for start of placeholder
            if (src.regionMatches(i, placeholderStartToken, i, lenPlaceholderStartToken))
            {
                synchronized(this)
                {
                    ++placeholders;
                }

                i = renderPlaceholder(src, dst, nil, i, new ArrayList<String>(), context);
                continue;
            }

            // just add plain character

            if(c != '\'' && c!= '"')
            {
                dst.append(c);
            }

            i++;
        }

        return i;
    }

    private int renderPlaceholder(String src, StringBuffer dst, String nil, int i, List<String> params, RenderContext... context){

        StringBuffer token = new StringBuffer(); // placeholder token
        StringBuffer end = new StringBuffer();  // placeholder end marker
        String value;

        i = renderTemplate(src, token, nil, i+lenPlaceholderStartToken, endTokens, end);

        String sToken = token.toString().trim();        
        String sEnd = end.toString().trim();
        boolean isFunction = sEnd.equals("(");

        // This is method name
        if(isFunction && placeholders > methodStack.size())
        {   // Method
            synchronized(this)
            {
                methodStack.push(sToken); // put method into stack
            }
        }
        else if(!isFunction && (methodStack.size()==0) && sEnd.equals(placeholderEndToken)) // Single template param such as <<param>>
        {
            value = getParam(sToken, context);

            if(value != null)
            {
                if(value.trim().startsWith(placeholderStartToken))
                {
                    value = render(src, context);                    
                }

                dst.append(value);
                return i;
            }
        }

       // TODO: Process method parameters to invoke
       //.... ?????????

        // Found end method token ')'
        // Pop method out of stack to invoke
        if ( (methodStack.size() >0) && (sEnd.length() == 0 || sEnd.equals(")")))
        {
            String method = null;

            synchronized(this)
            {
                // Pop method out of stack to invoke
                method = methodStack.pop();
                --placeholders;
                dst.append(invokeMethodEvaluator(method, methodParams.toArray(new String[0]), context));
                methodParams.clear();
            }
        }

        return i;
    }

    // Currently this method just implement to test so it just printout the method name 
    // and its parameter
    // We can register MethodExpressionEvaluator to process
    protected String invokeMethodEvaluator(String method, String[] params, RenderContext... context){
        StringBuffer result = new StringBuffer();

        result.append("[ ")
              .append(method)
              .append(" ( ");

        if(params != null)
        {
            for(int i=0; i<params.length; i++)
            {
                result.append(params[i]);

                if(i != params.length-1)
                {
                    result.append(" , ");
                }
            }
        }

        result.append(" ) ")
              .append(" ] ");

        return result.toString();
    }

}

呼び出すメソッドをレンダラーに簡単に登録できます。各メソッドはオブジェクトになり、再利用できます。しかし、ネストされたメソッドパラメーターを解決する方法に問題があります。メソッドパラメーターのネストされたテンプレートを処理して呼び出す方法について、誰かアドバイスをもらえますか??? 行には TODO があります。私のコードは正しい方法で入りますか???

4

2 に答える 2

0

あなたが次のようなことを評価するとき<< count( << getTransId() >> ) >>

  • 解析しながら直接評価を実行し、各関数をスタックにプッシュします。これにより、評価が完了しgetTransId()たら、スタックをポップし、(スタックからの)戻り値をcount()、またはの引数として使用します。
  • 行われるすべての関数呼び出しを表す解析ツリーを構築し、構築後に解析ツリーを評価できます。(ツリーを構築しても、おそらく何も購入しません。テンプレートエンジンを作成しているため、実行できる高レベルのツリー操作「最適化」はおそらくありません。)

私が本当に楽しんだ素晴らしい小さな本は、ParrによるLanguageImplementationPatternsでした。彼は、単純な言語から複雑な言語の構築について説明し、このような決定についてある程度詳しく説明します。(はい、彼は全体でANTLRパーサージェネレーターを使用していますが、コードは、さまざまなツールが気を散らすことのないように、手動で生成されたパーサーに精通しているように見えます。)

于 2011-05-30T22:03:11.083 に答える
0

バグを見つけて修正しました。

これは私の新しいソースです:

// AbstractMethodExpressionRenderer.java

public class AbstractMethodExpressionRenderer extends AbstractMessageRenderer {

    private boolean inSingleQuote = false;
    private boolean inDoubleQuote=false;
    private Stack<MethodExpressionDescriptor> functionStack;
    private String[] endTokens;
    private String marker;
    private String prefix = "~";

    public AbstractMethodExpressionRenderer() {
        super();        
        functionStack = new Stack<MethodExpressionDescriptor>();
        marker = ",";
        endTokens = new String[] { placeholderEndToken, "(", ")", };
    }

    private class MethodExpressionDescriptor {
        public List<String> params;
        public String function;

        public MethodExpressionDescriptor() {
            params = new ArrayList<String>();
        }

        public MethodExpressionDescriptor(String name) {
            this();
            this.function = name;
        }
    }

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getMarker() {
        return marker;
    }

    public void setMarker(String marker) {
        this.marker = marker;
        endTokens = new String[] { placeholderEndToken, marker };
    }

    @Override
    public void setPlaceholderEndToken(String placeholderEndToken) {
        super.setPlaceholderEndToken(placeholderEndToken);
        endTokens = new String[] { placeholderEndToken, marker };
    }

    protected String doRender(String s, RenderContext... context) {

        StringBuffer sb = new StringBuffer();

        try
        {            
            renderTemplate(s, sb, nullToken, 0, endTokens, null, context);
        } 
        catch (Exception e)
        {
            Log4j.app.error("Exception in rendering method expression message emplate: " + e.getMessage(), e);
            return nullToken;
        }

        return sb.toString();
    }

    private int renderTemplate(String src, StringBuffer dst, String nil, int i, String[] marks, StringBuffer end, RenderContext... context) {

        int len = src.length();

        while (i < len)
        {
            char c = src.charAt(i);

            if (escape)
            {
                if (c=='\\')
                {
                    i++;
                    char ch = src.charAt(i);

                    if(inSingleQuote)
                    {
                        if(ch=='\'')
                        {
                            inSingleQuote=false;
                        }
                    }
                    else if(inDoubleQuote)
                    {
                        if(ch=='"')
                        {
                            inDoubleQuote=false;
                        }
                    }
                    else
                    {
                        if(ch=='\'')
                        {
                            inSingleQuote=true;
                        }
                        else if(ch=='"')
                        {
                            inDoubleQuote=true;
                        }
                    }

                    dst.append(ch);
                    i++;
                    continue;
                }
            }

            if(inSingleQuote)
            {
                if(c=='\'')
                {
                    inSingleQuote=false;
                }
            }
            else if(inDoubleQuote)
            {
                if(c=='"')
                {
                    inDoubleQuote=false;
                }
            }
            else
            {
                if(c=='\'')
                {
                    inSingleQuote=true;
                }
                else if(c=='"')
                {
                    inDoubleQuote=true;
                }
            }

            // check for end marker
            if (marks != null && !inSingleQuote && !inDoubleQuote)
            {
                for (int m = 0; m < marks.length; m++)
                {
                    // If one of markers found
                    if (src.regionMatches(i, marks[m], 0, marks[m].length()))
                    {
                        // return marker if required
                        if (end != null)
                        {
                            end.append(marks[m]);
                        }

                        return i+marks[m].length();
                    }
                }
            }

            // check for start of placeholder
            if (src.regionMatches(i, placeholderStartToken, 0, lenPlaceholderStartToken))
            {
                i = renderPlaceholder(src, dst, nil, i, new ArrayList<String>(), context);
                continue;
            }

            // just add plain character
            if(c != '\'' && c!= '"')
            {
                dst.append(c);
            }

            i++;
        }

        return i;
    }

    /**
     * Render a placeholder as follows:
     *
     * <<key>>: Simple render, key value map
     * <<function(<<param1>>, <<param2>>)>> : Function object render
     * 
     * @param src
     * @param dst
     * @param nil
     * @param i
     * @param params
     * @param context
     * @return
     */
    private int renderPlaceholder(String src, StringBuffer dst, String nil, int i, List<String> params, RenderContext... context){

        StringBuffer token = new StringBuffer(); // placeholder token
        StringBuffer end = new StringBuffer();  // placeholder end marker
        String value = null;

        // Simple key
        i = renderTemplate(src, token, nil, i+lenPlaceholderStartToken, endTokens, end, context);

        String sToken = token.toString().trim();        
        String sEnd = end.toString().trim();

        // This is method name
        if(sEnd.equals("("))
        {   // Method
            functionStack.add(new MethodExpressionDescriptor(sToken));
        }
        else    // Try to resolve value
        {
            if(sToken.startsWith(placeholderStartToken))
            {
                value = render(sToken, context);
            }
            else if(sToken.startsWith(prefix))
            {
                if(functionStack.size() > 0)
                {
                    functionStack.peek().params.add(sToken.substring(1));
                }

                return i;
            }
            else
            {
                value = getParam(sToken, context);
            }            
        }

        if (sEnd.length() == 0 || sEnd.equals(placeholderEndToken))
        {
            // No method found but found the end of placeholder token
            if(functionStack.size() == 0)
            {
                if(value != null)
                {
                    dst.append(value);
                }
                else
                {
                    dst.append(nil);
                }
            }
            else
            {
                functionStack.peek().params.add(value);
            }
        }
        else
        {
            if(value != null)
            {
                value = value.trim();
            }

            if(end.substring(0, 1).equals("(") ||
               end.substring(0, 1).equals(marker))
            {
                // right hand side is remainder of placeholder
                StringBuffer tmp = new StringBuffer();

                end = new StringBuffer();
                i = renderTemplate(src, tmp, nil, i, endTokens, end, context);                
            }

            if(end.substring(0, 1).equals(")"))
            {
                if ( functionStack.size() > 0 )
                {
                    // Pop method out of stack to invoke
                    MethodExpressionDescriptor descriptor = functionStack.pop();

                    if(functionStack.size() > 0 )
                    {
                        functionStack.peek().params.add(invokeMethodEvaluator(descriptor.function, descriptor.params.toArray(new String[0]), context));
                    }
                    else
                    {
                        dst.append(invokeMethodEvaluator(descriptor.function, descriptor.params.toArray(new String[0]), context));
                    }

                    end = new StringBuffer();
                    StringBuffer tmp = new StringBuffer();
                    i = renderTemplate(src, tmp, nil, i, endTokens, end, context);
                }
            }
        }

        return i;
    }

    protected String invokeMethodEvaluator(String method, String[] params, RenderContext... context){

        StringBuffer result = new StringBuffer();

        result.append("[ ")
              .append(method)
              .append(" ( ");

        if(params != null)
        {
            for(int i=0; i<params.length; i++)
            {
                result.append(params[i]);

                if(i != params.length-1)
                {
                    result.append(" , ");
                }
            }
        }

        result.append(" ) ")
              .append(" ] ");

        return result.toString();
    }

}
于 2011-05-31T06:22:51.690 に答える