Quickly testing freemarker expressions - freemarker

Today I was faced with the prospect of writing quite a few freemarker expressions. While the overall difficulty is not high, some of them will contain quite a few builtin calls (i.e. parsing formatted string to number, increasing it and formatting again).
That got me to thinking - how can I test that for development purpuses only to minimize the time spent? I know there are IDE tools that help with the syntax - however, what about testing the functionality I wrote on sample strings? Something that would allow me to parse ${" b lah"?trim} and check whether the output is what I expect? Running my app is obviously a possibility, but in my case it takes way too long to get to the part where using freemarker happens.

I would use a simple JUnit test to test that.
Example:
public class FreemarkerSandbox {
#Test
public void testFreemarker() throws TemplateException, IOException {
Configuration cfg = new Configuration();
Template template = cfg.getTemplate("trim.ftl", CharEncoding.UTF_8);
HashMap<String, Object> model = new HashMap<String, Object>();
Writer out = new StringWriter();
template.process(model, out);
assertEquals("b lah", out.toString());
}
}
With trim.ftl just containing the expression to test.
${" b lah"?trim}
It can be improved by passing the test cases in the model.

Related

How to get that the limit exceeded when I use limit() on a range of items from stream using Java 8 lambda?

How should I know without using another condition to compare the map.size() with limitValue, that the limit was exceeding when my stream iterated?
Here,
for limitValue = 3, it should return false.
for limitValue = 4, it should return true.
I can not use an outside int field as it must be final to be used inside lambda.
import java.util.*;
import java.util.stream.*;
public class Test {
public static void main(String[] args) throws Exception {
Map<Integer, String> map = new HashMap<>();
map.put(1, "foo");
map.put(2, "bar");
map.put(3, "baz");
int limitValue = 3;
String result = map.entrySet()
.stream()
.limit(limitValue)
.map(entry -> entry.getKey() + " - " + entry.getValue())
.collect(Collectors.joining(", "));
System.out.println(result);
}
}
I can not use an outside int field as it must be final to be used
inside lambda.
Yes, this is because, within a lambda expression, you can only reference local variables whose value doesn’t change (in java).
This is a good thing in a way as mutating a variable(s) inside a lambda is not thread safe when executing in parallel.
So, the system is helping you prevent such scenarios at compile time by allowing only final or effectively final variables to be used in lambdas.
Note, this restriction only holds for local variables.
Anyhow, my advice is not to mutate variables that are not solely contained within a given function itself as it introduces a side-effect and side-effects in behavioral parameters to stream operations are, in general, discouraged.
Keep things simple and proceed with the below approach.
boolean exceeded = limitValue > map.size();

Factoring out a duplicated java 8 expression

I find myself duplicating over and over the same java 8 expression:
In one method, I have:
List<Message> latestMessages = new ArrayList<>();
...
return latestMessages.stream().map(messageMapper::asMessageDto).collect(toList());
Then in another method of the same class, I have:
List<Message> messagesBetweenTwoUserAccounts = ...;
return messagesBetweenTwoUserAccounts.stream().map(messageMapper::asMessageDto).collect(toList());
The return type of both methods is: List<MessageDto>
I basically convert from a List<Message> to a List<MessageDto>.
Notice the duplicated expression:
stream().map(messageMapper::asMessageDto).collect(toList());
What would be the best way to factor out the above expression using java 8 constructs?
If you don't want to repeat the latestMessages.stream().map(messageMapper::asMessageDto).collect(toList()); multiple times, write a method that contains it :
public static List<MessageDto> transformMessages (List<Message> messages) {
return messages.stream().map(messageMapper::asMessageDto).collect(toList());
}
Now you can call it from multiple places without repeating that Stream pipeline code.
I don't know if that method should be static or not. That depends on where you are calling it from, and where messageMapper comes from (as Holger commented). You can add messageMapper as an argument if different invocations of the method require different mappers.

Is <element>_has_next a legacy Freemarker syntax?

Came across the follow line in a freemarker document for checking if there are more elements:
<#list myList as myVar>...<#if myVar_has_next>...</#if></#list>
I know about the "?" for calling built-ins, and about "has_next", i.e. myVar?has_next. What I am not familiar with using an underscore between the var and the built-in. Is this a legacy syntax?
It seems that this is an undocumented a feature. The relevant code in freemarker.core.IteratorBlock shows that myVar_has_next is really just the name of a variable. But because the code checks for occurences of _has_next (and _index, fwiw) appended to the loop variable name (myVar), Freemarker is able to put a value to this variable.
At least with Freemarker 2.3.23, I cannot reproduce the error that you mentioned. The following code works just fine although it uses myVar?has_next
public static void main(String[] args) throws IOException, TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
Template template = new Template("test", "<#list myList as myVar>${myVar} <#if myVar?has_next>has next. </#if></#list>", cfg);
template.process((Object) Collections.singletonMap("myList", Arrays.asList("a", "b", "c")), new OutputStreamWriter(System.out));
}
Produces
a has next. b has next. c
Edit: This feature has been deprecated in v2.3.23 (see #ddekany's comment)

Java8 streams map - check if all map operations succeeded?

I am trying to map one list to another using streams.
Some elements of the original list fail to map. That is, the mapping function may not be able to find an appropriate new value.
I want to know if any of the mappings has failed. Ideally I would also like to stop the processing once a failure happened.
What I am currently doing is:
The mapping function returns null if there's no mapped value
I filter() to remove nulls from the stream
I collect(), and then
I compare the size of the result to the size of the original list.
For example:
List<String> func(List<String> old, Map<String, String> oldToNew)
{
List<String> holger = old.stream()
.map(oldToNew::get)
.filter(Objects::nonNull)
.collect(Collectors.toList);
if (holger.size() < old.size()) {
// ... appropriate error handling code ...
}
else {
return holger;
}
}
This is not very elegant. Also, everything is processed even when the whole thing should fail.
Suggestions for a better way of doing it?
Or maybe I should ditch streams altogether and use good old loops?
There is no best solution because that heavily depends on the use case. E.g. if lookup failures are expected to be unlikely or the error handling implies throwing an exception anyway, just throwing an exception at the first failed lookup within the mapping function might indeed be a good choice. Then, no follow-up code has to care about error conditions.
Another way of handling it might be:
List<String> func(List<String> old, Map<String, String> oldToNew) {
Map<Boolean,List<String>> map=old.stream()
.map(oldToNew::get)
.collect(Collectors.partitioningBy(Objects::nonNull));
List<String> failed=map.get(false);
if(!failed.isEmpty())
throw new IllegalStateException(failed.size()+" lookups failed");
return map.get(true);
}
This can still be considered being optimized for the successful case as it collects a mostly meaningless list containing null values for the failures. But it has the point of being able to tell the number of failures (unlike using a throwing map function).
If a detailed error analysis has a high priority, you may use a solution like this:
List<String> func(List<String> old, Map<String, String> oldToNew) {
Map<Boolean,List<String>> map=old.stream()
.map(s -> new AbstractMap.SimpleImmutableEntry<>(s, oldToNew.get(s)))
.collect(Collectors.partitioningBy(e -> e.getValue()!=null,
Collectors.mapping(e -> Optional.ofNullable(e.getValue()).orElse(e.getKey()),
Collectors.toList())));
List<String> failed=map.get(false);
if(!failed.isEmpty())
throw new IllegalStateException("The following key(s) failed: "+failed);
return map.get(true);
}
It collects two meaningful lists, containing the failed keys for failed lookups and a list of successfully mapped values. Note that both lists could be returned.
You could change your filter to Objects::requireNonNull and catch a NullPointerException outside the stream

Combining Multiple Maps together in Pig

I am using pig for the first time. I've gotten to the point where I have exactly the answer I want, but in a weirdly nested format:
{(price,49),(manages,"1d74426f-2b0a-4777-ac1b-042268cab09c")}
I'd like the output to be a single map, without any wrapping:
[price#49, manages#"1d74426f-2b0a-4777-ac1b-042268cab09c"]
I've managed to use TOMAP to get this far, but I can't figure out how to merge and flatten it away.
{([price_specification#{"amount":49,"currency":"USD"}]),([manages#"newest-nodes/1d74426f-2b0a-4777-ac1b-042268cab09c"])}
How should I be going about this?
Unfortunately, there are no built-in functions to do this for you. You'll have to write your own UDF. Fortunately, this is a simple one.
The exec method would just go something like:
public Map<String, Object> exec(Tuple input) {
Map<String, Object> m = new HashMap<String, Object>();
for (int i = 0; i < input.size(); i++)
m.putAll((Map<String, Object>) input.get(i));
return m;
}
The UDF could take any number of maps as arguments.
Note that if two or more maps share a key, then the final one encountered will be the one that is kept and the others get overwritten.

Resources