3
votes

I am using apache POI 3.7. I am trying to replace the value of a table column in a word document (docx). However, what I have done is it keeps appending the value of the current value in the document. But if a table column value is null, it places the value. Can you give me some thoughts how to resolve this. Below is the code I have done so far.

Thanks in advance.

package test.doc;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

public class POIDocXTableTest {

    public static void main(String[] args)throws IOException {
        String fileName = "C:\\Test.docx";
        InputStream fis = new FileInputStream(fileName);
        XWPFDocument document = new XWPFDocument(fis);
        List<XWPFParagraph> paragraphs = document.getParagraphs();

        for (int x=0; x<paragraphs.size();x++)
        {
            XWPFParagraph paragraph = paragraphs.get(x);
            System.out.println(paragraph.getParagraphText());
        }
        List<XWPFTable> tables = document.getTables();
        for (int x=0; x<tables.size();x++)
        {
            XWPFTable table = tables.get(x);
            List<XWPFTableRow> tableRows = table.getRows();
            tableRows.remove(x);
            for (int r=0; r<tableRows.size();r++)
            {
                System.out.println("Row "+ (r+1)+ ":");
                XWPFTableRow tableRow = tableRows.get(r);
                List<XWPFTableCell> tableCells = tableRow.getTableCells();
                for (int c=0; c<tableCells.size();c++)
                {
                    System.out.print("Column "+ (c+1)+ ": ");
                    XWPFTableCell tableCell = tableCells.get(c);
                    //tableCell.setText("TAE");
                    String tableCellVal = tableCell.getText();
                    if ((c+1)==2){
                        if (tableCellVal!=null){
                            if (tableCellVal.length()>0){
                                 char c1 = tableCellVal.charAt(0);
                                 String s2 = "-TEST";
                                 char c2 = s2.charAt(0);
                                 String test = tableCell.getText().replace(tableCellVal,s2);
                                 tableCell.setText(test);
                            }else{
                                //tableCell.setText("NULL");
                            }
                        }
                    }
                    System.out.println("tableCell.getText(" + (c) + "):" + tableCellVal);
                }
            }
            System.out.println("\n");
        }
        OutputStream out = new FileOutputStream(fileName);
        document.write(out);
        out.close();
    }
}
3
Have you tried with POI 3.8 beta 4? (or even better a recent nightly build?)Gagravarr
Thanks for the reply. I tried to use apache POI 3.8.4 (Latest build) using the same code I have but its still appending the values. I found a link for replace but this it works for paragraph. Can anyone give some thoughts on how to do this in table cells? [link]apache-poi.1045710.n5.nabble.com/…jamojo
Thank you for the answer but it seems that it does not work for me when trying to replace text in paragraphs. Hence, I did this dangerous modification. Appreciate your comments: p.removeRun(i); { XWPFRun rB4=rs.get(i-1); String textB4 = rB4.getText(0); textB4 = StringUtils.remove(textB4, searchValue); rB4.setText(textB4); }hamzali

3 Answers

9
votes

The best solution to prevent styles in paragraphs and find search strings with different styles is this method:

  private long replaceInParagraphs(Map<String, String> replacements, List<XWPFParagraph> xwpfParagraphs) {
    long count = 0;
    for (XWPFParagraph paragraph : xwpfParagraphs) {
      List<XWPFRun> runs = paragraph.getRuns();

      for (Map.Entry<String, String> replPair : replacements.entrySet()) {    
        String find = replPair.getKey();
        String repl = replPair.getValue();
        TextSegement found = paragraph.searchText(find, new PositionInParagraph());
        if ( found != null ) {
          count++;
          if ( found.getBeginRun() == found.getEndRun() ) {
            // whole search string is in one Run
           XWPFRun run = runs.get(found.getBeginRun());
           String runText = run.getText(run.getTextPosition());
           String replaced = runText.replace(find, repl);
           run.setText(replaced, 0);
          } else {
            // The search string spans over more than one Run
            // Put the Strings together
            StringBuilder b = new StringBuilder();
            for (int runPos = found.getBeginRun(); runPos <= found.getEndRun(); runPos++) {
              XWPFRun run = runs.get(runPos);
              b.append(run.getText(run.getTextPosition()));
            }                       
            String connectedRuns = b.toString();
            String replaced = connectedRuns.replace(find, repl);

            // The first Run receives the replaced String of all connected Runs
            XWPFRun partOne = runs.get(found.getBeginRun());
            partOne.setText(replaced, 0);
            // Removing the text in the other Runs.
            for (int runPos = found.getBeginRun()+1; runPos <= found.getEndRun(); runPos++) {
              XWPFRun partNext = runs.get(runPos);
              partNext.setText("", 0);
            }                          
          }
        }
      }      
    }
    return count;
  }

This method works with search strings spanning over more than one Run. The replaced part gets the style from the first found Run.

4
votes

well, I have done something like that, to replace marks in a word template by specified words...:

    public DotxTemplateFiller() {
    String filename = "/poi/ls_Template_modern_de.dotx";
    String outputPath = "/poi/output/output" + new Date().getTime()
            + ".dotx";
    OutputStream out = null;
    try {
        File file = new File(filename);
        XWPFDocument template = new XWPFDocument(new FileInputStream(file));

        List<XWPFParagraph> xwpfParagraphs = template.getParagraphs();
        replaceInParagraphs(xwpfParagraphs);

        List<XWPFTable> tables = template.getTables();
        for (XWPFTable xwpfTable : tables) {
            List<XWPFTableRow> tableRows = xwpfTable.getRows();
            for (XWPFTableRow xwpfTableRow : tableRows) {
                List<XWPFTableCell> tableCells = xwpfTableRow
                        .getTableCells();
                for (XWPFTableCell xwpfTableCell : tableCells) {
                    xwpfParagraphs = xwpfTableCell.getParagraphs();
                    replaceInParagraphs(xwpfParagraphs);
                }
            }
        }

        out = new FileOutputStream(new File(outputPath));
        template.write(out);
        out.flush();
        out.close();
        //System.exit(0);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                // nothing to do ....
            }
        }
    }
}

/**
 * @param xwpfParagraphs
 */
private void replaceInParagraphs(List<XWPFParagraph> xwpfParagraphs) {
    for (XWPFParagraph xwpfParagraph : xwpfParagraphs) {
        List<XWPFRun> xwpfRuns = xwpfParagraph.getRuns();
        for (XWPFRun xwpfRun : xwpfRuns) {
            String xwpfRunText = xwpfRun.getText(xwpfRun
                    .getTextPosition());
            for (Map.Entry<String, String> entry : replacements
                    .entrySet()) {
                if (xwpfRunText != null
                        && xwpfRunText.contains(entry.getKey())) {
                    xwpfRunText = xwpfRunText.replaceAll(
                            entry.getKey(), entry.getValue());
                }
            }
            xwpfRun.setText(xwpfRunText, 0);
        }
    }
}

public static void main(String[] args) {
    new DotxTemplateFiller();
}

First I did it for regular paragraphs in the MS Word template and than for paragraphs inside table cells. Hope it is helpful for you and I hope I understood your problem right... :-)

Best wishes.

0
votes

Adding on to Josh's solution, the map I am building has ended up with over a thousand tags and continues to grow. To cut down on processing, I decided to build a small subset of the tags that I know appear in the paragraph, typically ending up with a map of only one or two tags that I then pass as the Map to the replaceInParagraphs method provided above. Also, using the Substitution object to store the substitution text, allows me to add methods into that object (such as formatting) that I can call once the substitution has been completed. Using the subset Map also allows me to know what replacements have been made in any paragraph.

private Map<String, Substitution> buildTagList(Map<String, Substitution> replacements, List<XWPFParagraph> xwpfParagraphs, String start, String end) {

    Map<String, Substitution> returnMap = new HashMap<String, Substitution> ();
    for (XWPFParagraph paragraph : xwpfParagraphs) {
        List<XWPFRun> runs = paragraph.getRuns();
        // Check is there is a tag in the paragraph
        TextSegment found = paragraph.searchText(start, new PositionInParagraph());
        String runText = "";
        XWPFRun run = null;
        if ( found != null ) {
            StringBuilder b = new StringBuilder();
            for (int runPos = found.getBeginRun(); runPos < runs.size(); runPos++) {
                run = runs.get(runPos);
                b.append(run.getText(run.getTextPosition()));                    
                runText = b.toString();
            }
            // Now we need to find all tags in the run
            boolean finished = false;
            int tagStart = 0;
            int tagEnd = 0;
            while ( ! finished ) {
                // get the first tag
                tagStart = runText.indexOf(start,tagStart);
                tagEnd = runText.indexOf(end, tagEnd);
                if ( tagStart >= 0 ) {
                    String tag = runText.substring(tagStart, tagEnd + end.length());
                    Substitution s = replacements.get(tag);
                    if (s != null) {
                        returnMap.putIfAbsent(tag,s);
                    }
                }
                else
                    finished = true;
                tagStart = tagEnd + end.length();
                tagEnd = tagStart;
            }
        }
    }
    return returnMap;
}