我正在尝试创建一个字符串格式化机制,它看起来很像Winamp Advanced Title Formatting。
我有一些'变量'(或元数据字段)绑定到对象属性,格式为%varname%。因此,例如,%title%元数据字段绑定到歌曲标题,例如“Conquest of Paradise”,%artist%元数据字段绑定到歌曲艺术家,说'Vangelis',以及%feat%元数据领域是特色艺术家,比如'英国室内合唱团'。
现在我想根据给定的格式显示歌曲,例如:
%title%[ (by %artist%[ featuring %feat%])]
方括号表示不显示,除非括号内的(所有)元数据已设置。应该可以嵌套方括号
所以上面提到的格式化字符串说:显示元数据字段%title%,如果设置了%artist%(不是空字符串),则显示(by %artist%)
,但是如果%feat%元数据字段也非空,则也显示该字段。在上述例子中,它将成为:
征服天堂(由Vangelis饰演英国室内合唱团)
现在我该如何制作这样的机制?我从哪里开始?
我想我必须对字符串进行标记,然后根据'section'搜索元数据标签?
答案 0 :(得分: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);
}
}
剩下要做的就是解决如何解析字符串以创建树结构。
答案 1 :(得分:1)
根据suggestion的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;
}
}