3

我正在尝试创建一个字符串格式化机制,它看起来很像Winamp Advanced Title Formatting

我有一些“变量”(或元数据字段)绑定到对象属性,格式为 %varname%。因此,例如,%title%元数据字段绑定到歌曲标题,例如“征服天堂”,%artist%元数据字段绑定到歌曲艺术家,例如“Vangelis”,而%feat%元数据字段绑定到特色艺术家,比如“英国室内合唱团”。

现在我想根据给定的格式显示歌曲,例如:

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

方括号表示不显示,除非括号内的(所有)元数据被设置。方括号的嵌套应该是可能的。
所以上面提到的格式化字符串说:显示元数据字段%title%,如果设置了%artist%(不是空字符串),则显示(by %artist%),但如果%feat%元数据字段也非空,则也显示该字段。在上述示例中,它将变为:

征服天堂(Vangelis 以英国室内合唱团为特色)

现在我该如何制作这样的机制?我从哪里开始?

我想我必须对字符串进行标记,然后按“部分”搜索元数据标签?

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 回答