git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1836538 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_0_0_FINAL
@@ -0,0 +1,499 @@ | |||
package org.apache.poi.xwpf.usermodel; | |||
import java.math.BigInteger; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.apache.poi.ooxml.POIXMLDocumentPart; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.xmlbeans.XmlCursor; | |||
import org.apache.xmlbeans.XmlObject; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdnRef; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; | |||
/** | |||
* Base class for both bottom-of-the-page footnotes {@link XWPFFootnote} and end | |||
* notes {@link XWPFEndnote}). | |||
* <p>The only significant difference between footnotes and | |||
* end notes is which part they go on. Footnotes are managed by the Footnotes part | |||
* {@link XWPFFootnotes} and end notes are managed by the Endnotes part {@link XWPFEndnotes}.</p> | |||
* @since 4.0.0 | |||
*/ | |||
public abstract class AbstractXWPFFootnoteEndnote implements Iterable<XWPFParagraph>, IBody { | |||
private List<XWPFParagraph> paragraphs = new ArrayList<>(); | |||
private List<XWPFTable> tables = new ArrayList<>(); | |||
private List<XWPFPictureData> pictures = new ArrayList<>(); | |||
private List<IBodyElement> bodyElements = new ArrayList<>(); | |||
protected CTFtnEdn ctFtnEdn; | |||
protected AbstractXWPFFootnotesEndnotes footnotes; | |||
protected XWPFDocument document; | |||
public AbstractXWPFFootnoteEndnote() { | |||
super(); | |||
} | |||
@Internal | |||
protected AbstractXWPFFootnoteEndnote(XWPFDocument document, CTFtnEdn body) { | |||
ctFtnEdn = body; | |||
this.document = document; | |||
init(); | |||
} | |||
@Internal | |||
protected AbstractXWPFFootnoteEndnote(CTFtnEdn note, AbstractXWPFFootnotesEndnotes footnotes) { | |||
this.footnotes = footnotes; | |||
ctFtnEdn = note; | |||
document = footnotes.getXWPFDocument(); | |||
init(); | |||
} | |||
protected void init() { | |||
XmlCursor cursor = ctFtnEdn.newCursor(); | |||
//copied from XWPFDocument...should centralize this code | |||
//to avoid duplication | |||
cursor.selectPath("./*"); | |||
while (cursor.toNextSelection()) { | |||
XmlObject o = cursor.getObject(); | |||
if (o instanceof CTP) { | |||
XWPFParagraph p = new XWPFParagraph((CTP) o, this); | |||
bodyElements.add(p); | |||
paragraphs.add(p); | |||
} else if (o instanceof CTTbl) { | |||
XWPFTable t = new XWPFTable((CTTbl) o, this); | |||
bodyElements.add(t); | |||
tables.add(t); | |||
} else if (o instanceof CTSdtBlock) { | |||
XWPFSDT c = new XWPFSDT((CTSdtBlock) o, this); | |||
bodyElements.add(c); | |||
} | |||
} | |||
cursor.dispose(); | |||
} | |||
/** | |||
* Get the list of {@link XWPFParagraph}s in the footnote. | |||
* @return List of paragraphs | |||
*/ | |||
public List<XWPFParagraph> getParagraphs() { | |||
return paragraphs; | |||
} | |||
/** | |||
* Get an iterator over the {@link XWPFParagraph}s in the footnote. | |||
* @return Iterator over the paragraph list. | |||
*/ | |||
public Iterator<XWPFParagraph> iterator() { | |||
return paragraphs.iterator(); | |||
} | |||
/** | |||
* Get the list of {@link XWPFTable}s in the footnote. | |||
* @return List of tables | |||
*/ | |||
public List<XWPFTable> getTables() { | |||
return tables; | |||
} | |||
/** | |||
* Gets the list of {@link XWPFPictureData}s in the footnote. | |||
* @return List of pictures | |||
*/ | |||
public List<XWPFPictureData> getPictures() { | |||
return pictures; | |||
} | |||
/** | |||
* Gets the body elements ({@link IBodyElement}) of the footnote. | |||
* @return List of body elements. | |||
*/ | |||
public List<IBodyElement> getBodyElements() { | |||
return bodyElements; | |||
} | |||
/** | |||
* Gets the underlying CTFtnEdn object for the footnote. | |||
* @return CTFtnEdn object | |||
*/ | |||
public CTFtnEdn getCTFtnEdn() { | |||
return ctFtnEdn; | |||
} | |||
/** | |||
* Set the underlying CTFtnEdn for the footnote. | |||
* <p>Use {@link XWPFDocument#createFootnote()} to create new footnotes.</p> | |||
* @param footnote The CTFtnEdn object that will underly the footnote. | |||
*/ | |||
public void setCTFtnEdn(CTFtnEdn footnote) { | |||
ctFtnEdn = footnote; | |||
} | |||
/** | |||
* Gets the {@link XWPFTable} at the specified position from the footnote's table array. | |||
* @param pos in table array | |||
* @return The {@link XWPFTable} at position pos, or null if there is no table at position pos. | |||
* @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; | |||
} | |||
/** | |||
* Inserts an existing {@link XWPFTable) into the arrays bodyElements and tables. | |||
* | |||
* @param pos Position, in the bodyElements array, to insert the table | |||
* @param table {@link XWPFTable) to be inserted | |||
* @see org.apache.poi.xwpf.usermodel.IBody#insertTable(int pos, XWPFTable table) | |||
*/ | |||
public void insertTable(int pos, XWPFTable table) { | |||
bodyElements.add(pos, table); | |||
int i = 0; | |||
for (CTTbl tbl : ctFtnEdn.getTblList()) { | |||
if (tbl == table.getCTTbl()) { | |||
break; | |||
} | |||
i++; | |||
} | |||
tables.add(i, table); | |||
} | |||
/** | |||
* if there is a corresponding {@link XWPFTable} of the parameter | |||
* ctTable in the tableList of this header | |||
* the method will return this table, or null if there is no | |||
* corresponding {@link XWPFTable}. | |||
* | |||
* @param ctTable | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getTable(CTTbl ctTable) | |||
*/ | |||
public XWPFTable getTable(CTTbl ctTable) { | |||
for (XWPFTable table : tables) { | |||
if (table == null) | |||
return null; | |||
if (table.getCTTbl().equals(ctTable)) | |||
return table; | |||
} | |||
return null; | |||
} | |||
/** | |||
* if there is a corresponding {@link XWPFParagraph} of the parameter p in the paragraphList of this header or footer | |||
* the method will return that paragraph, otherwise the method will return null. | |||
* | |||
* @param p The CTP paragraph to find the corresponding {@link XWPFParagraph} for. | |||
* @return The {@link XWPFParagraph} that corresponds to the CTP paragraph in the paragraph | |||
* list of this footnote or null if no paragraph is found. | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getParagraph(CTP p) | |||
*/ | |||
public XWPFParagraph getParagraph(CTP p) { | |||
for (XWPFParagraph paragraph : paragraphs) { | |||
if (paragraph.getCTP().equals(p)) | |||
return paragraph; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Returns the {@link XWPFParagraph} at position pos in footnote's paragraph array. | |||
* @param pos Array position of the paragraph to get. | |||
* @return the {@link XWPFParagraph} at position pos, or null if there is no paragraph at that position. | |||
* | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getParagraphArray(int pos) | |||
*/ | |||
public XWPFParagraph getParagraphArray(int pos) { | |||
if(pos >=0 && pos < paragraphs.size()) { | |||
return paragraphs.get(pos); | |||
} | |||
return null; | |||
} | |||
/** | |||
* get the {@link XWPFTableCell} that belongs to the CTTc cell. | |||
* | |||
* @param cell | |||
* @return {@link XWPFTableCell} that corresponds to the CTTc cell, if there is one, otherwise null. | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getTableCell(CTTc cell) | |||
*/ | |||
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); | |||
} | |||
/** | |||
* Verifies that cursor is on the right position. | |||
* | |||
* @param cursor | |||
* @return true if the cursor is within a CTFtnEdn element. | |||
*/ | |||
private boolean isCursorInFtn(XmlCursor cursor) { | |||
XmlCursor verify = cursor.newCursor(); | |||
verify.toParent(); | |||
if (verify.getObject() == this.ctFtnEdn) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* The owning object for this footnote | |||
* | |||
* @return The {@link XWPFFootnotes} object that contains this footnote. | |||
*/ | |||
public POIXMLDocumentPart getOwner() { | |||
return footnotes; | |||
} | |||
/** | |||
* Insert a table constructed from OOXML table markup. | |||
* @param cursor | |||
* @return the inserted {@link XWPFTable} | |||
* @see org.apache.poi.xwpf.usermodel.IBody#insertNewTbl(XmlCursor cursor) | |||
*/ | |||
public XWPFTable insertNewTbl(XmlCursor cursor) { | |||
if (isCursorInFtn(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); | |||
XmlCursor c2 = t.newCursor(); | |||
cursor.toCursor(c2); | |||
cursor.toEndToken(); | |||
c2.dispose(); | |||
return newT; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Add a new {@link XWPFParagraph} at position of the cursor. | |||
* | |||
* @param cursor | |||
* @return The inserted {@link XWPFParagraph} | |||
* @see org.apache.poi.xwpf.usermodel.IBody#insertNewParagraph(XmlCursor cursor) | |||
*/ | |||
public XWPFParagraph insertNewParagraph(final XmlCursor cursor) { | |||
if (isCursorInFtn(cursor)) { | |||
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)) || 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); | |||
cursor.toEndToken(); | |||
p2.dispose(); | |||
return newP; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Add a new {@link XWPFTable} to the end of the footnote. | |||
* | |||
* @param table CTTbl object from which to construct the {@link XWPFTable} | |||
* @return The added {@link XWPFTable} | |||
*/ | |||
public XWPFTable addNewTbl(CTTbl table) { | |||
CTTbl newTable = ctFtnEdn.addNewTbl(); | |||
newTable.set(table); | |||
XWPFTable xTable = new XWPFTable(newTable, this); | |||
tables.add(xTable); | |||
return xTable; | |||
} | |||
/** | |||
* Add a new {@link XWPFParagraph} to the end of the footnote. | |||
* | |||
* @param paragraph CTP paragraph from which to construct the {@link XWPFParagraph} | |||
* @return The added {@link XWPFParagraph} | |||
*/ | |||
public XWPFParagraph addNewParagraph(CTP paragraph) { | |||
CTP newPara = ctFtnEdn.addNewP(); | |||
newPara.set(paragraph); | |||
XWPFParagraph xPara = new XWPFParagraph(newPara, this); | |||
paragraphs.add(xPara); | |||
return xPara; | |||
} | |||
/** | |||
* Get the {@link XWPFDocument} the footnote is part of. | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getXWPFDocument() | |||
*/ | |||
public XWPFDocument getXWPFDocument() { | |||
return document; | |||
} | |||
/** | |||
* Get the Part to which the footnote belongs, which you need for adding relationships to other parts | |||
* @return {@link POIXMLDocumentPart} that contains the footnote. | |||
* | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getPart() | |||
*/ | |||
public POIXMLDocumentPart getPart() { | |||
return footnotes; | |||
} | |||
/** | |||
* Get the part type {@link BodyType} of the footnote. | |||
* @return The {@link BodyType} value. | |||
* | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getPartType() | |||
*/ | |||
public BodyType getPartType() { | |||
return BodyType.FOOTNOTE; | |||
} | |||
/** | |||
* Get the ID of the footnote. | |||
* <p>Footnote IDs are unique across all bottom-of-the-page and | |||
* end note footnotes.</p> | |||
* | |||
* @return Footnote ID | |||
* @since 4.0.0 | |||
*/ | |||
public BigInteger getId() { | |||
return this.ctFtnEdn.getId(); | |||
} | |||
/** | |||
* Appends a new {@link XWPFParagraph} to this footnote. | |||
* | |||
* @return The new {@link XWPFParagraph} | |||
* @since 4.0.0 | |||
*/ | |||
public XWPFParagraph createParagraph() { | |||
XWPFParagraph p = new XWPFParagraph(this.ctFtnEdn.addNewP(), this); | |||
paragraphs.add(p); | |||
bodyElements.add(p); | |||
// If the paragraph is the first paragraph in the footnote, | |||
// ensure that it has a footnote reference run. | |||
if (p.equals(getParagraphs().get(0))) { | |||
ensureFootnoteRef(p); | |||
} | |||
return p; | |||
} | |||
/** | |||
* Ensure that the specified paragraph has a reference marker for this | |||
* footnote by adding a footnote reference if one is not found. | |||
* <p>This method is for the first paragraph in the footnote, not | |||
* paragraphs that will refer to the footnote. For references to | |||
* the footnote, use {@link XWPFParagraph#addFootnoteReference(XWPFFootnote)}. | |||
* </p> | |||
* <p>The first run of the first paragraph in a footnote should | |||
* contain a {@link CTFtnEdnRef} object.</p> | |||
* | |||
* @param p The {@link XWPFParagraph} to ensure | |||
* @since 4.0.0 | |||
*/ | |||
public abstract void ensureFootnoteRef(XWPFParagraph p); | |||
/** | |||
* Appends a new {@link XWPFTable} to this footnote | |||
* | |||
* @return The new {@link XWPFTable} | |||
* @since 4.0.0 | |||
*/ | |||
public XWPFTable createTable() { | |||
XWPFTable table = new XWPFTable(ctFtnEdn.addNewTbl(), this); | |||
if (bodyElements.size() == 0) { | |||
XWPFParagraph p = createParagraph(); | |||
ensureFootnoteRef(p); | |||
} | |||
bodyElements.add(table); | |||
tables.add(table); | |||
return table; | |||
} | |||
/** | |||
* Appends a new {@link XWPFTable} to this footnote | |||
* @param rows Number of rows to initialize the table with | |||
* @param cols Number of columns to initialize the table with | |||
* @return the new {@link XWPFTable} with the specified number of rows and columns | |||
* @since 4.0.0 | |||
*/ | |||
public XWPFTable createTable(int rows, int cols) { | |||
XWPFTable table = new XWPFTable(ctFtnEdn.addNewTbl(), this, rows, cols); | |||
bodyElements.add(table); | |||
tables.add(table); | |||
return table; | |||
} | |||
} |
@@ -0,0 +1,74 @@ | |||
package org.apache.poi.xwpf.usermodel; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.ooxml.POIXMLDocumentPart; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
/** | |||
* Base class for the Footnotes and Endnotes part implementations. | |||
* @since 4.0.0 | |||
*/ | |||
public abstract class AbstractXWPFFootnotesEndnotes extends POIXMLDocumentPart { | |||
protected XWPFDocument document; | |||
protected List<AbstractXWPFFootnoteEndnote> listFootnote = new ArrayList<>(); | |||
private FootnoteEndnoteIdManager idManager; | |||
public AbstractXWPFFootnotesEndnotes(OPCPackage pkg) { | |||
super(pkg); | |||
} | |||
public AbstractXWPFFootnotesEndnotes(OPCPackage pkg, | |||
String coreDocumentRel) { | |||
super(pkg, coreDocumentRel); | |||
} | |||
public AbstractXWPFFootnotesEndnotes() { | |||
super(); | |||
} | |||
public AbstractXWPFFootnotesEndnotes(PackagePart part) { | |||
super(part); | |||
} | |||
public AbstractXWPFFootnotesEndnotes(POIXMLDocumentPart parent, PackagePart part) { | |||
super(parent, part); | |||
} | |||
public AbstractXWPFFootnoteEndnote getFootnoteById(int id) { | |||
for (AbstractXWPFFootnoteEndnote note : listFootnote) { | |||
if (note.getCTFtnEdn().getId().intValue() == id) | |||
return note; | |||
} | |||
return null; | |||
} | |||
/** | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getPart() | |||
*/ | |||
public XWPFDocument getXWPFDocument() { | |||
if (document != null) { | |||
return document; | |||
} else { | |||
return (XWPFDocument) getParent(); | |||
} | |||
} | |||
public void setXWPFDocument(XWPFDocument doc) { | |||
document = doc; | |||
} | |||
public void setIdManager(FootnoteEndnoteIdManager footnoteIdManager) { | |||
this.idManager = footnoteIdManager; | |||
} | |||
public FootnoteEndnoteIdManager getIdManager() { | |||
return this.idManager; | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
package org.apache.poi.xwpf.usermodel; | |||
import java.math.BigInteger; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* Manages IDs for footnotes and endnotes. | |||
* <p>Footnotes and endnotes are managed in separate parts but | |||
* represent a single namespace of IDs.</p> | |||
*/ | |||
public class FootnoteEndnoteIdManager { | |||
private XWPFDocument document; | |||
public FootnoteEndnoteIdManager(XWPFDocument document) { | |||
this.document = document; | |||
} | |||
/** | |||
* Gets the next ID number. | |||
* | |||
* @return ID number to use. | |||
*/ | |||
public BigInteger nextId() { | |||
List<BigInteger> ids = new ArrayList<BigInteger>(); | |||
for (AbstractXWPFFootnoteEndnote note : document.getFootnotes()) { | |||
ids.add(note.getId()); | |||
} | |||
for (AbstractXWPFFootnoteEndnote note : document.getEndnotes()) { | |||
ids.add(note.getId()); | |||
} | |||
int cand = ids.size(); | |||
BigInteger newId = BigInteger.valueOf(cand); | |||
while (ids.contains(newId)) { | |||
cand++; | |||
newId = BigInteger.valueOf(cand); | |||
} | |||
return newId; | |||
} | |||
} |
@@ -54,11 +54,9 @@ import org.apache.poi.openxml4j.opc.PackagingURIHelper; | |||
import org.apache.poi.openxml4j.opc.TargetMode; | |||
import org.apache.poi.poifs.crypt.HashAlgorithm; | |||
import org.apache.poi.util.IOUtils; | |||
import org.apache.poi.ooxml.util.IdentifierManager; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.ooxml.util.PackageHelper; | |||
import org.apache.poi.wp.usermodel.HeaderFooterType; | |||
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; | |||
import org.apache.xmlbeans.XmlCursor; | |||
@@ -100,6 +98,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; | |||
* http://www.ecma-international.org/publications/standards/Ecma-376.htm | |||
* at some point in your use.</p> | |||
*/ | |||
@SuppressWarnings("unused") | |||
public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
private static final POILogger LOG = POILogFactory.getLogger(XWPFDocument.class); | |||
@@ -113,7 +112,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
protected List<IBodyElement> bodyElements = new ArrayList<>(); | |||
protected List<XWPFPictureData> pictures = new ArrayList<>(); | |||
protected Map<Long, List<XWPFPictureData>> packagePictures = new HashMap<>(); | |||
protected Map<Integer, XWPFFootnote> endnotes = new HashMap<>(); | |||
protected XWPFEndnotes endnotes; | |||
protected XWPFNumbering numbering; | |||
protected XWPFStyles styles; | |||
protected XWPFFootnotes footnotes; | |||
@@ -124,6 +123,9 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
* Keeps track on all id-values used in this document and included parts, like headers, footers, etc. | |||
*/ | |||
private IdentifierManager drawingIdManager = new IdentifierManager(0L, 4294967295L); | |||
private FootnoteEndnoteIdManager footnoteIdManager = new FootnoteEndnoteIdManager(this); | |||
/** | |||
* Handles the joy of different headers/footers for different pages | |||
*/ | |||
@@ -286,12 +288,11 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
if (relation.equals(XWPFRelation.FOOTNOTE.getRelation())) { | |||
this.footnotes = (XWPFFootnotes) p; | |||
this.footnotes.onDocumentRead(); | |||
this.footnotes.setIdManager(footnoteIdManager); | |||
} else if (relation.equals(XWPFRelation.ENDNOTE.getRelation())) { | |||
EndnotesDocument endnotesDocument = EndnotesDocument.Factory.parse(p.getPackagePart().getInputStream(), DEFAULT_XML_OPTIONS); | |||
for (CTFtnEdn ctFtnEdn : endnotesDocument.getEndnotes().getEndnoteArray()) { | |||
endnotes.put(ctFtnEdn.getId().intValue(), new XWPFFootnote(this, ctFtnEdn)); | |||
} | |||
this.endnotes = (XWPFEndnotes) p; | |||
this.endnotes.onDocumentRead(); | |||
this.endnotes.setIdManager(footnoteIdManager); | |||
} | |||
} | |||
} | |||
@@ -416,14 +417,14 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
if (footnotes == null) { | |||
return null; | |||
} | |||
return footnotes.getFootnoteById(id); | |||
return (XWPFFootnote)footnotes.getFootnoteById(id); | |||
} | |||
public XWPFFootnote getEndnoteByID(int id) { | |||
public XWPFEndnote getEndnoteByID(int id) { | |||
if (endnotes == null) { | |||
return null; | |||
} | |||
return endnotes.get(id); | |||
return endnotes.getFootnoteById(id); | |||
} | |||
public List<XWPFFootnote> getFootnotes() { | |||
@@ -898,19 +899,34 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
XWPFFootnotes wrapper = (XWPFFootnotes) createRelationship(relation, XWPFFactory.getInstance(), i); | |||
wrapper.setFootnotes(footnotesDoc.addNewFootnotes()); | |||
wrapper.setIdManager(this.footnoteIdManager); | |||
footnotes = wrapper; | |||
} | |||
return footnotes; | |||
} | |||
/** | |||
* Add a CTFtnEdn footnote to the document. | |||
* | |||
* @param note CTFtnEnd to be added. | |||
* @return New {@link XWPFFootnote} | |||
*/ | |||
@Internal | |||
public XWPFFootnote addFootnote(CTFtnEdn note) { | |||
return footnotes.addFootnote(note); | |||
} | |||
public XWPFFootnote addEndnote(CTFtnEdn note) { | |||
XWPFFootnote endnote = new XWPFFootnote(this, note); | |||
endnotes.put(note.getId().intValue(), endnote); | |||
/** | |||
* Add a CTFtnEdn endnote to the document. | |||
* | |||
* @param note CTFtnEnd to be added. | |||
* @return New {@link XWPFEndnote} | |||
*/ | |||
@Internal | |||
public XWPFEndnote addEndnote(CTFtnEdn note) { | |||
XWPFEndnote endnote = new XWPFEndnote(this, note); | |||
endnotes.addEndnote(note); | |||
return endnote; | |||
} | |||
@@ -1658,12 +1674,10 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
} | |||
/** | |||
* Create a new footnote and add it to the document. | |||
* <p>The new note will have one paragraph with the style "FootnoteText" | |||
* and one run containing the required footnote reference with the | |||
* style "FootnoteReference". | |||
* Create a new footnote and add it to the document. | |||
* | |||
* @return New XWPFFootnote. | |||
* @since 4.0.0 | |||
*/ | |||
public XWPFFootnote createFootnote() { | |||
XWPFFootnotes footnotes = this.createFootnotes(); | |||
@@ -1675,8 +1689,9 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
/** | |||
* Remove the specified footnote if present. | |||
* | |||
* @param pos | |||
* @param pos Array position of the footnote to be removed. | |||
* @return True if the footnote was removed. | |||
* @since 4.0.0 | |||
*/ | |||
public boolean removeFootnote(int pos) { | |||
if (null != footnotes) { | |||
@@ -1685,4 +1700,62 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
return false; | |||
} | |||
} | |||
/** | |||
* Create a new end note and add it to the document. | |||
* | |||
* @return New {@link XWPFEndnote}. | |||
* @since 4.0.0 | |||
*/ | |||
public XWPFEndnote createEndnote() { | |||
XWPFEndnotes endnotes = this.createEndnotes(); | |||
XWPFEndnote endnote = endnotes.createEndnote(); | |||
return endnote; | |||
} | |||
public XWPFEndnotes createEndnotes() { | |||
if (endnotes == null) { | |||
EndnotesDocument endnotesDoc = EndnotesDocument.Factory.newInstance(); | |||
XWPFRelation relation = XWPFRelation.ENDNOTE; | |||
int i = getRelationIndex(relation); | |||
XWPFEndnotes wrapper = (XWPFEndnotes) createRelationship(relation, XWPFFactory.getInstance(), i); | |||
wrapper.setEndnotes(endnotesDoc.addNewEndnotes()); | |||
wrapper.setIdManager(footnoteIdManager); | |||
endnotes = wrapper; | |||
} | |||
return endnotes; | |||
} | |||
/** | |||
* Gets the list of end notes for the document. | |||
* | |||
* @return List, possibly empty, of {@link XWPFEndnote}s. | |||
*/ | |||
public List<XWPFEndnote> getEndnotes() { | |||
if (endnotes == null) { | |||
return Collections.emptyList(); | |||
} | |||
return endnotes.getEndnotesList(); | |||
} | |||
/** | |||
* Remove the specified end note if present. | |||
* | |||
* @param pos Array position of the end note to be removed. | |||
* @return True if the end note was removed. | |||
* @since 4.0.0 | |||
*/ | |||
public boolean removeEndnote(int pos) { | |||
if (null != endnotes) { | |||
return endnotes.removeEndnote(pos); | |||
} else { | |||
return false; | |||
} | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
package org.apache.poi.xwpf.usermodel; | |||
import org.apache.poi.util.Internal; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdnRef; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; | |||
/** | |||
* Represents an end note footnote. | |||
* <p>End notes are collected at the end of a document or section rather than | |||
* at the bottom of a page.</p> | |||
* <p>Create a new footnote using {@link XWPFDocument#createEndnote()} or | |||
* {@link XWPFEndnotes#createFootnote()}.</p> | |||
* <p>The first body element of a footnote should (or possibly must) be a paragraph | |||
* with the first run containing a CTFtnEdnRef object. The {@link XWPFFootnote#createParagraph()} | |||
* and {@link XWPFFootnote#createTable()} methods do this for you.</p> | |||
* <p>Footnotes have IDs that are unique across all footnotes in the document. You use | |||
* the footnote ID to create a reference to a footnote from within a paragraph.</p> | |||
* <p>To create a reference to a footnote within a paragraph you create a run | |||
* with a CTFtnEdnRef that specifies the ID of the target paragraph. | |||
* The {@link XWPFParagraph#addFootnoteReference(AbstractXWPFFootnoteEndnote)} | |||
* method does this for you.</p> | |||
* @since 4.0.0 | |||
*/ | |||
public class XWPFEndnote extends AbstractXWPFFootnoteEndnote { | |||
public XWPFEndnote() {} | |||
@Internal | |||
public XWPFEndnote(XWPFDocument document, CTFtnEdn body) { | |||
super(document, body); | |||
} | |||
@Internal | |||
public XWPFEndnote(CTFtnEdn note, AbstractXWPFFootnotesEndnotes footnotes) { | |||
super(note, footnotes); | |||
} | |||
/** | |||
* Ensure that the specified paragraph has a reference marker for this | |||
* end note by adding a footnote reference if one is not found. | |||
* <p>This method is for the first paragraph in the footnote, not | |||
* paragraphs that will refer to the footnote. For references to | |||
* the footnote, use {@link XWPFParagraph#addFootnoteReference(AbstractXWPFFootnoteEndnote))}. | |||
* </p> | |||
* <p>The first run of the first paragraph in a footnote should | |||
* contain a {@link CTFtnEdnRef} object.</p> | |||
* | |||
* @param p The {@link XWPFParagraph} to ensure | |||
* @since 4.0.0 | |||
*/ | |||
public void ensureFootnoteRef(XWPFParagraph p) { | |||
XWPFRun r = null; | |||
if (p.getRuns().size() > 0) { | |||
r = p.getRuns().get(0); | |||
} | |||
if (r == null) { | |||
r = p.createRun(); | |||
} | |||
CTR ctr = r.getCTR(); | |||
boolean foundRef = false; | |||
for (CTFtnEdnRef ref : ctr.getEndnoteReferenceList()) { | |||
if (getId().equals(ref.getId())) { | |||
foundRef = true; | |||
break; | |||
} | |||
} | |||
if (!foundRef) { | |||
ctr.addNewRPr().addNewRStyle().setVal("FootnoteReference"); | |||
ctr.addNewEndnoteRef(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,195 @@ | |||
package org.apache.poi.xwpf.usermodel; | |||
import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.math.BigInteger; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import javax.xml.namespace.QName; | |||
import org.apache.poi.ooxml.POIXMLException; | |||
import org.apache.poi.openxml4j.exceptions.OpenXML4JException; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.apache.xmlbeans.XmlOptions; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTEndnotes; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.EndnotesDocument; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFtnEdn; | |||
/** | |||
* Looks after the collection of end notes for a document. | |||
* Managed end notes ({@link XWPFEndnote}). | |||
* @since 4.0.0 | |||
*/ | |||
public class XWPFEndnotes extends AbstractXWPFFootnotesEndnotes { | |||
protected CTEndnotes ctEndnotes; | |||
public XWPFEndnotes() { | |||
super(); | |||
} | |||
/** | |||
* Construct XWPFEndnotes from a package part | |||
* | |||
* @param part the package part holding the data of the footnotes, | |||
* | |||
* @since POI 3.14-Beta1 | |||
*/ | |||
public XWPFEndnotes(PackagePart part) throws IOException, OpenXML4JException { | |||
super(part); | |||
} | |||
/** | |||
* Set the end notes for this part. | |||
* | |||
* @param endnotes The endnotes to be added. | |||
*/ | |||
@Internal | |||
public void setEndnotes(CTEndnotes endnotes) { | |||
ctEndnotes = endnotes; | |||
} | |||
/** | |||
* Create a new end note and add it to the document. | |||
* | |||
* @return New XWPFEndnote | |||
* @since 4.0.0 | |||
*/ | |||
public XWPFEndnote createEndnote() { | |||
CTFtnEdn newNote = CTFtnEdn.Factory.newInstance(); | |||
newNote.setType(STFtnEdn.NORMAL); | |||
XWPFEndnote footnote = addEndnote(newNote); | |||
footnote.getCTFtnEdn().setId(getIdManager().nextId()); | |||
return footnote; | |||
} | |||
/** | |||
* Remove the specified footnote if present. | |||
* | |||
* @param pos | |||
* @return True if the footnote was removed. | |||
* @since 4.0.0 | |||
*/ | |||
public boolean removeFootnote(int pos) { | |||
if (ctEndnotes.sizeOfEndnoteArray() >= pos - 1) { | |||
ctEndnotes.removeEndnote(pos); | |||
listFootnote.remove(pos); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
/** | |||
* Read document | |||
*/ | |||
@Override | |||
protected void onDocumentRead() throws IOException { | |||
EndnotesDocument notesDoc; | |||
InputStream is = null; | |||
try { | |||
is = getPackagePart().getInputStream(); | |||
notesDoc = EndnotesDocument.Factory.parse(is, DEFAULT_XML_OPTIONS); | |||
ctEndnotes = notesDoc.getEndnotes(); | |||
} catch (XmlException e) { | |||
throw new POIXMLException(); | |||
} finally { | |||
if (is != null) { | |||
is.close(); | |||
} | |||
} | |||
for (CTFtnEdn note : ctEndnotes.getEndnoteList()) { | |||
listFootnote.add(new XWPFEndnote(note, this)); | |||
} | |||
} | |||
@Override | |||
protected void commit() throws IOException { | |||
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); | |||
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTEndnotes.type.getName().getNamespaceURI(), "endnotes")); | |||
PackagePart part = getPackagePart(); | |||
OutputStream out = part.getOutputStream(); | |||
ctEndnotes.save(out, xmlOptions); | |||
out.close(); | |||
} | |||
/** | |||
* add an {@link XWPFEndnote} to the document | |||
* | |||
* @param endnote | |||
* @throws IOException | |||
*/ | |||
public void addEndnote(XWPFEndnote endnote) { | |||
listFootnote.add(endnote); | |||
ctEndnotes.addNewEndnote().set(endnote.getCTFtnEdn()); | |||
} | |||
/** | |||
* Add an endnote to the document | |||
* | |||
* @param note Note to add | |||
* @return New {@link XWPFEndnote} | |||
* @throws IOException | |||
*/ | |||
@Internal | |||
public XWPFEndnote addEndnote(CTFtnEdn note) { | |||
CTFtnEdn newNote = ctEndnotes.addNewEndnote(); | |||
newNote.set(note); | |||
XWPFEndnote xNote = new XWPFEndnote(newNote, this); | |||
listFootnote.add(xNote); | |||
return xNote; | |||
} | |||
/** | |||
* Get the end note with the specified ID, if any. | |||
* @param id End note ID. | |||
* @return The end note or null if not found. | |||
*/ | |||
public XWPFEndnote getFootnoteById(int id) { | |||
return (XWPFEndnote)super.getFootnoteById(id); | |||
} | |||
/** | |||
* Get the list of {@link XWPFEndnote} in the Endnotes part. | |||
* | |||
* @return List, possibly empty, of end notes. | |||
*/ | |||
public List<XWPFEndnote> getEndnotesList() { | |||
List<XWPFEndnote> resultList = new ArrayList<XWPFEndnote>(); | |||
for (AbstractXWPFFootnoteEndnote note : listFootnote) { | |||
resultList.add((XWPFEndnote)note); | |||
} | |||
return resultList; | |||
} | |||
/** | |||
* Remove the specified end note if present. | |||
* | |||
* @param pos Array position of the endnote to be removed | |||
* @return True if the end note was removed. | |||
* @since 4.0.0 | |||
*/ | |||
public boolean removeEndnote(int pos) { | |||
if (ctEndnotes.sizeOfEndnoteArray() >= pos - 1) { | |||
ctEndnotes.removeEndnote(pos); | |||
listFootnote.remove(pos); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
} |
@@ -16,22 +16,10 @@ | |||
==================================================================== */ | |||
package org.apache.poi.xwpf.usermodel; | |||
import java.math.BigInteger; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.apache.poi.ooxml.POIXMLDocumentPart; | |||
import org.apache.xmlbeans.XmlCursor; | |||
import org.apache.xmlbeans.XmlObject; | |||
import org.apache.poi.util.Internal; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdnRef; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; | |||
/** | |||
* Represents a bottom-of-the-page footnote. | |||
@@ -44,425 +32,21 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; | |||
* the footnote ID to create a reference to a footnote from within a paragraph.</p> | |||
* <p>To create a reference to a footnote within a paragraph you create a run | |||
* with a CTFtnEdnRef that specifies the ID of the target paragraph. | |||
* The {@link XWPFParagraph#addFootnoteReference(XWPFFootnote)} | |||
* The {@link XWPFParagraph#addFootnoteReference(AbstractXWPFFootnoteEndnote)} | |||
* method does this for you.</p> | |||
*/ | |||
public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody { | |||
private List<XWPFParagraph> paragraphs = new ArrayList<>(); | |||
private List<XWPFTable> tables = new ArrayList<>(); | |||
private List<XWPFPictureData> pictures = new ArrayList<>(); | |||
private List<IBodyElement> bodyElements = new ArrayList<>(); | |||
private CTFtnEdn ctFtnEdn; | |||
private XWPFFootnotes footnotes; | |||
private XWPFDocument document; | |||
public XWPFFootnote(CTFtnEdn note, XWPFFootnotes xFootnotes) { | |||
footnotes = xFootnotes; | |||
ctFtnEdn = note; | |||
document = xFootnotes.getXWPFDocument(); | |||
init(); | |||
public class XWPFFootnote extends AbstractXWPFFootnoteEndnote { | |||
@Internal | |||
public XWPFFootnote(CTFtnEdn note, AbstractXWPFFootnotesEndnotes xFootnotes) { | |||
super(note, xFootnotes); | |||
} | |||
@Internal | |||
public XWPFFootnote(XWPFDocument document, CTFtnEdn body) { | |||
ctFtnEdn = body; | |||
this.document = document; | |||
init(); | |||
} | |||
private void init() { | |||
XmlCursor cursor = ctFtnEdn.newCursor(); | |||
//copied from XWPFDocument...should centralize this code | |||
//to avoid duplication | |||
cursor.selectPath("./*"); | |||
while (cursor.toNextSelection()) { | |||
XmlObject o = cursor.getObject(); | |||
if (o instanceof CTP) { | |||
XWPFParagraph p = new XWPFParagraph((CTP) o, this); | |||
bodyElements.add(p); | |||
paragraphs.add(p); | |||
} else if (o instanceof CTTbl) { | |||
XWPFTable t = new XWPFTable((CTTbl) o, this); | |||
bodyElements.add(t); | |||
tables.add(t); | |||
} else if (o instanceof CTSdtBlock) { | |||
XWPFSDT c = new XWPFSDT((CTSdtBlock) o, this); | |||
bodyElements.add(c); | |||
} | |||
} | |||
cursor.dispose(); | |||
} | |||
/** | |||
* Get the list of {@link XWPFParagraph}s in the footnote. | |||
* @return List of paragraphs | |||
*/ | |||
public List<XWPFParagraph> getParagraphs() { | |||
return paragraphs; | |||
} | |||
/** | |||
* Get an iterator over the {@link XWPFParagraph}s in the footnote. | |||
* @return Iterator over the paragraph list. | |||
*/ | |||
public Iterator<XWPFParagraph> iterator() { | |||
return paragraphs.iterator(); | |||
} | |||
/** | |||
* Get the list of {@link XWPFTable}s in the footnote. | |||
* @return List of tables | |||
*/ | |||
public List<XWPFTable> getTables() { | |||
return tables; | |||
} | |||
/** | |||
* Gets the list of {@link XWPFPictureData}s in the footnote. | |||
* @return List of pictures | |||
*/ | |||
public List<XWPFPictureData> getPictures() { | |||
return pictures; | |||
} | |||
/** | |||
* Gets the body elements ({@link IBodyElement}) of the footnote. | |||
* @return List of body elements. | |||
*/ | |||
public List<IBodyElement> getBodyElements() { | |||
return bodyElements; | |||
} | |||
/** | |||
* Gets the underlying CTFtnEdn object for the footnote. | |||
* @return CTFtnEdn object | |||
*/ | |||
public CTFtnEdn getCTFtnEdn() { | |||
return ctFtnEdn; | |||
} | |||
/** | |||
* Set the underlying CTFtnEdn for the footnote. | |||
* <p>Use {@link XWPFDocument#createFootnote()} to create new footnotes.</p> | |||
* @param footnote The CTFtnEdn object that will underly the footnote. | |||
*/ | |||
public void setCTFtnEdn(CTFtnEdn footnote) { | |||
ctFtnEdn = footnote; | |||
} | |||
/** | |||
* Gets the {@link XWPFTable} at the specified position from the footnote's table array. | |||
* @param pos in table array | |||
* @return The {@link XWPFTable} at position pos, or null if there is no table at position pos. | |||
* @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; | |||
} | |||
/** | |||
* Inserts an existing {@link XWPFTable) into the arrays bodyElements and tables. | |||
* | |||
* @param pos Position, in the bodyElements array, to insert the table | |||
* @param table {@link XWPFTable) to be inserted | |||
* @see org.apache.poi.xwpf.usermodel.IBody#insertTable(int pos, XWPFTable table) | |||
*/ | |||
public void insertTable(int pos, XWPFTable table) { | |||
bodyElements.add(pos, table); | |||
int i = 0; | |||
for (CTTbl tbl : ctFtnEdn.getTblList()) { | |||
if (tbl == table.getCTTbl()) { | |||
break; | |||
} | |||
i++; | |||
} | |||
tables.add(i, table); | |||
} | |||
/** | |||
* if there is a corresponding {@link XWPFTable} of the parameter | |||
* ctTable in the tableList of this header | |||
* the method will return this table, or null if there is no | |||
* corresponding {@link XWPFTable}. | |||
* | |||
* @param ctTable | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getTable(CTTbl ctTable) | |||
*/ | |||
public XWPFTable getTable(CTTbl ctTable) { | |||
for (XWPFTable table : tables) { | |||
if (table == null) | |||
return null; | |||
if (table.getCTTbl().equals(ctTable)) | |||
return table; | |||
} | |||
return null; | |||
} | |||
/** | |||
* if there is a corresponding {@link XWPFParagraph} of the parameter p in the paragraphList of this header or footer | |||
* the method will return that paragraph, otherwise the method will return null. | |||
* | |||
* @param p The CTP paragraph to find the corresponding {@link XWPFParagraph} for. | |||
* @return The {@link XWPFParagraph} that corresponds to the CTP paragraph in the paragraph | |||
* list of this footnote or null if no paragraph is found. | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getParagraph(CTP p) | |||
*/ | |||
public XWPFParagraph getParagraph(CTP p) { | |||
for (XWPFParagraph paragraph : paragraphs) { | |||
if (paragraph.getCTP().equals(p)) | |||
return paragraph; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Returns the {@link XWPFParagraph} at position pos in footnote's paragraph array. | |||
* @param pos Array position of the paragraph to get. | |||
* @return the {@link XWPFParagraph} at position pos, or null if there is no paragraph at that position. | |||
* | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getParagraphArray(int pos) | |||
*/ | |||
public XWPFParagraph getParagraphArray(int pos) { | |||
if(pos >=0 && pos < paragraphs.size()) { | |||
return paragraphs.get(pos); | |||
} | |||
return null; | |||
} | |||
/** | |||
* get the {@link XWPFTableCell} that belongs to the CTTc cell. | |||
* | |||
* @param cell | |||
* @return {@link XWPFTableCell} that corresponds to the CTTc cell, if there is one, otherwise null. | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getTableCell(CTTc cell) | |||
*/ | |||
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); | |||
super(document, body); | |||
} | |||
/** | |||
* Verifies that cursor is on the right position. | |||
* | |||
* @param cursor | |||
* @return true if the cursor is within a CTFtnEdn element. | |||
*/ | |||
private boolean isCursorInFtn(XmlCursor cursor) { | |||
XmlCursor verify = cursor.newCursor(); | |||
verify.toParent(); | |||
if (verify.getObject() == this.ctFtnEdn) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* The owning object for this footnote | |||
* | |||
* @return The {@link XWPFFootnotes} object that contains this footnote. | |||
*/ | |||
public POIXMLDocumentPart getOwner() { | |||
return footnotes; | |||
} | |||
/** | |||
* Insert a table constructed from OOXML table markup. | |||
* @param cursor | |||
* @return the inserted {@link XWPFTable} | |||
* @see org.apache.poi.xwpf.usermodel.IBody#insertNewTbl(XmlCursor cursor) | |||
*/ | |||
public XWPFTable insertNewTbl(XmlCursor cursor) { | |||
if (isCursorInFtn(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); | |||
XmlCursor c2 = t.newCursor(); | |||
cursor.toCursor(c2); | |||
cursor.toEndToken(); | |||
c2.dispose(); | |||
return newT; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Add a new {@link XWPFParagraph} at position of the cursor. | |||
* | |||
* @param cursor | |||
* @return The inserted {@link XWPFParagraph} | |||
* @see org.apache.poi.xwpf.usermodel.IBody#insertNewParagraph(XmlCursor cursor) | |||
*/ | |||
public XWPFParagraph insertNewParagraph(final XmlCursor cursor) { | |||
if (isCursorInFtn(cursor)) { | |||
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)) || 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); | |||
cursor.toEndToken(); | |||
p2.dispose(); | |||
return newP; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Add a new {@link XWPFTable} to the end of the footnote. | |||
* | |||
* @param table CTTbl object from which to construct the {@link XWPFTable} | |||
* @return The added {@link XWPFTable} | |||
*/ | |||
public XWPFTable addNewTbl(CTTbl table) { | |||
CTTbl newTable = ctFtnEdn.addNewTbl(); | |||
newTable.set(table); | |||
XWPFTable xTable = new XWPFTable(newTable, this); | |||
tables.add(xTable); | |||
return xTable; | |||
} | |||
/** | |||
* Add a new {@link XWPFParagraph} to the end of the footnote. | |||
* | |||
* @param paragraph CTP paragraph from which to construct the {@link XWPFParagraph} | |||
* @return The added {@link XWPFParagraph} | |||
*/ | |||
public XWPFParagraph addNewParagraph(CTP paragraph) { | |||
CTP newPara = ctFtnEdn.addNewP(); | |||
newPara.set(paragraph); | |||
XWPFParagraph xPara = new XWPFParagraph(newPara, this); | |||
paragraphs.add(xPara); | |||
return xPara; | |||
} | |||
/** | |||
* Get the {@link XWPFDocument} the footnote is part of. | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getXWPFDocument() | |||
*/ | |||
public XWPFDocument getXWPFDocument() { | |||
return document; | |||
} | |||
/** | |||
* Get the Part to which the footnote belongs, which you need for adding relationships to other parts | |||
* @return {@link POIXMLDocumentPart} that contains the footnote. | |||
* | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getPart() | |||
*/ | |||
public POIXMLDocumentPart getPart() { | |||
return footnotes; | |||
} | |||
/** | |||
* Get the part type {@link BodyType} of the footnote. | |||
* @return The {@link BodyType} value. | |||
* | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getPartType() | |||
*/ | |||
public BodyType getPartType() { | |||
return BodyType.FOOTNOTE; | |||
} | |||
/** | |||
* Get the ID of the footnote. | |||
* <p>Footnote IDs are unique across all bottom-of-the-page and | |||
* end note footnotes.</p> | |||
* | |||
* @return Footnote ID | |||
*/ | |||
public BigInteger getId() { | |||
return this.ctFtnEdn.getId(); | |||
} | |||
/** | |||
* Appends a new {@link XWPFParagraph} to this footnote. | |||
* | |||
* @return The new {@link XWPFParagraph} | |||
*/ | |||
public XWPFParagraph createParagraph() { | |||
XWPFParagraph p = new XWPFParagraph(this.ctFtnEdn.addNewP(), this); | |||
paragraphs.add(p); | |||
bodyElements.add(p); | |||
// If the paragraph is the first paragraph in the footnote, | |||
// ensure that it has a footnote reference run. | |||
if (p.equals(getParagraphs().get(0))) { | |||
ensureFootnoteRef(p); | |||
} | |||
return p; | |||
} | |||
/** | |||
* Ensure that the specified paragraph has a reference marker for this | |||
* footnote by adding a footnote reference if one is not found. | |||
@@ -474,7 +58,8 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody { | |||
* contain a {@link CTFtnEdnRef} object.</p> | |||
* | |||
* @param p The {@link XWPFParagraph} to ensure | |||
*/ | |||
* @since 4.0.0 | |||
*/ | |||
public void ensureFootnoteRef(XWPFParagraph p) { | |||
XWPFRun r = null; | |||
@@ -496,34 +81,6 @@ public class XWPFFootnote implements Iterable<XWPFParagraph>, IBody { | |||
ctr.addNewRPr().addNewRStyle().setVal("FootnoteReference"); | |||
ctr.addNewFootnoteRef(); | |||
} | |||
} | |||
/** | |||
* Appends a new {@link XWPFTable} to this footnote | |||
* | |||
* @return The new {@link XWPFTable} | |||
*/ | |||
public XWPFTable createTable() { | |||
XWPFTable table = new XWPFTable(ctFtnEdn.addNewTbl(), this); | |||
if (bodyElements.size() == 0) { | |||
XWPFParagraph p = createParagraph(); | |||
ensureFootnoteRef(p); | |||
} | |||
bodyElements.add(table); | |||
tables.add(table); | |||
return table; | |||
} | |||
/** | |||
* Appends a new {@link XWPFTable} to this footnote | |||
* @param rows Number of rows to initialize the table with | |||
* @param cols Number of columns to initialize the table with | |||
* @return the new {@link XWPFTable} with the specified number of rows and columns | |||
*/ | |||
public XWPFTable createTable(int rows, int cols) { | |||
XWPFTable table = new XWPFTable(ctFtnEdn.addNewTbl(), this, rows, cols); | |||
bodyElements.add(table); | |||
tables.add(table); | |||
return table; | |||
} | |||
} |
@@ -28,27 +28,23 @@ import java.util.List; | |||
import javax.xml.namespace.QName; | |||
import org.apache.poi.ooxml.POIXMLDocumentPart; | |||
import org.apache.poi.ooxml.POIXMLException; | |||
import org.apache.poi.openxml4j.exceptions.OpenXML4JException; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.apache.xmlbeans.XmlOptions; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFootnotes; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.FootnotesDocument; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFtnEdn; | |||
/** | |||
* Looks after the collection of Footnotes for a document. | |||
* Manages both bottom-of-the-page footnotes and end notes. | |||
* Manages bottom-of-the-page footnotes ({@link XWPFFootnote}). | |||
*/ | |||
public class XWPFFootnotes extends POIXMLDocumentPart { | |||
protected XWPFDocument document; | |||
private List<XWPFFootnote> listFootnote = new ArrayList<>(); | |||
private CTFootnotes ctFootnotes; | |||
public class XWPFFootnotes extends AbstractXWPFFootnotesEndnotes { | |||
protected CTFootnotes ctFootnotes; | |||
/** | |||
* Construct XWPFFootnotes from a package part | |||
@@ -67,6 +63,49 @@ public class XWPFFootnotes extends POIXMLDocumentPart { | |||
public XWPFFootnotes() { | |||
} | |||
/** | |||
* Sets the ctFootnotes | |||
* | |||
* @param footnotes Collection of CTFntEdn objects. | |||
*/ | |||
@Internal | |||
public void setFootnotes(CTFootnotes footnotes) { | |||
ctFootnotes = footnotes; | |||
} | |||
/** | |||
* Create a new footnote and add it to the document. | |||
* | |||
* @return New {@link XWPFFootnote} | |||
* @since 4.0.0 | |||
*/ | |||
public XWPFFootnote createFootnote() { | |||
CTFtnEdn newNote = CTFtnEdn.Factory.newInstance(); | |||
newNote.setType(STFtnEdn.NORMAL); | |||
XWPFFootnote footnote = addFootnote(newNote); | |||
footnote.getCTFtnEdn().setId(getIdManager().nextId()); | |||
return footnote; | |||
} | |||
/** | |||
* Remove the specified footnote if present. | |||
* | |||
* @param pos Array position of the footnote to be removed | |||
* @return True if the footnote was removed. | |||
* @since 4.0.0 | |||
*/ | |||
public boolean removeFootnote(int pos) { | |||
if (ctFootnotes.sizeOfFootnoteArray() >= pos - 1) { | |||
ctFootnotes.removeFootnote(pos); | |||
listFootnote.remove(pos); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
/** | |||
* Read document | |||
*/ | |||
@@ -85,9 +124,8 @@ public class XWPFFootnotes extends POIXMLDocumentPart { | |||
is.close(); | |||
} | |||
} | |||
// Find our footnotes | |||
for (CTFtnEdn note : ctFootnotes.getFootnoteArray()) { | |||
for (CTFtnEdn note : ctFootnotes.getFootnoteList()) { | |||
listFootnote.add(new XWPFFootnote(note, this)); | |||
} | |||
} | |||
@@ -102,31 +140,10 @@ public class XWPFFootnotes extends POIXMLDocumentPart { | |||
out.close(); | |||
} | |||
public List<XWPFFootnote> getFootnotesList() { | |||
return listFootnote; | |||
} | |||
public XWPFFootnote getFootnoteById(int id) { | |||
for (XWPFFootnote note : listFootnote) { | |||
if (note.getCTFtnEdn().getId().intValue() == id) | |||
return note; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Sets the ctFootnotes | |||
* Add an {@link XWPFFootnote} to the document | |||
* | |||
* @param footnotes | |||
*/ | |||
public void setFootnotes(CTFootnotes footnotes) { | |||
ctFootnotes = footnotes; | |||
} | |||
/** | |||
* add an XWPFFootnote to the document | |||
* | |||
* @param footnote | |||
* @param footnote Footnote to add | |||
* @throws IOException | |||
*/ | |||
public void addFootnote(XWPFFootnote footnote) { | |||
@@ -135,11 +152,12 @@ public class XWPFFootnotes extends POIXMLDocumentPart { | |||
} | |||
/** | |||
* add a footnote to the document | |||
* Add a CT footnote to the document | |||
* | |||
* @param note | |||
* @param note CTFtnEdn to add. | |||
* @throws IOException | |||
*/ | |||
@Internal | |||
public XWPFFootnote addFootnote(CTFtnEdn note) { | |||
CTFtnEdn newNote = ctFootnotes.addNewFootnote(); | |||
newNote.set(note); | |||
@@ -149,52 +167,18 @@ public class XWPFFootnotes extends POIXMLDocumentPart { | |||
} | |||
/** | |||
* @see org.apache.poi.xwpf.usermodel.IBody#getPart() | |||
*/ | |||
public XWPFDocument getXWPFDocument() { | |||
if (document != null) { | |||
return document; | |||
} else { | |||
return (XWPFDocument) getParent(); | |||
} | |||
} | |||
public void setXWPFDocument(XWPFDocument doc) { | |||
document = doc; | |||
} | |||
/** | |||
* Create a new footnote and add it to the document. | |||
* <p>The new note will have one paragraph with the style "FootnoteText" | |||
* and one run containing the required footnote reference with the | |||
* style "FootnoteReference". | |||
* </p> | |||
* @return New XWPFFootnote | |||
*/ | |||
public XWPFFootnote createFootnote() { | |||
CTFtnEdn newNote = CTFtnEdn.Factory.newInstance(); | |||
newNote.setType(STFtnEdn.NORMAL); | |||
XWPFFootnote footnote = addFootnote(newNote); | |||
int id = ctFootnotes.sizeOfFootnoteArray(); | |||
footnote.getCTFtnEdn().setId(BigInteger.valueOf(id)); | |||
return footnote; | |||
} | |||
/** | |||
* Remove the specified footnote if present. | |||
* Get the list of {@link XWPFFootnote} in the Footnotes part. | |||
* | |||
* @param pos | |||
* @return True if the footnote was removed. | |||
* @return List, possibly empty, of footnotes. | |||
*/ | |||
public boolean removeFootnote(int pos) { | |||
if (ctFootnotes.sizeOfFootnoteArray() >= pos - 1) { | |||
ctFootnotes.removeFootnote(pos); | |||
listFootnote.remove(pos); | |||
return true; | |||
} else { | |||
return false; | |||
public List<XWPFFootnote> getFootnotesList() { | |||
List<XWPFFootnote> resultList = new ArrayList<XWPFFootnote>(); | |||
for (AbstractXWPFFootnoteEndnote note : listFootnote) { | |||
resultList.add((XWPFFootnote)note); | |||
} | |||
return resultList; | |||
} | |||
} |
@@ -76,21 +76,24 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para | |||
if (o instanceof CTFtnEdnRef) { | |||
CTFtnEdnRef ftn = (CTFtnEdnRef) o; | |||
footnoteText.append(" [").append(ftn.getId()).append(": "); | |||
XWPFFootnote footnote = | |||
AbstractXWPFFootnoteEndnote footnote = | |||
ftn.getDomNode().getLocalName().equals("footnoteReference") ? | |||
document.getFootnoteByID(ftn.getId().intValue()) : | |||
document.getEndnoteByID(ftn.getId().intValue()); | |||
boolean first = true; | |||
for (XWPFParagraph p : footnote.getParagraphs()) { | |||
if (!first) { | |||
footnoteText.append("\n"); | |||
if (null != footnote) { | |||
boolean first = true; | |||
for (XWPFParagraph p : footnote.getParagraphs()) { | |||
if (!first) { | |||
footnoteText.append("\n"); | |||
} | |||
first = false; | |||
footnoteText.append(p.getText()); | |||
} | |||
first = false; | |||
footnoteText.append(p.getText()); | |||
} else { | |||
footnoteText.append("!!! End note with ID \"" + ftn.getId() + "\" not found in document."); | |||
} | |||
footnoteText.append("] "); | |||
} | |||
} | |||
c.dispose(); | |||
@@ -1674,11 +1677,16 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para | |||
* The footnote reference run will have the style name "FootnoteReference". | |||
* | |||
* @param footnote Footnote to which to add a reference. | |||
* @since 4.0.0 | |||
*/ | |||
public void addFootnoteReference(XWPFFootnote footnote) { | |||
public void addFootnoteReference(AbstractXWPFFootnoteEndnote footnote) { | |||
XWPFRun run = createRun(); | |||
CTR ctRun = run.getCTR(); | |||
ctRun.addNewRPr().addNewRStyle().setVal("FootnoteReference"); | |||
ctRun.addNewFootnoteReference().setId(footnote.getId()); | |||
if (footnote instanceof XWPFEndnote) { | |||
ctRun.addNewEndnoteReference().setId(footnote.getId()); | |||
} else { | |||
ctRun.addNewFootnoteReference().setId(footnote.getId()); | |||
} | |||
} | |||
} |
@@ -147,10 +147,10 @@ public final class XWPFRelation extends POIXMLRelation { | |||
XWPFFootnotes.class | |||
); | |||
public static final XWPFRelation ENDNOTE = new XWPFRelation( | |||
null, | |||
"application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml", | |||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes", | |||
null, | |||
null | |||
"/word/endnotes.xml", | |||
XWPFEndnotes.class | |||
); | |||
/** | |||
* Supported image formats |
@@ -0,0 +1,160 @@ | |||
/* ==================================================================== | |||
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 static org.junit.Assert.*; | |||
import java.io.IOException; | |||
import java.math.BigInteger; | |||
import java.util.List; | |||
import org.apache.poi.xwpf.XWPFTestDataSamples; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdnRef; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; | |||
public class TestXWPFEndnote { | |||
private XWPFDocument docOut; | |||
private String p1Text; | |||
private String p2Text; | |||
private BigInteger endnoteId; | |||
private XWPFEndnote endnote; | |||
@Before | |||
public void setUp() { | |||
docOut = new XWPFDocument(); | |||
p1Text = "First paragraph in footnote"; | |||
p2Text = "Second paragraph in footnote"; | |||
// NOTE: XWPFDocument.createEndnote() delegates directly | |||
// to XWPFEndnotes.createEndnote() so this tests | |||
// both creation of new XWPFEndnotes in document | |||
// and XWPFEndnotes.createEndnote(); | |||
// NOTE: Creating the endnote does not automatically | |||
// create a first paragraph. | |||
endnote = docOut.createEndnote(); | |||
endnoteId = endnote.getId(); | |||
} | |||
@Test | |||
public void testAddParagraphsToFootnote() throws IOException { | |||
// Add a run to the first paragraph: | |||
XWPFParagraph p1 = endnote.createParagraph(); | |||
p1.createRun().setText(p1Text); | |||
// Create a second paragraph: | |||
XWPFParagraph p = endnote.createParagraph(); | |||
assertNotNull("Paragraph is null", p); | |||
p.createRun().setText(p2Text); | |||
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut); | |||
XWPFEndnote testEndnote = docIn.getEndnoteByID(endnoteId.intValue()); | |||
assertNotNull(testEndnote); | |||
assertEquals(2, testEndnote.getParagraphs().size()); | |||
XWPFParagraph testP1 = testEndnote.getParagraphs().get(0); | |||
assertEquals(p1Text, testP1.getText()); | |||
XWPFParagraph testP2 = testEndnote.getParagraphs().get(1); | |||
assertEquals(p2Text, testP2.getText()); | |||
// The first paragraph added using createParagraph() should | |||
// have the required footnote reference added to the first | |||
// run. | |||
// Verify that we have a footnote reference in the first paragraph and not | |||
// in the second paragraph. | |||
XWPFRun r1 = testP1.getRuns().get(0); | |||
assertNotNull(r1); | |||
assertTrue("No endnote reference in testP1", r1.getCTR().getEndnoteRefList().size() > 0); | |||
assertNotNull("No endnote reference in testP1", r1.getCTR().getEndnoteRefArray(0)); | |||
XWPFRun r2 = testP2.getRuns().get(0); | |||
assertNotNull("Expected a run in testP2", r2); | |||
assertTrue("Found an endnote reference in testP2", r2.getCTR().getEndnoteRefList().size() == 0); | |||
} | |||
@Test | |||
public void testAddTableToFootnote() throws IOException { | |||
XWPFTable table = endnote.createTable(); | |||
assertNotNull(table); | |||
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut); | |||
XWPFEndnote testFootnote = docIn.getEndnoteByID(endnoteId.intValue()); | |||
XWPFTable testTable = testFootnote.getTableArray(0); | |||
assertNotNull(testTable); | |||
table = endnote.createTable(2, 3); | |||
assertEquals(2, table.getNumberOfRows()); | |||
assertEquals(3, table.getRow(0).getTableCells().size()); | |||
// If the table is the first body element of the footnote then | |||
// a paragraph with the footnote reference should have been | |||
// added automatically. | |||
assertEquals("Expected 3 body elements", 3, endnote.getBodyElements().size()); | |||
IBodyElement testP1 = endnote.getBodyElements().get(0); | |||
assertTrue("Expected a paragraph, got " + testP1.getClass().getSimpleName() , testP1 instanceof XWPFParagraph); | |||
XWPFRun r1 = ((XWPFParagraph)testP1).getRuns().get(0); | |||
assertNotNull(r1); | |||
assertTrue("No footnote reference in testP1", r1.getCTR().getEndnoteRefList().size() > 0); | |||
assertNotNull("No footnote reference in testP1", r1.getCTR().getEndnoteRefArray(0)); | |||
} | |||
@Test | |||
public void testRemoveEndnote() { | |||
// NOTE: XWPFDocument.removeEndnote() delegates directly to | |||
// XWPFEndnotes. | |||
docOut.createEndnote(); | |||
assertEquals("Expected 2 endnotes", 2, docOut.getEndnotes().size()); | |||
assertNotNull("Didn't get second endnote", docOut.getEndnotes().get(1)); | |||
boolean result = docOut.removeEndnote(0); | |||
assertTrue("Remove endnote did not return true", result); | |||
assertEquals("Expected 1 endnote after removal", 1, docOut.getEndnotes().size()); | |||
} | |||
@Test | |||
public void testAddFootnoteRefToParagraph() { | |||
XWPFParagraph p = docOut.createParagraph(); | |||
List<XWPFRun> runs = p.getRuns(); | |||
assertEquals("Expected no runs in new paragraph", 0, runs.size()); | |||
p.addFootnoteReference(endnote); | |||
XWPFRun run = p.getRuns().get(0); | |||
CTR ctr = run.getCTR(); | |||
assertNotNull("Expected a run", run); | |||
List<CTFtnEdnRef> endnoteRefList = ctr.getEndnoteReferenceList(); | |||
assertNotNull(endnoteRefList); | |||
CTFtnEdnRef ref = endnoteRefList.get(0); | |||
assertNotNull(ref); | |||
assertEquals("Endnote ID and reference ID did not match", endnote.getId(), ref.getId()); | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
/* ==================================================================== | |||
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.io.IOException; | |||
import java.math.BigInteger; | |||
import org.apache.poi.xwpf.XWPFTestDataSamples; | |||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFtnEdn; | |||
import junit.framework.TestCase; | |||
public class TestXWPFEndnotes extends TestCase { | |||
public void testCreateEndnotes() throws IOException{ | |||
XWPFDocument docOut = new XWPFDocument(); | |||
XWPFEndnotes footnotes = docOut.createEndnotes(); | |||
assertNotNull(footnotes); | |||
XWPFEndnotes secondFootnotes = docOut.createEndnotes(); | |||
assertSame(footnotes, secondFootnotes); | |||
docOut.close(); | |||
} | |||
public void testAddEndnotesToDocument() throws IOException { | |||
XWPFDocument docOut = new XWPFDocument(); | |||
// NOTE: XWPFDocument.createEndnote() delegates directly | |||
// to XWPFFootnotes.createEndnote() so this tests | |||
// both creation of new XWPFFootnotes in document | |||
// and XWPFFootnotes.createEndnote(); | |||
XWPFEndnote endnote = docOut.createEndnote(); | |||
BigInteger noteId = endnote.getId(); | |||
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut); | |||
XWPFEndnote note = docIn.getEndnoteByID(noteId.intValue()); | |||
assertNotNull(note); | |||
assertEquals(STFtnEdn.NORMAL, note.getCTFtnEdn().getType()); | |||
} | |||
} | |||
@@ -150,9 +150,11 @@ public class TestXWPFFootnote { | |||
assertNotNull("Expected a run", run); | |||
CTFtnEdnRef ref = ctr.getFootnoteReferenceList().get(0); | |||
assertNotNull(ref); | |||
// FIXME: Verify that the footnote reference is w:endnoteReference, not w:footnoteReference | |||
assertEquals("Footnote ID and reference ID did not match", footnote.getId(), ref.getId()); | |||
} | |||
} |
@@ -31,11 +31,11 @@ public class TestXWPFFootnotes extends TestCase { | |||
public void testCreateFootnotes() throws IOException{ | |||
XWPFDocument docOut = new XWPFDocument(); | |||
XWPFFootnotes footnotes = docOut.createFootnotes(); | |||
AbstractXWPFFootnotesEndnotes footnotes = docOut.createFootnotes(); | |||
assertNotNull(footnotes); | |||
XWPFFootnotes secondFootnotes = docOut.createFootnotes(); | |||
AbstractXWPFFootnotesEndnotes secondFootnotes = docOut.createFootnotes(); | |||
assertSame(footnotes, secondFootnotes); | |||
@@ -17,17 +17,15 @@ | |||
package org.apache.poi.xwpf.usermodel; | |||
import static org.apache.poi.POITestCase.assertContains; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.apache.poi.POITestCase.assertContains; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.poi.xwpf.XWPFTestDataSamples; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
public final class TestXWPFSDT { | |||
@@ -168,8 +166,8 @@ public final class TestXWPFSDT { | |||
for (XWPFFootnote footnote : doc.getFootnotes()) { | |||
sdts.addAll(extractSDTsFromBodyElements(footnote.getBodyElements())); | |||
} | |||
for (Map.Entry<Integer, XWPFFootnote> e : doc.endnotes.entrySet()) { | |||
sdts.addAll(extractSDTsFromBodyElements(e.getValue().getBodyElements())); | |||
for (XWPFEndnote footnote : doc.getEndnotes()) { | |||
sdts.addAll(extractSDTsFromBodyElements(footnote.getBodyElements())); | |||
} | |||
return sdts; | |||
} |