javaapache-poixwpf

XWPFTable create new row (looping)


I have a problem about how to loop data using XWPFTable? i have some problems;

  1. I am confused why XWPFRun examRun = examInfoRowP.createRun(); (automatically reads the 2nd column, not the 1st column first)
  2. My coding structure, I think it is less efficient in using XWPFTable, how to make it cleaner code

note: List listLHA is the data I'm using apache-poi 3.8

XWPFTable Table = document.getTableArray(16);
            
            XWPFTableRow getRow1 = Table.getRow(1);
            
            
            XWPFTableRow getRow0 = Table.getRow(0);
            
            //baris 1
            for(int i = 0; i < listLHA.size(); i++) {
                getRow0.getCell(0).setText(listLHA.get(0).getKeyProsses()+ " KEY PROSES");
                break;
            }
            
            //baris 2
            
            for(int i = 0; i < listLHA.size(); i++) {
                getRow1.getCell(0).setText(listLHA.get(0).getRiskRating());
                getRow1.getCell(1).setText(listLHA.get(0).getAuditObservationTitle()+ " AO TITLE");
                break;
            }
            
            XWPFTableRow examInfoRow = Table.createRow();
            XWPFTableCell cellRowInfo = examInfoRow.addNewTableCell();

            XWPFParagraph examInfoRowP = cellRowInfo.getParagraphs().get(0);
            XWPFRun examRun = examInfoRowP.createRun(); //problem 1
            
            examInfoRowP.setAlignment(ParagraphAlignment.LEFT);
            //list Action plan
            examRun.setText("Action Plan:");
            examRun.addBreak();
            for (AuditEngagementLHA lha : listLHA) {
                int i = listLHA.indexOf(lha);
                examRun.setText(i+1 +"."+lha.getDescAP().replaceAll("\\<[^>]*>",""));
                examRun.addBreak();
            }
            for(int i = 0; i < listLHA.size(); i++) {
                examRun.setText("Target Date: ");
                examRun.setText(listLHA.get(0).getTargetDateAP());
                examRun.addBreak();
                break;
            }
            
            examRun.addBreak();
            for(int i = 0; i < listLHA.size(); i++) {
                examInfoRow.getCell(0).setText(listLHA.get(0).getDescAO()+" Desc AO");
                examRun.addBreak();
                break;
            }
            //List penanggung jawab
            examRun.setText("Penanggung Jawab:");
            examRun.addBreak();
            for (AuditEngagementLHA lha : listLHA) {
                int i = listLHA.indexOf(lha);
                
                examRun.setText(i+1 +"."+lha.getPicAP()+" - ");
                examRun.setText(lha.getJabatanPicAP());
                examRun.addBreak();
            }

*my word template is like this

enter image description here

*the result from now is like this

enter image description here

*and the result should be like this and neat

enter image description here


Solution

  • Apache POI is highly in development. Therefore, versions become obsolete very quickly. The version apache poi 3.8 was released in 2012. It is much too old to be used ten years later in 2022.

    To your first question:

    According to the documentation XWPFTable.createRow creates a new XWPFTableRow object with as many cells as the number of columns defined in that moment.

    So in your case XWPFTableRow examInfoRow = Table.createRow(); creates a examInfoRow which has at least two columns already, since the table contains at least two columns already from rows above.

    Then XWPFTableCell cellRowInfo = examInfoRow.addNewTableCell(); adds an additional column by adding a new table cell to that row. That's why cellRowInfo will not be in 1st column.

    Your second question is too broad to be completely answered here.

    What I can tell you is that Word tables are beastly things. They have a underlying table grid defining the columns. And if rows need different column sizes, then setting column spans is necessary, just like in HTML tables too. The table, you show as the wanted result, contains three columns. In first row the first column spans the others. In second row the second column spans the third. In third row the first column spans the second. That's the only way to produce those different columns for each row.

    Knowing this one should avoid to mess with inserting and/or adding or creating table cells. If a template is possible, then that template should contain all possible row templates already. Then only copying the rows is necessary instead of messing around with inserting cells.

    Complete example:

    Template:

    enter image description here

    Code:

    import java.io.FileOutputStream;
    import java.io.FileInputStream;
    
    import org.apache.poi.xwpf.usermodel.*;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
    
    import java.util.List; 
    import java.util.ArrayList; 
    
    public class WordInsertContentInTable {
        
     static void setText(XWPFTableCell cell, String text) {
      String[] lines = text.split("\n");
      List<XWPFParagraph> paragraphs = cell.getParagraphs();
      for (int i = 0; i < lines.length; i++) {
       String line = lines[i];
       XWPFParagraph paragraph = null;
       if (paragraphs.size() > i) paragraph = paragraphs.get(i);
       if (paragraph == null) paragraph = cell.addParagraph();
       XWPFRun run = null;
       if (paragraph.getRuns().size() > 0) run = paragraph.getRuns().get(0);
       if (run == null) run = paragraph.createRun();
       run.setText(line, 0);
      }
      for (int i = paragraphs.size()-1; i >= lines.length; i--) {
       cell.removeParagraph(i);
      }
     }
        
     static void insertContentInTable(XWPFTable table, List<POJO> listOfPOJOs) throws Exception {
      XWPFTableRow titleRowTemplate = table.getRow(0);
      if (titleRowTemplate == null) throw new Exception("Table template does not match: No title row.");
      if (titleRowTemplate.getTableCells().size() != 1) throw new Exception("Table template does not match: Wrong title row column count.");
      XWPFTableRow subTitleRowTemplate = table.getRow(1);
      if (subTitleRowTemplate == null) throw new Exception("Table template does not match: No sub title row.");
      if (subTitleRowTemplate.getTableCells().size() != 2) throw new Exception("Table template does not match: Wrong sub title row column count.");
      XWPFTableRow contentRowTemplate = table.getRow(2);
      if (contentRowTemplate == null) throw new Exception("Table template does not match: No content row.");
      if (contentRowTemplate.getTableCells().size() != 2) throw new Exception("Table template does not match: Wrong content row column count.");
      
      XWPFTableRow titleRow = titleRowTemplate;
      XWPFTableRow subTitleRow = subTitleRowTemplate;
      XWPFTableRow contentRow = contentRowTemplate;
      XWPFTableCell cell;
      for (int i = 0; i < listOfPOJOs.size(); i++) {
       POJO pojo = listOfPOJOs.get(i); 
       if (i > 0) {
        titleRow = new XWPFTableRow((CTRow)titleRowTemplate.getCtRow().copy(), table);
        subTitleRow = new XWPFTableRow((CTRow)subTitleRowTemplate.getCtRow().copy(), table);
        contentRow = new XWPFTableRow((CTRow)contentRowTemplate.getCtRow().copy(), table);
       }
       String titleRowText = pojo.getTitleRowText();
       cell = titleRow.getCell(0);
       setText(cell, titleRowText);   
       String subTitleRowLeftText = pojo.getSubTitleRowLeftText();   
       String subTitleRowLeftColor = pojo.getSubTitleRowLeftColor();   
       String subTitleRowRightText = pojo.getSubTitleRowRightText();   
       cell = subTitleRow.getCell(0);
       setText(cell,subTitleRowLeftText);   
       cell.setColor(subTitleRowLeftColor);   
       cell = subTitleRow.getCell(1);
       setText(cell,subTitleRowRightText);
       String contentRowLeftText = pojo.getContentRowLeftText();   
       String contentRowRightText = pojo.getContentRowRightText();   
       cell = contentRow.getCell(0);
       setText(cell, contentRowLeftText);   
       cell = contentRow.getCell(1);
       setText(cell, contentRowRightText);
       if (i > 0) {
        table.addRow(titleRow);
        table.addRow(subTitleRow);
        table.addRow(contentRow);
       }
      }     
     }
    
     public static void main(String[] args) throws Exception {
         
      List<POJO> listOfPOJOs = new ArrayList<POJO>();
      listOfPOJOs.add(new POJO("Title row text 1", 
                               "Sub title row left text 1", "FF0000", "Sub title row right text 1\nSub title row right text 1\nSub title row right text 1", 
                               "Content row left text 1\nContent row left text 1\nContent row left text 1", 
                               "Content row right text 1\nContent row right text 1\nContent row right text 1"));
      listOfPOJOs.add(new POJO("Title row text 2", 
                               "Sub title row left text 2", "00FF00", "Sub title row right text 2\nSub title row right text 2", 
                               "Content row left text 2\nContent row left text 2",
                               "Content row right text 2\nContent row right text 2"));
      listOfPOJOs.add(new POJO("Title row text 3", 
                               "Sub title row left text 3", "0000FF", "Sub title row right text 3", 
                               "Content row left text 3", 
                               "Content row right text 3"));
    
      XWPFDocument document = new XWPFDocument(new FileInputStream("./WordTenplate.docx"));
    
      XWPFTable table = document.getTableArray(0);
      
      insertContentInTable(table, listOfPOJOs);
    
      FileOutputStream out = new FileOutputStream("./WordResult.docx");
      document.write(out);
      out.close();
      document.close();
     }
     
     static class POJO {
      private String titleRowText;
      private String subTitleRowLeftText;
      private String subTitleRowLeftColor;
      private String subTitleRowRightText;
      private String contentRowLeftText;
      private String contentRowRightText;
      public POJO ( String titleRowText,
                    String subTitleRowLeftText,
                    String subTitleRowLeftColor,
                    String subTitleRowRightText,
                    String contentRowLeftText,
                    String contentRowRightText ) {
       this.titleRowText = titleRowText;
       this.subTitleRowLeftText = subTitleRowLeftText;
       this.subTitleRowLeftColor = subTitleRowLeftColor;
       this.subTitleRowRightText = subTitleRowRightText;
       this.contentRowLeftText = contentRowLeftText;
       this.contentRowRightText = contentRowRightText;     
      }
      public String getTitleRowText() {
       return this.titleRowText;
      }
      public String getSubTitleRowLeftText() {
       return this.subTitleRowLeftText;
      }
      public String getSubTitleRowLeftColor() {
       return this.subTitleRowLeftColor;
      }
      public String getSubTitleRowRightText() {
       return this.subTitleRowRightText;
      }
      public String getContentRowLeftText() {
       return this.contentRowLeftText;
      }
      public String getContentRowRightText() {
       return this.contentRowRightText;
      }
     }
    }
    

    Result:

    enter image description here

    This code is minimized to show the principle. If text formatting is necessary, then the method setText(XWPFTableCell cell, String text) needs to be extended. Text formatting in Word needs XWPFRuns for each text which shall be formatted different. Of course also the POJO needs fields to determine the needed formatting then.