From e3ae88792bc4c413f36b252374cb2fbcef9953ff Mon Sep 17 00:00:00 2001
From: PJ Fanning
Date: Tue, 24 Jul 2018 09:46:44 +0000
Subject: [PATCH] [github-115] implement endnote. This closes #115
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1836538 13f79535-47bb-0310-9956-ffa450edef68
---
.../AbstractXWPFFootnoteEndnote.java | 499 ++++++++++++++++++
.../AbstractXWPFFootnotesEndnotes.java | 74 +++
.../usermodel/FootnoteEndnoteIdManager.java | 45 ++
.../poi/xwpf/usermodel/XWPFDocument.java | 111 +++-
.../poi/xwpf/usermodel/XWPFEndnote.java | 76 +++
.../poi/xwpf/usermodel/XWPFEndnotes.java | 195 +++++++
.../poi/xwpf/usermodel/XWPFFootnote.java | 469 +---------------
.../poi/xwpf/usermodel/XWPFFootnotes.java | 144 +++--
.../poi/xwpf/usermodel/XWPFParagraph.java | 30 +-
.../poi/xwpf/usermodel/XWPFRelation.java | 6 +-
.../poi/xwpf/usermodel/TestXWPFEndnote.java | 160 ++++++
.../poi/xwpf/usermodel/TestXWPFEndnotes.java | 62 +++
.../poi/xwpf/usermodel/TestXWPFFootnote.java | 2 +
.../poi/xwpf/usermodel/TestXWPFFootnotes.java | 4 +-
.../poi/xwpf/usermodel/TestXWPFSDT.java | 8 +-
15 files changed, 1309 insertions(+), 576 deletions(-)
create mode 100644 src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnoteEndnote.java
create mode 100644 src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnotesEndnotes.java
create mode 100644 src/ooxml/java/org/apache/poi/xwpf/usermodel/FootnoteEndnoteIdManager.java
create mode 100644 src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnote.java
create mode 100644 src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java
create mode 100644 src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnote.java
create mode 100644 src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnotes.java
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnoteEndnote.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnoteEndnote.java
new file mode 100644
index 000000000..7c30725ee
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnoteEndnote.java
@@ -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}).
+ * 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}.
+ * @since 4.0.0
+ */
+public abstract class AbstractXWPFFootnoteEndnote implements Iterable, IBody {
+
+ private List paragraphs = new ArrayList<>();
+ private List tables = new ArrayList<>();
+ private List pictures = new ArrayList<>();
+ private List 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 getParagraphs() {
+ return paragraphs;
+ }
+
+ /**
+ * Get an iterator over the {@link XWPFParagraph}s in the footnote.
+ * @return Iterator over the paragraph list.
+ */
+ public Iterator iterator() {
+ return paragraphs.iterator();
+ }
+
+ /**
+ * Get the list of {@link XWPFTable}s in the footnote.
+ * @return List of tables
+ */
+ public List getTables() {
+ return tables;
+ }
+
+ /**
+ * Gets the list of {@link XWPFPictureData}s in the footnote.
+ * @return List of pictures
+ */
+ public List getPictures() {
+ return pictures;
+ }
+
+ /**
+ * Gets the body elements ({@link IBodyElement}) of the footnote.
+ * @return List of body elements.
+ */
+ public List 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.
+ * Use {@link XWPFDocument#createFootnote()} to create new footnotes.
+ * @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.
+ * Footnote IDs are unique across all bottom-of-the-page and
+ * end note footnotes.
+ *
+ * @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.
+ * 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)}.
+ *
+ * The first run of the first paragraph in a footnote should
+ * contain a {@link CTFtnEdnRef} object.
+ *
+ * @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;
+ }
+
+}
\ No newline at end of file
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnotesEndnotes.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnotesEndnotes.java
new file mode 100644
index 000000000..a3560c531
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnotesEndnotes.java
@@ -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 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;
+ }
+
+}
\ No newline at end of file
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/FootnoteEndnoteIdManager.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/FootnoteEndnoteIdManager.java
new file mode 100644
index 000000000..5104b487f
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/FootnoteEndnoteIdManager.java
@@ -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.
+ * Footnotes and endnotes are managed in separate parts but
+ * represent a single namespace of IDs.
+ */
+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 ids = new ArrayList();
+ 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;
+ }
+
+
+}
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
index a45ed2197..fa252662b 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
@@ -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.
*/
+@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 bodyElements = new ArrayList<>();
protected List pictures = new ArrayList<>();
protected Map> packagePictures = new HashMap<>();
- protected Map 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 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.
- * 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 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;
+ }
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnote.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnote.java
new file mode 100644
index 000000000..a6c3815b0
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnote.java
@@ -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.
+ * End notes are collected at the end of a document or section rather than
+ * at the bottom of a page.
+ * Create a new footnote using {@link XWPFDocument#createEndnote()} or
+ * {@link XWPFEndnotes#createFootnote()}.
+ * 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.
+ * 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.
+ * 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.
+ * @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.
+ * 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))}.
+ *
+ * The first run of the first paragraph in a footnote should
+ * contain a {@link CTFtnEdnRef} object.
+ *
+ * @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();
+ }
+
+ }
+
+}
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java
new file mode 100644
index 000000000..0f5f2d625
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java
@@ -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 getEndnotesList() {
+ List resultList = new ArrayList();
+ 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;
+ }
+ }
+
+
+
+}
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java
index 9444dbf6e..d4afd6451 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java
@@ -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.
* 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.
*/
-public class XWPFFootnote implements Iterable, IBody {
- private List paragraphs = new ArrayList<>();
- private List tables = new ArrayList<>();
- private List pictures = new ArrayList<>();
- private List 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();
+ super(document, body);
}
-
- 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 getParagraphs() {
- return paragraphs;
- }
-
- /**
- * Get an iterator over the {@link XWPFParagraph}s in the footnote.
- * @return Iterator over the paragraph list.
- */
- public Iterator iterator() {
- return paragraphs.iterator();
- }
-
- /**
- * Get the list of {@link XWPFTable}s in the footnote.
- * @return List of tables
- */
- public List getTables() {
- return tables;
- }
-
- /**
- * Gets the list of {@link XWPFPictureData}s in the footnote.
- * @return List of pictures
- */
- public List getPictures() {
- return pictures;
- }
-
- /**
- * Gets the body elements ({@link IBodyElement}) of the footnote.
- * @return List of body elements.
- */
- public List 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.
- * Use {@link XWPFDocument#createFootnote()} to create new footnotes.
- * @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.
- * Footnote IDs are unique across all bottom-of-the-page and
- * end note footnotes.
- *
- * @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, IBody {
* contain a {@link CTFtnEdnRef} object.
*
* @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, 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;
+
}
}
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnotes.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnotes.java
index a4788673e..4f3fa83b0 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnotes.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnotes.java
@@ -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 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 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.
- * The new note will have one paragraph with the style "FootnoteText"
- * and one run containing the required footnote reference with the
- * style "FootnoteReference".
- *
- * @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 getFootnotesList() {
+ List resultList = new ArrayList();
+ for (AbstractXWPFFootnoteEndnote note : listFootnote) {
+ resultList.add((XWPFFootnote)note);
}
+ return resultList;
}
+
+
+
}
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java
index 661e1c1ab..5fbc7a522 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java
@@ -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());
+ }
}
}
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java
index d82eb5814..cb1bba8e3 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java
@@ -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
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnote.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnote.java
new file mode 100644
index 000000000..0bc0fed73
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnote.java
@@ -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 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 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());
+
+
+ }
+
+}
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnotes.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnotes.java
new file mode 100644
index 000000000..1195a1206
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnotes.java
@@ -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());
+ }
+
+}
+
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnote.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnote.java
index 8bb377fe5..b875644cb 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnote.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnote.java
@@ -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());
+
}
}
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnotes.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnotes.java
index 037bdad04..39fb39744 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnotes.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnotes.java
@@ -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);
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java
index f1ecf560e..39a3ac675 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java
@@ -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 e : doc.endnotes.entrySet()) {
- sdts.addAll(extractSDTsFromBodyElements(e.getValue().getBodyElements()));
+ for (XWPFEndnote footnote : doc.getEndnotes()) {
+ sdts.addAll(extractSDTsFromBodyElements(footnote.getBodyElements()));
}
return sdts;
}