FO ordered list item labels roman numerals - freemarker

I am converting two XSLT files to freemarker. One is HTML and the other is FO. I need to be able to generate list item labels based on a variable typeordered which can be one of the values 1, a, A, i, I (as used in html ordered list type).
Original html.xsl
<ol type="{typeordered}">
<li>...</li>
</ol>
Original fo.xsl
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block><xsl:number format="{typeordered}" /></fo:block>
</fo:list-item-label>
...
</fo:list-item>
FO freemarker version. can do lower / upper case alphabet but how to do roman numerals? seems overly complicated?
<#macro listItemM listItem listElement n>
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<#if listElement.type == "ordered">
<#if listElement.typeordered??>
<#if listElement.typeordered == "a">
${n?lower_abc}
<#elseif listElement.typeordered == "A">
${n?upper_abc}
<#else>
${n}
</#if>
<#else>
${n}
</#if>.
<#else>
•
</#if>
</fo:block>
</fo:list-item-label>
...
</fo:list-item>

There's no roman number formatting built into FreeMarker as of 2.3.28 (nor into Java, last time I have checked). Probably there should be... but for now, you have to roll your own (write a TemplateMethodModelEx for it).

Like ddekany mentioned you can create your own method. Here is an example of how you can do it:
Java Code
import freemarker.template.*;
import java.util.List;
import java.util.TreeMap;
public class RomanNumerals implements TemplateMethodModelEx {
private final static TreeMap<Integer, String> map = new TreeMap<>();
static {
map.put(1000, "M");
map.put(900, "CM");
map.put(500, "D");
map.put(400, "CD");
map.put(100, "C");
map.put(90, "XC");
map.put(50, "L");
map.put(40, "XL");
map.put(10, "X");
map.put(9, "IX");
map.put(5, "V");
map.put(4, "IV");
map.put(1, "I");
}
// Copied from Stackoverflow https://stackoverflow.com/a/19759564/2735286
private static String toRoman(int number) {
int l = map.floorKey(number);
if (number == l) {
return map.get(number);
}
return map.get(l) + toRoman(number - l);
}
#Override
public Object exec(List arguments) throws TemplateModelException {
final boolean upperCase = ((TemplateBooleanModel) arguments.get(0)).getAsBoolean();
final Integer number = ((SimpleNumber) arguments.get(1)).getAsNumber().intValue();
String roman = toRoman(number);
return new SimpleScalar(upperCase ? roman : roman.toLowerCase());
}
}
You will have to insert it into your data model map, before processing the template:
HashMap<String, Object> dataModel = new HashMap<>();
dataModel.put("date", new Date());
dataModel.put("roman", new RomanNumerals());
testTemplate.process(dataModel, new PrintWriter(System.out));
This is how you use it in Freemarker:
${roman(true, 1234)}

Related

How to collect map from the Set of objects that has a list using Collectors.toMap

I have class Element with a list, my intended output is like this:
Map<String , List<Element>>
{
1 = [Element3, Element1],
2 = [Element2, Element1],
3 = [Element2, Element1], 4=[Element2]
}
And my input is set of element objects, I used forEach to get the desired outcome, but I'm looking for how to collect it using collectors.toMap. Any inputs are much appreciated
Set<Element> changes = new HashSet();
List<String> interesetList = new ArrayList();
interesetList.add("1");
interesetList.add("2");
interesetList.add("3");
Element element = new Element(interesetList);
changes.add(element);
interesetList = new ArrayList();
interesetList.add("2");
interesetList.add("3");
interesetList.add("4");
element = new Element(interesetList);
changes.add(element);
Map<String, List<Element>> collect2 = new HashMap();
changes.forEach(element -> {
element.getInterestedList().forEach(tracker -> {
collect2.compute(tracker, ( key , val) -> {
List<Element> elementList = val == null ? new ArrayList<Element>() : val;
elementList.add(Element);
return elementList;
});
});
});
class Element {
List<String> interestedList;
static AtomicInteger sequencer = new AtomicInteger(0);
String mName;
public Element(List<String> aList) {
interestedList = aList;
mName = "Element" + sequencer.incrementAndGet();
}
public List<String> getInterestedList() {
return interestedList;
}
#Override
public String toString() {
return mName;
}
}
You can do it by using Collectors.groupingBy instead of Collectors.toMap, along with Collectors.mapping, which adapts a collector to another collector:
Map<String, List<Element>> result = changes.stream()
.flatMap(e -> e.getInterestedList().stream().map(t -> Map.entry(t, e)))
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
You need to use the Stream.flatMap method first and then pair the elements of the inner lists with the current Element instance. I did this via the new Java 9's Map.entry(key, value) method. If you're not on Java 9 yet, you could change it to new AbstractMap.SimpleEntry<>(key, value).
After flatmapping, we need to collect instances of Map.Entry. So I'm using Collectors.groupingBy to classify entries by key (where we had previously stored each element of the inner lists, aka what you call tracker in your code). Then, as we don't want to have instances of List<Map.Entry<String, Element>> as the values of the map, we need to transform each Map.Entry<String, Element> of the stream to just Element (that's why I'm using Map.Entry::getValue as the first argument of Collectors.mapping). We also need to specify a downstream collector (here Collectors.toList()), so that the outer Collectors.groupingBy collector knows where to place all the adapted elements of the stream that belong to each group.
A shorter and surely more efficient way to do the same (similar to your attempt) could be:
Map<String, List<Element>> result = new HashMap<>();
changes.forEach(e ->
e.getInterestedList().forEach(t ->
result.computeIfAbsent(t, k -> new ArrayList<>()).add(e)));
This uses Map.computeIfAbsent, which is a perfect fit for your use case.

How can I concatenate string elements from list in java8 based on their amount?

I have the following structure:
class MyClass {
String name;
String descr;
public String getName() {
return name;
}
}
Now I have a List of those objects and I want to print the name from the object above if the list contains any of those elements.
This is my code so far:
List<MyClass> list = getList();
if (list != null && list.size() > 0) {
System.out.println(list.get(0).getName());
} else {
System.out.println("list is empty");
}
This will work when the list contains only one element. Now I need to improve it and consider an example when there is more than one element - in that case I need to print all names, comma separated.
For example the output should be:
When there are 3 elements:
name1,name2,name3
when there's one element:
name1
and when there's none:
list is empty
what's the most efficient way of implementing it?
You could use Collectors.joining combined with Collectors.collectingAndThen:
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.joining;
...
String res =
list.stream()
.map(c -> c.name)
.collect(collectingAndThen(joining(","), s -> s.isEmpty() ? "list is empty" : s));
If you want to take in account the option when the list is null, you could do:
String res =
Optional.ofNullable(list).map(l -> l.stream()...).orElse("list is empty");
but to be honest I would use an if statement beforehand:
if(list == null || list.isEmpty()) {
return "list is empty";
} else {
return list.stream().map(c -> c.name).collect(joining(","));
}
You also can use the built-in class StringJoiner to simplify your code as following code:
StringJoiner joiner = new StringJoiner(",");
joiner.setEmptyValue("list is empty");
list.forEach(it -> joiner.add(it.getName()));
System.out.println(joiner);

ToString does not work

Why toString doesn´t work in my code? The output should be all elements that are in the idChild[].
Error:
child[Ljava.lang.String;#15db9742
public String[] onePointCrossover(int father, int mother) {
String linha1 = individualID.get(father);
idFather = linha1.split(" ");
String linha2 = individualDep.get(father);
depenFather= linha2.split(" ");
String linha3 = individualHour.get(father);
hourFather = linha3.split(" ");
String linhaA = individualID.get(mother);
idMother = linha1.split(" ");
String linhaB = individualDep.get(mother);
depenMother= linha2.split(" ");
String linhaC = individualHour.get(mother);
hourMother = linha3.split(" ");
String [] idChild = new String [idFather.length];
int crossPoint = (int) (Math.random()*idFather.length);
for(int i=0; i<idFather.length; i++)
{
if (i<crossPoint)
idChild[i] = idFather[i];
else
idChild [i] = idMother[i];
}
System.out.println("child" + idChild.toString());
return idChild;
}
If you want to loop through all childs in your array, then you need to loop through it, other wise you are attempting to read an array of objects as a string!
Try:
foreach (string s in idChild)
{
System.out.println(s);
}
This is the way toString() works (documentation here): the default implementation of the Object class (and of all arrays) shows the class name, the # symbol and the hexadecimal representation of the hash code of the object:
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
The documentation says:
Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object.
So it's really up to the programmer to choose what "textually represents" means.
If you want to print the String representation of all the items in an array you have to iterate over it.

How to sort String in Arraylist

I have an ArrayList where I add customers. What i wnat to do is that i want to sort them, so they appear sorted on Console.
private static ArrayList <Kund> klista = new ArrayList<>();
Kund kundd = new Kund("a","b");
System.out.print("Namn: ");
String namn = scr.next();
System.out.print("Adress: ");
String adress = scr.next();
if (!namnKontroll(namn)){
System.out.println (namn + " " +"har lagts till \n");
klista.add(kundd);
Kund k = new Kund(namn, adress);
klista.add(k);
}else{
System.out.println("Kunden med det namnet finns redan i systemet!");
}
// this is how i add customers to my ArrayList. So now, how it is possible to sort those names in ArrayList. I want to sort them with Collections. thanks
Try use Collections.sort(klista, theComparator). You will need create a Comparator, like this:
public class KundComparator implements Comparator<Kund> {
#Override
public int compare(Kund o1, Kund o2) {
// write comparison logic here
return o1.getID().compareTo(o2.getID());
}
}
Then use the Comparator:
Collections.sort(klista, new KundComparator());
If you are using Java 8, you can do like this:
Collections.sort(klista, (Kund k1, Kund k2) -> k1.getId().compareTo(k2.getId()));

Get string version of lambda expression property

Similar to question How can I get property name strings used in a Func of T.
Let's say I had a lambda expression
stored like this in a variable called "getter"
Expression<Func<Customer, string>> productNameSelector =
customer => customer.Product.Name;
how can I extract the string "Product.Name" from that?
I now fixed it somewhat haxy with
var expression = productNameSelector.ToString();
var token = expression.Substring(expression.IndexOf('.') + 1);
But i'd like to find a more solid way ;-)
The expression tree for your expression looks like this:
.
/ \
. Name
/ \
customer Product
As you can see, there is no node that would represent Product.Name. But you can use recursion and build the string yourself:
public static string GetPropertyPath(LambdaExpression expression)
{
return GetPropertyPathInternal(expression.Body);
}
private static string GetPropertyPathInternal(Expression expression)
{
// the node represents parameter of the expression; we're ignoring it
if (expression.NodeType == ExpressionType.Parameter)
return null;
// the node is a member access; use recursion to get the left part
// and then append the right part to it
if (expression.NodeType == ExpressionType.MemberAccess)
{
var memberExpression = (MemberExpression)expression;
string left = GetPropertyPathInternal(memberExpression.Expression);
string right = memberExpression.Member.Name;
if (left == null)
return right;
return string.Format("{0}.{1}", left, right);
}
throw new InvalidOperationException(
string.Format("Unknown expression type {0}.", expression.NodeType));
}
If you have an Expression you can use the ToString method to extract the string representation:
Expression<Func<Customer, string>> productNameSelector =
customer => customer.Product.Name;
var expression = productNameSelector.ToString();
var token = expression.Substring(expression.IndexOf('.') + 1);

Resources