/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2017 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.output.table.xls.helper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.pentaho.reporting.engine.classic.core.ElementAlignment;
import org.pentaho.reporting.engine.classic.core.layout.model.BorderEdge;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.CellBackground;
import org.pentaho.reporting.engine.classic.core.style.BorderStyle;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.style.TextRotation;
import org.pentaho.reporting.engine.classic.core.style.TextStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.TextWrap;
import org.pentaho.reporting.engine.classic.core.util.InstanceID;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import java.awt.Color;
import java.util.HashMap;
/**
* The cellstyle producer converts the JFreeReport content into excel cell styles. This class is able to use the POI 2.0
* features to build data cells.
*
* @author Thomas Morgner
*/
public class HSSFCellStyleProducer implements CellStyleProducer {
private static final Log logger = LogFactory.getLog( HSSFCellStyleProducer.class );
static class HSSFCellStyleKey {
/**
* The cell background color.
*/
private short color;
private Color xColor;
/**
* The top border's size.
*/
private short borderStrokeTop;
/**
* The bottom border's size.
*/
private short borderStrokeBottom;
/**
* The left border's size.
*/
private short borderStrokeLeft;
/**
* The right border's size.
*/
private short borderStrokeRight;
/**
* The top border's color.
*/
private short colorTop;
private Color xColorTop;
/**
* The left border's color.
*/
private short colorLeft;
private Color xColorLeft;
/**
* The bottom border's color.
*/
private short colorBottom;
private Color xColorBottom;
/**
* The right border's color.
*/
private short colorRight;
private Color xColorRight;
/**
* A flag indicating whether to enable excels word wrapping.
*/
private boolean wrapText;
/**
* the horizontal text alignment.
*/
private short horizontalAlignment;
/**
* the vertical text alignment.
*/
private short verticalAlignment;
/**
* the font definition for the cell.
*/
private short font;
/**
* the data style.
*/
private short dataStyle;
/**
* Indention level
*/
private short indention;
/**
* Text rotation
*/
private TextRotation textRotation;
private Integer hashCode;
/**
* @param background
* can be null
* @param contentStyle
* can be null
*/
protected HSSFCellStyleKey( final CellBackground background, final StyleSheet contentStyle,
final DataFormat dataFormat, final ExcelFontFactory fontFactory,
final ExcelColorProducer colorProducer,
final ExcelColorProducer fontColorProducer ) {
if ( dataFormat == null ) {
throw new NullPointerException();
}
if ( fontFactory == null ) {
throw new NullPointerException();
}
this.dataStyle = 0;
this.color = HSSFColor.AUTOMATIC.index;
this.colorBottom = HSSFColor.AUTOMATIC.index;
this.colorLeft = HSSFColor.AUTOMATIC.index;
this.colorRight = HSSFColor.AUTOMATIC.index;
this.colorTop = HSSFColor.AUTOMATIC.index;
if ( background != null ) {
if ( background.getBackgroundColor() != null ) {
this.color = colorProducer.getNearestColor( background.getBackgroundColor() );
this.xColor = background.getBackgroundColor();
}
final BorderEdge bottom = background.getBottom();
this.colorBottom = colorProducer.getNearestColor( bottom.getColor() );
this.xColorBottom = bottom.getColor();
this.borderStrokeBottom = HSSFCellStyleProducer.translateStroke( bottom.getBorderStyle(), bottom.getWidth() );
final BorderEdge left = background.getLeft();
this.colorLeft = colorProducer.getNearestColor( left.getColor() );
this.xColorLeft = left.getColor();
this.borderStrokeLeft = HSSFCellStyleProducer.translateStroke( left.getBorderStyle(), left.getWidth() );
final BorderEdge top = background.getTop();
this.colorTop = colorProducer.getNearestColor( top.getColor() );
this.xColorTop = top.getColor();
this.borderStrokeTop = HSSFCellStyleProducer.translateStroke( top.getBorderStyle(), top.getWidth() );
final BorderEdge right = background.getRight();
this.colorRight = colorProducer.getNearestColor( right.getColor() );
this.xColorRight = right.getColor();
this.borderStrokeRight = HSSFCellStyleProducer.translateStroke( right.getBorderStyle(), right.getWidth() );
}
if ( contentStyle != null ) {
final Color textColor = (Color) contentStyle.getStyleProperty( ElementStyleKeys.PAINT );
final HSSFFontWrapper wrapper =
new HSSFFontWrapper( contentStyle, fontColorProducer.getNearestColor( textColor ) );
final Font excelFont = fontFactory.getExcelFont( wrapper );
this.font = excelFont.getIndex();
final ElementAlignment horizontal =
(ElementAlignment) contentStyle.getStyleProperty( ElementStyleKeys.ALIGNMENT );
this.horizontalAlignment = HSSFCellStyleProducer.convertAlignment( horizontal );
final ElementAlignment vertical =
(ElementAlignment) contentStyle.getStyleProperty( ElementStyleKeys.VALIGNMENT );
this.verticalAlignment = HSSFCellStyleProducer.convertAlignment( vertical );
final String dataStyle = (String) contentStyle.getStyleProperty( ElementStyleKeys.EXCEL_DATA_FORMAT_STRING );
if ( dataStyle != null ) {
this.dataStyle = dataFormat.getFormat( dataStyle );
}
this.wrapText = isWrapText( contentStyle );
this.indention = getIndention( contentStyle );
this.textRotation = (TextRotation) contentStyle.getStyleProperty( TextStyleKeys.TEXT_ROTATION, null );
}
}
private boolean isWrapText( final StyleSheet styleSheet ) {
final Object excelWrap = styleSheet.getStyleProperty( ElementStyleKeys.EXCEL_WRAP_TEXT );
if ( excelWrap != null ) {
return Boolean.TRUE.equals( excelWrap );
}
return TextWrap.WRAP.equals( styleSheet.getStyleProperty( TextStyleKeys.TEXT_WRAP, TextWrap.WRAP ) );
}
private Short getIndention( final StyleSheet styleSheet ) {
Short indention = (Short) styleSheet.getStyleProperty( ElementStyleKeys.EXCEL_INDENTION );
return indention == null ? 0 : indention;
}
protected HSSFCellStyleKey( final XSSFCellStyle style ) {
this.color = style.getFillForegroundColor();
this.colorTop = style.getTopBorderColor();
this.colorLeft = style.getLeftBorderColor();
this.colorBottom = style.getBottomBorderColor();
this.colorRight = style.getRightBorderColor();
this.xColor = createColor( style.getFillBackgroundXSSFColor() );
this.xColorTop = createColor( style.getTopBorderXSSFColor() );
this.xColorLeft = createColor( style.getLeftBorderXSSFColor() );
this.xColorBottom = createColor( style.getBottomBorderXSSFColor() );
this.xColorRight = createColor( style.getRightBorderXSSFColor() );
this.borderStrokeTop = style.getBorderTop();
this.borderStrokeLeft = style.getBorderLeft();
this.borderStrokeBottom = style.getBorderBottom();
this.borderStrokeRight = style.getBorderRight();
this.dataStyle = style.getDataFormat();
this.font = style.getFontIndex();
this.horizontalAlignment = style.getAlignment();
this.verticalAlignment = style.getVerticalAlignment();
this.wrapText = style.getWrapText();
this.textRotation = TextRotation.getInstance( style.getRotation() );
}
private static Color createColor( final XSSFColor color ) {
if ( color == null ) {
return null;
}
final byte[] rgb = color.getRGB();
return new Color( 0xFF & rgb[0], 0xFF & rgb[1], 0xFF & rgb[2] );
}
protected HSSFCellStyleKey( final CellStyle style ) {
this.color = style.getFillForegroundColor();
this.colorTop = style.getTopBorderColor();
this.colorLeft = style.getLeftBorderColor();
this.colorBottom = style.getBottomBorderColor();
this.colorRight = style.getRightBorderColor();
this.borderStrokeTop = style.getBorderTop();
this.borderStrokeLeft = style.getBorderLeft();
this.borderStrokeBottom = style.getBorderBottom();
this.borderStrokeRight = style.getBorderRight();
this.dataStyle = style.getDataFormat();
this.font = style.getFontIndex();
this.horizontalAlignment = style.getAlignment();
this.verticalAlignment = style.getVerticalAlignment();
this.wrapText = style.getWrapText();
this.textRotation = TextRotation.getInstance( style.getRotation() );
}
public boolean equals( final Object o ) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final HSSFCellStyleKey that = (HSSFCellStyleKey) o;
if ( borderStrokeBottom != that.borderStrokeBottom ) {
return false;
}
if ( borderStrokeLeft != that.borderStrokeLeft ) {
return false;
}
if ( borderStrokeRight != that.borderStrokeRight ) {
return false;
}
if ( borderStrokeTop != that.borderStrokeTop ) {
return false;
}
if ( color != that.color ) {
return false;
}
if ( colorBottom != that.colorBottom ) {
return false;
}
if ( colorLeft != that.colorLeft ) {
return false;
}
if ( colorRight != that.colorRight ) {
return false;
}
if ( colorTop != that.colorTop ) {
return false;
}
if ( dataStyle != that.dataStyle ) {
return false;
}
if ( font != that.font ) {
return false;
}
if ( horizontalAlignment != that.horizontalAlignment ) {
return false;
}
if ( verticalAlignment != that.verticalAlignment ) {
return false;
}
if ( wrapText != that.wrapText ) {
return false;
}
if ( xColor == null ) {
if ( that.xColor != null ) {
return false;
}
} else {
if ( xColor.equals( that.xColor ) == false ) {
return false;
}
}
if ( xColorRight == null ) {
if ( that.xColorRight != null ) {
return false;
}
} else {
if ( xColorRight.equals( that.xColorRight ) == false ) {
return false;
}
}
if ( xColorLeft == null ) {
if ( that.xColorLeft != null ) {
return false;
}
} else {
if ( xColorLeft.equals( that.xColorLeft ) == false ) {
return false;
}
}
if ( xColorTop == null ) {
if ( that.xColorTop != null ) {
return false;
}
} else {
if ( xColorTop.equals( that.xColorTop ) == false ) {
return false;
}
}
if ( xColorBottom == null ) {
if ( that.xColorBottom != null ) {
return false;
}
} else {
return xColorBottom.equals( that.xColorBottom );
}
if ( textRotation == null ) {
if ( that.textRotation != null ) {
return false;
}
} else {
return textRotation.equals( that.textRotation );
}
return true;
}
public int hashCode() {
if ( hashCode == null ) {
int result = (int) color;
result = 29 * result + (int) borderStrokeTop;
result = 29 * result + (int) borderStrokeBottom;
result = 29 * result + (int) borderStrokeLeft;
result = 29 * result + (int) borderStrokeRight;
result = 29 * result + (int) colorTop;
result = 29 * result + (int) colorLeft;
result = 29 * result + (int) colorBottom;
result = 29 * result + (int) colorRight;
result = 29 * result + ( wrapText ? 1 : 0 );
result = 29 * result + (int) horizontalAlignment;
result = 29 * result + (int) verticalAlignment;
result = 29 * result + (int) font;
result = 29 * result + (int) dataStyle;
result = 29 * result + (int) indention;
result = 29 * result + ( ( xColor == null ) ? 0 : xColor.hashCode() );
result = 29 * result + ( ( xColorTop == null ) ? 0 : xColorTop.hashCode() );
result = 29 * result + ( ( xColorLeft == null ) ? 0 : xColorLeft.hashCode() );
result = 29 * result + ( ( xColorBottom == null ) ? 0 : xColorBottom.hashCode() );
result = 29 * result + ( ( xColorRight == null ) ? 0 : xColorRight.hashCode() );
result = 29 * result + ( ( textRotation == null ) ? 0 : textRotation.hashCode() );
hashCode = result;
}
return hashCode;
}
public short getColor() {
return color;
}
public short getBorderStrokeTop() {
return borderStrokeTop;
}
public short getBorderStrokeBottom() {
return borderStrokeBottom;
}
public short getBorderStrokeLeft() {
return borderStrokeLeft;
}
public short getBorderStrokeRight() {
return borderStrokeRight;
}
public short getColorTop() {
return colorTop;
}
public short getColorLeft() {
return colorLeft;
}
public short getColorBottom() {
return colorBottom;
}
public short getColorRight() {
return colorRight;
}
public Color getExtendedColor() {
return xColor;
}
public Color getExtendedColorTop() {
return xColorTop;
}
public Color getExtendedColorLeft() {
return xColorLeft;
}
public Color getExtendedColorBottom() {
return xColorBottom;
}
public Color getExtendedColorRight() {
return xColorRight;
}
public boolean isWrapText() {
return wrapText;
}
public short getHorizontalAlignment() {
return horizontalAlignment;
}
public short getVerticalAlignment() {
return verticalAlignment;
}
public short getFont() {
return font;
}
public short getDataStyle() {
return dataStyle;
}
public short getIndention() {
return indention;
}
}
/**
* the font factory is used to create excel fonts.
*/
private ExcelFontFactory fontFactory;
/**
* The workbook, which creates all cells and styles.
*/
private Workbook workbook;
/**
* The data format is used to create format strings.
*/
private DataFormat dataFormat;
/**
* The cache for all generated styles.
*/
private HashMap<HSSFCellStyleKey, CellStyle> styleCache;
private boolean warningDone;
private boolean hardLimit;
private ExcelColorProducer colorProducer;
private ExcelColorProducer fontColorProducer;
/**
* The class does the dirty work of creating the HSSF-objects.
*
* @param workbook
* the workbook for which the styles should be created.
*/
public HSSFCellStyleProducer( final Workbook workbook, final boolean hardLimit,
final ExcelColorProducer colorProducer, final ExcelColorProducer fontColorProducer ) {
this.fontColorProducer = fontColorProducer;
if ( workbook == null ) {
throw new NullPointerException();
}
if ( colorProducer == null ) {
throw new NullPointerException();
}
this.colorProducer = colorProducer;
this.styleCache = new HashMap<HSSFCellStyleKey, CellStyle>();
this.workbook = workbook;
this.fontFactory = new ExcelFontFactory( workbook, fontColorProducer );
this.dataFormat = workbook.createDataFormat();
this.hardLimit = hardLimit;
if ( workbook instanceof XSSFWorkbook ) {
final XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook;
final int predefinedStyles = workbook.getNumCellStyles();
for ( int i = 0; i < predefinedStyles; i++ ) {
final XSSFCellStyle cellStyleAt = xssfWorkbook.getCellStyleAt( i );
this.styleCache.put( new HSSFCellStyleKey( cellStyleAt ), cellStyleAt );
}
} else {
// Read in the styles ...
final int predefinedStyles = workbook.getNumCellStyles();
for ( int i = 0; i < predefinedStyles; i++ ) {
final CellStyle cellStyleAt = workbook.getCellStyleAt( i );
this.styleCache.put( new HSSFCellStyleKey( cellStyleAt ), cellStyleAt );
}
}
}
/**
* Creates a HSSFCellStyle based on the given ExcelDataCellStyle. If a similiar cell style was previously generated,
* then reuse that cached result.
*
* @param element
* can be null for background only cells.
* @param bg
* the optional background style for the table cell.
* @return the generated or cached HSSFCellStyle.
*/
public CellStyle createCellStyle( final InstanceID id, final StyleSheet element, final CellBackground bg ) {
// check, whether that style is already created
final HSSFCellStyleKey styleKey =
new HSSFCellStyleKey( bg, element, dataFormat, fontFactory, colorProducer, fontColorProducer );
if ( styleCache.containsKey( styleKey ) ) {
return styleCache.get( styleKey );
}
if ( ( styleCache.size() ) > 4000 ) {
if ( warningDone == false ) {
HSSFCellStyleProducer.logger.warn( "HSSFCellStyleProducer has reached the limit of 4000 created styles." );
warningDone = true;
}
if ( hardLimit ) {
HSSFCellStyleProducer.logger
.warn( "HSSFCellStyleProducer will not create more styles. New cells will not have any style." );
return null;
}
}
ExcelCellStyleBuilder builder = new ExcelCellStyleBuilder( this.workbook );
builder.withRotation( element );
builder.withElementStyle( element, styleKey );
builder.withBackgroundStyle( bg, styleKey );
final CellStyle hssfCellStyle = builder.build();
styleCache.put( styleKey, hssfCellStyle );
return hssfCellStyle;
}
/**
* Converts the given element alignment into one of the HSSFCellStyle-constants.
*
* @param e the JFreeReport element alignment.
* @return the HSSFCellStyle-Alignment.
* @throws IllegalArgumentException if an Unknown JFreeReport alignment is given.
*/
protected static short convertAlignment( final ElementAlignment e ) {
if ( ElementAlignment.LEFT.equals( e ) ) {
return HSSFCellStyle.ALIGN_LEFT;
}
if ( ElementAlignment.RIGHT.equals( e ) ) {
return HSSFCellStyle.ALIGN_RIGHT;
}
if ( ElementAlignment.JUSTIFY.equals( e ) ) {
return HSSFCellStyle.ALIGN_JUSTIFY;
}
if ( ElementAlignment.CENTER.equals( e ) ) {
return HSSFCellStyle.ALIGN_CENTER;
}
if ( ElementAlignment.TOP.equals( e ) ) {
return HSSFCellStyle.VERTICAL_TOP;
}
if ( ElementAlignment.BOTTOM.equals( e ) ) {
return HSSFCellStyle.VERTICAL_BOTTOM;
}
if ( ElementAlignment.MIDDLE.equals( e ) ) {
return HSSFCellStyle.VERTICAL_CENTER;
}
throw new IllegalArgumentException( "Invalid alignment" );
}
/**
* Tries to translate the given stroke width into one of the predefined excel border styles.
*
* @param widthRaw the AWT-Stroke-Width.
* @return the translated excel border width.
*/
protected static short translateStroke( final BorderStyle borderStyle, final long widthRaw ) {
final double width = StrictGeomUtility.toExternalValue( widthRaw );
if ( BorderStyle.NONE.equals( borderStyle ) ) {
return HSSFCellStyle.BORDER_NONE;
}
if ( BorderStyle.DASHED.equals( borderStyle ) ) {
if ( width <= 1.5 ) {
return HSSFCellStyle.BORDER_DASHED;
} else {
return HSSFCellStyle.BORDER_MEDIUM_DASHED;
}
}
if ( BorderStyle.DOT_DOT_DASH.equals( borderStyle ) ) {
if ( width <= 1.5 ) {
return HSSFCellStyle.BORDER_DASH_DOT_DOT;
} else {
return HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT;
}
}
if ( BorderStyle.DOT_DASH.equals( borderStyle ) ) {
if ( width <= 1.5 ) {
return HSSFCellStyle.BORDER_DASH_DOT;
} else {
return HSSFCellStyle.BORDER_MEDIUM_DASH_DOT;
}
}
if ( BorderStyle.DOTTED.equals( borderStyle ) ) {
return HSSFCellStyle.BORDER_DOTTED;
}
if ( BorderStyle.DOUBLE.equals( borderStyle ) ) {
return HSSFCellStyle.BORDER_DOUBLE;
}
if ( width == 0 ) {
return HSSFCellStyle.BORDER_NONE;
} else if ( width <= 0.5 ) {
return HSSFCellStyle.BORDER_HAIR;
} else if ( width <= 1 ) {
return HSSFCellStyle.BORDER_THIN;
} else if ( width <= 1.5 ) {
return HSSFCellStyle.BORDER_MEDIUM;
// }
// else if (width <= 2)
// {
// return HSSFCellStyle.BORDER_DOUBLE;
} else {
return HSSFCellStyle.BORDER_THICK;
}
}
public ExcelFontFactory getFontFactory() {
return fontFactory;
}
}