All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jett.tag.SpanTag 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.HSSFCellStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Color;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;

import net.sf.jett.exception.TagParseException;
import net.sf.jett.model.Block;
import net.sf.jett.model.CellStyleCache;
import net.sf.jett.model.ExcelColor;
import net.sf.jett.transform.BlockTransformer;
import net.sf.jett.util.AttributeUtil;
import net.sf.jett.util.SheetUtil;

/**
 * 

A SpanTag represents a cell or merged region that will span * extra rows and/or extra columns, depending on growth and/or adjustment * factors. If this tag is applied to a cell that is not part of a merged * region, then it may result in the creation of a merged region. If this tag * is applied to a cell that is part of a merged region, then it may result in * the removal of the merged region.

* *
Attributes: *
    *
  • Inherits all attributes from {@link BaseTag}.
  • *
  • factor (optional): int
  • *
  • adjust (optional): int
  • *
  • value (required): RichTextString
  • *
  • expandRight (optional): boolean
  • *
  • fixed (optional): boolean
  • *
* *

Either one or both of the factor and the adjust * attributes must be specified.

* * @author Randy Gettman */ public class SpanTag extends BaseTag { private static final boolean DEBUG = false; /** * Attribute for specifying the growth factor. */ public static final String ATTR_FACTOR = "factor"; /** * Attribute for specifying an adjustment to the size of the merged region. * @since 0.4.0 */ public static final String ATTR_ADJUST = "adjust"; /** * Attribute for forcing "expand right" behavior. (Default is expand down.) */ public static final String ATTR_EXPAND_RIGHT = "expandRight"; /** * Attribute that specifies the value of the cell/merged region. */ public static final String ATTR_VALUE = "value"; /** * Attribute that specifies the value of the cell/merged region. * @since 0.9.1 */ public static final String ATTR_FIXED = "fixed"; private static final List REQ_ATTRS = new ArrayList(Arrays.asList(ATTR_VALUE)); private static final List OPT_ATTRS = new ArrayList(Arrays.asList(ATTR_EXPAND_RIGHT, ATTR_FACTOR, ATTR_ADJUST, ATTR_FIXED)); private int myFactor = 1; private int myAdjust = 0; private RichTextString myValue; private boolean amIFixed; /** * Returns this Tag's name. * @return This Tag's name. */ public String getName() { return "span"; } /** * 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. Some optional * attributes are only valid for bodiless tags, and others are only valid * for tags without bodies. */ public void validateAttributes() { super.validateAttributes(); TagContext context = getContext(); Map beans = context.getBeans(); Map attributes = getAttributes(); Block block = context.getBlock(); if (!isBodiless()) throw new TagParseException("SpanTag: Must be bodiless. SpanTag with body found" + getLocation()); myValue = attributes.get(ATTR_VALUE); List atLeastOne = Arrays.asList(attributes.get(ATTR_FACTOR), attributes.get(ATTR_ADJUST)); AttributeUtil.ensureAtLeastOneExists(this, atLeastOne, Arrays.asList(ATTR_FACTOR, ATTR_ADJUST)); myFactor = AttributeUtil.evaluateNonNegativeInt(this, attributes.get(ATTR_FACTOR), beans, ATTR_FACTOR, 1); myAdjust = AttributeUtil.evaluateInt(this, attributes.get(ATTR_ADJUST), beans, ATTR_ADJUST, 0); boolean explicitlyExpandingRight = AttributeUtil.evaluateBoolean(this, attributes.get(ATTR_EXPAND_RIGHT), beans, false); if (explicitlyExpandingRight) block.setDirection(Block.Direction.HORIZONTAL); else block.setDirection(Block.Direction.VERTICAL); amIFixed = AttributeUtil.evaluateBoolean(this, attributes.get(ATTR_FIXED), beans, false); } /** *

If not already part of a merged region, and one of the factors is * greater than 1, then create a merged region. Else, replace the current * merged region with a new merged region.

* @return Whether the first Cell in the Block * associated with this Tag was processed. */ public boolean process() { // long start = System.nanoTime(); TagContext context = getContext(); Sheet sheet = context.getSheet(); Block block = context.getBlock(); if (DEBUG) System.err.println("SpanTag.process: factor=" + myFactor + ", block direction is " + block.getDirection()); int left = block.getLeftColNum(); int right = left; int top = block.getTopRowNum(); int bottom = top; // Assume a "merged region" of 1 X 1 for now. int height = 1; int width = 1; List sheetMergedRegions = context.getMergedRegions(); int index = findMergedRegionAtCell(sheetMergedRegions, left, top); if (index != -1) { // Get the height/width and remove the old merged region. CellRangeAddress remove = sheetMergedRegions.get(index); right = remove.getLastColumn(); bottom = remove.getLastRow(); height = remove.getLastRow() - remove.getFirstRow() + 1; width = remove.getLastColumn() - remove.getFirstColumn() + 1; if (DEBUG) System.err.println(" Removing region: " + remove + ", height=" + height + ", width=" + width); sheetMergedRegions.remove(index); } short borderBottomType = CellStyle.BORDER_NONE; short borderLeftType = CellStyle.BORDER_NONE; short borderRightType = CellStyle.BORDER_NONE; short borderTopType = CellStyle.BORDER_NONE; Color borderBottomColor = null; Color borderLeftColor = null; Color borderRightColor = null; Color borderTopColor = null; // Get current borders and border colors. Row rTop = sheet.getRow(top); if (rTop != null) { Cell cLeft = rTop.getCell(left); if (cLeft != null) { CellStyle cs = cLeft.getCellStyle(); borderLeftType = cs.getBorderLeft(); borderTopType = cs.getBorderTop(); // Border colors need instanceof check. if (cs instanceof HSSFCellStyle) { borderLeftColor = ExcelColor.getHssfColorByIndex(cs.getLeftBorderColor()); borderTopColor = ExcelColor.getHssfColorByIndex(cs.getTopBorderColor()); } else { // XSSFCellStyle XSSFCellStyle xcs = (XSSFCellStyle) cs; borderLeftColor = xcs.getLeftBorderXSSFColor(); borderTopColor = xcs.getTopBorderXSSFColor(); } } } Row rBottom = sheet.getRow(bottom); if (rBottom != null) { Cell cRight = rBottom.getCell(right); if (cRight != null) { CellStyle cs = cRight.getCellStyle(); borderRightType = cs.getBorderRight(); borderBottomType = cs.getBorderBottom(); // Border colors need instanceof check. if (cs instanceof HSSFCellStyle) { borderRightColor = ExcelColor.getHssfColorByIndex(cs.getRightBorderColor()); borderBottomColor = ExcelColor.getHssfColorByIndex(cs.getBottomBorderColor()); } else { // XSSFCellStyle XSSFCellStyle xcs = (XSSFCellStyle) cs; borderRightColor = xcs.getRightBorderXSSFColor(); borderBottomColor = xcs.getBottomBorderXSSFColor(); } } } if (borderTopType != CellStyle.BORDER_NONE || borderBottomType != CellStyle.BORDER_NONE || borderRightType != CellStyle.BORDER_NONE || borderLeftType != CellStyle.BORDER_NONE) { removeBorders(sheet, left, right, top, bottom); } // The block for which to shift content out of the way or to remove is // actually the old merged region. Block mergedBlock = new Block(block.getParent(), left, right, top, bottom); mergedBlock.setDirection(block.getDirection()); // Determine new height or width, plus new bottom or right. int change; if (block.getDirection() == Block.Direction.VERTICAL) { change = height * (myFactor - 1) + myAdjust; bottom += change; height = bottom - top + 1; } else { change = width * (myFactor - 1) + myAdjust; right += change; width = right - left + 1; } // Remove. if (height <= 0 || width <= 0) { if (DEBUG) System.err.println(" Calling removeBlock on block: " + mergedBlock); SheetUtil.removeBlock(sheet, context, mergedBlock, getWorkbookContext()); return false; } // Shrink. if (change < 0) { Block remove; if (block.getDirection() == Block.Direction.VERTICAL) remove = new Block(block.getParent(), left, right, bottom + 1, bottom - change); else remove = new Block(block.getParent(), right + 1, right - change, top, bottom); remove.setDirection(block.getDirection()); if (DEBUG) System.err.println(" Calling removeBlock on fabricated block: " + remove + " (change " + change + ")"); if (amIFixed) { SheetUtil.clearBlock(sheet, remove, getWorkbookContext()); } else { SheetUtil.removeBlock(sheet, context, remove, getWorkbookContext()); } } // Expand. if (change > 0 && !amIFixed) { Block expand; if (block.getDirection() == Block.Direction.VERTICAL) expand = new Block(block.getParent(), left, right, bottom - change, bottom - change); else expand = new Block(block.getParent(), right - change, right - change, top, bottom); expand.setDirection(block.getDirection()); if (DEBUG) System.err.println(" Calling shiftForBlock on fabricated block: " + expand + " with change " + (change + 1)); SheetUtil.shiftForBlock(sheet, context, expand, getWorkbookContext(), change + 1); } if (borderTopType != CellStyle.BORDER_NONE || borderBottomType != CellStyle.BORDER_NONE || borderRightType != CellStyle.BORDER_NONE || borderLeftType != CellStyle.BORDER_NONE) { putBackBorders(sheet, left, right, top, bottom, borderLeftType, borderRightType, borderTopType, borderBottomType, borderLeftColor, borderRightColor, borderTopColor, borderBottomColor); } // Set the value. Row row = sheet.getRow(top); Cell cell = row.getCell(left); SheetUtil.setCellValue(cell, myValue); // Create the replacement merged region, or the new merged region if it // didn't exist before. if (height > 1 || width > 1) { CellRangeAddress create = new CellRangeAddress(top, bottom, left, right); if (DEBUG) System.err.println(" Adding region: " + create); sheetMergedRegions.add(create); } BlockTransformer transformer = new BlockTransformer(); transformer.transform(context, getWorkbookContext()); // // long end = System.nanoTime(); // System.out.println("findMergedRegionAtCell: " + (end - start) + " ns"); return true; } /** * Identify the merged region on the given Sheet whose top-left * corner is at the specified column and row indexes. * @param sheetMergedRegions A List of * CellRangeAddresses. * @param col The 0-based column index of the top-left corner. * @param row The 0-based row index of the top-left corner. * @return A 0-based index into the Sheet's list of merged * regions, or -1 if not found. */ private int findMergedRegionAtCell(List sheetMergedRegions, int col, int row) { int numMergedRegions = sheetMergedRegions.size(); for (int i = 0; i < numMergedRegions; i++) { CellRangeAddress candidate = sheetMergedRegions.get(i); if (candidate.getFirstRow() == row && candidate.getFirstColumn() == col) return i; } return -1; } /** * Remove all borders from all cells in the region described by the left, * right, top, and bottom bounds. * @param sheet The Sheet. * @param left The 0-based index indicating the left-most part of the region. * @param right The 0-based index indicating the right-most part of the region. * @param top The 0-based index indicating the top-most part of the region. * @param bottom The 0-based index indicating the bottom-most part of the region. */ private void removeBorders(Sheet sheet, int left, int right, int top, int bottom) { if (DEBUG) System.err.println("removeBorders: " + left + ", " + right + ", " + top + ", " + bottom); CellStyleCache csCache = getWorkbookContext().getCellStyleCache(); for (int r = top; r <= bottom; r++) { Row row = sheet.getRow(r); for (int c = left; c <= right; c++) { Cell cell = row.getCell(c); if (cell != null) { CellStyle cs = cell.getCellStyle(); Font f = sheet.getWorkbook().getFontAt(cs.getFontIndex()); Color fontColor; if (cs instanceof HSSFCellStyle) { fontColor = ExcelColor.getHssfColorByIndex(f.getColor()); } else { fontColor = ((XSSFFont) f).getXSSFColor(); } // At this point, we have all of the desired CellStyle and Font // characteristics. Find a CellStyle if it exists. CellStyle foundStyle = csCache.retrieveCellStyle(f.getBoldweight(), f.getItalic(), fontColor, f.getFontName(), f.getFontHeightInPoints(), cs.getAlignment(), CellStyle.BORDER_NONE, CellStyle.BORDER_NONE, CellStyle.BORDER_NONE, CellStyle.BORDER_NONE, cs.getDataFormatString(), f.getUnderline(), f.getStrikeout(), cs.getWrapText(), cs.getFillBackgroundColorColor(), cs.getFillForegroundColorColor(), cs.getFillPattern(), cs.getVerticalAlignment(), cs.getIndention(), cs.getRotation(), null, null, null, null, f.getCharSet(), f.getTypeOffset(), cs.getLocked(), cs.getHidden()); if (foundStyle == null) { foundStyle = SheetUtil.createCellStyle(sheet.getWorkbook(), cs.getAlignment(), CellStyle.BORDER_NONE, CellStyle.BORDER_NONE, CellStyle.BORDER_NONE, CellStyle.BORDER_NONE, cs.getDataFormatString(), cs.getWrapText(), cs.getFillBackgroundColorColor(), cs.getFillForegroundColorColor(), cs.getFillPattern(), cs.getVerticalAlignment(), cs.getIndention(), cs.getRotation(), null, null, null, null, cs.getLocked(), cs.getHidden()); foundStyle.setFont(f); } cell.setCellStyle(foundStyle); } } } } /** * Puts back borders for the newly sized merged region. * @param sheet The Sheet. * @param left The 0-based index indicating the left-most part of the region. * @param right The 0-based index indicating the right-most part of the region. * @param top The 0-based index indicating the top-most part of the region. * @param bottom The 0-based index indicating the bottom-most part of the region. * @param borderLeft The left border type. * @param borderRight The right border type. * @param borderTop The top border type. * @param borderBottom The bottom border type. * @param borderLeftColor The left border color. * @param borderRightColor The right border color. * @param borderTopColor The top border color. * @param borderBottomColor The bottom border color. */ private void putBackBorders(Sheet sheet, int left, int right, int top, int bottom, short borderLeft, short borderRight, short borderTop, short borderBottom, Color borderLeftColor, Color borderRightColor, Color borderTopColor, Color borderBottomColor) { if (DEBUG) System.err.println("putBackBorders: " + left + ", " + right + ", " + top + ", " + bottom); CellStyleCache csCache = getWorkbookContext().getCellStyleCache(); for (int r = top; r <= bottom; r++) { Row row = sheet.getRow(r); if (row == null) row = sheet.createRow(r); for (int c = left; c <= right; c++) { Cell cell = row.getCell(c); if (cell == null) cell = row.createCell(c); CellStyle cs = cell.getCellStyle(); Font f = sheet.getWorkbook().getFontAt(cs.getFontIndex()); Color fontColor; if (cs instanceof HSSFCellStyle) { fontColor = ExcelColor.getHssfColorByIndex(f.getColor()); } else { fontColor = ((XSSFFont) f).getXSSFColor(); } short newBorderBottom = (r == bottom) ? borderBottom : CellStyle.BORDER_NONE; short newBorderLeft = (c == left) ? borderLeft : CellStyle.BORDER_NONE; short newBorderRight = (c == right) ? borderRight : CellStyle.BORDER_NONE; short newBorderTop = (r == top) ? borderTop : CellStyle.BORDER_NONE; Color newBorderBottomColor = (r == bottom) ? borderBottomColor : null; Color newBorderLeftColor = (c == left) ? borderLeftColor : null; Color newBorderRightColor = (c == right) ? borderRightColor : null; Color newBorderTopColor = (r == top) ? borderTopColor : null; // At this point, we have all of the desired CellStyle and Font // characteristics. Find a CellStyle if it exists. CellStyle foundStyle = csCache.retrieveCellStyle(f.getBoldweight(), f.getItalic(), fontColor, f.getFontName(), f.getFontHeightInPoints(), cs.getAlignment(), newBorderBottom, newBorderLeft, newBorderRight, newBorderTop, cs.getDataFormatString(), f.getUnderline(), f.getStrikeout(), cs.getWrapText(), cs.getFillBackgroundColorColor(), cs.getFillForegroundColorColor(), cs.getFillPattern(), cs.getVerticalAlignment(), cs.getIndention(), cs.getRotation(), newBorderBottomColor, newBorderLeftColor, newBorderRightColor, newBorderTopColor, f.getCharSet(), f.getTypeOffset(), cs.getLocked(), cs.getHidden()); if (foundStyle == null) { foundStyle = SheetUtil.createCellStyle(sheet.getWorkbook(), cs.getAlignment(), newBorderBottom, newBorderLeft, newBorderRight, newBorderTop, cs.getDataFormatString(), cs.getWrapText(), cs.getFillBackgroundColorColor(), cs.getFillForegroundColorColor(), cs.getFillPattern(), cs.getVerticalAlignment(), cs.getIndention(), cs.getRotation(), newBorderBottomColor, newBorderLeftColor, newBorderRightColor, newBorderTopColor, cs.getLocked(), cs.getHidden()); foundStyle.setFont(f); } cell.setCellStyle(foundStyle); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy