]> source.dussan.org Git - poi.git/commitdiff
[github-115] implement endnote. This closes #115
authorPJ Fanning <fanningpj@apache.org>
Tue, 24 Jul 2018 09:46:44 +0000 (09:46 +0000)
committerPJ Fanning <fanningpj@apache.org>
Tue, 24 Jul 2018 09:46:44 +0000 (09:46 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1836538 13f79535-47bb-0310-9956-ffa450edef68

15 files changed:
src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnoteEndnote.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xwpf/usermodel/AbstractXWPFFootnotesEndnotes.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xwpf/usermodel/FootnoteEndnoteIdManager.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnote.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFEndnotes.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnotes.java
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRelation.java
src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnote.java [new file with mode: 0644]
src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFEndnotes.java [new file with mode: 0644]
src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnote.java
src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFFootnotes.java
src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.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 (file)
index 0000000..7c30725
--- /dev/null
@@ -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;
+    }
+
+}
\ 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 (file)
index 0000000..a3560c5
--- /dev/null
@@ -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;
+    }
+
+}
\ 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 (file)
index 0000000..5104b48
--- /dev/null
@@ -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;
+    }
+
+
+}
index a45ed219728c2e754158bc2381a219406b2f9276..fa252662b855d50304bd271e1b9926cd1cdefa88 100644 (file)
@@ -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;
+        }
+    }
 }
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 (file)
index 0000000..a6c3815
--- /dev/null
@@ -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();
+        }
+        
+    }
+
+}
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 (file)
index 0000000..0f5f2d6
--- /dev/null
@@ -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;
+        }
+    }
+
+    
+
+}
index 9444dbf6e96cfed1d873bdf85c40b92acee88824..d4afd64512f484b16633fed5665cae57ac14e4fd 100644 (file)
 ==================================================================== */
 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;
+        
     }
 }
index a4788673eda898294e487f5e30eb7f4b0f5da31e..4f3fa83b0d3a14d28597ac251bcf3b3749f65232 100644 (file)
@@ -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;
     }
+    
+    
+
 }
index 661e1c1abeb793316e49c7d3a241932afe2fac47..5fbc7a5225a1b27d14e5f74824f99194bd5da778 100644 (file)
@@ -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());
+        }
     }
 }
index d82eb5814b04e329379cd2a1aae231bb2e0489be..cb1bba8e3794fdf42face64056457423f41fe860 100644 (file)
@@ -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 (file)
index 0000000..0bc0fed
--- /dev/null
@@ -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());
+        
+        
+    }
+
+}
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 (file)
index 0000000..1195a12
--- /dev/null
@@ -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());
+    }
+
+}
+
index 8bb377fe5a469736d78342f586ba020191d81434..b875644cb54c40e2f9b00f80f76d42654b61f55f 100644 (file)
@@ -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());
         
         
+        
     }
 
 }
index 037bdad04f8139865b40d8d267ad0941ac1d957b..39fb39744253aef90102f65a4afa776a3aef444d 100644 (file)
@@ -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);
         
index f1ecf560e1c90af743894d21b9260769c0b2949c..39a3ac6751481c047f99290386eeccd6dc254809 100644 (file)
 
 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;
     }