I'm trying to make a string formatting mechanism, which pretty much looks like the Winamp Advanced Title Formatting.
I have some 'variables' (or metadata fields) bound to object properties, in the form %varname%. So, for example, the %title% metadata field is bound to a song title, say 'Conquest of Paradise', the %artist% metadata field is bound to the song artist, say 'Vangelis', and the %feat% metadata field is bound to the featuring artists, say 'English Chamber Choir'.
Now I want to display the song depending on a given formatting, for example:
%title%[ (by %artist%[ featuring %feat%])]
Square brackets mean don't display unless (all) metadata inside the brackets were set. Nesting of square brackets should be possible.
So abovementioned formatting string says: display the metadata field %title% and, if %artist% is set (not an empty string), display (by %artist%), but if the %feat% metadata field is also non-empty, then display that field also. In the abovementioned example, it would become:
Conquest of Paradise (by Vangelis featuring English Chamber Choir)
Now how do I make such mechanism? Where do I start?
I guess that I have to tokenize the string and then per 'section' search for metadata tags?
I would build up a tree structure that represents the pattern. For your example, it would look like:
root
+ variable (title)
+ group
+ text (" (by ")
+ variable (artist)
+ group
+ text (" featuring ")
+ variable (feat)
+ text (")")
Then when you evaluate meta data against your tree, you store at the group level whether all variables and sub-groups in the group evaluated, and if so use the text.
Your tree classes would look something like:
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);
}
}
All that's left to do is to work out how to parse your string to create the tree structure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With