6

I have a table and I need to add multiple rows to it instead of the variables you see in the image. I'm using docx4j. table I change variables like this:

HashMap mappings = new HashMap();
VariablePrepare.prepare(template);
mappings.put("example", "example");
template.getMainDocumentPart().variableReplace(mappings);
Dan
  • 63
  • 1
  • 7

2 Answers2

1

This works for me - but I don't have column headers in my word template, so be careful they might break this functionality.
Just fill the HashMap correctly and it should work out of the box if you have everything setup right ;)

These are the 3 functions I use for replacement:

private void replaceTable(String[] placeholders, List<Map<String, String>> textToAdd, WordprocessingMLPackage template) throws Docx4JException, JAXBException {
    List<Object> tables = doc.getAllElementFromObject(template.getMainDocumentPart(), Tbl.class);
    Tbl tempTable = getTemplateTable(tables, placeholders[0]);
    List<Object> rows = doc.getAllElementFromObject(tempTable, Tr.class);
    if (rows.size() == 1) { //careful only tables with 1 row are considered here
        Tr templateRow = (Tr) rows.get(0);
        for (Map<String, String> replacements : textToAdd) {
            addRowToTable(tempTable, templateRow, replacements);
        }
        assert tempTable != null;
        tempTable.getContent().remove(templateRow);
    }
}

private void addRowToTable(Tbl reviewTable, Tr templateRow, Map<String, String> replacements) {
    Tr workingRow = (Tr) XmlUtils.deepCopy(templateRow);
    List<?> textElements = doc.getAllElementFromObject(workingRow, Text.class);
    for (Object object : textElements) {
        Text text = (Text) object;
        String replacementValue = (String) replacements.get(text.getValue());
        if (replacementValue != null)
            text.setValue(replacementValue);
    }
    reviewTable.getContent().add(workingRow);
}

private Tbl getTemplateTable(List<Object> tables, String templateKey) throws Docx4JException, JAXBException {
    for (Object tbl : tables) {
        List<?> textElements = doc.getAllElementFromObject(tbl, Text.class);
        for (Object text : textElements) {
            Text textElement = (Text) text;
            if (textElement.getValue() != null && textElement.getValue().equals(templateKey))
                return (Tbl) tbl;
        }
    }
    return null;
}

And here's roughly how to use it for your example:

ArrayList<Map<String, String>> list = new ArrayList<>();
//Create a loop here through all entries
Map<String, String> entry = new HashMap<>();
entry.put("${nrCrt}", "1");
list.add(entry);
//...
entry.put("${tva}", "55");
list.add(entry);
entry.put("${nrCrt}", "2");
list.add(entry);
//...

replaceTable(new String[]{"${nrCrt}"}, list, template);

I forgot to mention:
doc is just a helper class, this is the implementation of getAllElementFromObject:

public List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
    List<Object> result = new ArrayList<Object>();
    if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();

    if (obj.getClass().equals(toSearch))
        result.add(obj);
    else if (obj instanceof ContentAccessor) {
        List<?> children = ((ContentAccessor) obj).getContent();
        for (Object child : children) {
            result.addAll(getAllElementFromObject(child, toSearch));
        }
    }
    return result;
}
Cold_Class
  • 3,214
  • 4
  • 39
  • 82
  • @ShashankBodkhe Sorry, I forgot that the doc object is not a standard helper class, if you have any more questions about the code, go ahead – Cold_Class Feb 01 '19 at 10:33
0

VariableReplace isn't intended for repeating data.

You can use OpenDoPE content control data binding instead: you wrap a repeat content control around the table row.

https://github.com/plutext/docx4j/blob/master/src/main/java/org/docx4j/model/datastorage/migration/FromVariableReplacement.java may be of assistance in migrating from VariableReplace to OpenDoPE.

JasonPlutext
  • 15,352
  • 4
  • 44
  • 84