| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| package org.apache.poi.xwpf.usermodel; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.EnumMap; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| import org.apache.poi.POIXMLDocumentPart; |
| import org.apache.poi.util.Internal; |
| import org.apache.xmlbeans.XmlCursor; |
| import org.apache.xmlbeans.XmlObject; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtRun; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVerticalJc; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.STShd; |
| import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc; |
| |
| /** |
| * Represents a Cell within a {@link XWPFTable}. The |
| * Cell is the thing that holds the actual content (paragraphs etc) |
| */ |
| public class XWPFTableCell implements IBody, ICell { |
| private final CTTc ctTc; |
| protected List<XWPFParagraph> paragraphs = null; |
| protected List<XWPFTable> tables = null; |
| protected List<IBodyElement> bodyElements = null; |
| protected IBody part; |
| private XWPFTableRow tableRow = null; |
| // Create a map from this XWPF-level enum to the STVerticalJc.Enum values |
| public static enum XWPFVertAlign { TOP, CENTER, BOTH, BOTTOM }; |
| private static EnumMap<XWPFVertAlign, STVerticalJc.Enum> alignMap; |
| // Create a map from the STVerticalJc.Enum values to the XWPF-level enums |
| private static HashMap<Integer, XWPFVertAlign> stVertAlignTypeMap; |
| |
| static { |
| // populate enum maps |
| alignMap = new EnumMap<XWPFVertAlign, STVerticalJc.Enum>(XWPFVertAlign.class); |
| alignMap.put(XWPFVertAlign.TOP, STVerticalJc.Enum.forInt(STVerticalJc.INT_TOP)); |
| alignMap.put(XWPFVertAlign.CENTER, STVerticalJc.Enum.forInt(STVerticalJc.INT_CENTER)); |
| alignMap.put(XWPFVertAlign.BOTH, STVerticalJc.Enum.forInt(STVerticalJc.INT_BOTH)); |
| alignMap.put(XWPFVertAlign.BOTTOM, STVerticalJc.Enum.forInt(STVerticalJc.INT_BOTTOM)); |
| |
| stVertAlignTypeMap = new HashMap<Integer, XWPFVertAlign>(); |
| stVertAlignTypeMap.put(STVerticalJc.INT_TOP, XWPFVertAlign.TOP); |
| stVertAlignTypeMap.put(STVerticalJc.INT_CENTER, XWPFVertAlign.CENTER); |
| stVertAlignTypeMap.put(STVerticalJc.INT_BOTH, XWPFVertAlign.BOTH); |
| stVertAlignTypeMap.put(STVerticalJc.INT_BOTTOM, XWPFVertAlign.BOTTOM); |
| |
| } |
| |
| /** |
| * If a table cell does not include at least one block-level element, then this document shall be considered corrupt |
| */ |
| public XWPFTableCell(CTTc cell, XWPFTableRow tableRow, IBody part) { |
| this.ctTc = cell; |
| this.part = part; |
| this.tableRow = tableRow; |
| // NB: If a table cell does not include at least one block-level element, then this document shall be considered corrupt. |
| if(cell.sizeOfPArray()<1) |
| cell.addNewP(); |
| bodyElements = new ArrayList<IBodyElement>(); |
| paragraphs = new ArrayList<XWPFParagraph>(); |
| tables = new ArrayList<XWPFTable>(); |
| |
| XmlCursor cursor = ctTc.newCursor(); |
| cursor.selectPath("./*"); |
| while (cursor.toNextSelection()) { |
| XmlObject o = cursor.getObject(); |
| if (o instanceof CTP) { |
| XWPFParagraph p = new XWPFParagraph((CTP)o, this); |
| paragraphs.add(p); |
| bodyElements.add(p); |
| } |
| if (o instanceof CTTbl) { |
| XWPFTable t = new XWPFTable((CTTbl)o, this); |
| tables.add(t); |
| bodyElements.add(t); |
| } |
| if (o instanceof CTSdtBlock){ |
| XWPFSDT c = new XWPFSDT((CTSdtBlock)o, this); |
| bodyElements.add(c); |
| } |
| if (o instanceof CTSdtRun){ |
| XWPFSDT c = new XWPFSDT((CTSdtRun)o, this); |
| System.out.println(c.getContent().getText()); |
| bodyElements.add(c); |
| } |
| } |
| cursor.dispose(); |
| } |
| |
| |
| @Internal |
| public CTTc getCTTc() { |
| return ctTc; |
| } |
| |
| /** |
| * returns an Iterator with paragraphs and tables |
| * @see org.apache.poi.xwpf.usermodel.IBody#getBodyElements() |
| */ |
| public List<IBodyElement> getBodyElements(){ |
| return Collections.unmodifiableList(bodyElements); |
| } |
| |
| public void setParagraph(XWPFParagraph p) { |
| if (ctTc.sizeOfPArray() == 0) { |
| ctTc.addNewP(); |
| } |
| ctTc.setPArray(0, p.getCTP()); |
| } |
| |
| /** |
| * returns a list of paragraphs |
| */ |
| public List<XWPFParagraph> getParagraphs(){ |
| return paragraphs; |
| } |
| |
| /** |
| * Add a Paragraph to this Table Cell |
| * @return The paragraph which was added |
| */ |
| public XWPFParagraph addParagraph() { |
| XWPFParagraph p = new XWPFParagraph(ctTc.addNewP(), this); |
| addParagraph(p); |
| return p; |
| } |
| |
| /** |
| * add a Paragraph to this TableCell |
| * @param p the paragaph which has to be added |
| */ |
| public void addParagraph(XWPFParagraph p){ |
| paragraphs.add(p); |
| } |
| |
| /** |
| * removes a paragraph of this tablecell |
| * @param pos |
| */ |
| public void removeParagraph(int pos){ |
| paragraphs.remove(pos); |
| ctTc.removeP(pos); |
| } |
| |
| /** |
| * if there is a corresponding {@link XWPFParagraph} of the parameter ctTable in the paragraphList of this table |
| * the method will return this paragraph |
| * if there is no corresponding {@link XWPFParagraph} the method will return null |
| * @param p is instance of CTP and is searching for an XWPFParagraph |
| * @return null if there is no XWPFParagraph with an corresponding CTPparagraph in the paragraphList of this table |
| * XWPFParagraph with the correspondig CTP p |
| */ |
| public XWPFParagraph getParagraph(CTP p){ |
| for (XWPFParagraph paragraph : paragraphs) { |
| if(p.equals(paragraph.getCTP())){ |
| return paragraph; |
| } |
| } |
| return null; |
| } |
| |
| public void setText(String text) { |
| CTP ctP = (ctTc.sizeOfPArray() == 0) ? ctTc.addNewP() : ctTc.getPArray(0); |
| XWPFParagraph par = new XWPFParagraph(ctP, this); |
| par.createRun().setText(text); |
| } |
| |
| public XWPFTableRow getTableRow(){ |
| return tableRow; |
| } |
| |
| /** |
| * Set cell color. This sets some associated values; for finer control |
| * you may want to access these elements individually. |
| * @param rgbStr - the desired cell color, in the hex form "RRGGBB". |
| */ |
| public void setColor(String rgbStr) { |
| CTTcPr tcpr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr(); |
| CTShd ctshd = tcpr.isSetShd() ? tcpr.getShd() : tcpr.addNewShd(); |
| ctshd.setColor("auto"); |
| ctshd.setVal(STShd.CLEAR); |
| ctshd.setFill(rgbStr); |
| } |
| |
| /** |
| * Get cell color. Note that this method only returns the "fill" value. |
| * @return RGB string of cell color |
| */ |
| public String getColor() { |
| String color = null; |
| CTTcPr tcpr = ctTc.getTcPr(); |
| if (tcpr != null) { |
| CTShd ctshd = tcpr.getShd(); |
| if (ctshd != null) { |
| color = ctshd.xgetFill().getStringValue(); |
| } |
| } |
| return color; |
| } |
| |
| /** |
| * Set the vertical alignment of the cell. |
| * @param vAlign - the desired alignment enum value |
| */ |
| public void setVerticalAlignment(XWPFVertAlign vAlign) { |
| CTTcPr tcpr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr(); |
| CTVerticalJc va = tcpr.addNewVAlign(); |
| va.setVal(alignMap.get(vAlign)); |
| } |
| |
| /** |
| * Get the vertical alignment of the cell. |
| * @return the cell alignment enum value |
| */ |
| public XWPFVertAlign getVerticalAlignment() { |
| XWPFVertAlign vAlign = null; |
| CTTcPr tcpr = ctTc.getTcPr(); |
| if (ctTc != null) { |
| CTVerticalJc va = tcpr.getVAlign(); |
| vAlign = stVertAlignTypeMap.get(va.getVal().intValue()); |
| } |
| return vAlign; |
| } |
| |
| /** |
| * add a new paragraph at position of the cursor |
| * @param cursor |
| * @return the inserted paragraph |
| */ |
| public XWPFParagraph insertNewParagraph(XmlCursor cursor){ |
| if(!isCursorInTableCell(cursor)) { |
| return null; |
| } |
| |
| String uri = CTP.type.getName().getNamespaceURI(); |
| String localPart = "p"; |
| cursor.beginElement(localPart,uri); |
| cursor.toParent(); |
| CTP p = (CTP)cursor.getObject(); |
| XWPFParagraph newP = new XWPFParagraph(p, this); |
| XmlObject o = null; |
| while(!(o instanceof CTP)&&(cursor.toPrevSibling())){ |
| o = cursor.getObject(); |
| } |
| if((!(o instanceof CTP)) || (CTP)o == p){ |
| paragraphs.add(0, newP); |
| } |
| else{ |
| int pos = paragraphs.indexOf(getParagraph((CTP)o))+1; |
| paragraphs.add(pos,newP); |
| } |
| int i=0; |
| cursor.toCursor(p.newCursor()); |
| while(cursor.toPrevSibling()){ |
| o =cursor.getObject(); |
| if(o instanceof CTP || o instanceof CTTbl) |
| i++; |
| } |
| bodyElements.add(i, newP); |
| cursor.toCursor(p.newCursor()); |
| cursor.toEndToken(); |
| return newP; |
| } |
| |
| public XWPFTable insertNewTbl(XmlCursor cursor) { |
| if(isCursorInTableCell(cursor)){ |
| String uri = CTTbl.type.getName().getNamespaceURI(); |
| String localPart = "tbl"; |
| cursor.beginElement(localPart,uri); |
| cursor.toParent(); |
| CTTbl t = (CTTbl)cursor.getObject(); |
| XWPFTable newT = new XWPFTable(t, this); |
| cursor.removeXmlContents(); |
| XmlObject o = null; |
| while(!(o instanceof CTTbl)&&(cursor.toPrevSibling())){ |
| o = cursor.getObject(); |
| } |
| if(!(o instanceof CTTbl)){ |
| tables.add(0, newT); |
| } |
| else{ |
| int pos = tables.indexOf(getTable((CTTbl)o))+1; |
| tables.add(pos,newT); |
| } |
| int i=0; |
| cursor = t.newCursor(); |
| while(cursor.toPrevSibling()){ |
| o =cursor.getObject(); |
| if(o instanceof CTP || o instanceof CTTbl) |
| i++; |
| } |
| bodyElements.add(i, newT); |
| cursor = t.newCursor(); |
| cursor.toEndToken(); |
| return newT; |
| } |
| return null; |
| } |
| |
| /** |
| * verifies that cursor is on the right position |
| */ |
| private boolean isCursorInTableCell(XmlCursor cursor) { |
| XmlCursor verify = cursor.newCursor(); |
| verify.toParent(); |
| if(verify.getObject() == this.ctTc){ |
| return true; |
| } |
| return false; |
| } |
| |
| |
| |
| /** |
| * @see org.apache.poi.xwpf.usermodel.IBody#getParagraphArray(int) |
| */ |
| public XWPFParagraph getParagraphArray(int pos) { |
| if(pos > 0 && pos < paragraphs.size()){ |
| return paragraphs.get(pos); |
| } |
| return null; |
| } |
| |
| /** |
| * get the to which the TableCell belongs |
| * |
| * @see org.apache.poi.xwpf.usermodel.IBody#getPart() |
| */ |
| public POIXMLDocumentPart getPart() { |
| return tableRow.getTable().getPart(); |
| } |
| |
| /** |
| * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() |
| */ |
| public BodyType getPartType() { |
| return BodyType.TABLECELL; |
| } |
| |
| |
| /** |
| * get a table by its CTTbl-Object |
| * @see org.apache.poi.xwpf.usermodel.IBody#getTable(org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl) |
| */ |
| public XWPFTable getTable(CTTbl ctTable) { |
| for(int i=0; i<tables.size(); i++){ |
| if(getTables().get(i).getCTTbl() == ctTable) return getTables().get(i); |
| } |
| return null; |
| } |
| |
| |
| /** |
| * @see org.apache.poi.xwpf.usermodel.IBody#getTableArray(int) |
| */ |
| public XWPFTable getTableArray(int pos) { |
| if(pos > 0 && pos < tables.size()){ |
| return tables.get(pos); |
| } |
| return null; |
| } |
| |
| |
| /** |
| * @see org.apache.poi.xwpf.usermodel.IBody#getTables() |
| */ |
| public List<XWPFTable> getTables() { |
| return Collections.unmodifiableList(tables); |
| } |
| |
| |
| /** |
| * inserts an existing XWPFTable to the arrays bodyElements and tables |
| * @see org.apache.poi.xwpf.usermodel.IBody#insertTable(int, org.apache.poi.xwpf.usermodel.XWPFTable) |
| */ |
| @SuppressWarnings("deprecation") |
| public void insertTable(int pos, XWPFTable table) { |
| bodyElements.add(pos, table); |
| int i = 0; |
| for (CTTbl tbl : ctTc.getTblArray()) { |
| if (tbl == table.getCTTbl()) { |
| break; |
| } |
| i++; |
| } |
| tables.add(i, table); |
| } |
| |
| public String getText(){ |
| StringBuffer text = new StringBuffer(); |
| for (XWPFParagraph p : paragraphs) { |
| text.append(p.getText()); |
| } |
| return text.toString(); |
| } |
| |
| /** |
| * extracts all text recursively through embedded tables and embedded SDTs |
| */ |
| public String getTextRecursively(){ |
| |
| StringBuffer text = new StringBuffer(); |
| for (int i = 0; i < bodyElements.size(); i++){ |
| boolean isLast = (i== bodyElements.size()-1)? true : false; |
| appendBodyElementText(text, bodyElements.get(i), isLast); |
| } |
| |
| return text.toString(); |
| } |
| |
| private void appendBodyElementText(StringBuffer text, IBodyElement e, boolean isLast){ |
| if (e instanceof XWPFParagraph){ |
| text.append(((XWPFParagraph)e).getText()); |
| if (isLast == false){ |
| text.append('\t'); |
| } |
| } else if (e instanceof XWPFTable){ |
| XWPFTable eTable = (XWPFTable)e; |
| for (XWPFTableRow row : eTable.getRows()){ |
| for (XWPFTableCell cell : row.getTableCells()){ |
| List<IBodyElement> localBodyElements = cell.getBodyElements(); |
| for (int i = 0; i < localBodyElements.size(); i++){ |
| boolean localIsLast = (i== localBodyElements.size()-1)? true : false; |
| appendBodyElementText(text, localBodyElements.get(i), localIsLast); |
| } |
| } |
| } |
| |
| if (isLast == false){ |
| text.append('\n'); |
| } |
| } else if (e instanceof XWPFSDT){ |
| text.append(((XWPFSDT)e).getContent().getText()); |
| if (isLast == false){ |
| text.append('\t'); |
| } |
| } |
| } |
| |
| /** |
| * get the TableCell which belongs to the TableCell |
| */ |
| public XWPFTableCell getTableCell(CTTc cell) { |
| XmlCursor cursor = cell.newCursor(); |
| cursor.toParent(); |
| XmlObject o = cursor.getObject(); |
| if(!(o instanceof CTRow)){ |
| return null; |
| } |
| CTRow row = (CTRow)o; |
| cursor.toParent(); |
| o = cursor.getObject(); |
| cursor.dispose(); |
| if(! (o instanceof CTTbl)){ |
| return null; |
| } |
| CTTbl tbl = (CTTbl) o; |
| XWPFTable table = getTable(tbl); |
| if(table == null){ |
| return null; |
| } |
| XWPFTableRow tableRow = table.getRow(row); |
| if (tableRow == null) { |
| return null; |
| } |
| return tableRow.getTableCell(cell); |
| } |
| |
| public XWPFDocument getXWPFDocument() { |
| return part.getXWPFDocument(); |
| } |
| } |