3

Winamp Advanced Title Formattingのように見える文字列フォーマットメカニズムを作成しようとしています。

%varname%の形式で、オブジェクト プロパティにバインドされた「変数」(またはメタデータ フィールド) がいくつかあります。したがって、たとえば、%title%メタデータ フィールドは曲のタイトル (たとえば「Conquest of Paradise」) にバインドされ、%artist%メタデータ フィールドは曲のアーティスト (たとえば「Vangelis」) にバインドされ、%feat%メタデータ フィールドは'English Chamber Choir' などのフィーチャリング アーティストにバインドされています。

次に、特定のフォーマットに応じて曲を表示したいと思います。次に例を示します。

%title%[ (by %artist%[ featuring %feat%])]

角かっこは、かっこ内の (すべての) メタデータが設定されていない限り表示しないことを意味します。角括弧のネストが可能であるべきです。
上記のフォーマット文字列は、メタデータ フィールド%title%を表示し、%artist%が設定されている (空の文字列ではない) 場合は表示(by %artist%)しますが、%feat%メタデータ フィールドも空でない場合は、そのフィールドも表示します。上記の例では、次のようになります。

楽園の征服(英国室内合唱団をフィーチャーしたヴァンゲリスによる)

では、そのようなメカニズムをどのように作成すればよいでしょうか。どこから始めればよいですか?

文字列をトークン化し、「セクション」ごとにメタデータタグを検索する必要があると思いますか?

4

2 に答える 2

3

パターンを表すツリー構造を構築します。あなたの例では、次のようになります。

root
 + variable (title)
 + group 
   + text (" (by ")
   + variable (artist)
   + group
     + text (" featuring ")
     + variable (feat)
   + text (")")

次に、ツリーに対してメタ データを評価するときに、グループ内のすべての変数とサブグループが評価されたかどうかをグループ レベルで保存し、評価された場合はテキストを使用します。

ツリークラスは次のようになります。

interface Node { String evaluate(Map<String, String> metaData); }

class Group implements Node 
{
  private final List<Node> _children;

  Group(final List<Node> children) { _children = children; }

  @Override
  public String evaluate(final Map<String, String> metaData)
  {
    final StringBuilder sb = new StringBuilder(); 
    for (final Node node : _children)
    {
      final String subText = node.evaluate(metaData);
      if (subText == null)
        return null;
      sb.append(subText);
    }
    return sb.toString();
  }
}

class Text implements Node 
{
  private final String _text;

  Text(final String text) { _text = text; }

  @Override
  public String evaluate(final Map<String, String> metaData)
  {
    return _text;
  }
}

class Variable implements Node 
{
  private final String _variable;

  Variable(final String variable) { _variable = variable; }

  @Override
  public String evaluate(final Map<String, String> metaData)
  {
    return metaData.get(_variable);
  }
}

あとは、文字列を解析してツリー構造を作成する方法を考え出すだけです。

于 2013-04-05T07:03:40.940 に答える
1

SimonCの提案に基づいて、提案されたものを実行するトークナイザーを作成し、フォーマット文字列をトークンに分割しました。

public class Main {

    private static void buildTree(String format) {
        Stack<Token> st = new Stack<>();
        StringBuilder sb = new StringBuilder();
        GroupToken root = new GroupToken();
        st.push(root);

        boolean var = false;

        for (int i = 0; i < format.length(); i++) {
            char currentChar = format.charAt(i);
            switch (currentChar) {
                case '[':
                    String str = sb.toString();
                    sb.setLength(0); // Flush the StringBuilder
                    if (!str.equals("")) {
                        ((GroupToken) st.peek()).add(new TextToken(str));
                    }
                    GroupToken gt = new GroupToken();
                    ((GroupToken) st.peek()).add(gt);
                    st.push(gt);
                    break;
                case ']':
                    str = sb.toString();
                    sb.setLength(0); // Flush the StringBuilder
                    if (!str.equals("")) {
                        ((GroupToken) st.peek()).add(new TextToken(str));
                    }
                    st.pop();
                    break;
                case '%':
                    var = !var;
                    if (var) {
                        str = sb.toString();
                        sb.setLength(0); // Flush the StringBuilder
                        if (!str.equals("")) {
                            ((GroupToken) st.peek()).add(new TextToken(str));
                        }
                    }
                    else {
                        str = sb.toString();
                        sb.setLength(0); // Flush the StringBuilder
                        ((GroupToken) st.peek()).add(new VariableToken(str));
                    }
                    break;
                default:
                    sb.append(currentChar);
                    break;
            }
        }
        // Process the last remains of the string buffer...
        String str = sb.toString();
        sb.setLength(0); // Flush the StringBuilder
        if (!str.equals("")) {
            ((GroupToken) st.peek()).add(new TextToken(str));
        }
        st.pop();
        System.out.println(root);
    }

    public static void main(String[] arguments) throws Exception {
        buildTree("%title%[ (%alttitle%[, #%track%])]");
    }

}

abstract class Token {

    public abstract String toString(int indent);

}

class TextToken extends Token {

    private String text;

    public TextToken(String text) {
        this.text = text;
    }

    @Override
    public String toString() {
        return toString(0);
    }

    @Override
    public String toString(int indent) {
        return "TextToken[\"" + this.text + "\"]\n";
    }
}

class VariableToken extends Token {

    private String text;

    public VariableToken(String text) {
        this.text = text;
    }

    @Override
    public String toString() {
        return toString(0);
    }

    @Override
    public String toString(int indent) {
        return "VariableToken[\"" + this.text + "\"]\n";
    }
}

class GroupToken extends Token {

    ArrayList<Token> tokens = new ArrayList<>();

    public GroupToken() { }

    public void add(Token token) {
        this.tokens.add(token);
    }

    @Override
    public String toString() {
        return toString(0);
    }

    @Override
    public String toString(int indent) {
        String out = "GroupToken[\n";
        for (Token t : this.tokens) {
            out += StringUtils.pad("", 4 * (indent + 1), ' ') + t.toString(indent + 1);
        }
        out += StringUtils.pad("", 4 * indent, ' ') + "]\n";
        return out;
    }

}
于 2013-04-05T08:10:35.370 に答える