From 21d7a81d3ca11a7a57034b8390a8cd22e99c3c6f Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Fri, 27 Nov 2009 17:39:17 +0000 Subject: [PATCH] improved work with cell comments in XSSF, also added support for cell comments to SS interfaces git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@884918 13f79535-47bb-0310-9956-ffa450edef68 --- .../content/xdocs/spreadsheet/quick-guide.xml | 139 ++------- src/documentation/content/xdocs/status.xml | 5 +- .../xssf/usermodel/examples/CellComments.java | 80 ++++++ .../poi/hssf/usermodel/HSSFComment.java | 9 +- .../poi/hssf/usermodel/HSSFPatriarch.java | 5 + .../org/apache/poi/ss/usermodel/Cell.java | 5 + .../org/apache/poi/ss/usermodel/Comment.java | 2 +- .../org/apache/poi/ss/usermodel/Drawing.java | 2 + .../apache/poi/xssf/model/CommentsTable.java | 61 ++-- .../apache/poi/xssf/usermodel/XSSFCell.java | 24 +- .../poi/xssf/usermodel/XSSFClientAnchor.java | 4 + .../poi/xssf/usermodel/XSSFComment.java | 153 +++++++--- .../poi/xssf/usermodel/XSSFDrawing.java | 30 ++ .../poi/xssf/usermodel/XSSFRelation.java | 2 +- .../apache/poi/xssf/usermodel/XSSFSheet.java | 105 ++++--- .../poi/xssf/usermodel/XSSFVMLDrawing.java | 253 +++++++++++++++++ .../poi/xssf/model/TestCommentsTable.java | 150 ++++------ .../poi/xssf/usermodel/TestXSSFComment.java | 268 +++++++++--------- .../poi/xssf/usermodel/TestXSSFSheet.java | 40 ++- .../xssf/usermodel/TestXSSFVMLDrawing.java | 139 +++++++++ .../poi/hssf/usermodel/TestHSSFComment.java | 195 ++----------- .../poi/ss/usermodel/BaseTestCellComment.java | 246 ++++++++++++++++ test-data/spreadsheet/SimpleWithComments.xlsx | Bin 0 -> 10060 bytes test-data/spreadsheet/vmlDrawing1.vml | 42 +++ 24 files changed, 1314 insertions(+), 645 deletions(-) create mode 100755 src/examples/src/org/apache/poi/xssf/usermodel/examples/CellComments.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVMLDrawing.java create mode 100755 src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFVMLDrawing.java create mode 100644 src/testcases/org/apache/poi/ss/usermodel/BaseTestCellComment.java create mode 100644 test-data/spreadsheet/SimpleWithComments.xlsx create mode 100644 test-data/spreadsheet/vmlDrawing1.vml diff --git a/src/documentation/content/xdocs/spreadsheet/quick-guide.xml b/src/documentation/content/xdocs/spreadsheet/quick-guide.xml index eeca60ebe7..b2feca0679 100644 --- a/src/documentation/content/xdocs/spreadsheet/quick-guide.xml +++ b/src/documentation/content/xdocs/spreadsheet/quick-guide.xml @@ -43,7 +43,7 @@
  • Text Extraction
  • Aligning cells
  • Working with borders
  • -
  • Fills and color
  • +
  • Fills and color
  • Merging cells
  • Working with fonts
  • Custom colors
  • @@ -1328,123 +1328,36 @@ Examples: -
    Cell Comments - HSSF and XSSF (slight differences though) +
    Cell Comments - HSSF and XSSF

    - In HSSF Excel, cell comments were added to the file format as a bit of a - cludge. As such, comments are a kind of a text shape, so inserting a - comment is very similar to placing a text box in a worksheet. + A comment is a rich text note that is attached to & + associated with a cell, separate from other cell content. + Comment content is stored separate from the cell, and is displayed in a drawing object (like a text box) + that is separate from, but associated with, a cell

    -

    - In XSSF Excel, cell comments are more cleanly done. Each Sheet has a list - of its comments, and they can be added much like other cell properties. -

    -

    - Once you have created your comment, how you use it is very similar between - HSSF and XSSF. It is only the creation of a new comment where things - differ. -

    -

    - For HSSF, the process is: -

    - - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet sheet = wb.createSheet("Cell comments in POI HSSF"); - CreationHelper createHelper = wb.getCreationHelper(); - - // Create the drawing patriarch. This is the top level container for all shapes including cell comments. - HSSFPatriarch patr = sheet.createDrawingPatriarch(); - - // Create a cell in row 3 - Cell cell1 = sheet.createRow(3).createCell((short)1); - cell1.setCellValue(new HSSFRichTextString("Hello, World")); - - // Anchor defines size and position of the comment in worksheet - Comment comment1 = patr.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short)4, 2, (short) 6, 5)); - - // set text in the comment - comment1.setString(createHelper.createRichTextString("We can set comments in POI")); - - // set comment author. - // you can see it in the status bar when moving mouse over the commented cell - comment1.setAuthor("Apache Software Foundation"); - - // The first way to assign comment to a cell is via Cell.setCellComment method - cell1.setCellComment(comment1); - - - // Create another cell in row 6 - Cell cell2 = sheet.createRow(6).createCell((short)1); - cell2.setCellValue(36.6); - - // And a comment for it - HSSFComment comment2 = patr.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short)4, 8, (short) 6, 11)); - // Modify background color of the comment - comment2.setFillColor(204, 236, 255); - - HSSFRichTextString string = new HSSFRichTextString("Normal body temperature"); - - // Apply custom font to the text in the comment - HSSFFont font = wb.createFont(); - font.setFontName("Arial"); - font.setFontHeightInPoints((short)10); - font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); - font.setColor(HSSFColor.RED.index); - string.applyFont(font); - - comment2.setString(string); - // By default comments are hidden. This one is always visible. - comment2.setVisible(true); - - comment2.setAuthor("Bill Gates"); - - - /** - * The second way to assign comment to a cell is to implicitly specify its row and column. - * Note, it is possible to set row and column of a non-existing cell. - * It works, the comment is visible. - */ - comment2.setRow(6); - comment2.setColumn((short)1); - - FileOutputStream out = new FileOutputStream("poi_comment.xls"); - wb.write(out); - out.close(); - -

    - For XSSF, the simpler process is: -

    - - XSSFWorkbook wb = new XSSFWorkbook(); - XSSFSheet sheet = wb.createSheet("Cell comments in POI XSSF"); - CreationHelper createHelper = wb.getCreationHelper(); - - // Create a cell in row 3 - Cell cell1 = sheet.createRow(3).createCell((short)1); - cell1.setCellValue(new XSSFRichTextString("Hello, World")); - - // Create a comment, and set the text and author - // (You can see the author in the status bar when moving mouse - // over the commented cell) - Comment comment1 = sheet.createComment(); - comment1.setString(createHelper.createRichTextString("We can set comments in POI")); - comment1.setAuthor("Apache Software Foundation"); - - - // The first way to assign comment to a cell is via Cell.setCellComment method - cell1.setCellComment(comment1); + + Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook(); + CreationHelper factory = wb.getCreationHelper(); - // The other way is to set the row and column - // This could point to a cell that isn't defined, and the comment will - // will still show up all the same - Comment comment2 = sheet.createComment(); - comment2.setString(createHelper.createRichTextString("Comment for missing cell")); - comment2.setAuthor("Apache POI"); - comment2.setRow(11); - comment2.setColumn(1); + Sheet sheet = wb.createSheet(); + + Cell cell = sheet.createRow(3).createCell(5); + cell.setCellValue("F4"); + + Drawing drawing = sheet.createDrawingPatriarch(); - // Write out - FileOutputStream out = new FileOutputStream("poi_comment.xls"); + ClientAnchor anchor = factory.createClientAnchor(); + Comment comment = drawing.createCellComment(anchor); + RichTextString str = factory.createRichTextString("Hello, World!"); + comment.setString(str); + comment.setAuthor("Apache POI"); + //assign the comment to the cell + cell.setCellComment(comment); + + String fname = "comment-xssf.xls"; + if(wb instanceof XSSFWorkbook) fname += "x"; + FileOutputStream out = new FileOutputStream(fname); wb.write(out); out.close(); diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index bda9cecca0..9109c2b37e 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,7 +34,10 @@ - Add support for creating SummaryInformation and DocumentSummaryInformation properties on POIDocuments that don't have them, via POIDocument.createInformationProperties() + 47188 - avoid corruption of workbook when adding cell comments + 48106 - improved work with cell comments in XSSF + Add support for creating SummaryInformation and DocumentSummaryInformation properties + on POIDocuments that don't have them, via POIDocument.createInformationProperties() 48180 - be more forgiving of short chart records, which skip some unused fields 48274 - fix erronious wrapping of byte colours in HSSFPalette.findSimilarColor 48269 - fix fetching of error codes from XSSF formula cells diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/CellComments.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/CellComments.java new file mode 100755 index 0000000000..4eaa33d92d --- /dev/null +++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/CellComments.java @@ -0,0 +1,80 @@ +/* ==================================================================== + 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.xssf.usermodel.examples; + +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import java.io.IOException; +import java.io.FileOutputStream; + +/** + * Demonstrates how to work with excel cell comments. + * + *

    + * Excel comment is a kind of a text shape, + * so inserting a comment is very similar to placing a text box in a worksheet + *

    + * + * @author Yegor Kozlov + */ +public class CellComments { + public static void main(String[] args) throws IOException { + Workbook wb = new XSSFWorkbook(); + + CreationHelper factory = wb.getCreationHelper(); + + Sheet sheet = wb.createSheet(); + + Cell cell1 = sheet.createRow(3).createCell(5); + cell1.setCellValue("F4"); + + Drawing drawing = sheet.createDrawingPatriarch(); + + ClientAnchor anchor = factory.createClientAnchor(); + + Comment comment1 = drawing.createCellComment(anchor); + RichTextString str1 = factory.createRichTextString("Hello, World!"); + comment1.setString(str1); + comment1.setAuthor("Apache POI"); + cell1.setCellComment(comment1); + + Cell cell2 = sheet.createRow(2).createCell(2); + cell2.setCellValue("C3"); + + Comment comment2 = drawing.createCellComment(anchor); + RichTextString str2 = factory.createRichTextString("XSSF can set cell comments"); + //apply custom font to the text in the comment + Font font = wb.createFont(); + font.setFontName("Arial"); + font.setFontHeightInPoints((short)14); + font.setBoldweight(Font.BOLDWEIGHT_BOLD); + font.setColor(IndexedColors.RED.getIndex()); + str2.applyFont(font); + + comment2.setString(str2); + comment2.setAuthor("Apache POI"); + comment2.setColumn(2); + comment2.setRow(2); + + String fname = "comments.xlsx"; + FileOutputStream out = new FileOutputStream(fname); + wb.write(out); + out.close(); + + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java b/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java index ddcef6a43c..225e6da0e9 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java @@ -127,12 +127,19 @@ public class HSSFComment extends HSSFTextbox implements Comment { * * @param col the 0-based column of the cell that contains the comment */ - public void setColumn(short col) { + public void setColumn(int col) { if(_note != null) { _note.setColumn(col); } _col = col; } + /** + * @deprecated (Nov 2009) use {@link HSSFComment#setColumn(int)} } + */ + @Deprecated + public void setColumn(short col) { + setColumn((int)col); + } /** * Name of the original comment author diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java index 47e0c813f7..65267a17c4 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -157,6 +157,11 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { return shape; } + @Override + public HSSFComment createCellComment(ClientAnchor anchor){ + return createComment((HSSFAnchor)anchor); + } + /** * Returns a list of all shapes contained by the patriarch. */ diff --git a/src/java/org/apache/poi/ss/usermodel/Cell.java b/src/java/org/apache/poi/ss/usermodel/Cell.java index 0a76061e45..0d581db352 100644 --- a/src/java/org/apache/poi/ss/usermodel/Cell.java +++ b/src/java/org/apache/poi/ss/usermodel/Cell.java @@ -351,6 +351,11 @@ public interface Cell { */ Comment getCellComment(); + /** + * Removes the comment for this cell, if there is one. + */ + void removeCellComment(); + /** * Returns hyperlink associated with this cell * diff --git a/src/java/org/apache/poi/ss/usermodel/Comment.java b/src/java/org/apache/poi/ss/usermodel/Comment.java index 83a7834e4a..463f701348 100644 --- a/src/java/org/apache/poi/ss/usermodel/Comment.java +++ b/src/java/org/apache/poi/ss/usermodel/Comment.java @@ -59,7 +59,7 @@ public interface Comment { * * @param col the 0-based column of the cell that contains the comment */ - void setColumn(short col); + void setColumn(int col); /** * Name of the original comment author diff --git a/src/java/org/apache/poi/ss/usermodel/Drawing.java b/src/java/org/apache/poi/ss/usermodel/Drawing.java index b27c3098c0..82c420af62 100644 --- a/src/java/org/apache/poi/ss/usermodel/Drawing.java +++ b/src/java/org/apache/poi/ss/usermodel/Drawing.java @@ -21,4 +21,6 @@ package org.apache.poi.ss.usermodel; */ public interface Drawing { Picture createPicture(ClientAnchor anchor, int pictureIndex); + + Comment createCellComment(ClientAnchor anchor); } 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 6caec6e734..da47d90a2c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java @@ -24,7 +24,6 @@ import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.usermodel.XSSFComment; import org.apache.poi.POIXMLDocumentPart; import org.apache.xmlbeans.XmlException; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTAuthors; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComments; @@ -33,11 +32,13 @@ import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; public class CommentsTable extends POIXMLDocumentPart { - protected CTComments comments; + private CTComments comments; public CommentsTable() { super(); comments = CTComments.Factory.newInstance(); + comments.addNewCommentList(); + comments.addNewAuthors().addAuthor(""); } public CommentsTable(PackagePart part, PackageRelationship rel) throws IOException { @@ -70,62 +71,60 @@ public class CommentsTable extends POIXMLDocumentPart { public int getNumberOfComments() { return comments.getCommentList().sizeOfCommentArray(); } + public int getNumberOfAuthors() { - return getCommentsAuthors().sizeOfAuthorArray(); + return comments.getAuthors().sizeOfAuthorArray(); } public String getAuthor(long authorId) { - return getCommentsAuthors().getAuthorArray((int)authorId); + return comments.getAuthors().getAuthorArray((int)authorId); } public int findAuthor(String author) { - for (int i = 0 ; i < getCommentsAuthors().sizeOfAuthorArray() ; i++) { - if (getCommentsAuthors().getAuthorArray(i).equals(author)) { + for (int i = 0 ; i < comments.getAuthors().sizeOfAuthorArray() ; i++) { + if (comments.getAuthors().getAuthorArray(i).equals(author)) { return i; } } return addNewAuthor(author); } - public XSSFComment findCellComment(int row, int column) { - return findCellComment( - (new CellReference(row, column)).formatAsString() ); + public XSSFComment findCellComment(String cellRef) { + CTComment ct = getCTComment(cellRef); + return ct == null ? null : new XSSFComment(this, ct, null); } - public XSSFComment findCellComment(String cellRef) { - for (CTComment comment : getCommentsList().getCommentArray()) { + public CTComment getCTComment(String cellRef) { + for (CTComment comment : comments.getCommentList().getCommentArray()) { if (cellRef.equals(comment.getRef())) { - return new XSSFComment(this, comment); + return comment; } } return null; } - /** - * Generates a new XSSFComment, associated with the - * current comments list. - */ - public XSSFComment addComment() { - return new XSSFComment(this, getCommentsList().addNewComment()); - } - - private CTCommentList getCommentsList() { - if (comments.getCommentList() == null) { - comments.addNewCommentList(); - } - return comments.getCommentList(); + public CTComment newComment() { + CTComment ct = comments.getCommentList().addNewComment(); + ct.setRef("A1"); + ct.setAuthorId(0); + return ct; } - private CTAuthors getCommentsAuthors() { - if (comments.getAuthors() == null) { - comments.addNewAuthors(); + public boolean removeComment(String cellRef) { + CTCommentList lst = comments.getCommentList(); + if(lst != null) for(int i=0; i < lst.sizeOfCommentArray(); i++) { + CTComment comment = lst.getCommentArray(i); + if (cellRef.equals(comment.getRef())) { + lst.removeComment(i); + return true; + } } - return comments.getAuthors(); + return false; } private int addNewAuthor(String author) { - int index = getCommentsAuthors().sizeOfAuthorArray(); - getCommentsAuthors().insertAuthor(index, author); + int index = comments.getAuthors().sizeOfAuthorArray(); + comments.getAuthors().insertAuthor(index, author); return index; } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java index cacc057352..258415efa9 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -807,11 +807,29 @@ public final class XSSFCell implements Cell { * Assign a comment to this cell. If the supplied comment is null, * the comment for this cell will be removed. * - * @param comment comment associated with this cell + * @param comment the XSSFComment associated with this cell */ public void setCellComment(Comment comment) { - String cellRef = new CellReference(_row.getRowNum(), getColumnIndex()).formatAsString(); - getSheet().setCellComment(cellRef, (XSSFComment)comment); + if(comment == null) { + removeCellComment(); + return; + } + + comment.setRow(getRowIndex()); + comment.setColumn(getColumnIndex()); + } + + /** + * Removes the comment for this cell, if there is one. + */ + public void removeCellComment() { + XSSFComment comment = getCellComment(); + if(comment != null){ + String ref = _cell.getR(); + XSSFSheet sh = getSheet(); + sh.getCommentsTable(false).removeComment(ref); + sh.getVMLDrawing(false).removeCommentShape(getRowIndex(), getColumnIndex()); + } } /** diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFClientAnchor.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFClientAnchor.java index 7bf4057131..28efca9def 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFClientAnchor.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFClientAnchor.java @@ -217,4 +217,8 @@ public final class XSSFClientAnchor extends XSSFAnchor implements ClientAnchor { return anchorType; } + public boolean isSet(){ + return !(cell1.getCol() == 0 && cell2.getCol() == 0 && + cell1.getRow() == 0 && cell2.getRow() == 0); + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFComment.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFComment.java index 1868fc251d..a7e577d190 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFComment.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFComment.java @@ -20,84 +20,149 @@ import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.RichTextString; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.model.CommentsTable; -import org.apache.poi.xssf.usermodel.helpers.RichTextStringHelper; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst; +import schemasMicrosoftComVml.CTShape; + +import java.math.BigInteger; public class XSSFComment implements Comment { - private CTComment comment; - private CommentsTable comments; + private final CTComment _comment; + private final CommentsTable _comments; + private final CTShape _vmlShape; + + /** + * cached reference to the string with the comment text + */ + private XSSFRichTextString _str; - /** + /** * Creates a new XSSFComment, associated with a given * low level comment object. - * If, as an end user, you want a new XSSFComment - * object, the please ask your sheet for one. */ - public XSSFComment(CommentsTable comments, CTComment comment) { - this.comment = comment; - this.comments = comments; + public XSSFComment(CommentsTable comments, CTComment comment, CTShape vmlShape) { + _comment = comment; + _comments = comments; + _vmlShape = vmlShape; } - public String getAuthor() { - return comments.getAuthor((int)comment.getAuthorId()); + /** + * + * @return Name of the original comment author. Default value is blank. + */ + public String getAuthor() { + return _comments.getAuthor((int) _comment.getAuthorId()); } + /** + * Name of the original comment author. Default value is blank. + * + * @param author the name of the original author of the comment + */ + public void setAuthor(String author) { + _comment.setAuthorId( + _comments.findAuthor(author) + ); + } + + /** + * @return the 0-based column of the cell that the comment is associated with. + */ public int getColumn() { - return (new CellReference(comment.getRef())).getCol(); + return new CellReference(_comment.getRef()).getCol(); } + /** + * @return the 0-based row index of the cell that the comment is associated with. + */ public int getRow() { - return (new CellReference(comment.getRef())).getRow(); + return new CellReference(_comment.getRef()).getRow(); } - public boolean isVisible() { - // TODO Auto-generated method stub - return true; + /** + * @return whether the comment is visible + */ + public boolean isVisible() { + boolean visible = false; + if(_vmlShape != null){ + String style = _vmlShape.getStyle(); + visible = style != null && style.indexOf("visibility:visible") != -1; + } + return visible; } - public void setAuthor(String author) { - comment.setAuthorId( - comments.findAuthor(author) - ); - } + /** + * @param visible whether the comment is visible + */ + public void setVisible(boolean visible) { + if(_vmlShape != null){ + String style; + if(visible) style = "position:absolute;visibility:visible"; + else style = "position:absolute;visibility:hidden"; + _vmlShape.setStyle(style); + } + } - public void setColumn(short col) { - initializeRef(); - String newRef = - (new CellReference(getRow(), col)).formatAsString(); - comment.setRef(newRef); - } - - private void initializeRef() { - if (comment.getRef() == null) { - comment.setRef("A1"); - } + /** + * Set the column of the cell that contains the comment + * + * @param col the 0-based column of the cell that contains the comment + */ + public void setColumn(int col) { + CellReference ref = new CellReference(getRow(), col); + _comment.setRef(ref.formatAsString()); + if(_vmlShape != null) _vmlShape.getClientDataArray(0).setColumnArray(0, new BigInteger(String.valueOf(col))); } + /** + * Set the row of the cell that contains the comment + * + * @param row the 0-based row of the cell that contains the comment + */ public void setRow(int row) { - initializeRef(); String newRef = (new CellReference(row, getColumn())).formatAsString(); - comment.setRef(newRef); - } + _comment.setRef(newRef); + if(_vmlShape != null) _vmlShape.getClientDataArray(0).setRowArray(0, new BigInteger(String.valueOf(row))); + } - public RichTextString getString() { - return RichTextStringHelper.convertFromRst(comment.getText()); + /** + * @return the rich text string of the comment + */ + public XSSFRichTextString getString() { + if(_str == null) { + CTRst rst = _comment.getText(); + if(rst != null) _str = new XSSFRichTextString(_comment.getText()); + } + return _str; } + /** + * Sets the rich text string used by this comment. + * + * @param string the XSSFRichTextString used by this object. + */ public void setString(RichTextString string) { - CTRst text = comment.addNewText(); - RichTextStringHelper.convertToRst(string, text); + if(!(string instanceof XSSFRichTextString)){ + throw new IllegalArgumentException("Only XSSFRichTextString argument is supported"); + } + _str = (XSSFRichTextString)string; + _comment.setText(_str.getCTRst()); } public void setString(String string) { - RichTextString richTextString = new XSSFRichTextString(string); - setString(richTextString); + setString(new XSSFRichTextString(string)); } - public void setVisible(boolean visible) { - // TODO Auto-generated method stub - } + /** + * @return the xml bean holding this comment's properties + */ + protected CTComment getCTComment(){ + return _comment; + } + + protected CTShape getCTShape(){ + return _vmlShape; + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java index fc38802a3e..230819a02f 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java @@ -25,6 +25,7 @@ import java.util.Map; import javax.xml.namespace.QName; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePartName; import org.apache.poi.openxml4j.opc.PackageRelationship; @@ -233,6 +234,35 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing { return shape; } + /** + * Creates a cell comment. + * + * @param anchor the client anchor describes how this comment is attached + * to the sheet. + * @return the newly created comment. + */ + @Override + public XSSFComment createCellComment(ClientAnchor anchor) + { + XSSFClientAnchor ca = (XSSFClientAnchor)anchor; + XSSFSheet sheet = (XSSFSheet)getParent(); + + //create comments and vmlDrawing parts if they don't exist + CommentsTable comments = sheet.getCommentsTable(true); + XSSFVMLDrawing vml = sheet.getVMLDrawing(true); + schemasMicrosoftComVml.CTShape vmlShape = vml.newCommentShape(); + if(ca.isSet()){ + String position = + ca.getCol1() + ", 0, " + ca.getRow1() + ", 0, " + + ca.getCol2() + ", 0, " + ca.getRow2() + ", 0"; + vmlShape.getClientDataArray(0).setAnchorArray(0, position); + } + XSSFComment shape = new XSSFComment(comments, comments.newComment(), vmlShape); + shape.setColumn(ca.getCol1()); + shape.setRow(ca.getRow1()); + return shape; + } + /** * Create and initialize a CTTwoCellAnchor that anchors a shape against top-left and bottom-right cells. * diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java index e617acbab6..d2ba007e3a 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java @@ -118,7 +118,7 @@ public final class XSSFRelation extends POIXMLRelation { "application/vnd.openxmlformats-officedocument.vmlDrawing", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", "/xl/drawings/vmlDrawing#.vml", - null + XSSFVMLDrawing.class ); public static final XSSFRelation CUSTOM_XML_MAPPINGS = new XSSFRelation( diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 879f63357f..d0f0d13941 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -54,38 +54,7 @@ import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBreak; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDrawing; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHeaderFooter; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCell; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMergeCells; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOutlinePr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageBreak; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageMargins; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageSetUpPr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPane; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPrintOptions; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSelection; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetData; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetFormatPr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetPr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetProtection; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetView; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetViews; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPaneState; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; /** * High level representation of a SpreadsheetML worksheet. @@ -351,6 +320,48 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { return drawing; } + /** + * Get VML drawing for this sheet (aka 'legacy' drawig) + * + * @param autoCreate if true, then a new VML drawing part is created + * + * @return the VML drawing of null if the drawing was not found and autoCreate=false + */ + protected XSSFVMLDrawing getVMLDrawing(boolean autoCreate) { + XSSFVMLDrawing drawing = null; + CTLegacyDrawing ctDrawing = worksheet.getLegacyDrawing(); + if(ctDrawing == null) { + if(autoCreate) { + //drawingNumber = #drawings.size() + 1 + int drawingNumber = getPackagePart().getPackage().getPartsByContentType(XSSFRelation.VML_DRAWINGS.getContentType()).size() + 1; + drawing = (XSSFVMLDrawing)createRelationship(XSSFRelation.VML_DRAWINGS, XSSFFactory.getInstance(), drawingNumber); + String relId = drawing.getPackageRelationship().getId(); + + //add CTLegacyDrawing element which indicates that this sheet contains drawing components built on the drawingML platform. + //The relationship Id references the part containing the drawing definitions. + ctDrawing = worksheet.addNewLegacyDrawing(); + ctDrawing.setId(relId); + } + } else { + //search the referenced drawing in the list of the sheet's relations + for(POIXMLDocumentPart p : getRelations()){ + if(p instanceof XSSFVMLDrawing) { + XSSFVMLDrawing dr = (XSSFVMLDrawing)p; + String drId = dr.getPackageRelationship().getId(); + if(drId.equals(ctDrawing.getId())){ + drawing = dr; + break; + } + break; + } + } + if(drawing == null){ + logger.log(POILogger.ERROR, "Can't find VML drawing with id=" + ctDrawing.getId() + " in the list of the sheet's relationships"); + } + } + return drawing; + } + /** * Creates a split (freezepane). Any existing freezepane or split pane is overwritten. * @param colSplit Horizonatal position of split. @@ -392,12 +403,14 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { /** * Creates a new comment for this sheet. You still * need to assign it to a cell though + * + * @deprecated since Nov 2009 this method is not compatible with the common SS interfaces, + * use {@link org.apache.poi.xssf.usermodel.XSSFDrawing#createCellComment + * (org.apache.poi.ss.usermodel.ClientAnchor)} instead */ + @Deprecated public XSSFComment createComment() { - if (sheetComments == null) { - sheetComments = (CommentsTable)createRelationship(XSSFRelation.SHEET_COMMENTS, XSSFFactory.getInstance(), (int)sheet.getSheetId()); - } - return sheetComments.addComment(); + return createDrawingPatriarch().createCellComment(new XSSFClientAnchor()); } /** @@ -438,7 +451,14 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { if (sheetComments == null) { return null; } - return sheetComments.findCellComment(row, column); + + String ref = new CellReference(row, column).formatAsString(); + CTComment ctComment = sheetComments.getCTComment(ref); + if(ctComment == null) return null; + + XSSFVMLDrawing vml = getVMLDrawing(false); + return new XSSFComment(sheetComments, ctComment, + vml == null ? null : vml.findCommentShape(row, column)); } public XSSFHyperlink getHyperlink(int row, int column) { @@ -2161,8 +2181,10 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { * * @param cellRef cell region * @param comment the comment to assign + * @deprecated since Nov 2009 use {@link XSSFCell#setCellComment(org.apache.poi.ss.usermodel.Comment)} instead */ - public void setCellComment(String cellRef, XSSFComment comment) { + @Deprecated + public static void setCellComment(String cellRef, XSSFComment comment) { CellReference cellReference = new CellReference(cellRef); comment.setRow(cellReference.getRow()); @@ -2234,8 +2256,13 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { /** * Returns the sheet's comments object if there is one, * or null if not + * + * @param create create a new comments table if it does not exist */ - protected CommentsTable getCommentsTable() { + protected CommentsTable getCommentsTable(boolean create) { + if(sheetComments == null && create){ + sheetComments = (CommentsTable)createRelationship(XSSFRelation.SHEET_COMMENTS, XSSFFactory.getInstance(), (int)sheet.getSheetId()); + } return sheetComments; } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVMLDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVMLDrawing.java new file mode 100644 index 0000000000..a27d657bce --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVMLDrawing.java @@ -0,0 +1,253 @@ +/* ==================================================================== + 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.xssf.usermodel; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; +import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlCursor; +import org.w3c.dom.Node; +import schemasMicrosoftComOfficeOffice.*; + +import javax.xml.namespace.QName; +import java.io.*; +import java.util.*; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.math.BigInteger; + +import schemasMicrosoftComVml.*; +import schemasMicrosoftComVml.STTrueFalse; +import schemasMicrosoftComOfficeExcel.CTClientData; +import schemasMicrosoftComOfficeExcel.STObjectType; + +/** + * Represents a SpreadsheetML VML drawing. + * + *

    + * In Excel 2007 VML drawings are used to describe properties of cell comments, + * although the spec says that VML is deprecated: + *

    + *

    + * The VML format is a legacy format originally introduced with Office 2000 and is included and fully defined + * in this Standard for backwards compatibility reasons. The DrawingML format is a newer and richer format + * created with the goal of eventually replacing any uses of VML in the Office Open XML formats. VML should be + * considered a deprecated format included in Office Open XML for legacy reasons only and new applications that + * need a file format for drawings are strongly encouraged to use preferentially DrawingML + *

    + * + * See 6.4 VML - SpreadsheetML Drawing in Office Open XML Part 4 - Markup Language Reference.pdf + * + * @author Yegor Kozlov + */ +public final class XSSFVMLDrawing extends POIXMLDocumentPart { + private static final QName QNAME_SHAPE_LAYOUT = new QName("urn:schemas-microsoft-com:office:office", "shapelayout"); + private static final QName QNAME_SHAPE_TYPE = new QName("urn:schemas-microsoft-com:vml", "shapetype"); + private static final QName QNAME_SHAPE = new QName("urn:schemas-microsoft-com:vml", "shape"); + + /** + * regexp to parse shape ids, in VML they have weird form of id="_x0000_s1026" + */ + private static final Pattern ptrn_shapeId = Pattern.compile("_x0000_s(\\d+)"); + + private List _qnames = new ArrayList(); + private List _items = new ArrayList(); + private String _shapeTypeId; + private int _shapeId = 1024; + + /** + * Create a new SpreadsheetML drawing + * + * @see XSSFSheet#createDrawingPatriarch() + */ + protected XSSFVMLDrawing() { + super(); + newDrawing(); + } + + /** + * Construct a SpreadsheetML drawing from a package part + * + * @param part the package part holding the drawing data, + * the content type must be application/vnd.openxmlformats-officedocument.drawing+xml + * @param rel the package relationship holding this drawing, + * the relationship type must be http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing + */ + protected XSSFVMLDrawing(PackagePart part, PackageRelationship rel) throws IOException, XmlException { + super(part, rel); + read(getPackagePart().getInputStream()); + } + + + protected void read(InputStream is) throws IOException, XmlException { + XmlObject root = XmlObject.Factory.parse(is); + + _qnames = new ArrayList(); + _items = new ArrayList(); + for(XmlObject obj : root.selectPath("$this/xml/*")) { + Node nd = obj.getDomNode(); + QName qname = new QName(nd.getNamespaceURI(), nd.getLocalName()); + if (qname.equals(QNAME_SHAPE_LAYOUT)) { + _items.add(CTShapeLayout.Factory.parse(obj.xmlText())); + } else if (qname.equals(QNAME_SHAPE_TYPE)) { + CTShapetype st = CTShapetype.Factory.parse(obj.xmlText()); + _items.add(st); + _shapeTypeId = st.getId(); + } else if (qname.equals(QNAME_SHAPE)) { + CTShape shape = CTShape.Factory.parse(obj.xmlText()); + String id = shape.getId(); + if(id != null) { + Matcher m = ptrn_shapeId.matcher(id); + if(m.find()) _shapeId = Math.max(_shapeId, Integer.parseInt(m.group(1))); + } + _items.add(shape); + } else { + _items.add(XmlObject.Factory.parse(obj.xmlText())); + } + _qnames.add(qname); + } + } + + protected List getItems(){ + return _items; + } + + protected void write(OutputStream out) throws IOException { + XmlObject rootObject = XmlObject.Factory.newInstance(); + XmlCursor rootCursor = rootObject.newCursor(); + rootCursor.toNextToken(); + rootCursor.beginElement("xml"); + + for(int i=0; i < _items.size(); i++){ + XmlCursor xc = _items.get(i).newCursor(); + rootCursor.beginElement(_qnames.get(i)); + while(xc.toNextToken() == XmlCursor.TokenType.ATTR) { + Node anode = xc.getDomNode(); + rootCursor.insertAttributeWithValue(anode.getLocalName(), anode.getNamespaceURI(), anode.getNodeValue()); + } + xc.toStartDoc(); + xc.copyXmlContents(rootCursor); + rootCursor.toNextToken(); + xc.dispose(); + } + rootCursor.dispose(); + + XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); + xmlOptions.setSavePrettyPrint(); + HashMap map = new HashMap(); + map.put("urn:schemas-microsoft-com:vml", "v"); + map.put("urn:schemas-microsoft-com:office:office", "o"); + map.put("urn:schemas-microsoft-com:office:excel", "x"); + xmlOptions.setSaveSuggestedPrefixes(map); + + rootObject.save(out, xmlOptions); + } + + @Override + protected void commit() throws IOException { + PackagePart part = getPackagePart(); + OutputStream out = part.getOutputStream(); + write(out); + out.close(); + } + + /** + * Initialize a new Speadsheet VML drawing + */ + private void newDrawing(){ + CTShapeLayout layout = CTShapeLayout.Factory.newInstance(); + layout.setExt(STExt.EDIT); + CTIdMap idmap = layout.addNewIdmap(); + idmap.setExt(STExt.EDIT); + idmap.setData("1"); + _items.add(layout); + _qnames.add(QNAME_SHAPE_LAYOUT); + + CTShapetype shapetype = CTShapetype.Factory.newInstance(); + _shapeTypeId = "_xssf_cell_comment"; + shapetype.setId(_shapeTypeId); + shapetype.setCoordsize("21600,21600"); + shapetype.setSpt(202); + shapetype.setPath2("m,l,21600r21600,l21600,xe"); + shapetype.addNewStroke().setJoinstyle(STStrokeJoinStyle.MITER); + CTPath path = shapetype.addNewPath(); + path.setGradientshapeok(STTrueFalse.T); + path.setConnecttype(STConnectType.RECT); + _items.add(shapetype); + _qnames.add(QNAME_SHAPE_TYPE); + } + + protected CTShape newCommentShape(){ + CTShape shape = CTShape.Factory.newInstance(); + shape.setId("_x0000_s" + (++_shapeId)); + shape.setType("#" + _shapeTypeId); + shape.setStyle("position:absolute; visibility:hidden"); + shape.setFillcolor("#ffffe1"); + shape.setInsetmode(STInsetMode.AUTO); + shape.addNewFill().setColor("#ffffe1"); + CTShadow shadow = shape.addNewShadow(); + shadow.setOn(STTrueFalse.T); + shadow.setColor("black"); + shadow.setObscured(STTrueFalse.T); + shape.addNewPath().setConnecttype(STConnectType.NONE); + shape.addNewTextbox().setStyle("mso-direction-alt:auto"); + CTClientData cldata = shape.addNewClientData(); + cldata.setObjectType(STObjectType.NOTE); + cldata.addNewMoveWithCells(); + cldata.addNewSizeWithCells(); + cldata.addNewAnchor().setStringValue("1, 15, 0, 2, 3, 15, 3, 16"); + cldata.addNewAutoFill().setStringValue("False"); + cldata.addNewRow().setBigIntegerValue(new BigInteger("0")); + cldata.addNewColumn().setBigIntegerValue(new BigInteger("0")); + _items.add(shape); + _qnames.add(QNAME_SHAPE); + return shape; + } + + /** + * Find a shape with ClientData of type "NOTE" and the specified row and column + * + * @return the comment shape or null + */ + protected CTShape findCommentShape(int row, int col){ + for(XmlObject itm : _items){ + if(itm instanceof CTShape){ + CTShape sh = (CTShape)itm; + if(sh.sizeOfClientDataArray() > 0){ + CTClientData cldata = sh.getClientDataArray(0); + if(cldata.getObjectType() == STObjectType.NOTE){ + int crow = cldata.getRowArray(0).intValue(); + int ccol = cldata.getColumnArray(0).intValue(); + if(crow == row && ccol == col) { + return sh; + } + } + } + } + } + return null; + } + + protected boolean removeCommentShape(int row, int col){ + CTShape shape = findCommentShape(row, col); + return shape != null && _items.remove(shape); + } +} \ No newline at end of file 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 6555d65938..b423afd73b 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java @@ -25,10 +25,7 @@ import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.POIXMLDocumentPart; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Comment; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.usermodel.XSSFComment; import org.apache.poi.xssf.usermodel.XSSFRichTextString; @@ -47,32 +44,24 @@ public class TestCommentsTable extends TestCase { private static final String TEST_A1_TEXT = "test A1 text"; private static final String TEST_AUTHOR = "test author"; - public void testfindAuthor() throws Exception { - CommentsDocument doc = CommentsDocument.Factory.newInstance(); - doc.setComments(CTComments.Factory.newInstance()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - doc.save(out, POIXMLDocumentPart.DEFAULT_XML_OPTIONS); + public void testFindAuthor() throws Exception { CommentsTable sheetComments = new CommentsTable(); - sheetComments.readFrom(new ByteArrayInputStream(out.toByteArray())); - - assertEquals(0, sheetComments.findAuthor(TEST_AUTHOR)); - assertEquals(1, sheetComments.findAuthor("another author")); - assertEquals(0, sheetComments.findAuthor(TEST_AUTHOR)); - assertEquals(2, sheetComments.findAuthor("YAA")); - assertEquals(1, sheetComments.findAuthor("another author")); + assertEquals(1, sheetComments.getNumberOfAuthors()); + assertEquals(0, sheetComments.findAuthor("")); + assertEquals("", sheetComments.getAuthor(0)); + + assertEquals(1, sheetComments.findAuthor(TEST_AUTHOR)); + assertEquals(2, sheetComments.findAuthor("another author")); + assertEquals(1, sheetComments.findAuthor(TEST_AUTHOR)); + assertEquals(3, sheetComments.findAuthor("YAA")); + assertEquals(2, sheetComments.findAuthor("another author")); } public void testGetCellComment() throws Exception { - CommentsDocument doc = CommentsDocument.Factory.newInstance(); - doc.setComments(CTComments.Factory.newInstance()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - doc.save(out, POIXMLDocumentPart.DEFAULT_XML_OPTIONS); CommentsTable sheetComments = new CommentsTable(); - sheetComments.readFrom(new ByteArrayInputStream(out.toByteArray())); - CTComments comments = sheetComments.getCTComments(); - CTCommentList commentList = comments.addNewCommentList(); + CTCommentList commentList = comments.getCommentList(); // Create 2 comments for A1 and A" cells CTComment comment0 = commentList.insertNewComment(0); @@ -87,86 +76,14 @@ public class TestCommentsTable extends TestCase { comment1.setText(ctrst1); // test finding the right comment for a cell - assertEquals(TEST_A1_TEXT, sheetComments.findCellComment("A1").getString().getString()); - assertEquals(TEST_A1_TEXT, sheetComments.findCellComment(0, 0).getString().getString()); - assertEquals(TEST_A2_TEXT, sheetComments.findCellComment("A2").getString().getString()); - assertEquals(TEST_A2_TEXT, sheetComments.findCellComment(1, 0).getString().getString()); - assertNull(sheetComments.findCellComment("A3")); - assertNull(sheetComments.findCellComment(2, 0)); - } - - public void testAddCellComment() throws Exception { - CommentsDocument doc = CommentsDocument.Factory.newInstance(); - doc.setComments(CTComments.Factory.newInstance()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - doc.save(out, POIXMLDocumentPart.DEFAULT_XML_OPTIONS); - CommentsTable sheetComments = new CommentsTable(); - sheetComments.readFrom(new ByteArrayInputStream(out.toByteArray())); - - CTCommentList commentList = sheetComments.getCTComments().addNewCommentList(); - assertEquals(0, commentList.sizeOfCommentArray()); - - XSSFComment comment = sheetComments.addComment(); - comment.setAuthor("test A1 author"); - comment.setRow(0); - comment.setColumn((short)0); - - assertEquals(1, commentList.sizeOfCommentArray()); - assertEquals("test A1 author", sheetComments.getAuthor(commentList.getCommentArray(0).getAuthorId())); - assertEquals("test A1 author", comment.getAuthor()); - - // Change the author, check it updates - comment.setAuthor("Another Author"); - assertEquals(1, commentList.sizeOfCommentArray()); - assertEquals("Another Author", comment.getAuthor()); + assertSame(comment0, sheetComments.getCTComment("A1")); + assertSame(comment1, sheetComments.getCTComment("A2")); + assertNull(sheetComments.getCTComment("A3")); } - public void testDontLoostNewLines() { - XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithVariousData.xlsx"); - List rels = wb.getSheetAt(0).getRelations(); - CommentsTable ct = null; - for(POIXMLDocumentPart p : rels) { - if(p instanceof CommentsTable){ - ct = (CommentsTable)p; - break; - } - } - if (ct == null) { - throw new AssertionFailedError("didn't find comments table"); - } - assertEquals(2, ct.getNumberOfComments()); - assertEquals(1, ct.getNumberOfAuthors()); - - XSSFComment comment = ct.findCellComment("C5"); - - assertEquals("Nick Burch", comment.getAuthor()); - assertEquals("Nick Burch:\nThis is a comment", comment.getString().getString()); - - wb = XSSFTestDataSamples.writeOutAndReadBack(wb); - rels = wb.getSheetAt(0).getRelations(); - ct = null; - for(POIXMLDocumentPart p : rels) { - if(p instanceof CommentsTable){ - ct = (CommentsTable)p; - break; - } - } - if (ct == null) { - throw new AssertionFailedError("didn't find comments table"); - } - - assertEquals(2, ct.getNumberOfComments()); - assertEquals(1, ct.getNumberOfAuthors()); - - comment = ct.findCellComment("C5"); - - assertEquals("Nick Burch", comment.getAuthor()); - - assertEquals("Nick Burch:\nThis is a comment", comment.getString().getString()); - } public void testExisting() { - XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("WithVariousData.xlsx"); + Workbook workbook = XSSFTestDataSamples.openSampleWorkbook("WithVariousData.xlsx"); Sheet sheet1 = workbook.getSheetAt(0); Sheet sheet2 = workbook.getSheetAt(1); @@ -238,7 +155,7 @@ public class TestCommentsTable extends TestCase { assertEquals("Also POI", sheet2.getRow(2).getCell(1).getCellComment().getAuthor()); - assertEquals("Nick Burch:\nThis is a comment", + assertEquals("Hello!", sheet1.getRow(4).getCell(2).getCellComment().getString().getString()); } @@ -276,4 +193,37 @@ public class TestCommentsTable extends TestCase { // Todo - check text too, once bug fixed } + + public void testRemoveComment() throws Exception { + CommentsTable sheetComments = new CommentsTable(); + CTComment a1 = sheetComments.newComment(); + a1.setRef("A1"); + CTComment a2 = sheetComments.newComment(); + a2.setRef("A2"); + CTComment a3 = sheetComments.newComment(); + a3.setRef("A3"); + + assertSame(a1, sheetComments.getCTComment("A1")); + assertSame(a2, sheetComments.getCTComment("A2")); + assertSame(a3, sheetComments.getCTComment("A3")); + assertEquals(3, sheetComments.getNumberOfComments()); + + assertTrue(sheetComments.removeComment("A1")); + assertEquals(2, sheetComments.getNumberOfComments()); + assertNull(sheetComments.getCTComment("A1")); + assertSame(a2, sheetComments.getCTComment("A2")); + assertSame(a3, sheetComments.getCTComment("A3")); + + assertTrue(sheetComments.removeComment("A2")); + assertEquals(1, sheetComments.getNumberOfComments()); + assertNull(sheetComments.getCTComment("A1")); + assertNull(sheetComments.getCTComment("A2")); + assertSame(a3, sheetComments.getCTComment("A3")); + + assertTrue(sheetComments.removeComment("A3")); + assertEquals(0, sheetComments.getNumberOfComments()); + assertNull(sheetComments.getCTComment("A1")); + assertNull(sheetComments.getCTComment("A2")); + assertNull(sheetComments.getCTComment("A3")); + } } 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 c64aa405e0..34edb48f3f 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFComment.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFComment.java @@ -18,171 +18,177 @@ package org.apache.poi.xssf.usermodel; import org.apache.poi.hssf.usermodel.HSSFRichTextString; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Comment; -import org.apache.poi.ss.usermodel.RichTextString; -import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.XSSFTestDataSamples; +import org.apache.poi.xssf.XSSFITestDataProvider; import org.apache.poi.POIXMLDocumentPart; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTAuthors; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComments; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CommentsDocument; +import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; import junit.framework.TestCase; +import junit.framework.AssertionFailedError; import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.List; +import schemasMicrosoftComVml.CTShape; -public class TestXSSFComment extends TestCase { + +public class TestXSSFComment extends BaseTestCellComment { private static final String TEST_RICHTEXTSTRING = "test richtextstring"; - private static final String TEST_AUTHOR = "test_author"; - public void testConstructors() { - CommentsTable sheetComments = new CommentsTable(); - XSSFComment comment = sheetComments.addComment(); - assertNotNull(comment); + @Override + protected XSSFITestDataProvider getTestDataProvider(){ + return XSSFITestDataProvider.getInstance(); + } - CTComment ctComment = CTComment.Factory.newInstance(); - XSSFComment comment2 = new XSSFComment(sheetComments, ctComment); - assertNotNull(comment2); + /** + * test that we can read cell comments from an existing workbook. + */ + public void testReadComments() { + readComments("SimpleWithComments.xlsx"); } - public void testGetColumn() { - CommentsTable sheetComments = new CommentsTable(); - CTComment ctComment = CTComment.Factory.newInstance(); - ctComment.setRef("A1"); - XSSFComment comment = new XSSFComment(sheetComments, ctComment); - assertNotNull(comment); - assertEquals(0, comment.getColumn()); - ctComment.setRef("C10"); - assertEquals(2, comment.getColumn()); + /** + * test that we can modify existing cell comments + */ + public void testModifyComments() throws IOException { + modifyComments("SimpleWithComments.xlsx"); } - public void testGetRow() { - CommentsTable sheetComments = new CommentsTable(); - CTComment ctComment = CTComment.Factory.newInstance(); - ctComment.setRef("A1"); - XSSFComment comment = new XSSFComment(sheetComments, ctComment); - assertNotNull(comment); - assertEquals(0, comment.getRow()); - ctComment.setRef("C10"); - assertEquals(9, comment.getRow()); + public void testDeleteComments() throws Exception { + deleteComments("SimpleWithComments.xlsx"); } - public void testGetAuthor() throws Exception { - CommentsDocument doc = CommentsDocument.Factory.newInstance(); - CTComments ctComments = CTComments.Factory.newInstance(); - CTComment ctComment = ctComments.addNewCommentList().addNewComment(); - CTAuthors ctAuthors = ctComments.addNewAuthors(); - ctAuthors.insertAuthor(0, TEST_AUTHOR); - ctComment.setAuthorId(0); - doc.setComments(ctComments); + /** + * test properties of a newly constructed comment + */ + public void testConstructor() { + CommentsTable sheetComments = new CommentsTable(); + assertNotNull(sheetComments.getCTComments().getCommentList()); + assertNotNull(sheetComments.getCTComments().getAuthors()); + assertEquals(1, sheetComments.getCTComments().getAuthors().sizeOfAuthorArray()); + assertEquals(1, sheetComments.getNumberOfAuthors()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - doc.save(out, POIXMLDocumentPart.DEFAULT_XML_OPTIONS); + CTComment ctComment = sheetComments.newComment(); + CTShape vmlShape = CTShape.Factory.newInstance(); - CommentsTable sheetComments = new CommentsTable(); - sheetComments.readFrom(new ByteArrayInputStream(out.toByteArray())); - XSSFComment comment = new XSSFComment(sheetComments, ctComment); - assertEquals(TEST_AUTHOR, comment.getAuthor()); + XSSFComment comment = new XSSFComment(sheetComments, ctComment, vmlShape); + assertEquals(null, comment.getString()); + assertEquals(0, comment.getRow()); + assertEquals(0, comment.getColumn()); + assertEquals("", comment.getAuthor()); + assertEquals(false, comment.isVisible()); } - public void testSetColumn() { + public void testGetSetCol() { CommentsTable sheetComments = new CommentsTable(); - CTComment ctComment = CTComment.Factory.newInstance(); - XSSFComment comment = new XSSFComment(sheetComments, ctComment); - comment.setColumn((short)3); - assertEquals(3, comment.getColumn()); - assertEquals(3, (new CellReference(ctComment.getRef()).getCol())); - assertEquals("D1", ctComment.getRef()); - - comment.setColumn((short)13); - assertEquals(13, comment.getColumn()); + XSSFVMLDrawing vml = new XSSFVMLDrawing(); + CTComment ctComment = sheetComments.newComment(); + CTShape vmlShape = vml.newCommentShape(); + + XSSFComment comment = new XSSFComment(sheetComments, ctComment, vmlShape); + comment.setColumn(1); + assertEquals(1, comment.getColumn()); + assertEquals(1, new CellReference(ctComment.getRef()).getCol()); + assertEquals(1, vmlShape.getClientDataArray(0).getColumnArray(0).intValue()); + + comment.setColumn(5); + assertEquals(5, comment.getColumn()); + assertEquals(5, new CellReference(ctComment.getRef()).getCol()); + assertEquals(5, vmlShape.getClientDataArray(0).getColumnArray(0).intValue()); } - public void testSetRow() { + public void testGetSetRow() { CommentsTable sheetComments = new CommentsTable(); - CTComment ctComment = CTComment.Factory.newInstance(); - XSSFComment comment = new XSSFComment(sheetComments, ctComment); - comment.setRow(20); - assertEquals(20, comment.getRow()); - assertEquals(20, (new CellReference(ctComment.getRef()).getRow())); - assertEquals("A21", ctComment.getRef()); - - comment.setRow(19); - assertEquals(19, comment.getRow()); + XSSFVMLDrawing vml = new XSSFVMLDrawing(); + CTComment ctComment = sheetComments.newComment(); + CTShape vmlShape = vml.newCommentShape(); + + XSSFComment comment = new XSSFComment(sheetComments, ctComment, vmlShape); + comment.setRow(1); + assertEquals(1, comment.getRow()); + assertEquals(1, new CellReference(ctComment.getRef()).getRow()); + assertEquals(1, vmlShape.getClientDataArray(0).getRowArray(0).intValue()); + + comment.setRow(5); + assertEquals(5, comment.getRow()); + assertEquals(5, new CellReference(ctComment.getRef()).getRow()); + assertEquals(5, vmlShape.getClientDataArray(0).getRowArray(0).intValue()); } - public void testSetAuthor() { - CommentsTable sheetComments = new CommentsTable(); - CTComment ctComment = CTComment.Factory.newInstance(); - XSSFComment comment = new XSSFComment(sheetComments, ctComment); - comment.setAuthor(TEST_AUTHOR); - assertEquals(TEST_AUTHOR, comment.getAuthor()); + public void testSetString() { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sh = wb.createSheet(); + XSSFComment comment = sh.createDrawingPatriarch().createCellComment(new XSSFClientAnchor()); + + //passing HSSFRichTextString is incorrect + try { + comment.setString(new HSSFRichTextString(TEST_RICHTEXTSTRING)); + fail("expected exception"); + } catch (IllegalArgumentException e){ + ; + } + + //simple string argument + comment.setString(TEST_RICHTEXTSTRING); + assertEquals(TEST_RICHTEXTSTRING, comment.getString().getString()); + + //if the text is already set, it should be overridden, not added twice! + comment.setString(TEST_RICHTEXTSTRING); + + CTComment ctComment = comment.getCTComment(); + XmlObject[] obj = ctComment.selectPath( + "declare namespace w='http://schemas.openxmlformats.org/spreadsheetml/2006/main' .//w:text"); + assertEquals(1, obj.length); + assertEquals(TEST_RICHTEXTSTRING, comment.getString().getString()); + + //sequential call of comment.getString() should return the same XSSFRichTextString object + assertSame(comment.getString(), comment.getString()); + + XSSFRichTextString richText = new XSSFRichTextString(TEST_RICHTEXTSTRING); + XSSFFont font1 = wb.createFont(); + font1.setFontName("Tahoma"); + font1.setFontHeight(8.5); + font1.setItalic(true); + font1.setColor(IndexedColors.BLUE_GREY.getIndex()); + richText.applyFont(0, 5, font1); + + //check the low-level stuff + comment.setString(richText); + obj = ctComment.selectPath( + "declare namespace w='http://schemas.openxmlformats.org/spreadsheetml/2006/main' .//w:text"); + assertEquals(1, obj.length); + assertSame(comment.getString(), richText); + //check that the rich text is set in the comment + CTRPrElt rPr = richText.getCTRst().getRArray(0).getRPr(); + assertEquals(true, rPr.getIArray()[0].getVal()); + assertEquals(8.5, rPr.getSzArray()[0].getVal()); + assertEquals(IndexedColors.BLUE_GREY.getIndex(), rPr.getColorArray()[0].getIndexed()); + assertEquals("Tahoma", rPr.getRFontArray()[0].getVal()); } - public void testSetString() { + public void testAuthor() { CommentsTable sheetComments = new CommentsTable(); - CTComment ctComment = CTComment.Factory.newInstance(); - XSSFComment comment = new XSSFComment(sheetComments, ctComment); - RichTextString richTextString = new HSSFRichTextString(TEST_RICHTEXTSTRING); - comment.setString(richTextString); - assertEquals(TEST_RICHTEXTSTRING, ctComment.getText().getT()); + CTComment ctComment = sheetComments.newComment(); + + assertEquals(1, sheetComments.getNumberOfAuthors()); + XSSFComment comment = new XSSFComment(sheetComments, ctComment, null); + assertEquals("", comment.getAuthor()); + comment.setAuthor("Apache POI"); + assertEquals("Apache POI", comment.getAuthor()); + assertEquals(2, sheetComments.getNumberOfAuthors()); + comment.setAuthor("Apache POI"); + assertEquals(2, sheetComments.getNumberOfAuthors()); + comment.setAuthor(""); + assertEquals("", comment.getAuthor()); + assertEquals(2, sheetComments.getNumberOfAuthors()); } - /** - * Tests that we can add comments to a new - * file, save, load, and still see them - * @throws Exception - */ - public void testCreateSave() { - XSSFWorkbook wb = new XSSFWorkbook(); - XSSFSheet s1 = wb.createSheet(); - Row r1 = s1.createRow(0); - Cell r1c1 = r1.createCell(0); - r1c1.setCellValue(2.2); - - assertEquals(0, s1.getNumberOfComments()); - - Comment c1 = s1.createComment(); - c1.setAuthor("Author 1"); - c1.setString(new XSSFRichTextString("Comment 1")); - r1c1.setCellComment(c1); - - assertEquals(1, s1.getNumberOfComments()); - - // Save and re-load - wb = XSSFTestDataSamples.writeOutAndReadBack(wb); - s1 = wb.getSheetAt(0); - - assertEquals(1, s1.getNumberOfComments()); - assertNotNull(s1.getRow(0).getCell(0).getCellComment()); - assertEquals("Author 1", s1.getRow(0).getCell(0).getCellComment().getAuthor()); - assertEquals("Comment 1", s1.getRow(0).getCell(0).getCellComment().getString().getString()); - - // Now add an orphaned one - Comment c2 = s1.createComment(); - c2.setAuthor("Author 2"); - c2.setString(new XSSFRichTextString("Second Comment")); - c2.setRow(0); - c2.setColumn((short)1); - assertEquals(2, s1.getNumberOfComments()); - - // Save and re-load - - wb = XSSFTestDataSamples.writeOutAndReadBack(wb); - s1 = wb.getSheetAt(0); - - assertEquals(2, s1.getNumberOfComments()); - assertNotNull(s1.getCellComment(0, 0)); - assertNotNull(s1.getCellComment(0, 1)); - - assertEquals("Author 1", s1.getCellComment(0, 0).getAuthor()); - assertEquals("Author 2", s1.getCellComment(0, 1).getAuthor()); - } + } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java index d0110f410b..87ce14e922 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java @@ -177,7 +177,7 @@ public class TestXSSFSheet extends BaseTestSheet { XSSFComment comment = sheet.createComment(); Cell cell = sheet.createRow(0).createCell((short) 0); - CommentsTable comments = sheet.getCommentsTable(); + CommentsTable comments = sheet.getCommentsTable(false); CTComments ctComments = comments.getCTComments(); sheet.setCellComment("A1", comment); @@ -843,4 +843,42 @@ public class TestXSSFSheet extends BaseTestSheet { assertFalse(sheet.isColumnHidden(4)); assertFalse(sheet.isColumnHidden(5)); } + + public void testCommentsTable() { + XSSFWorkbook workbook = new XSSFWorkbook(); + XSSFSheet sheet1 = workbook.createSheet(); + CommentsTable comment1 = sheet1.getCommentsTable(false); + assertNull(comment1); + + comment1 = sheet1.getCommentsTable(true); + assertNotNull(comment1); + assertEquals("/xl/comments1.xml", comment1.getPackageRelationship().getTargetURI().toString()); + + assertSame(comment1, sheet1.getCommentsTable(true)); + + //second sheet + XSSFSheet sheet2 = workbook.createSheet(); + CommentsTable comment2 = sheet2.getCommentsTable(false); + assertNull(comment2); + + comment2 = sheet2.getCommentsTable(true); + assertNotNull(comment2); + + assertSame(comment2, sheet2.getCommentsTable(true)); + assertEquals("/xl/comments2.xml", comment2.getPackageRelationship().getTargetURI().toString()); + + //comment1 and comment2 are different objects + assertNotSame(comment1, comment2); + + //now test against a workbook containing cell comments + workbook = XSSFTestDataSamples.openSampleWorkbook("WithMoreVariousData.xlsx"); + sheet1 = workbook.getSheetAt(0); + comment1 = sheet1.getCommentsTable(true); + assertNotNull(comment1); + assertEquals("/xl/comments1.xml", comment1.getPackageRelationship().getTargetURI().toString()); + assertSame(comment1, sheet1.getCommentsTable(true)); + + + } + } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFVMLDrawing.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFVMLDrawing.java new file mode 100755 index 0000000000..538d4a11b1 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFVMLDrawing.java @@ -0,0 +1,139 @@ +/* ==================================================================== + 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.xssf.usermodel; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.poi.POIDataSamples; +import org.apache.xmlbeans.XmlObject; +import schemasMicrosoftComVml.*; +import schemasMicrosoftComOfficeOffice.CTShapeLayout; +import schemasMicrosoftComOfficeOffice.STConnectType; +import schemasMicrosoftComOfficeOffice.STInsetMode; +import schemasMicrosoftComOfficeExcel.CTClientData; +import schemasMicrosoftComOfficeExcel.STObjectType; + +/** + * @author Yegor Kozlov + */ +public class TestXSSFVMLDrawing extends TestCase { + + public void testNew() throws Exception { + XSSFVMLDrawing vml = new XSSFVMLDrawing(); + List items = vml.getItems(); + assertEquals(2, items.size()); + assertTrue(items.get(0) instanceof CTShapeLayout); + CTShapeLayout layout = (CTShapeLayout)items.get(0); + assertEquals(STExt.EDIT, layout.getExt()); + assertEquals(STExt.EDIT, layout.getIdmap().getExt()); + assertEquals("1", layout.getIdmap().getData()); + + assertTrue(items.get(1) instanceof CTShapetype); + CTShapetype type = (CTShapetype)items.get(1); + assertEquals("21600,21600", type.getCoordsize()); + assertEquals(202.0f, type.getSpt()); + assertEquals("m,l,21600r21600,l21600,xe", type.getPath2()); + assertEquals("_xssf_cell_comment", type.getId()); + assertEquals(STTrueFalse.T, type.getPathArray(0).getGradientshapeok()); + assertEquals(STConnectType.RECT, type.getPathArray(0).getConnecttype()); + + CTShape shape = vml.newCommentShape(); + assertEquals(3, items.size()); + assertSame(items.get(2), shape); + assertEquals("#_xssf_cell_comment", shape.getType()); + assertEquals("position:absolute; visibility:hidden", shape.getStyle()); + assertEquals("#ffffe1", shape.getFillcolor()); + assertEquals(STInsetMode.AUTO, shape.getInsetmode()); + assertEquals("#ffffe1", shape.getFillArray(0).getColor()); + CTShadow shadow = shape.getShadowArray(0); + assertEquals(STTrueFalse.T, shadow.getOn()); + assertEquals("black", shadow.getColor()); + assertEquals(STTrueFalse.T, shadow.getObscured()); + assertEquals(STConnectType.NONE, shape.getPathArray(0).getConnecttype()); + assertEquals("mso-direction-alt:auto", shape.getTextboxArray(0).getStyle()); + CTClientData cldata = shape.getClientDataArray(0); + assertEquals(STObjectType.NOTE, cldata.getObjectType()); + assertEquals(1, cldata.sizeOfMoveWithCellsArray()); + assertEquals(1, cldata.sizeOfSizeWithCellsArray()); + assertEquals("1, 15, 0, 2, 3, 15, 3, 16", cldata.getAnchorArray(0)); + assertEquals("False", cldata.getAutoFillArray(0).toString()); + assertEquals(0, cldata.getRowArray(0).intValue()); + assertEquals(0, cldata.getColumnArray(0).intValue()); + + //serialize and read again + ByteArrayOutputStream out = new ByteArrayOutputStream(); + vml.write(out); + + XSSFVMLDrawing vml2 = new XSSFVMLDrawing(); + vml2.read(new ByteArrayInputStream(out.toByteArray())); + List items2 = vml2.getItems(); + assertEquals(3, items2.size()); + assertTrue(items2.get(0) instanceof CTShapeLayout); + assertTrue(items2.get(1) instanceof CTShapetype); + assertTrue(items2.get(2) instanceof CTShape); + } + + public void testFindCommentShape() throws Exception { + + XSSFVMLDrawing vml = new XSSFVMLDrawing(); + vml.read(POIDataSamples.getSpreadSheetInstance().openResourceAsStream("vmlDrawing1.vml")); + + CTShape sh_a1 = vml.findCommentShape(0, 0); + assertNotNull(sh_a1); + assertEquals("_x0000_s1025", sh_a1.getId()); + + CTShape sh_b1 = vml.findCommentShape(0, 1); + assertNotNull(sh_b1); + assertEquals("_x0000_s1026", sh_b1.getId()); + + CTShape sh_c1 = vml.findCommentShape(0, 2); + assertNull(sh_c1); + + CTShape sh_d1 = vml.newCommentShape(); + assertEquals("_x0000_s1027", sh_d1.getId()); + sh_d1.getClientDataArray(0).setRowArray(0, new BigInteger("0")); + sh_d1.getClientDataArray(0).setColumnArray(0, new BigInteger("3")); + assertSame(sh_d1, vml.findCommentShape(0, 3)); + + //newly created drawing + XSSFVMLDrawing newVml = new XSSFVMLDrawing(); + assertNull(newVml.findCommentShape(0, 0)); + + sh_a1 = newVml.newCommentShape(); + assertEquals("_x0000_s1025", sh_a1.getId()); + sh_a1.getClientDataArray(0).setRowArray(0, new BigInteger("0")); + sh_a1.getClientDataArray(0).setColumnArray(0, new BigInteger("1")); + assertSame(sh_a1, newVml.findCommentShape(0, 1)); + } + + public void testRemoveCommentShape() throws Exception { + XSSFVMLDrawing vml = new XSSFVMLDrawing(); + vml.read(POIDataSamples.getSpreadSheetInstance().openResourceAsStream("vmlDrawing1.vml")); + + CTShape sh_a1 = vml.findCommentShape(0, 0); + assertNotNull(sh_a1); + + assertTrue(vml.removeCommentShape(0, 0)); + assertNull(vml.findCommentShape(0, 0)); + + } +} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java index bb4707b321..0130f9471d 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFComment.java @@ -16,208 +16,45 @@ ==================================================================== */ package org.apache.poi.hssf.usermodel; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import junit.framework.TestCase; - import org.apache.poi.hssf.HSSFTestDataSamples; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Comment; -import org.apache.poi.ss.util.CellReference; +import org.apache.poi.hssf.HSSFITestDataProvider; +import org.apache.poi.ss.usermodel.BaseTestCellComment; /** * Tests TestHSSFCellComment. * * @author Yegor Kozlov */ -public final class TestHSSFComment extends TestCase { - - /** - * Test that we can create cells and add comments to it. - */ - public static void testWriteComments() throws Exception { - String cellText = "Hello, World"; - String commentText = "We can set comments in POI"; - String commentAuthor = "Apache Software Foundation"; - int cellRow = 3; - int cellColumn = 1; - - HSSFWorkbook wb = new HSSFWorkbook(); - - HSSFSheet sheet = wb.createSheet(); - - HSSFCell cell = sheet.createRow(cellRow).createCell(cellColumn); - cell.setCellValue(new HSSFRichTextString(cellText)); - assertNull(cell.getCellComment()); - - HSSFPatriarch patr = sheet.createDrawingPatriarch(); - HSSFClientAnchor anchor = new HSSFClientAnchor(); - anchor.setAnchor( (short)4, 2, 0, 0, (short) 6, 5, 0, 0); - HSSFComment comment = patr.createComment(anchor); - HSSFRichTextString string1 = new HSSFRichTextString(commentText); - comment.setString(string1); - comment.setAuthor(commentAuthor); - cell.setCellComment(comment); - if (false) { - // TODO - the following line should break this test, but it doesn't - cell.removeCellComment(); - } - - //verify our settings - assertEquals(HSSFSimpleShape.OBJECT_TYPE_COMMENT, comment.getShapeType()); - assertEquals(commentAuthor, comment.getAuthor()); - assertEquals(commentText, comment.getString().getString()); - assertEquals(cellRow, comment.getRow()); - assertEquals(cellColumn, comment.getColumn()); - - //serialize the workbook and read it again - ByteArrayOutputStream out = new ByteArrayOutputStream(); - wb.write(out); - out.close(); +public final class TestHSSFComment extends BaseTestCellComment { - wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray())); - sheet = wb.getSheetAt(0); - cell = sheet.getRow(cellRow).getCell(cellColumn); - comment = cell.getCellComment(); - - assertNotNull(comment); - assertEquals(HSSFSimpleShape.OBJECT_TYPE_COMMENT, comment.getShapeType()); - assertEquals(commentAuthor, comment.getAuthor()); - assertEquals(commentText, comment.getString().getString()); - assertEquals(cellRow, comment.getRow()); - assertEquals(cellColumn, comment.getColumn()); - - - // Change slightly, and re-test - comment.setString(new HSSFRichTextString("New Comment Text")); - - out = new ByteArrayOutputStream(); - wb.write(out); - out.close(); - - wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray())); - sheet = wb.getSheetAt(0); - cell = sheet.getRow(cellRow).getCell(cellColumn); - comment = cell.getCellComment(); + @Override + protected HSSFITestDataProvider getTestDataProvider(){ + return HSSFITestDataProvider.getInstance(); + } - assertNotNull(comment); + public static void testDefaultShapeType() throws Exception { + HSSFComment comment = new HSSFComment((HSSFShape)null, (HSSFAnchor)null); assertEquals(HSSFSimpleShape.OBJECT_TYPE_COMMENT, comment.getShapeType()); - assertEquals(commentAuthor, comment.getAuthor()); - assertEquals("New Comment Text", comment.getString().getString()); - assertEquals(cellRow, comment.getRow()); - assertEquals(cellColumn, comment.getColumn()); } /** * test that we can read cell comments from an existing workbook. */ - public static void testReadComments() { - - HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SimpleWithComments.xls"); - - HSSFSheet sheet = wb.getSheetAt(0); - - HSSFCell cell; - HSSFRow row; - HSSFComment comment; - - for (int rownum = 0; rownum < 3; rownum++) { - row = sheet.getRow(rownum); - cell = row.getCell(0); - comment = cell.getCellComment(); - assertNull("Cells in the first column are not commented", comment); - assertNull(sheet.getCellComment(rownum, 0)); - } - - for (int rownum = 0; rownum < 3; rownum++) { - row = sheet.getRow(rownum); - cell = row.getCell(1); - comment = cell.getCellComment(); - assertNotNull("Cells in the second column have comments", comment); - assertNotNull("Cells in the second column have comments", sheet.getCellComment(rownum, 1)); - - assertEquals(HSSFSimpleShape.OBJECT_TYPE_COMMENT, comment.getShapeType()); - assertEquals("Yegor Kozlov", comment.getAuthor()); - assertFalse("cells in the second column have not empyy notes", - "".equals(comment.getString().getString())); - assertEquals(rownum, comment.getRow()); - assertEquals(cell.getColumnIndex(), comment.getColumn()); - } - } + public void testReadComments() { + readComments("SimpleWithComments.xls"); + } /** * test that we can modify existing cell comments */ - public static void testModifyComments() throws IOException { - - HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SimpleWithComments.xls"); - - HSSFSheet sheet = wb.getSheetAt(0); - - HSSFCell cell; - HSSFRow row; - HSSFComment comment; - - for (int rownum = 0; rownum < 3; rownum++) { - row = sheet.getRow(rownum); - cell = row.getCell(1); - comment = cell.getCellComment(); - comment.setAuthor("Mofified["+rownum+"] by Yegor"); - comment.setString(new HSSFRichTextString("Modified comment at row " + rownum)); - } - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - wb.write(out); - out.close(); - - wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray())); - sheet = wb.getSheetAt(0); - - for (int rownum = 0; rownum < 3; rownum++) { - row = sheet.getRow(rownum); - cell = row.getCell(1); - comment = cell.getCellComment(); - - assertEquals("Mofified["+rownum+"] by Yegor", comment.getAuthor()); - assertEquals("Modified comment at row " + rownum, comment.getString().getString()); - } - - } + public void testModifyComments() throws IOException { + modifyComments("SimpleWithComments.xls"); + } public void testDeleteComments() throws Exception { - HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SimpleWithComments.xls"); - HSSFSheet sheet = wb.getSheetAt(0); - - // Zap from rows 1 and 3 - assertNotNull(sheet.getRow(0).getCell(1).getCellComment()); - assertNotNull(sheet.getRow(1).getCell(1).getCellComment()); - assertNotNull(sheet.getRow(2).getCell(1).getCellComment()); - - sheet.getRow(0).getCell(1).removeCellComment(); - sheet.getRow(2).getCell(1).setCellComment(null); - - // Check gone so far - assertNull(sheet.getRow(0).getCell(1).getCellComment()); - assertNotNull(sheet.getRow(1).getCell(1).getCellComment()); - assertNull(sheet.getRow(2).getCell(1).getCellComment()); - - // Save and re-load - ByteArrayOutputStream out = new ByteArrayOutputStream(); - wb.write(out); - out.close(); - wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray())); - - // Check - assertNull(sheet.getRow(0).getCell(1).getCellComment()); - assertNotNull(sheet.getRow(1).getCell(1).getCellComment()); - assertNull(sheet.getRow(2).getCell(1).getCellComment()); - -// FileOutputStream fout = new FileOutputStream("/tmp/c.xls"); -// wb.write(fout); -// fout.close(); + deleteComments("SimpleWithComments.xls"); } /** diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestCellComment.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestCellComment.java new file mode 100644 index 0000000000..f55767115d --- /dev/null +++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestCellComment.java @@ -0,0 +1,246 @@ +/* ==================================================================== + 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.ss.usermodel; + +import junit.framework.TestCase; + +import org.apache.poi.ss.ITestDataProvider; + +/** + * Common superclass for testing implementatiosn of + * {@link Comment} + */ +public abstract class BaseTestCellComment extends TestCase { + + protected abstract ITestDataProvider getTestDataProvider(); + + public final void testFind() { + Workbook book = getTestDataProvider().createWorkbook(); + Sheet sheet = book.createSheet(); + assertNull(sheet.getCellComment(0, 0)); + + Row row = sheet.createRow(0); + Cell cell = row.createCell(0); + assertNull(sheet.getCellComment(0, 0)); + assertNull(cell.getCellComment()); + } + + public final void testCreate() throws Exception { + String cellText = "Hello, World"; + String commentText = "We can set comments in POI"; + String commentAuthor = "Apache Software Foundation"; + int cellRow = 3; + int cellColumn = 1; + + Workbook wb = getTestDataProvider().createWorkbook(); + CreationHelper factory = wb.getCreationHelper(); + + Sheet sheet = wb.createSheet(); + assertNull(sheet.getCellComment(cellRow, cellColumn)); + + Cell cell = sheet.createRow(cellRow).createCell(cellColumn); + cell.setCellValue(factory.createRichTextString(cellText)); + assertNull(cell.getCellComment()); + assertNull(sheet.getCellComment(cellRow, cellColumn)); + + Drawing patr = sheet.createDrawingPatriarch(); + ClientAnchor anchor = factory.createClientAnchor(); + anchor.setCol1(2); + anchor.setCol2(5); + anchor.setRow1(1); + anchor.setRow2(2); + Comment comment = patr.createCellComment(anchor); + assertFalse(comment.isVisible()); + comment.setVisible(true); + assertTrue(comment.isVisible()); + RichTextString string1 = factory.createRichTextString(commentText); + comment.setString(string1); + comment.setAuthor(commentAuthor); + cell.setCellComment(comment); + assertNotNull(cell.getCellComment()); + assertNotNull(sheet.getCellComment(cellRow, cellColumn)); + + //verify our settings + assertEquals(commentAuthor, comment.getAuthor()); + assertEquals(commentText, comment.getString().getString()); + assertEquals(cellRow, comment.getRow()); + assertEquals(cellColumn, comment.getColumn()); + + wb = getTestDataProvider().writeOutAndReadBack(wb); + sheet = wb.getSheetAt(0); + cell = sheet.getRow(cellRow).getCell(cellColumn); + comment = cell.getCellComment(); + + assertNotNull(comment); + assertEquals(commentAuthor, comment.getAuthor()); + assertEquals(commentText, comment.getString().getString()); + assertEquals(cellRow, comment.getRow()); + assertEquals(cellColumn, comment.getColumn()); + assertTrue(comment.isVisible()); + + // Change slightly, and re-test + comment.setString(factory.createRichTextString("New Comment Text")); + comment.setVisible(false); + + wb = getTestDataProvider().writeOutAndReadBack(wb); + + sheet = wb.getSheetAt(0); + cell = sheet.getRow(cellRow).getCell(cellColumn); + comment = cell.getCellComment(); + + assertNotNull(comment); + assertEquals(commentAuthor, comment.getAuthor()); + assertEquals("New Comment Text", comment.getString().getString()); + assertEquals(cellRow, comment.getRow()); + assertEquals(cellColumn, comment.getColumn()); + assertFalse(comment.isVisible()); + } + + /** + * test that we can read cell comments from an existing workbook. + */ + public void readComments(String sampleFile) { + + Workbook wb = getTestDataProvider().openSampleWorkbook(sampleFile); + + Sheet sheet = wb.getSheetAt(0); + + Cell cell; + Row row; + Comment comment; + + for (int rownum = 0; rownum < 3; rownum++) { + row = sheet.getRow(rownum); + cell = row.getCell(0); + comment = cell.getCellComment(); + assertNull("Cells in the first column are not commented", comment); + assertNull(sheet.getCellComment(rownum, 0)); + } + + for (int rownum = 0; rownum < 3; rownum++) { + row = sheet.getRow(rownum); + cell = row.getCell(1); + comment = cell.getCellComment(); + assertNotNull("Cells in the second column have comments", comment); + assertNotNull("Cells in the second column have comments", sheet.getCellComment(rownum, 1)); + + assertEquals("Yegor Kozlov", comment.getAuthor()); + assertFalse("cells in the second column have not empyy notes", + "".equals(comment.getString().getString())); + assertEquals(rownum, comment.getRow()); + assertEquals(cell.getColumnIndex(), comment.getColumn()); + } + } + + /** + * test that we can modify existing cell comments + */ + public void modifyComments(String sampleFile) { + + Workbook wb = getTestDataProvider().openSampleWorkbook(sampleFile); + CreationHelper factory = wb.getCreationHelper(); + + Sheet sheet = wb.getSheetAt(0); + + Cell cell; + Row row; + Comment comment; + + for (int rownum = 0; rownum < 3; rownum++) { + row = sheet.getRow(rownum); + cell = row.getCell(1); + comment = cell.getCellComment(); + comment.setAuthor("Mofified[" + rownum + "] by Yegor"); + comment.setString(factory.createRichTextString("Modified comment at row " + rownum)); + } + + wb = getTestDataProvider().writeOutAndReadBack(wb); + sheet = wb.getSheetAt(0); + + for (int rownum = 0; rownum < 3; rownum++) { + row = sheet.getRow(rownum); + cell = row.getCell(1); + comment = cell.getCellComment(); + + assertEquals("Mofified[" + rownum + "] by Yegor", comment.getAuthor()); + assertEquals("Modified comment at row " + rownum, comment.getString().getString()); + } + + } + + public void deleteComments(String sampleFile) throws Exception { + Workbook wb = getTestDataProvider().openSampleWorkbook(sampleFile); + Sheet sheet = wb.getSheetAt(0); + + // Zap from rows 1 and 3 + assertNotNull(sheet.getRow(0).getCell(1).getCellComment()); + assertNotNull(sheet.getRow(1).getCell(1).getCellComment()); + assertNotNull(sheet.getRow(2).getCell(1).getCellComment()); + + sheet.getRow(0).getCell(1).removeCellComment(); + sheet.getRow(2).getCell(1).setCellComment(null); + + // Check gone so far + assertNull(sheet.getRow(0).getCell(1).getCellComment()); + assertNotNull(sheet.getRow(1).getCell(1).getCellComment()); + assertNull(sheet.getRow(2).getCell(1).getCellComment()); + + // Save and re-load + wb = getTestDataProvider().writeOutAndReadBack(wb); + sheet = wb.getSheetAt(0); + // Check + assertNull(sheet.getRow(0).getCell(1).getCellComment()); + assertNotNull(sheet.getRow(1).getCell(1).getCellComment()); + assertNull(sheet.getRow(2).getCell(1).getCellComment()); + + } + + /** + * code from the quick guide + */ + public void testQuickGuide(){ + Workbook wb = getTestDataProvider().createWorkbook(); + + CreationHelper factory = wb.getCreationHelper(); + + Sheet sheet = wb.createSheet(); + + Cell cell = sheet.createRow(3).createCell(5); + cell.setCellValue("F4"); + + Drawing drawing = sheet.createDrawingPatriarch(); + + ClientAnchor anchor = factory.createClientAnchor(); + Comment comment = drawing.createCellComment(anchor); + RichTextString str = factory.createRichTextString("Hello, World!"); + comment.setString(str); + comment.setAuthor("Apache POI"); + //assign the comment to the cell + cell.setCellComment(comment); + + wb = getTestDataProvider().writeOutAndReadBack(wb); + sheet = wb.getSheetAt(0); + cell = sheet.getRow(3).getCell(5); + comment = cell.getCellComment(); + assertNotNull(comment); + assertEquals("Hello, World!", comment.getString().getString()); + assertEquals("Apache POI", comment.getAuthor()); + assertEquals(3, comment.getRow()); + assertEquals(5, comment.getColumn()); + } +} \ No newline at end of file diff --git a/test-data/spreadsheet/SimpleWithComments.xlsx b/test-data/spreadsheet/SimpleWithComments.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..bfc6ed353abdcb42808ffdea23bc3a4da36b64b2 GIT binary patch literal 10060 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 SKl{s=fX}cXRHXj-?*9SvRW}>} literal 0 HcmV?d00001 diff --git a/test-data/spreadsheet/vmlDrawing1.vml b/test-data/spreadsheet/vmlDrawing1.vml new file mode 100644 index 0000000000..5253962b3a --- /dev/null +++ b/test-data/spreadsheet/vmlDrawing1.vml @@ -0,0 +1,42 @@ + + + + + + + + + +