diff --git a/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java b/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java index 3cbc49dd5..60d95a6d2 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java @@ -23,10 +23,15 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.util.CellAddress; +import org.apache.poi.util.Internal; import org.apache.poi.xssf.usermodel.XSSFComment; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; @@ -34,20 +39,26 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComments; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CommentsDocument; +@Internal public class CommentsTable extends POIXMLDocumentPart { + public static final String DEFAULT_AUTHOR = ""; + public static final int DEFAULT_AUTHOR_ID = 0; + /** + * Underlying XML Beans CTComment list. + */ private CTComments comments; /** * XML Beans uses a list, which is very slow * to search, so we wrap things with our own * map for fast lookup. */ - private Map commentRefs; + private Map commentRefs; public CommentsTable() { super(); comments = CTComments.Factory.newInstance(); comments.addNewCommentList(); - comments.addNewAuthors().addAuthor(""); + comments.addNewAuthors().addAuthor(DEFAULT_AUTHOR); } public CommentsTable(PackagePart part, PackageRelationship rel) throws IOException { @@ -80,11 +91,22 @@ public class CommentsTable extends POIXMLDocumentPart { /** * Called after the reference is updated, so that * we can reflect that in our cache + * @deprecated 2015-11-23 (circa POI 3.14beta1). Use {@link #referenceUpdated(CellAddress, CTComment)} instead */ public void referenceUpdated(String oldReference, CTComment comment) { + referenceUpdated(new CellAddress(oldReference), comment); + } + + /** + * Called after the reference is updated, so that + * we can reflect that in our cache + * @param oldReference the comment to remove from the commentRefs map + * @param comment the comment to replace in the commentRefs map + */ + public void referenceUpdated(CellAddress oldReference, CTComment comment) { if(commentRefs != null) { commentRefs.remove(oldReference); - commentRefs.put(comment.getRef(), comment); + commentRefs.put(new CellAddress(comment.getRef()), comment); } } @@ -100,8 +122,8 @@ public class CommentsTable extends POIXMLDocumentPart { return comments.getAuthors().getAuthorArray((int)authorId); } - @SuppressWarnings("deprecation") public int findAuthor(String author) { + @SuppressWarnings("deprecation") String[] authorArray = comments.getAuthors().getAuthorArray(); for (int i = 0 ; i < authorArray.length; i++) { if (authorArray[i].equals(author)) { @@ -111,53 +133,144 @@ public class CommentsTable extends POIXMLDocumentPart { return addNewAuthor(author); } + /** + * Finds the cell comment at cellAddress, if one exists + * + * @param cellAddress the address of the cell to find a comment + * @return cell comment if one exists, otherwise returns null + * @deprecated 2015-11-23 (circa POI 3.14beta1). Use {@link #findCellComment(CellAddress)} instead + */ public XSSFComment findCellComment(String cellRef) { - CTComment ct = getCTComment(cellRef); - return ct == null ? null : new XSSFComment(this, ct, null); + return findCellComment(new CellAddress(cellRef)); } - @SuppressWarnings("deprecation") //YK: getXYZArray() array accessors are deprecated in xmlbeans with JDK 1.5 support - public CTComment getCTComment(String cellRef) { + /** + * Finds the cell comment at cellAddress, if one exists + * + * @param cellAddress the address of the cell to find a comment + * @return cell comment if one exists, otherwise returns null + */ + public XSSFComment findCellComment(CellAddress cellAddress) { + CTComment ct = getCTComment(cellAddress); + return ct == null ? null : new XSSFComment(this, ct, null); + } + + + /** + * Get the underlying CTComment xmlbean for a comment located at cellRef, if it exists + * + * @param cellRef the location of the cell comment + * @return CTComment xmlbean if comment exists, otherwise return null. + * @deprecated 2015-11-23 (circa POI 3.14beta1). Use {@link CommentsTable#getCTComment(CellAddress)} instead + */ + @Internal + public CTComment getCTComment(String ref) { + return getCTComment(new CellAddress(ref)); + } + + /** + * Get the underlying CTComment xmlbean for a comment located at cellRef, if it exists + * + * @param cellRef the location of the cell comment + * @return CTComment xmlbean if comment exists, otherwise return null. + */ + @Internal + public CTComment getCTComment(CellAddress cellRef) { // Create the cache if needed - if(commentRefs == null) { - commentRefs = new HashMap(); - for (CTComment comment : comments.getCommentList().getCommentArray()) { - commentRefs.put(comment.getRef(), comment); - } - } + prepareCTCommentCache(); // Return the comment, or null if not known return commentRefs.get(cellRef); } + + /** + * Returns all cell comments on this sheet. + * @return A map of each Comment in this sheet, keyed on the cell address where + * the comment is located. + */ + public Map getCellComments(){ + prepareCTCommentCache(); + final TreeMap map = new TreeMap(); + + for (final Entry e: commentRefs.entrySet()) { + map.put(e.getKey(), new XSSFComment(this, e.getValue(), null)); + } + + return map; + } /** - * This method is deprecated and should not be used any more as - * it silently overwrites the comment in Cell A1 if present - * @deprecated Use {@link #newComment(String)} instead to explicitly set the cell reference to create for + * Refresh Map commentRefs cache, + * Calls that use the commentRefs cache will perform in O(1) + * time rather than O(n) lookup time for List comments. */ - @Deprecated - public CTComment newComment() { - return newComment("A1"); + @SuppressWarnings("deprecation") //YK: getXYZArray) array accessors are deprecated in xmlbeans with JDK 1.5 support + private void prepareCTCommentCache() { + // Create the cache if needed + if(commentRefs == null) { + commentRefs = new HashMap(); + for (CTComment comment : comments.getCommentList().getCommentArray()) { + commentRefs.put(new CellAddress(comment.getRef()), comment); + } + } } + /** + * Create a new comment located at cell address + * + * @param ref the location to add the comment + * @return a new CTComment located at ref with default author + * @deprecated 2015-11-23 (circa POI 3.14beta1). Use {@link #newComment(CellAddress)} instead + */ + @Internal public CTComment newComment(String ref) { + return newComment(new CellAddress(ref)); + } + + /** + * Create a new comment located` at cell address + * + * @param ref the location to add the comment + * @return a new CTComment located at ref with default author + */ + @Internal + public CTComment newComment(CellAddress ref) { CTComment ct = comments.getCommentList().addNewComment(); - ct.setRef(ref); - ct.setAuthorId(0); + ct.setRef(ref.formatAsString()); + ct.setAuthorId(DEFAULT_AUTHOR_ID); if(commentRefs != null) { - commentRefs.put(ct.getRef(), ct); + commentRefs.put(ref, ct); } return ct; } - + + /** + * Remove the comment at cellRef location, if one exists + * + * @param cellRef the location of the comment to remove + * @return returns true if a comment was removed + * @deprecated 2015-11-23 (circa POI 3.14beta1). Use {@link #removeComment(CellAddress)} instead + */ public boolean removeComment(String cellRef) { + return removeComment(new CellAddress(cellRef)); + } + + /** + * Remove the comment at cellRef location, if one exists + * + * @param cellRef the location of the comment to remove + * @return returns true if a comment was removed + */ + public boolean removeComment(CellAddress cellRef) { + final String stringRef = cellRef.formatAsString(); CTCommentList lst = comments.getCommentList(); if(lst != null) { + @SuppressWarnings("deprecation") //YK: getXYZArray() array accessors are deprecated in xmlbeans with JDK 1.5 support CTComment[] commentArray = lst.getCommentArray(); for (int i = 0; i < commentArray.length; i++) { CTComment comment = commentArray[i]; - if (cellRef.equals(comment.getRef())) { + if (stringRef.equals(comment.getRef())) { lst.removeComment(i); if(commentRefs != null) { @@ -170,12 +283,25 @@ public class CommentsTable extends POIXMLDocumentPart { return false; } + /** + * Add a new author to the CommentsTable. + * This does not check if the author already exists. + * + * @param author the name of the comment author + * @return the index of the new author + */ private int addNewAuthor(String author) { int index = comments.getAuthors().sizeOfAuthorArray(); comments.getAuthors().insertAuthor(index, author); return index; } + /** + * Returns the underlying CTComments list xmlbean + * + * @return underlying comments list xmlbean + */ + @Internal public CTComments getCTComments(){ return comments; } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java index 1ef9d19b4..6e88e4563 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java @@ -34,6 +34,7 @@ import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellAddress; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFRichTextString; @@ -86,9 +87,9 @@ public class TestCommentsTable { comment1.setText(ctrst1); // test finding the right comment for a cell - assertSame(comment0, sheetComments.getCTComment("A1")); - assertSame(comment1, sheetComments.getCTComment("A2")); - assertNull(sheetComments.getCTComment("A3")); + assertSame(comment0, sheetComments.getCTComment(new CellAddress("A1"))); + assertSame(comment1, sheetComments.getCTComment(new CellAddress("A2"))); + assertNull(sheetComments.getCTComment(new CellAddress("A3"))); } @@ -210,33 +211,37 @@ public class TestCommentsTable { @Test public void removeComment() throws Exception { + final CellAddress addrA1 = new CellAddress("A1"); + final CellAddress addrA2 = new CellAddress("A2"); + final CellAddress addrA3 = new CellAddress("A3"); + CommentsTable sheetComments = new CommentsTable(); - CTComment a1 = sheetComments.newComment("A1"); - CTComment a2 = sheetComments.newComment("A2"); - CTComment a3 = sheetComments.newComment("A3"); + CTComment a1 = sheetComments.newComment(addrA1); + CTComment a2 = sheetComments.newComment(addrA2); + CTComment a3 = sheetComments.newComment(addrA3); - assertSame(a1, sheetComments.getCTComment("A1")); - assertSame(a2, sheetComments.getCTComment("A2")); - assertSame(a3, sheetComments.getCTComment("A3")); + assertSame(a1, sheetComments.getCTComment(addrA1)); + assertSame(a2, sheetComments.getCTComment(addrA2)); + assertSame(a3, sheetComments.getCTComment(addrA3)); assertEquals(3, sheetComments.getNumberOfComments()); - assertTrue(sheetComments.removeComment("A1")); + assertTrue(sheetComments.removeComment(addrA1)); assertEquals(2, sheetComments.getNumberOfComments()); - assertNull(sheetComments.getCTComment("A1")); - assertSame(a2, sheetComments.getCTComment("A2")); - assertSame(a3, sheetComments.getCTComment("A3")); + assertNull(sheetComments.getCTComment(addrA1)); + assertSame(a2, sheetComments.getCTComment(addrA2)); + assertSame(a3, sheetComments.getCTComment(addrA3)); - assertTrue(sheetComments.removeComment("A2")); + assertTrue(sheetComments.removeComment(addrA2)); assertEquals(1, sheetComments.getNumberOfComments()); - assertNull(sheetComments.getCTComment("A1")); - assertNull(sheetComments.getCTComment("A2")); - assertSame(a3, sheetComments.getCTComment("A3")); + assertNull(sheetComments.getCTComment(addrA1)); + assertNull(sheetComments.getCTComment(addrA2)); + assertSame(a3, sheetComments.getCTComment(addrA3)); - assertTrue(sheetComments.removeComment("A3")); + assertTrue(sheetComments.removeComment(addrA3)); assertEquals(0, sheetComments.getNumberOfComments()); - assertNull(sheetComments.getCTComment("A1")); - assertNull(sheetComments.getCTComment("A2")); - assertNull(sheetComments.getCTComment("A3")); + assertNull(sheetComments.getCTComment(addrA1)); + assertNull(sheetComments.getCTComment(addrA2)); + assertNull(sheetComments.getCTComment(addrA3)); } @Test diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFComment.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFComment.java index 0687c4b32..79a9c2122 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFComment.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFComment.java @@ -38,6 +38,7 @@ import org.apache.poi.ss.usermodel.RichTextString; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellAddress; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.XSSFITestDataProvider; import org.apache.poi.xssf.XSSFTestDataSamples; @@ -73,7 +74,7 @@ public final class TestXSSFComment extends BaseTestCellComment { assertEquals(1, sheetComments.getCTComments().getAuthors().sizeOfAuthorArray()); assertEquals(1, sheetComments.getNumberOfAuthors()); - CTComment ctComment = sheetComments.newComment("A1"); + CTComment ctComment = sheetComments.newComment(CellAddress.A1); CTShape vmlShape = CTShape.Factory.newInstance(); XSSFComment comment = new XSSFComment(sheetComments, ctComment, vmlShape); @@ -88,7 +89,7 @@ public final class TestXSSFComment extends BaseTestCellComment { public void getSetCol() { CommentsTable sheetComments = new CommentsTable(); XSSFVMLDrawing vml = new XSSFVMLDrawing(); - CTComment ctComment = sheetComments.newComment("A1"); + CTComment ctComment = sheetComments.newComment(CellAddress.A1); CTShape vmlShape = vml.newCommentShape(); XSSFComment comment = new XSSFComment(sheetComments, ctComment, vmlShape); @@ -107,7 +108,7 @@ public final class TestXSSFComment extends BaseTestCellComment { public void getSetRow() { CommentsTable sheetComments = new CommentsTable(); XSSFVMLDrawing vml = new XSSFVMLDrawing(); - CTComment ctComment = sheetComments.newComment("A1"); + CTComment ctComment = sheetComments.newComment(CellAddress.A1); CTShape vmlShape = vml.newCommentShape(); XSSFComment comment = new XSSFComment(sheetComments, ctComment, vmlShape); @@ -179,7 +180,7 @@ public final class TestXSSFComment extends BaseTestCellComment { @Test public void author() { CommentsTable sheetComments = new CommentsTable(); - CTComment ctComment = sheetComments.newComment("A1"); + CTComment ctComment = sheetComments.newComment(CellAddress.A1); assertEquals(1, sheetComments.getNumberOfAuthors()); XSSFComment comment = new XSSFComment(sheetComments, ctComment, null); @@ -230,8 +231,7 @@ public final class TestXSSFComment extends BaseTestCellComment { } // create the comment in two different ways and verify that there is no difference - @SuppressWarnings("deprecation") - XSSFComment shape1 = new XSSFComment(comments, comments.newComment(), vmlShape1); + XSSFComment shape1 = new XSSFComment(comments, comments.newComment(CellAddress.A1), vmlShape1); shape1.setColumn(ca.getCol1()); shape1.setRow(ca.getRow1()); @@ -243,7 +243,7 @@ public final class TestXSSFComment extends BaseTestCellComment { vmlShape2.getClientDataArray(0).setAnchorArray(0, position); } - String ref = new CellReference(ca.getRow1(), ca.getCol1()).formatAsString(); + CellAddress ref = new CellAddress(ca.getRow1(), ca.getCol1()); XSSFComment shape2 = new XSSFComment(comments, comments.newComment(ref), vmlShape2); assertEquals(shape1.getAuthor(), shape2.getAuthor());