/* * 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 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.engine.classic.core.modules.misc.survey; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Shape; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Arrays; import org.pentaho.reporting.engine.classic.core.DataRow; import org.pentaho.reporting.engine.classic.core.function.AbstractExpression; import org.pentaho.reporting.libraries.serializer.SerializerHelper; /** * An expression that takes values from one or more fields in the current row of the report, builds a * {@link SurveyScale} instance that will present those values, and returns that instance as the expression result. The * fields used by the expression are defined using properties named '0', '1', ... 'N', which need to be specified after * the expression is created. These fields should contain {@link Number} instances.The {@link SurveyScale} class * implements the Drawable interface, so it can be displayed using a * {@link org.pentaho.reporting.engine.classic.core.filter.types.ContentFieldType}. * * @noinspection UnusedDeclaration */ public class SurveyScaleExpression extends AbstractExpression { /** * The lowest value on the scale. */ private int lowest; /** * The highest value on the scale. */ private int highest; /** * An ordered list containing the fieldnames used in the expression. */ private ArrayList<String> fieldList; /** * The name of the field containing the lower bound of the highlighted range. */ private String rangeLowerBoundField; /** * The name of the field containing the upper bound of the highlighted range. */ private String rangeUpperBoundField; /** * An optional shape that is used (if present) for the first data value. */ private transient Shape overrideShape; /** * A flag that controls whether or not the override shape is filled or not filled. */ private boolean overrideShapeFilled; /** * The font used to display the scale values. */ private Font scaleValueFont; /** * The paint used to draw the scale values. */ private Color scaleValuePaint; /** * The range paint. */ private Color rangeColor; /** * The fill paint. */ private Color fillPaint; /** * The tick mark paint. */ private Color tickMarkPaint; private boolean autoConfigure; private double upperMargin; private double lowerMargin; private ArrayList<Boolean> fillShapes; private boolean drawTickMarks; private boolean drawScaleValues; private ArrayList<SurveyScaleShapeType> shapes; private SurveyScaleShapeType defaultShape; private transient BasicStroke outlineStroke; public SurveyScaleExpression() { this( 0, 1 ); } /** * Creates a new expression. * * @param lowest * the lowest value on the response scale. * @param highest * the highest value on the response scale. */ public SurveyScaleExpression( final int lowest, final int highest ) { this( lowest, highest, null, null, null ); } /** * Creates a new expression. * * @param lowest * the lowest value on the response scale. * @param highest * the highest value on the response scale. * @param lowerBoundsField * the name of the field containing the lower bound of the highlighted range (<code>null</code> permitted). * @param upperBoundsField * the name of the field containing the upper bound of the highlighted range (<code>null</code> permitted). * @param shape * a shape that will be used to override the shape displayed for the first series (<code>null</code> * permitted). */ public SurveyScaleExpression( final int lowest, final int highest, final String lowerBoundsField, final String upperBoundsField, final Shape shape ) { this.fillShapes = new ArrayList<Boolean>(); this.shapes = new ArrayList<SurveyScaleShapeType>(); this.lowest = lowest; this.highest = highest; this.fieldList = new ArrayList<String>(); this.overrideShape = shape; this.overrideShapeFilled = false; this.rangeLowerBoundField = lowerBoundsField; this.rangeUpperBoundField = upperBoundsField; this.rangeColor = Color.lightGray; this.tickMarkPaint = Color.gray; this.scaleValuePaint = Color.black; this.upperMargin = 0.1; this.lowerMargin = 0.1; this.defaultShape = SurveyScaleShapeType.DownTriangle; this.outlineStroke = new BasicStroke( 0.5f ); this.fillPaint = Color.BLACK; } public SurveyScaleShapeType getDefaultShape() { return defaultShape; } public void setDefaultShape( final SurveyScaleShapeType defaultShape ) { this.defaultShape = defaultShape; } public void setFillShapes( final int index, final boolean fill ) { if ( fillShapes.size() == index ) { fillShapes.add( fill ); } else { fillShapes.set( index, fill ); } } public boolean getFillShapes( final int index ) { return fillShapes.get( index ); } public int getFillShapesCount() { return fillShapes.size(); } public boolean[] getFillShapes() { final boolean[] retval = new boolean[fillShapes.size()]; for ( int i = 0; i < retval.length; i++ ) { retval[i] = Boolean.TRUE.equals( fillShapes.get( i ) ); } return retval; } public void setFillShapes( final boolean[] fields ) { this.fillShapes.clear(); for ( int i = 0; i < fields.length; i++ ) { this.fillShapes.add( fields[i] ); } } public void setShapes( final int index, final SurveyScaleShapeType fill ) { if ( shapes.size() == index ) { shapes.add( fill ); } else { shapes.set( index, fill ); } } public SurveyScaleShapeType getShapes( final int index ) { return shapes.get( index ); } public int getShapesCount() { return shapes.size(); } public SurveyScaleShapeType[] getShapes() { final SurveyScaleShapeType[] retval = new SurveyScaleShapeType[shapes.size()]; for ( int i = 0; i < retval.length; i++ ) { retval[i] = shapes.get( i ); } return retval; } public void setShapes( final SurveyScaleShapeType[] fields ) { this.shapes.clear(); for ( int i = 0; i < fields.length; i++ ) { this.shapes.add( fields[i] ); } } public boolean isAutoConfigure() { return autoConfigure; } public void setAutoConfigure( final boolean autoConfigure ) { this.autoConfigure = autoConfigure; } /** * Returns the name of the field containing the lower bound of the range that is to be highlighted on the scale. * * @return A string (possibly <code>null</code>). */ public String getRangeLowerBoundField() { return this.rangeLowerBoundField; } /** * Sets the name of the field containing the lower bound of the range that is to be highlighted on the scale. Set this * to <code>null</code> if you have no range to highlight. * * @param field * the field name (<code>null</code> permitted). */ public void setRangeLowerBoundField( final String field ) { this.rangeLowerBoundField = field; } /** * Returns the name of the field containing the upper bound of the range that is to be highlighted on the scale. * * @return A string (possibly <code>null</code>). */ public String getRangeUpperBoundField() { return this.rangeUpperBoundField; } /** * Sets the name of the field containing the upper bound of the range that is to be highlighted on the scale. Set this * to <code>null</code> if you have no range to highlight. * * @param field * the field name (<code>null</code> permitted). */ public void setRangeUpperBoundField( final String field ) { this.rangeUpperBoundField = field; } /** * Returns the override shape. * * @return The override shape (possibly <code>null</code>). */ public Shape getOverrideShape() { return this.overrideShape; } /** * Sets the override shape. The {@link SurveyScale} is created with a set of default shapes, this method allows you to * replace the *first* shape if you need to (leave it as <code>null</code> otherwise). * * @param shape * the shape (<code>null</code> permitted). */ public void setOverrideShape( final Shape shape ) { this.overrideShape = shape; } public boolean isOverrideShapeFilled() { return overrideShapeFilled; } /** * Sets a flag that controls whether the override shape is filled or not. * * @param b * the flag. */ public void setOverrideShapeFilled( final boolean b ) { this.overrideShapeFilled = b; } public int getLowest() { return lowest; } public void setLowest( final int lowest ) { this.lowest = lowest; } public int getHighest() { return highest; } public void setHighest( final int highest ) { this.highest = highest; } public Font getScaleValueFont() { return scaleValueFont; } public void setScaleValueFont( final Font scaleValueFont ) { this.scaleValueFont = scaleValueFont; } public Color getScaleValuePaint() { return scaleValuePaint; } public void setScaleValuePaint( final Color scaleValuePaint ) { this.scaleValuePaint = scaleValuePaint; } public Color getRangeColor() { return rangeColor; } public void setRangeColor( final Color rangeColor ) { this.rangeColor = rangeColor; } public Color getFillPaint() { return fillPaint; } public void setFillPaint( final Color fillPaint ) { this.fillPaint = fillPaint; } public Color getTickMarkPaint() { return tickMarkPaint; } public void setTickMarkPaint( final Color tickMarkPaint ) { this.tickMarkPaint = tickMarkPaint; } public double getUpperMargin() { return upperMargin; } public void setUpperMargin( final double upperMargin ) { this.upperMargin = upperMargin; } public double getLowerMargin() { return lowerMargin; } public void setLowerMargin( final double lowerMargin ) { this.lowerMargin = lowerMargin; } public boolean isDrawTickMarks() { return drawTickMarks; } public void setDrawTickMarks( final boolean drawTickMarks ) { this.drawTickMarks = drawTickMarks; } public boolean isDrawScaleValues() { return drawScaleValues; } public void setDrawScaleValues( final boolean drawScaleValues ) { this.drawScaleValues = drawScaleValues; } public BasicStroke getOutlineStroke() { return outlineStroke; } public void setOutlineStroke( final BasicStroke outlineStroke ) { this.outlineStroke = outlineStroke; } /** * Returns a {@link SurveyScale} instance that is set up to display the values in the current row. * * @return a {@link SurveyScale} instance. */ public Object getValue() { final SurveyScale result = new SurveyScale( this.lowest, this.highest, collectValues() ); if ( this.rangeLowerBoundField != null && this.rangeUpperBoundField != null ) { final DataRow dataRow = getDataRow(); final Object b0 = dataRow.get( this.rangeLowerBoundField ); final Object b1 = dataRow.get( this.rangeUpperBoundField ); if ( b0 instanceof Number ) { result.setRangeLowerBound( (Number) b0 ); } if ( b1 instanceof Number ) { result.setRangeUpperBound( (Number) b1 ); } } result.setRangePaint( this.rangeColor ); result.setTickMarkPaint( this.tickMarkPaint ); result.setFillPaint( this.fillPaint ); result.setScaleValueFont( this.scaleValueFont ); result.setScaleValuePaint( this.scaleValuePaint ); result.setUpperMargin( this.upperMargin ); result.setLowerMargin( this.lowerMargin ); result.setDrawScaleValues( this.drawScaleValues ); result.setDrawTickMarks( this.drawTickMarks ); result.setDefaultShape( this.defaultShape ); if ( this.overrideShape != null ) { result.setShape( 0, this.overrideShape ); result.setShapeFilled( 0, this.overrideShapeFilled ); } for ( int i = 0; i < fillShapes.size(); i++ ) { final Boolean fill = fillShapes.get( i ); result.setShapeFilled( i, Boolean.TRUE.equals( fill ) ); } return result; } /** * collects the values of all fields defined in the fieldList. * * @return an Objectarray containing all defined values from the datarow */ private Number[] collectValues() { final Number[] retval = new Number[this.fieldList.size()]; for ( int i = 0; i < this.fieldList.size(); i++ ) { final String field = this.fieldList.get( i ); retval[i] = (Number) getDataRow().get( field ); } return retval; } /** * Clones the expression. * * @return a copy of this expression. * @throws CloneNotSupportedException * this should never happen. */ public Object clone() throws CloneNotSupportedException { final SurveyScaleExpression fva = (SurveyScaleExpression) super.clone(); fva.fieldList = (ArrayList<String>) this.fieldList.clone(); fva.fillShapes = (ArrayList<Boolean>) this.fillShapes.clone(); fva.shapes = (ArrayList<SurveyScaleShapeType>) this.shapes.clone(); return fva; } public String[] getField() { return fieldList.toArray( new String[fieldList.size()] ); } public void setField( final String[] fields ) { this.fieldList.clear(); this.fieldList.addAll( Arrays.asList( fields ) ); } public String getField( final int idx ) { return this.fieldList.get( idx ); } public void setField( final int index, final String field ) { if ( fieldList.size() == index ) { fieldList.add( field ); } else { fieldList.set( index, field ); } } @Deprecated public Paint getRangePaint() { return rangeColor; } @Deprecated public void setRangePaint( final Paint rangePaint ) { if ( rangePaint == null ) { throw new NullPointerException(); } if ( rangePaint instanceof Color ) { this.rangeColor = (Color) rangePaint; } } private void writeObject( final ObjectOutputStream out ) throws IOException { out.defaultWriteObject(); final SerializerHelper helper = SerializerHelper.getInstance(); helper.writeObject( outlineStroke, out ); } private void readObject( final ObjectInputStream in ) throws IOException, ClassNotFoundException { in.defaultReadObject(); final SerializerHelper helper = SerializerHelper.getInstance(); outlineStroke = (BasicStroke) helper.readObject( in ); } }