net.sf.jett.tag.CommentTag Maven / Gradle / Ivy
package net.sf.jett.tag;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Drawing;
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.xssf.usermodel.XSSFCell;
import net.sf.jett.exception.TagParseException;
import net.sf.jett.model.Block;
import net.sf.jett.model.WorkbookContext;
import net.sf.jett.transform.BlockTransformer;
import net.sf.jett.util.AttributeUtil;
import net.sf.jett.util.SheetUtil;
/**
* A CommentTag
represents a Cell that needs to have an Excel
* Comment attached to it. It controls Comment properties such author, the
* Rich Text string comment, and whether the Comment is initially visible.
*
* This tag uses the POI method createDrawingPatriarch
to
* create an Excel comment on the Cell
on which the tag is
* located. The POI documentation warns of corrupting other "drawings" such as
* charts and "complex" drawings when calling getDrawingPatriarch
* (HSSF code). When testing both .xls and .xlsx template spreadsheets, it
* appears that drawings and charts do get corrupted in .xls spreadsheets, but
* they do NOT get corrupted in .xlsx spreadsheets.
*
*
Attributes:
*
* - Inherits all attributes from {@link BaseTag}.
* - value (required):
RichTextString
* - author (required):
String
* - comment (required):
RichTextString
* - visible (optional):
boolean
*
*
* @author Randy Gettman
* @since 0.2.0
*/
public class CommentTag extends BaseTag
{
/**
* Attribute that specifies the value of the Cell itself after
* transformation.
*/
public static final String ATTR_VALUE = "value";
/**
* Attribute that specifies the author of the Comment to be created.
*/
public static final String ATTR_AUTHOR = "author";
/**
* Attribute that specifies the comment text.
*/
public static final String ATTR_COMMENT = "comment";
/**
* Attribute that specifies whether the comment is initially visible. Even
* if not initially visible, the user can mouseover a cell with a comment to
* view the comment text as a pop-up.
*/
public static final String ATTR_VISIBLE = "visible";
private static final List REQ_ATTRS =
new ArrayList<>(Arrays.asList(ATTR_VALUE, ATTR_AUTHOR, ATTR_COMMENT));
private static final List OPT_ATTRS =
new ArrayList<>(Arrays.asList(ATTR_VISIBLE));
private RichTextString myValue;
private RichTextString myAuthor;
private RichTextString myComment;
private boolean amIVisible;
/**
* Returns this Tag's
name.
* @return This Tag's
name.
*/
@Override
public String getName()
{
return "comment";
}
/**
* Returns a List
of required attribute names.
* @return A List
of required attribute names.
*/
@Override
protected List getRequiredAttributes()
{
List reqAttrs = new ArrayList<>(super.getRequiredAttributes());
reqAttrs.addAll(REQ_ATTRS);
return reqAttrs;
}
/**
* Returns a List
of optional attribute names.
* @return A List
of optional attribute names.
*/
@Override
protected List getOptionalAttributes()
{
List optAttrs = new ArrayList<>(super.getOptionalAttributes());
optAttrs.addAll(OPT_ATTRS);
return optAttrs;
}
/**
* Validates the attributes for this Tag
. This tag must be
* bodiless.
*/
@SuppressWarnings("unchecked")
@Override
public void validateAttributes() throws TagParseException
{
super.validateAttributes();
if (!isBodiless())
throw new TagParseException("Comment tags must not have a body. Comment tag with body found" + getLocation());
TagContext context = getContext();
Map beans = context.getBeans();
Map attributes = getAttributes();
myValue = attributes.get(ATTR_VALUE);
myAuthor = attributes.get(ATTR_AUTHOR);
myComment = attributes.get(ATTR_COMMENT);
amIVisible = AttributeUtil.evaluateBoolean(this, attributes.get(ATTR_VISIBLE), beans, false);
}
/**
* Place the "value" attribute in the cell, and the rest of the
* attributes control the creation of a cell comment.
* @return Whether the first Cell
in the Block
* associated with this Tag
was processed.
*/
@Override
public boolean process()
{
TagContext context = getContext();
Sheet sheet = context.getSheet();
Block block = context.getBlock();
Map beans = context.getBeans();
int left = block.getLeftColNum();
int top = block.getTopRowNum();
// It should exist in this Cell; this Tag was found in it.
Row row = sheet.getRow(top);
Cell cell = row.getCell(left);
WorkbookContext workbookContext = getWorkbookContext();
SheetUtil.setCellValue(workbookContext, cell, myValue);
Drawing drawing = context.getOrCreateDrawing();
CreationHelper helper = sheet.getWorkbook().getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
Object commentString = AttributeUtil.evaluateRichTextStringNotNull(this, myComment, helper, beans, ATTR_COMMENT, "");
String author = AttributeUtil.evaluateStringNotNull(this, myAuthor, beans, ATTR_AUTHOR, "");
int commentLength;
if (commentString instanceof RichTextString)
{
commentLength = ((RichTextString) commentString).length();
}
else
{
commentLength = commentString.toString().length();
}
// Calculate number of rows/cols to fit the comment text. Try adding a
// column, then adding a row, repeatedly, until we are sure that a box of
// that size will hold the comment text.
int rows = 0;
int cols = 0;
double width = 0;
double height = 0;
double fontHeightPoints = (cell instanceof HSSFCell) ?
((HSSFCell) cell).getCellStyle().getFont(sheet.getWorkbook()).getFontHeightInPoints() :
((XSSFCell) cell).getCellStyle().getFont().getFontHeightInPoints();
while (width * height < commentLength)
{
cols++;
width += sheet.getColumnWidth(left + cols) / 256;
if (width * height >= commentLength)
break;
Row r = sheet.getRow(top + rows);
if (r == null)
r = sheet.createRow(top + rows);
height += r.getHeightInPoints() / fontHeightPoints;
rows++;
}
// HSSFComments seem to need an extra row's worth of height.
if (cell instanceof HSSFCell)
rows++;
anchor.setCol1(left);
anchor.setCol2(left + cols);
anchor.setRow1(top);
anchor.setRow2(top + rows);
Comment comment = drawing.createCellComment(anchor);
comment.setAuthor(author);
if (commentString instanceof RichTextString)
{
comment.setString((RichTextString) commentString);
}
else
{
comment.setString(helper.createRichTextString(commentString.toString()));
}
comment.setVisible(amIVisible);
cell.setCellComment(comment);
BlockTransformer transformer = new BlockTransformer();
transformer.transform(context, workbookContext);
return true;
}
}