Java Regex doesn't match although debug tools do - java

I have written a regular expression to parse strings of the format
OBJECT_NAME KEY1=value KEY2=value
(actually done by 2 regexps)
This is my utils class:
package de.hs.settlers.util;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ParseUtils {
public static final Pattern OBJECT_NAME_PATTERN =
Pattern.compile("^([A-Z0-9 ]+) ([A-Z]+=.+)$");
public static final Pattern KEY_VALUE_PATTERN =
Pattern.compile("^([A-Z0-9]+)=([^=]+)( [A-Z]+=.+)?$");
public static ParseResult parseKeyValueLine(String line) {
Matcher object = OBJECT_NAME_PATTERN.matcher(line.trim());
String objectName = object.group(1);
HashMap<String, String> data = new HashMap<String, String>();
String vals = object.group(2);
do {
Matcher matcher = KEY_VALUE_PATTERN.matcher(vals);
if (!matcher.matches()) {
break;
}
String key = matcher.group(1);
String value = matcher.group(2);
data.put(key, value);
vals = matcher.group(3);
if (vals != null) {
vals = vals.trim();
}
} while (vals != null);
return new ParseResult(objectName, data);
}
public static class ParseResult {
private String objectName;
private HashMap<String, String> data;
public ParseResult(String objectName, HashMap<String, String> data) {
super();
this.objectName = objectName;
this.data = data;
}
public String getObjectName() {
return objectName;
}
public HashMap<String, String> getData() {
return data;
}
public String get(String key) {
return getData().get(key);
}
}
}
I've written a test that tests the method parseKeyValueLine with "USER TEAM=Team A USER=tuxitux OTHER=bla" as the line argument, but the execution fails because the first expression (the one in OBJECT_NAME_PATTERN) apparenly didn't match.
The problem I have is that when I paste the expression and the string to test it with into regex debuggers, they all tell me it matches and give me the correct groups. (tested with http://gskinner.com/RegExr/ and http://www.debuggex.com/).
Is there anything wrong with how java does regular expressions?

The problem is here:
Matcher object = OBJECT_NAME_PATTERN.matcher(line.trim());
String objectName = object.group(1);
You created the matcher, but you didn't tell it to actually do its work on the string. As a result, even if there was a match you'd have no groups available.
You need to call one of the matching methods (.find(), .lookingAt() or .matches(), but all three will be equivalent for you since your regexes are anchored both at the beginning and end of input), and then collect the groups.
Example (.find()):
Matcher object = OBJECT_NAME_PATTERN.matcher(line.trim());
object.find(); // or you could have an if statement here
String objectName = object.group(1);

Make sure you do not have any watch expressions. For me it was the IDE watch expressions which caused the issue.

Related

regex replace adding unwanted additional bracket

Okay, so here's my code:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class View {
private String item;
private static final Pattern p = Pattern.compile("\\{(.*?)\\}");
Matcher m;
Map map;
public View(String str){
this.item = str;
m = p.matcher(item);
map = new HashMap();
while(m.find()){
String key = m.group(1);
map.put(key, null);
}
}
public void add(String str, Object obj){
for(Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext();){
Map.Entry<String, Object> e = it.next();
String key = e.getKey();
Object value = e.getValue();
if(str.equals(key)){
e.setValue(obj);
}
}
}
public Object render(){
for(Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext();){
Map.Entry<String, Object> e = it.next();
String key = e.getKey();
Object value = e.getValue();
System.out.println(key + "" + value);
item = item.replaceAll("\\{" + key + "\\}", value.toString());
}
return item;
}
}
and I need it to pass these two tests:
#Test
public void testList() {
View view = new View("<table>{rows}</table>");
view.add("rows", Arrays.asList("<tr><td>Louis</td><td>Armstrong</td></tr>", "<tr><td>Benny</td><td>Goodman</td></tr>"));
Assert.assertEquals("<table><tr><td>Louis</td><td>Armstrong</td></tr><tr><td>Benny</td><td>Goodman</td></tr></table>", view.render());
}
#Test
public void testView() {
View view = new View("<table>{rows}</table>");
View row1 = new View("<tr><td>{firstName}</td><td>{lastName}</td></tr>");
row1.add("firstName", "Louis");
row1.add("lastName", "Armstrong");
View row2 = new View("<tr><td>{firstName}</td><td>{lastName}</td></tr>");
row2.add("firstName", "Benny");
row2.add("lastName", "Goodman");
view.add("rows", Arrays.asList(row1, row2));
Assert.assertEquals("<table><tr><td>Louis</td><td>Armstrong</td></tr><tr><td>Benny</td><td>Goodman</td></tr></table>", view.render());
}
However, I keep getting errors when I run the tests and I'm not sure how to go about fixing the issue.
I've noticed that what is going on is that instead of simply putting one bracket [ around the item to replace the string with, it is putting two.
Here's the error I'm getting:
org.junit.ComparisonFailure: expected:<<table>[<tr><td>Louis</td><td>Armstrong</td></tr><tr><td>Benny</td><td>Goodman</td></tr>]</table>> but was:<<table>[[<tr><td>Louis</td><td>Armstrong</td></tr>, <tr><td>Benny</td><td>Goodman</td></tr>]]</table>>
at org.junit.Assert.assertEquals(Assert.java:115)
If you look, it says it expects [<tr><td>Louis... but is instead getting [[<tr><td>Louis...
Im not sure how to fix this issue, so if someone would please point me in the right direction.
Your problem is that the String representation of the List you pass in the tests is not what you expect it to be.
If you run this code
List list = Arrays.asList("<p>foo</p>", "<p>bar</p>");
System.out.println(list.toString());
it will output to the console
[<p>foo</p>, <p>bar</p>]
In your test, you expect it to output HTML without any further markup, like so
<p>foo</p><p>bar</p>
To solve this issue, you'll have to iterate over the object you pass if applicable. The easiest method is to change the signature of the add method so that you can always iterate over the object
void add(String str, Iterable obj);
If that is not an option because you will also pass non-iterables (e.g. plain String objects), you can overload your method. Alternatively, you can do some custom type checking at runtime but that can get ugly fast.
EDIT:
you can just add (not replace) a method with the signature I provided before. For the second test, you need to implement the toString() method. Code like so:
public void add(String str, Iterable obj){
for(Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext();){
Map.Entry<String, Object> e = it.next();
String key = e.getKey();
if(str.equals(key)){
String val = "";
for (Object o : obj)
{
val += o.toString();
}
e.setValue(val);
}
}
}
#Override
public String toString() {
return render().toString();
}

Case-insensitive String Substitutor

I am using org.apache.commons.lang3.text.StrSubstitutor to parse a String. I have it setup similar to this:
StrSubstitutor sub = new StrSubstitutor(messageValues, "&(", ")");
String format = sub.replace("Information: &(killer) killed &(target)!");
This no longer works if I write the keys in different cases:
"Information: &(KILLER) killed &(TARGET)!"
Is there a way of making the keys for the String Substitutor case-insensitive?
I cannot use toLowerCase() because I only want the keys to be case-insensitive.
StrSubstitutor has a constructor that takes an instance of StrLookup. You can create an implementation of StrLookup that lowercases the keys its looking for before actually looking for them.
Here's how it looks like:
public class CaseInsensitiveStrLookup<V> extends StrLookup<V> {
private final Map<String, V> map;
CaseInsensitiveStrLookup(final Map<String, V> map) {
this.map = map;
}
#Override
public String lookup(final String key) {
String lowercaseKey = key.toLowerCase(); //lowercase the key you're looking for
if (map == null) {
return null;
}
final Object obj = map.get(lowercaseKey);
if (obj == null) {
return null;
}
return obj.toString();
}
}
Using this StrLookup implementation you don't need to worry about what kind of Map you're passing to the constructor.
The following test case returns succesfully, using the above implementation:
import org.apache.commons.lang3.text.StrSubstitutor;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.Map;
#Test
public class TestClass {
#Test
public void test() {
Map<String, String> messageValues = new HashMap<String, String>();
messageValues.put("killer", "Johnson");
messageValues.put("target", "Quagmire");
StrSubstitutor sub = new StrSubstitutor(new CaseInsensitiveStrLookup<String>(messageValues), "&(", ")", '\\');
String format2 = sub.replace("Information: &(killer) killed &(target)!");
String format = sub.replace("Information: &(KILLER) killed &(TARGET)!");
Assert.assertEquals(format, "Information: Johnson killed Quagmire!");
Assert.assertEquals(format2, "Information: Johnson killed Quagmire!");
}
}
You don't need to write a custom class. Assuming you can live with the log(n) access times, just use a case-insensitive TreeMap.
public static void main(String[] args) {
Map<String, String> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
m.put("foo", "bar");
StrSubstitutor sub = new StrSubstitutor(m);
String s = sub.replace("${FOO}");
System.out.println(s);
} // prints "bar"
I think this case-insensitive map would work:
import java.util.HashMap;
import java.util.Map;
public class CaseMap<V> extends HashMap<String, V> {
public CaseMap() {
}
public CaseMap(int capacity) {
super(capacity);
}
public CaseMap(int capacity, float loadFactor) {
super(capacity, loadFactor);
}
public CaseMap(Map<String, ? extends V> map) {
putAll(map);
}
public V put(String key, V value) {
return super.put(key.toUpperCase(), value);
}
public V get(Object key) {
if (!(key instanceof String)) return null;
return super.get(((String)key).toUpperCase());
}
}
If you don't control the creation of the messageValues map, you could build a CaseMap from it like this:
CaseMap<String> caseFreeMessageValues = new CaseMap<String>(messageValues);
And then build your StrSubstitutor like this:
StrSubstitutor sub = new StrSubstitutor(messageValues, "&(", ")");
String format = sub.replace("Information: &(KILLER) killed &(TARGET)!");
You might want to think about other methods of Map that should be overridden as well, such as containsKey.
In case you need flexibility with both the Map and the Tokens being case insensitive AND you are not in control of the map being built you can use something like this.
String replaceTokens(String str, Map<String, String> messageValues) {
if(tokenToValue == null || tokenToValue.size() < 1) return str;
StrSubstitutor caseInsensitiveTokenReplacer = new StrSubstitutor(new CaseInsensitiveStrLookup<>(messageValues),
"&(", ")", '\\');
return caseInsensitiveTokenReplacer.replace(str);
}
StrLookup Implementation
public class CaseInsensitiveStrLookup<V> extends StrLookup<V> {
private final Map<String, V> map = new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER);
public CaseInsensitiveStrLookup(final Map<String, V> map) throws NullValueKeyNotSupported {
if(map.containsKey(null)) throw new Exception(); // Dont want to support null
this.map.putAll(map);
}
#Override
public String lookup(final String key) {
V value = map.get(key);
if(value == null) return null;
return value.toString();
}}

How to check if variable name contains string and then output string variable content

So I have these 4 variables
private final String PROG_DEPT = "PROGRAMMING/ENGINEERING";
private final String DES_DEPT = "DESIGN/WRITING";
private final String ART_DEPT = "VISUAL ARTS";
private final String SOUND_DEPT = "AUDIO";
What I want to be able to do is to get a string and compare it to the variable and then out put what the variable contains if it equals it.
For example if my string equals "ART_DEPT" then it check if there is a variable called ART_DEPT and then output "VISUAL ARTS"
I was thinking of putting it in a 2D String array or a list but I'm not really sure as to how to do what I want to do
The data type you're looking for is Map<String, String>.
Map<String, String> departmentNames = new HashMap<String, String>();
departmentNames.put("PROG_DEPT", "PROGRAMMING/ENGINEERING");
departmentNames.put("DES_DEPT", "DESIGN/WRITING");
//...etc...
//...
String dept = "PROG_DEPT";
String deptName = departmentNames.get(dept);
System.out.println(deptName); //outputs "PROGRAMMING/ENGINEERING"
A Map binds a unique key to a value. In this case both have the type String. You add bindings using put(key, value) and get the binding for a key using get(key).
I would go with an enum:
package com.stackoverflow.so18327373;
public class App {
public static void main(final String[] args) {
final String in = "DES_DEPT";
try {
final Departement departement = Departement.valueOf(in);
System.out.println(departement.getLabel());
} catch (final IllegalArgumentException ex) {
// in was not a known departement
System.err.println("Bad value: " + in);
}
}
public static enum Departement {
PROG_DEPT("PROGRAMMING/ENGINEERING"),
DES_DEPT("DESIGN/WRITING"),
ART_DEPT("VISUAL ARTS"),
SOUND_DEPT("AUDIO");
private final String label;
private Departement(final String label) {
this.label = label;
}
public String getLabel() {
return this.label;
}
}
}
then use valueOf()
You probably want to use some kind of Map, such as a HashMap<String,String>. I suggest you read the Javadocs for the Map interface and the HashMap class.
What you need to use is a Map.
private final Map<String,String> myMap= new HashMap<String,String>() ;
{
myMap.put("PROG_DEPT","PROGRAMMING/ENGINEERING");
myMap.put("DES_DEPT","DESIGN/WRITING");
myMap.put("ART_DEPT","VISUAL ARTS");
myMap.put("SOUND_DEPT","AUDIO");
}
Then use it in the following way:
String input= "ART_DEPT" ;
System.out.println( myMap.get(input) );
Try this
List<String> list=new ArrayList<>();
list.add("private final String PROG_DEPT = \"PROGRAMMING/ENGINEERING\";");
list.add("private final String DES_DEPT = \"DESIGN/WRITING\";");
list.add("private final String ART_DEPT = \"VISUAL ARTS\";");
list.add("private final String SOUND_DEPT = \"AUDIO\";");
String search="ART_DEPT";
for (String i:list){
if(i.contains(search)){
System.out.println(i.split("=")[1].replaceAll(";",""));
}
}
Live Demo here. You can do this using Map but to do that you have to create a map from these Strings.
Sounds like you are looking for reflection (or if you want to use a different data type instead of looking up a variable in a class then a Map<String, String>). Looks like the Map approach is well covered, so only because this is interesting to me, here is the reflection approach (not that this is not the best way to solve this problem, but since you asked for checking if a variable exists and then getting it's value)
import java.lang.reflect.Field;
public class SOQuestion {
private final String PROG_DEPT = "PROGRAMMING/ENGINEERING";
private final String DES_DEPT = "DESIGN/WRITING";
private final String ART_DEPT = "VISUAL ARTS";
private final String SOUND_DEPT = "AUDIO";
public static void main(String ... args) throws IllegalArgumentException, IllegalAccessException, InstantiationException {
System.out.println(reflectValue("ART_DEPT", SOQuestion.class));
System.out.println(reflectValue("COMP_DEPT", SOQuestion.class));
}
public static String reflectValue(String varible, Class thing) throws IllegalArgumentException, IllegalAccessException, InstantiationException {
Field[] fs = thing.getDeclaredFields();
for(int i = 0; i < fs.length; i++) {
if(fs[i].getName().equals(varible)) {
fs[i].setAccessible(true);
return (String) fs[i].get(thing.newInstance());
}
}
return null;
}
}
The first request to print "ATR_DEPT" will print VISUAL ARTS and the second request to the nonexistent "COMP_DEPT" will return null;
private String getStaticFieldValue(String fieldName){
String value = null;
try {
Field field = getClass().getDeclaredField(fieldName);
if (Modifier.isStatic(field.getModifiers())){
value = field.get(null).toString();
}
}
catch (Exception e) {
return null;
}
return value;
}
you have few options as mentioned above :
using a Map , the disadvantage of using a map for this case is that you will have to maintain it, it means that every time you will need to add/remove/edit one of your final static fields, you will have to edit the map as well.
using reflection as mentioned in this post, which is my favorite solution (the above code snippet)
Use the concept of Map
import java.util.HashMap;
import java.util.Map;
public class MajorMap {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<String, String> deptMap = new HashMap<String, String>();
deptMap.put("PROG_DEPT", "PROGRAMMING/ENGINEERING");
deptMap.put("DES_DEPT","DESIGN/WRITING");
deptMap.put("ART_DEPT","VISUAL ARTS");
deptMap.put("SOUND_DEPT","AUDIO");
System.out.println("ART_DEPT----->>"+deptMap.get("ART_DEPT"));
}
}

Java string templatizer / formatter with named arguments

Is there a standard or at least widespread implementation of something like String.format, but with named arguments?
I'd like to format a templatized string in a way like that:
Map<String, Object> args = new HashMap<String, Object>();
args.put("PATH", "/usr/bin");
args.put("file", "foo");
String s = someHypotheticalMethod("#{PATH}/ls #{file}");
// "/usr/bin/ls foo"
Technically, it's almost the same as:
String[] args = new String[] { "/usr/bin", "foo" };
String s = String.format("%1$s/ls %2$s", args);
// "/usr/bin/ls foo"
but with named arguments.
I'm aware of:
String.format
Formatter
MessageFormat
but all of them use ordered or at least numbered arguments, not named ones. I know it's trivial to implement one, but is there a mechanism I'm looking for in standard Java libraries or at least in Apache Commons / Guava / something similar, without bringing in high-profile template engines?
NOTE: I'm not really interested in full-blown template engines, with features like some imperative / functional logic, flow control, modifiers, sub-templates / inclusions, iterators, etc. Generally the following method is a working 4-line implementation - that's all I need:
public static String interpolate(String format, Map<String, ? extends Object> args) {
String out = format;
for (String arg : args.keySet()) {
out = Pattern.compile(Pattern.quote("#{" + arg + "}")).
matcher(out).
replaceAll(args.get(arg).toString());
}
return out;
}
You might also try org.apache.commons.lang3.text.StrSubstitutor if Java 7 is not an option. It does exactly what you want it to do. Whether it’s light-weight might depend on whether you use something else of commons-lang as well.
Matcher#appendReplacement() would help
I recently discovered JUEL which fits the description nicely. It is the expression language taken out of JSP. It claims to be very fast, too.
I'm about to try it out in one of my own projects.
But for a lighter-weight, which is a variant of yours, try this (wrapped in a unit test):
public class TestInterpolation {
public static class NamedFormatter {
public final static Pattern pattern = Pattern.compile("#\\{(?<key>.*)}");
public static String format(final String format, Map<String, ? extends Object> kvs) {
final StringBuffer buffer = new StringBuffer();
final Matcher match = pattern.matcher(format);
while (match.find()) {
final String key = match.group("key");
final Object value = kvs.get(key);
if (value != null)
match.appendReplacement(buffer, value.toString());
else if (kvs.containsKey(key))
match.appendReplacement(buffer, "null");
else
match.appendReplacement(buffer, "");
}
match.appendTail(buffer);
return buffer.toString();
}
}
#Test
public void test() {
assertEquals("hello world", NamedFormatter.format("hello #{name}", map("name", "world")));
assertEquals("hello null", NamedFormatter.format("hello #{name}", map("name", null)));
assertEquals("hello ", NamedFormatter.format("hello #{name}", new HashMap<String, Object>()));
}
private Map<String, Object> map(final String key, final Object value) {
final Map<String, Object> kvs = new HashMap<>();
kvs.put(key, value);
return kvs;
}
}
I'd extend it to add convenience methods to for quick key-value pairs
format(format, key1, value1)
format(format, key1, value1, key2, value2)
format(format, key1, value1, key2, value2, key3, value3)
...
And it shouldn't be too hard to convert from java 7+ to java 6-
StringTemplate may be as light-weight an interpolation engine as you're likely to get, although I don't know how it stacks up resource-wise against things like FreeMarker, Mustache, or Velocity.
Another option might be an EL engine like MVEL, which has a templating engine.
Here is my solution:
public class Template
{
private Pattern pattern;
protected Map<CharSequence, String> tokens;
private String template;
public Template(String template)
{
pattern = Pattern.compile("\\$\\{\\w+\\}");
tokens = new HashMap<CharSequence, String>();
this.template = template;
}
public void clearAllTokens()
{
tokens.clear();
}
public void setToken(String token, String replacement)
{
if(token == null)
{
throw new NullPointerException("Token can't be null");
}
if(replacement == null)
{
throw new NullPointerException("Replacement string can't be null");
}
tokens.put(token, Matcher.quoteReplacement(replacement));
}
public String getText()
{
final Matcher matcher = pattern.matcher(template);
final StringBuffer sb = new StringBuffer();
while(matcher.find())
{
final String entry = matcher.group();
final CharSequence key = entry.subSequence(2, entry.length() - 1);
if(tokens.containsKey(key))
{
matcher.appendReplacement(sb, tokens.get(key));
}
}
matcher.appendTail(sb);
return sb.toString();
}
public static void main(String[] args) {
Template template = new Template("Hello, ${name}.");
template.setToken("name", "Eldar");
System.out.println(template.getText());
}
}
I know my answer comes a little late, but if you still need this functionality, without the need to download a full-fledged template engine you can take a look at aleph-formatter (I am one of the authors):
Student student = new Student("Andrei", 30, "Male");
String studStr = template("#{id}\tName: #{st.getName}, Age: #{st.getAge}, Gender: #{st.getGender}")
.arg("id", 10)
.arg("st", student)
.format();
System.out.println(studStr);
Or you can chain the arguments:
String result = template("#{x} + #{y} = #{z}")
.args("x", 5, "y", 10, "z", 15)
.format();
System.out.println(result);
// Output: "5 + 10 = 15"
Internally it works using a StringBuilder creating the result by "parsing" the expression, no string concatenation, regex/replace is performed.
I also made one in my str utils (not tested) string.MapFormat("abcd {var}",map).
//util
public static String mapFormat(String template, HashMap<String, String> mapSet) {
String res = template;
for (String key : mapSet.keySet()) {
res = template.replace(String.format("{%s}", key), mapSet.get(key));
}
return res;
}
//use
public static void main(String[] args) {
boolean isOn=false;
HashMap<String, String> kvMap=new HashMap<String, String>();
kvMap.put("isOn", isOn+"");
String exp=StringUtils.mapFormat("http://localhost/api/go?isOn={isOn}", kvMap);
System.out.println(exp);
}

How do I format a string with properties from a bean

I want to create a String using a format, replacing some tokens in the format with properties from a bean. Is there a library that supports this or am I going to have to create my own implementation?
Let me demonstate with an example. Say I have a bean Person;
public class Person {
private String id;
private String name;
private String age;
//getters and setters
}
I want to be able to specify format strings something like;
"{name} is {age} years old."
"Person id {id} is called {name}."
and automatically populate the format placeholders with values from the bean, something like;
String format = "{name} is {age} old."
Person p = new Person(1, "Fred", "32 years");
String formatted = doFormat(format, person); //returns "Fred is 32 years old."
I've had a look at MessageFormat but this only seems to allow me to pass numeric indexes, not bean properties.
Rolled my own, testing now. Comments welcome.
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BeanFormatter<E> {
private Matcher matcher;
private static final Pattern pattern = Pattern.compile("\\{(.+?)\\}");
public BeanFormatter(String formatString) {
this.matcher = pattern.matcher(formatString);
}
public String format(E bean) throws Exception {
StringBuffer buffer = new StringBuffer();
try {
matcher.reset();
while (matcher.find()) {
String token = matcher.group(1);
String value = getProperty(bean, token);
matcher.appendReplacement(buffer, value);
}
matcher.appendTail(buffer);
} catch (Exception ex) {
throw new Exception("Error formatting bean " + bean.getClass() + " with format " + matcher.pattern().toString(), ex);
}
return buffer.toString();
}
private String getProperty(E bean, String token) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field field = bean.getClass().getDeclaredField(token);
field.setAccessible(true);
return String.valueOf(field.get(bean));
}
public static void main(String[] args) throws Exception {
String format = "{name} is {age} old.";
Person p = new Person("Fred", "32 years", 1);
BeanFormatter<Person> bf = new BeanFormatter<Person>(format);
String s = bf.format(p);
System.out.println(s);
}
}
Yes, it's possible using the Pojomatic library. Implement and plug in your own implementation of PojoFormatter. Pojomator#doToString(T) may be also interesting.
Don't really know how complex is the model you're up to consume but if you want to deal with object trees I would implement my own formatter using Jexl as expession language this way:
Initialize a singleton Jexl engine
Populate a MapContext with all the objects you want to consume when formatting strings
Parse your strings and create a Jexl expression per "${}" construct you have.
Evaluate the previous created expressions against the object context map.
The good thing about Jexl is that it will allow you to use method calls, not just properties.
Hope it helps.
Not quite close, but you can look at StringTemplate, your bean:
public static class User {
public int id; // template can directly access via u.id
private String name; // template can't access this
public User(int id, String name) { this.id = id; this.name = name; }
public boolean isManager() { return true; } // u.manager
public boolean hasParkingSpot() { return true; } // u.parkingSpot
public String getName() { return name; } // u.name
public String toString() { return id+":"+name; } // u
}
Then you can render it like this:
ST st = new ST("<b>$u.id$</b>: $u.name$", '$', '$');
st.add("u", new User(999, "parrt"));
String result = st.render(); // "<b>999</b>: parrt"
Code sample above taken from ST4 Introduction

Categories