Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Generate a String from a String Template in Java?

I want my Java App to read a String from a User, the String may contains some tags, for example :

String text = " value 1 = #value1 and value 2 = #value2 ";
int[] intArray = new int[] {4,5};

Also the user will enter an array of values to the application. As a programmer I don't know the exact number of values in the String. and I want to programatically generate this String :

String result = " value 1 = 4 and value 2 = 5 "

to do so I've implemented a method that search for the #value* regex and replace it with the first element in a stack of values. it Loops until the program cannot find any #value in the primary String, the problem is that for big Texts, the program take too much time to get executed, which is normal considering the approach adopted.

I've also heard of some Templating techniques using Velocity and FreeMarker, but I've never used them ( any clarification in this point is very welcome ).

So my question is : what is the best way to solve this problem (shortest execution time)?

PS : I don't need code, I want just an approach or API that may solve this issue.

like image 450
TheByeByeMan Avatar asked Aug 29 '14 08:08

TheByeByeMan


3 Answers

If you are creating a new complete String at each replacement you will indeed run into problems. You can try building a StringBuffer as you go using the helper methods from Matcher instead. This should be much faster for large inputs:

String text = " value 1 = #value1 and value 2 = #value2 ";
int[] intArray = new int[] { 4, 5 };
Pattern p = Pattern.compile("#value(\\d+)");
Matcher m = p.matcher(text);
StringBuffer result = new StringBuffer();
while (m.find()) {
    m.appendReplacement(result, String.valueOf(intArray[Integer.parseInt(m.group(1)) - 1]));
}
m.appendTail(result);
System.out.println(result.toString());

EDIT
A number of people have pointed out that StringBuilder is better suited for this job. I agree, but unfortunately the Matcher API doesn't accept StringBuilder as argument to the appendReplacement() and appendTail() methods.

like image 98
Keppil Avatar answered Nov 12 '22 23:11

Keppil


There are a huge number of Template/Expression language engines to help you with that, Velocity and Freemaker can also solve the problem, however they can be too heavy for this rather simple task.

As already mentioned by @Keppil (+1), the simplest way is to go with regexp. Correct code will take O(n) time.

Just for the reference, for more complex case you can check Apache Jexl library. Is is pretty light and has clear and simple API. Your case can be solved with following code:

    JexlEngine jexl = new JexlEngine();
    Expression expression = jexl.createExpression("value 1 = #value1 value 2 = #value2");
    int[] values = {1, 2};
    JexlContext context = new MapContext();
    for (int i = 0; i <  values.length; i++) {
        context.set("#value" + (i + 1), values[i]);
    }
    String result = (String)expression.evaluate(context);
like image 24
udalmik Avatar answered Nov 13 '22 01:11

udalmik


Use static String.format(String format, Object... args) method to format your string with specific arguments.

To find more details on how to propertly create a valid template string please refer to this documentation: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html#detail

like image 21
SimY4 Avatar answered Nov 13 '22 00:11

SimY4