/* ==================================================================== | |
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()); | |
} else { | |
vAlign = XWPFVertAlign.TOP; | |
} | |
if (va != null && va.getVal() != 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 | |
} | |
} |