| /* ==================================================================== |
| 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 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); |
| |
| } |
| |
| 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; |
| |
| /** |
| * 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 The position in the list of paragraphs, 0-based |
| */ |
| 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 XWPFTableRow getTableRow() { |
| return tableRow; |
| } |
| |
| /** |
| * 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 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 the vertical alignment of the cell. |
| * |
| * @return the cell alignment enum value or <code>null</code> |
| * if no vertical alignment is set. |
| */ |
| public XWPFVertAlign getVerticalAlignment() { |
| XWPFVertAlign vAlign = null; |
| CTTcPr tcpr = ctTc.getTcPr(); |
| if (tcpr != null) { |
| CTVerticalJc va = tcpr.getVAlign(); |
| if(va != null) { |
| vAlign = stVertAlignTypeMap.get(va.getVal().intValue()); |
| } |
| } |
| return vAlign; |
| } |
| |
| /** |
| * 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)); |
| } |
| |
| /** |
| * add a new paragraph at position of the cursor |
| * |
| * @param cursor The XmlCursor structure created with XmlBeans |
| * @return the inserted paragraph |
| */ |
| public XWPFParagraph insertNewParagraph(final 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; |
| XmlCursor p2 = p.newCursor(); |
| cursor.toCursor(p2); |
| p2.dispose(); |
| while (cursor.toPrevSibling()) { |
| o = cursor.getObject(); |
| if (o instanceof CTP || o instanceof CTTbl) |
| i++; |
| } |
| bodyElements.add(i, newP); |
| p2 = p.newCursor(); |
| cursor.toCursor(p2); |
| p2.dispose(); |
| cursor.toEndToken(); |
| return newP; |
| } |
| |
| public XWPFTable insertNewTbl(final 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; |
| XmlCursor cursor2 = t.newCursor(); |
| while (cursor2.toPrevSibling()) { |
| o = cursor2.getObject(); |
| if (o instanceof CTP || o instanceof CTTbl) |
| i++; |
| } |
| cursor2.dispose(); |
| bodyElements.add(i, newT); |
| cursor2 = t.newCursor(); |
| cursor.toCursor(cursor2); |
| cursor.toEndToken(); |
| cursor2.dispose(); |
| return newT; |
| } |
| return null; |
| } |
| |
| /** |
| * verifies that cursor is on the right position |
| */ |
| private boolean isCursorInTableCell(XmlCursor cursor) { |
| XmlCursor verify = cursor.newCursor(); |
| verify.toParent(); |
| boolean result = (verify.getObject() == this.ctTc); |
| verify.dispose(); |
| return result; |
| } |
| |
| /** |
| * @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) |
| */ |
| 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() { |
| StringBuilder text = new StringBuilder(); |
| for (XWPFParagraph p : paragraphs) { |
| text.append(p.getText()); |
| } |
| return text.toString(); |
| } |
| |
| 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); |
| } |
| |
| /** |
| * 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); |
| 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) { |
| 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); |
| appendBodyElementText(text, localBodyElements.get(i), localIsLast); |
| } |
| } |
| } |
| |
| if (!isLast) { |
| text.append('\n'); |
| } |
| } else if (e instanceof XWPFSDT) { |
| text.append(((XWPFSDT) e).getContent().getText()); |
| if (!isLast) { |
| 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 tr = table.getRow(row); |
| if (tr == null) { |
| return null; |
| } |
| return tr.getTableCell(cell); |
| } |
| |
| public XWPFDocument getXWPFDocument() { |
| return part.getXWPFDocument(); |
| } |
| |
| // Create a map from this XWPF-level enum to the STVerticalJc.Enum values |
| public enum XWPFVertAlign { |
| TOP, CENTER, BOTH, BOTTOM |
| } |
| } |