AutoScale.java from BIRT at Krugle
Show AutoScale.java syntax highlighted
/***********************************************************************
* Copyright (c) 2004 Actuate Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Actuate Corporation - initial API and implementation
***********************************************************************/
package org.eclipse.birt.chart.computation.withaxes;
import java.util.Arrays;
import org.eclipse.birt.chart.computation.BoundingBox;
import org.eclipse.birt.chart.computation.DataSetIterator;
import org.eclipse.birt.chart.computation.IConstants;
import org.eclipse.birt.chart.computation.Methods;
import org.eclipse.birt.chart.computation.Point;
import org.eclipse.birt.chart.computation.RotatedRectangle;
import org.eclipse.birt.chart.computation.ValueFormatter;
import org.eclipse.birt.chart.device.IDisplayServer;
import org.eclipse.birt.chart.engine.i18n.Messages;
import org.eclipse.birt.chart.exception.ChartException;
import org.eclipse.birt.chart.factory.RunTimeContext;
import org.eclipse.birt.chart.internal.factory.DateFormatWrapperFactory;
import org.eclipse.birt.chart.internal.factory.IDateFormatWrapper;
import org.eclipse.birt.chart.log.ILogger;
import org.eclipse.birt.chart.log.Logger;
import org.eclipse.birt.chart.model.attribute.FormatSpecifier;
import org.eclipse.birt.chart.model.component.Label;
import org.eclipse.birt.chart.model.data.DataElement;
import org.eclipse.birt.chart.model.data.DateTimeDataElement;
import org.eclipse.birt.chart.model.data.NumberDataElement;
import org.eclipse.birt.chart.model.data.impl.NumberDataElementImpl;
import org.eclipse.birt.chart.plugin.ChartEnginePlugin;
import org.eclipse.birt.chart.util.CDateTime;
import org.eclipse.birt.chart.util.ChartUtil;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.util.Calendar;
/**
* Encapsulates the auto scaling algorithms used by the rendering and chart
* computation framework.
*/
public final class AutoScale extends Methods implements Cloneable
{
private final int iType;
private Object oMinimum;
private Object oMaximum;
private Object oStep;
private Object oUnit;
private double dStartShift;
private double dEndShift;
private double dZoomFactor = 1.0;
private transient double dStart, dEnd;
private transient double[] daTickCoordinates;
private transient boolean[] baTickLabelVisible;
private transient boolean[] baTickLabelStaggered;
private transient DataSetIterator dsiData;
private transient boolean bCategoryScale = false;
private RunTimeContext rtc;;
/**
* A default numeric pattern for integer number representation of axis
* labels
*/
private static final String sNumericPattern = "0"; //$NON-NLS-1$
/**
* Quick static lookup for linear scaling
*/
// private static int[] iaLinearDeltas = { 1, 2, 5 };
private static int[] iaLinearDeltas = {
1, 2, 5, 10
};
/**
* Quick static lookup for logarithmic scaling
*/
// private static int[] iaLogarithmicDeltas = { 2, 4, 5, 10 };
private static int[] iaLogarithmicDeltas = {
10
};
/**
* Quick static lookup for datetime scaling
*/
private static int[] iaCalendarUnits = {
Calendar.SECOND,
Calendar.MINUTE,
Calendar.HOUR_OF_DAY,
Calendar.DATE,
Calendar.MONTH,
Calendar.YEAR
};
private static int[] iaSecondDeltas = {
1, 5, 10, 15, 20, 30
};
private static int[] iaMinuteDeltas = {
1, 5, 10, 15, 20, 30
};
private static int[] iaHourDeltas = {
1, 2, 3, 4, 12
};
private static int[] iaDayDeltas = {
1, 7, 14
};
private static int[] iaMonthDeltas = {
1, 2, 3, 4, 6
};
private static int[][] iaCalendarDeltas = {
iaSecondDeltas,
iaMinuteDeltas,
iaHourDeltas,
iaDayDeltas,
iaMonthDeltas,
null
};
private boolean bIntegralZoom = true;
private boolean bMinimumFixed = false;
private boolean bMaximumFixed = false;
private boolean bStepFixed = false;
private int iScaleDirection = AUTO;
private boolean bAxisLabelStaggered = false;
private int iLabelShowingInterval = 0;
private FormatSpecifier fs = null;
private static ILogger logger = Logger.getLogger( "org.eclipse.birt.chart.engine/computation.withaxes" ); //$NON-NLS-1$
/**
* The constructor.
*
* @param _iType
*/
AutoScale( int _iType )
{
iType = _iType;
}
/**
* The constructor.
*
* @param _iType
* @param _oMinimum
* @param _oMaximum
* @param _oStep
*/
public AutoScale( int _iType, Object _oMinimum, Object _oMaximum,
Object _oStep )
{
oMinimum = _oMinimum;
oMaximum = _oMaximum;
oStep = _oStep;
iType = _iType;
}
/**
* The constructor.
*
* @param _iType
* @param _oMinimum
* @param _oMaximum
* @param _oUnit
* @param _oStep
*/
AutoScale( int _iType, Object _oMinimum, Object _oMaximum, Object _oUnit,
Object _oStep )
{
oMinimum = _oMinimum;
oMaximum = _oMaximum;
oUnit = _oUnit;
oStep = _oStep;
iType = _iType;
}
final void setFixed( boolean _bMinimum, boolean _bMaximum, boolean _bStep )
{
bMinimumFixed = _bMinimum;
bMaximumFixed = _bMaximum;
bStepFixed = _bStep;
}
/**
* Sets the scale direction.
*
* @param val
*/
public final void setDirection( int iValue )
{
iScaleDirection = iValue;
}
/**
* Returns the scale direction.
*
* @return
*/
public int getDirection( )
{
return iScaleDirection;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#clone()
*/
public final Object clone( )
{
final AutoScale sc = new AutoScale( iType, oMinimum, oMaximum, oStep );
sc.dStart = dStart;
sc.dEnd = dEnd;
sc.daTickCoordinates = daTickCoordinates;
sc.dStartShift = dStartShift;
sc.dEndShift = dEndShift;
sc.dsiData = dsiData;
sc.oStep = oStep;
sc.oUnit = oUnit;
sc.bMaximumFixed = bMaximumFixed;
sc.bMinimumFixed = bMinimumFixed;
sc.bStepFixed = bStepFixed;
sc.fs = fs;
sc.rtc = rtc;
sc.bIntegralZoom = bIntegralZoom;
sc.bCategoryScale = bCategoryScale;
sc.iScaleDirection = iScaleDirection;
sc.baTickLabelVisible = baTickLabelVisible;
sc.baTickLabelStaggered = baTickLabelStaggered;
sc.bAxisLabelStaggered = bAxisLabelStaggered;
sc.iLabelShowingInterval = iLabelShowingInterval;
return sc;
}
/**
* Zooms IN 'once' into a scale of type numerical or datetime Typically,
* this is called in a loop until label overlaps occur
*/
public final boolean zoomIn( )
{
if ( bStepFixed )
{
return false; // CANNOT ZOOM FOR FIXED STEPS
}
if ( ChartUtil.mathEqual( 0, ( (Number) oStep ).doubleValue( ) ) )
{
return false; // CANNOT ZOOM ANY MORE
}
if ( ( iType & NUMERICAL ) == NUMERICAL )
{
if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
{
final double dStep = asDouble( oStep ).doubleValue( );
if ( ( Math.log( dStep ) / LOG_10 ) > 1 )
{
oStep = new Double( dStep / 10 );
}
else
{
int n = iaLogarithmicDeltas.length;
for ( int i = n - 1; i >= 0; i-- )
{
if ( (int) dStep == iaLogarithmicDeltas[i] )
{
if ( i > 0 )
{
oStep = new Double( iaLogarithmicDeltas[i - 1] );
return true;
}
else
{
return false;
}
}
}
return false;
}
}
else if ( ( iType & LINEAR ) == LINEAR )
{
double dStep = asDouble( oStep ).doubleValue( );
if ( bIntegralZoom )
{
double dPower = ( Math.log( dStep ) / LOG_10 );
dPower = Math.floor( dPower );
dPower = Math.pow( 10.0, dPower );
dStep /= dPower;
dStep = Math.round( dStep );
int n = iaLinearDeltas.length;
for ( int i = 0; i < n; i++ )
{
if ( (int) dStep == iaLinearDeltas[i] )
{
if ( i > 0 )
{
dStep = iaLinearDeltas[i - 1] * dPower;
}
else
{
dPower /= 10;
dStep = iaLinearDeltas[n - 2] * dPower;
}
break;
}
}
oStep = new Double( dStep );
}
else
{
dStep /= 2;
oStep = new Double( dStep );
}
}
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
int[] ia = null;
int iStep = asInteger( oStep );
int iUnit = asInteger( oUnit );
for ( int icu = 0; icu < iaCalendarUnits.length; icu++ )
{
if ( iUnit == iaCalendarUnits[icu] )
{
ia = iaCalendarDeltas[icu];
if ( ia == null ) // HANDLE YEARS SEPARATELY
{
iStep--;
if ( iStep == 0 )
{
oStep = new Integer( iaMonthDeltas[iaMonthDeltas.length - 1] );
oUnit = new Integer( Calendar.MONTH );
}
}
else
// HANDLE SECONDS, MINUTES, HOURS, DAYS, MONTHS
{
int i = 0;
for ( ; i < ia.length; i++ )
{
if ( ia[i] == iStep )
{
break;
}
}
if ( i == 0 ) // WE'RE AT THE FIRST ELEMENT IN THE
// DELTAS ARRAY
{
if ( icu == 0 )
return false; // CAN'T ZOOM ANYMORE THAN
// 1-SECOND INTERVALS (AT INDEX=0)
ia = iaCalendarDeltas[icu - 1]; // DOWNGRADE ARRAY
// TO PREVIOUS
// DELTAS ARRAY
i = ia.length; // MANIPULATE OFFSET TO END+1
oUnit = new Integer( iaCalendarUnits[icu - 1] ); // DOWNGRADE
// UNIT
}
oStep = new Integer( ia[i - 1] ); // RETURN PREVIOUS
// STEP IN DELTAS
// ARRAY
break;
}
}
}
}
return true;
}
/**
* Zooms OUT 'once' into a scale of type numerical or datetime Typically,
* this is called in a loop until label overlaps occur
*/
public final boolean zoomOut( )
{
if ( bStepFixed )
{
return false;
}
if ( ( (Number) oStep ).doubleValue( ) >= Double.MAX_VALUE )
{
return false; // CANNOT ZOOM ANY MORE
}
if ( ( iType & NUMERICAL ) == NUMERICAL )
{
if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
{
final double dStep = asDouble( oStep ).doubleValue( );
if ( ( Math.log( dStep ) / LOG_10 ) >= 1 )
{
oStep = new Double( dStep * 10 );
}
else
{
final int n = iaLogarithmicDeltas.length;
for ( int i = 0; i < n; i++ )
{
if ( (int) dStep == iaLogarithmicDeltas[i] )
{
oStep = new Double( iaLogarithmicDeltas[i + 1] );
return true;
}
}
return false;
}
}
else if ( ( iType & LINEAR ) == LINEAR )
{
double dStep = asDouble( oStep ).doubleValue( );
if ( bIntegralZoom )
{
double dPower = ( Math.log( dStep ) / LOG_10 );
if ( dPower < 0 )
{
dPower = Math.floor( dPower );
}
dPower = Math.pow( 10, dPower );
dStep /= dPower;
dStep = Math.round( dStep );
int n = iaLinearDeltas.length;
int i = 0;
for ( ; i < n; i++ )
{
if ( dStep == iaLinearDeltas[i] )
{
if ( i < n - 1 )
{
dStep = iaLinearDeltas[i + 1] * dPower;
if ( dStep > 1 )
{
dStep = Math.round( dStep );
}
}
else
{
dPower *= 20;
dStep = iaLinearDeltas[0] * dPower;
if ( dStep > 1 )
{
dStep = Math.round( dStep );
}
}
break;
}
}
if ( i == n )
{
throw new RuntimeException( new ChartException( ChartEnginePlugin.ID,
ChartException.COMPUTATION,
"exception.step.zoom.out", //$NON-NLS-1$
new Object[]{
new Double( dStep )
},
Messages.getResourceBundle( rtc.getULocale( ) ) ) );
}
if ( ( (Number) oStep ).doubleValue( ) == dStep )
{
// Can not zoom any more, result is always the same;
return false;
}
oStep = new Double( dStep );
}
else
{
dStep *= 2;
oStep = new Double( dStep );
}
}
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
int[] ia = null;
int iStep = asInteger( oStep );
int iUnit = asInteger( oUnit );
for ( int icu = 0; icu < iaCalendarUnits.length; icu++ )
{
if ( iUnit == iaCalendarUnits[icu] )
{
ia = iaCalendarDeltas[icu];
if ( ia == null ) // HANDLE YEARS SEPARATELY
{
iStep++; // NO UPPER LIMIT FOR YEARS
oStep = new Integer( iStep );
}
else
// HANDLE SECONDS, MINUTES, HOURS, DAYS, MONTHS
{
int i = 0, n = ia.length;
for ( ; i < n; i++ )
{
if ( ia[i] == iStep )
{
break;
}
}
if ( i == n - 1 ) // WE'RE AT THE LAST ELEMENT IN THE
// DELTAS ARRAY
{
ia = iaCalendarDeltas[icu + 1]; // UPGRADE UNIT TO
// NEXT DELTAS ARRAY
oUnit = new Integer( iaCalendarUnits[icu + 1] );
if ( ia == null ) // HANDLE YEARS
{
oStep = new Integer( 1 );
return false; // DO NO PROCEED FOR YEARS
}
i = -1; // MANIPULATE OFFSET TO START-1
}
oStep = new Integer( ia[i + 1] ); // RETURN NEXT STEP
// IN
// DELTAS ARRAY
break;
}
}
}
}
return true;
}
/**
* Returns an auto computed decimal format pattern for representing axis
* labels on a numeric axis This is used for representing logarithmic values
*
* @return
*/
public final String getNumericPattern( double dValue )
{
if ( dValue - (int) dValue == 0 ) // IF MANTISSA IS INSIGNIFICANT,
// SHOW
// LABELS AS INTEGERS
{
return sNumericPattern;
}
final DecimalFormatSymbols dfs = new DecimalFormatSymbols( );
final String sMinimum = String.valueOf( dValue );
final int iDecimalPosition = sMinimum.indexOf( dfs.getDecimalSeparator( ) ); // THIS
// RELIES
// ON
// THE
// FACT
// THAT
// IN
// ANY LOCALE, DECIMAL IS A DOT
if ( iDecimalPosition >= 0 )
{
int n = sMinimum.length( );
for ( int i = n - 1; i > 0; i-- )
{
if ( sMinimum.charAt( i ) == '0' )
{
n--;
}
else
{
break;
}
}
final int iMantissaCount = n - 1 - iDecimalPosition;
final StringBuffer sb = new StringBuffer( sNumericPattern );
sb.append( '.' );
for ( int i = 0; i < iMantissaCount; i++ )
{
sb.append( '0' );
}
return sb.toString( );
}
return sNumericPattern;
}
/**
* Returns an auto computed decimal format pattern for representing axis
* labels on a numeric axis
*
* @return
*/
public final String getNumericPattern( )
{
if ( oMinimum == null || oStep == null )
{
return "0.00"; //$NON-NLS-1$
}
final double dMinValue = asDouble( oMinimum ).doubleValue( );
final double dStep = asDouble( oStep ).doubleValue( );
if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
{
final DecimalFormatSymbols dfs = new DecimalFormatSymbols( );
final String sMinimum = oMinimum.toString( );
final int iDecimalPosition = sMinimum.indexOf( dfs.getDecimalSeparator( ) );
if ( iDecimalPosition >= 0 )
{
int n = sMinimum.length( );
for ( int i = n - 1; i > 0; i-- )
{
if ( sMinimum.charAt( i ) == '0' )
{
n--;
}
else
{
break;
}
}
final int iMantissaCount = n - 1 - iDecimalPosition;
final StringBuffer sb = new StringBuffer( sNumericPattern );
sb.append( '.' );
for ( int i = 0; i < iMantissaCount; i++ )
{
sb.append( '0' );
}
return sb.toString( );
}
else
{
return sNumericPattern;
}
}
if ( dMinValue - (int) dMinValue == 0 // IF MANTISSA IS INSIGNIFICANT,
// SHOW LABELS AS INTEGERS
&& dStep - (int) dStep == 0 )
{
return sNumericPattern;
}
final DecimalFormatSymbols dfs = new DecimalFormatSymbols( );
final String sStep = oStep.toString( );
final int iDecimalPosition = sStep.indexOf( dfs.getDecimalSeparator( ) );
if ( iDecimalPosition >= 0 )
{
int n = sStep.length( );
for ( int i = n - 1; i > 0; i-- )
{
if ( sStep.charAt( i ) == '0' )
{
n--;
}
else
{
break;
}
}
final int iMantissaCount = n - 1 - iDecimalPosition;
final StringBuffer sb = new StringBuffer( sNumericPattern );
sb.append( '.' );
for ( int i = 0; i < iMantissaCount; i++ )
{
sb.append( '0' );
}
return sb.toString( );
}
else
{
return sNumericPattern;
}
}
/**
*
* @return
*/
public final int getType( )
{
return iType;
}
/**
*
* @param _oaData
*/
public final void setData( DataSetIterator _oaData )
{
dsiData = _oaData;
}
/**
* @return
*/
public final FormatSpecifier getFormatSpecifier( )
{
return this.fs;
}
/**
* @param fs
*/
public final void setFormatSpecifier( FormatSpecifier fs )
{
this.fs = fs;
}
/**
*
* @return
*/
public final Object getUnit( )
{
return oUnit;
}
/**
*
* @return
*/
public final DataSetIterator getData( )
{
return dsiData;
}
/**
*
* @param da
*/
final void setTickCordinates( double[] da )
{
if ( da != null && da.length == 1 )
{
throw new RuntimeException( new ChartException( ChartEnginePlugin.ID,
ChartException.COMPUTATION,
"exception.tick.computations", //$NON-NLS-1$
Messages.getResourceBundle( rtc.getULocale( ) ) ) );
}
daTickCoordinates = da;
}
/**
* @param index
* @return
*/
public final boolean isTickLabelVisible( int index )
{
if ( baTickLabelVisible == null
|| index < 0
|| index > baTickLabelVisible.length - 1 )
{
return false;
}
return baTickLabelVisible[index];
}
/**
* @param index
* @return
*/
public final boolean isTickLabelStaggered( int index )
{
if ( baTickLabelStaggered == null
|| index < 0
|| index > baTickLabelStaggered.length - 1 )
{
return false;
}
return baTickLabelStaggered[index];
}
/**
* @return
*/
public final boolean isAxisLabelStaggered( )
{
return bAxisLabelStaggered;
}
/**
*
* @return
*/
public final double[] getTickCordinates( )
{
return daTickCoordinates;
}
/**
* Returns the normalized tick coordinates. that means the start point is
* always zero, and the array lines forward.
*
* @return
*/
public final double[] getNormalizedTickCoordinates( )
{
if ( daTickCoordinates != null )
{
double[] daNomTickCoordinates = new double[daTickCoordinates.length];
for ( int i = 0; i < daNomTickCoordinates.length; i++ )
{
daNomTickCoordinates[i] = daTickCoordinates[i]
- daTickCoordinates[0];
}
return daNomTickCoordinates;
}
return null;
}
/**
* Returns the normalized start point. always be Zero.
*
* @return
*/
public final double getNormalizedStart( )
{
return 0;
}
/**
* Returns the normalized end point. this will be the (orginal end - orginal
* start).
*
* @return
*/
public final double getNormalizedEnd( )
{
return dEnd - dStart;
}
/**
* Returns the normalized start and end point.
*
* @return
*/
public final double[] getNormalizedEndPoints( )
{
return new double[]{
0, dEnd - dStart
};
}
/**
*
* @return
*/
public final double[] getEndPoints( )
{
return new double[]{
dStart, dEnd
};
}
/**
*
* @param _dStart
* @param _dEnd
*/
final void setEndPoints( double _dStart, double _dEnd )
{
if ( _dStart != -1 )
{
dStart = _dStart;
}
if ( _dEnd != -1 )
{
dEnd = _dEnd;
}
if ( daTickCoordinates != null )
{
int n = daTickCoordinates.length - 1;
double dDelta = ( dEnd - dStart ) / n;
double d = dStart;
for ( int i = 0; i < n; i++ )
{
daTickCoordinates[i] = d;
d += dDelta;
}
daTickCoordinates[n] = dEnd;
}
}
/**
*
* @return
*/
public final int getTickCount( )
{
int nTicks = 2;
if ( ( iType & TEXT ) == TEXT || bCategoryScale )
{
if ( dsiData != null )
{
nTicks = dsiData.size( ) + 1;
}
}
else if ( ( iType & NUMERICAL ) == NUMERICAL )
{
if ( ( iType & LINEAR ) == LINEAR )
{
double dMax = asDouble( oMaximum ).doubleValue( );
double dMin = asDouble( oMinimum ).doubleValue( );
double dStep = asDouble( oStep ).doubleValue( );
nTicks = (int) Math.ceil( ( dMax - dMin ) / dStep ) + 1;
// !Remove artificial limit for tick count.
// // ARTIFICIAL LIMIT TO TICK COUNT
// if ( nTicks > 100 )
// {
// nTicks = 100;
// }
// at least 2 ticks
if ( nTicks < 2 )
{
nTicks = 2;
}
}
else if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
{
double dMax = asDouble( oMaximum ).doubleValue( );
double dMin = asDouble( oMinimum ).doubleValue( );
double dStep = asDouble( oStep ).doubleValue( );
double dMaxLog = ( Math.log( dMax ) / LOG_10 );
double dMinLog = ( Math.log( dMin ) / LOG_10 );
double dStepLog = ( Math.log( dStep ) / LOG_10 );
nTicks = (int) Math.ceil( ( dMaxLog - dMinLog ) / dStepLog ) + 1;
if ( nTicks < 2 )
{
nTicks = 2;
}
}
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
final CDateTime cdt1 = (CDateTime) oMinimum;
final CDateTime cdt2 = (CDateTime) oMaximum;
nTicks = (int) ( Math.ceil( CDateTime.computeDifference( cdt2,
cdt1,
asInteger( oUnit ) ) ) / asInteger( oStep ) ) + 1;
if ( nTicks < 2 )
{
nTicks = 2;
}
}
return nTicks;
}
/**
* Returns the absolute value of the scale unit.
*
* @return
*/
public final double getUnitSize( )
{
if ( daTickCoordinates == null )
{
throw new RuntimeException( new ChartException( ChartEnginePlugin.ID,
ChartException.COMPUTATION,
"exception.unit.size.failure", //$NON-NLS-1$
Messages.getResourceBundle( rtc.getULocale( ) ) ) );
}
return Math.abs( daTickCoordinates[1] - daTickCoordinates[0] );
}
/**
*
* @return
*/
public final Object getMinimum( )
{
return oMinimum;
}
/**
* @param o
*/
public final void setMinimum( Object o )
{
this.oMinimum = o;
}
/**
*
* @return
*/
public final Object getMaximum( )
{
return oMaximum;
}
/**
* @param o
*/
public final void setMaximum( Object o )
{
this.oMaximum = o;
}
/**
*
* @return
*/
public final Object getStep( )
{
return oStep;
}
/**
* @param o
*/
public final void setStep( Object o )
{
this.oStep = o;
}
/**
*
* @param dValueMin
* @param dValueMax
*/
final void adjustAxisMinMax( CDateTime cdtMinValue, CDateTime cdtMaxValue )
{
int iUnit = asInteger( oUnit );
int iStep = asInteger( oStep );
if ( !bMinimumFixed )
{
oMinimum = cdtMinValue.backward( iUnit, iStep );
}
if ( !bMaximumFixed )
{
oMaximum = cdtMaxValue.forward( iUnit, iStep );
}
( (CDateTime) oMinimum ).clearBelow( iUnit );
( (CDateTime) oMaximum ).clearBelow( iUnit );
/*
* SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
* String sMin = sdf.format(((CDateTime) oMinimum).getTime()); String
* sMax = sdf.format(((CDateTime) oMaximum).getTime());
*/
}
/**
*
* @return
*/
final Object[] getMinMax( ) throws ChartException
{
Object oValue = null;
try
{
if ( ( iType & NUMERICAL ) == NUMERICAL )
{
double dValue, dMinValue = Double.MAX_VALUE, dMaxValue = -Double.MAX_VALUE;
dsiData.reset( );
while ( dsiData.hasNext( ) )
{
oValue = dsiData.next( );
if ( oValue == null ) // NULL VALUE CHECK
{
continue;
}
dValue = ( (Double) oValue ).doubleValue( );
if ( dValue < dMinValue )
dMinValue = dValue;
if ( dValue > dMaxValue )
dMaxValue = dValue;
}
return new Object[]{
new Double( dMinValue ), new Double( dMaxValue )
};
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
Calendar cValue;
Calendar caMin = null, caMax = null;
dsiData.reset( );
while ( dsiData.hasNext( ) )
{
oValue = dsiData.next( );
cValue = (Calendar) oValue;
if ( caMin == null )
{
caMin = cValue;
}
if ( caMax == null )
{
caMax = cValue;
}
if ( cValue == null ) // NULL VALUE CHECK
{
continue;
}
if ( cValue.before( caMin ) )
caMin = cValue;
else if ( cValue.after( caMax ) )
caMax = cValue;
}
return new Object[]{
new CDateTime( caMin ), new CDateTime( caMax )
};
}
}
catch ( ClassCastException ex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.invalid.axis.data.type", //$NON-NLS-1$
new Object[]{
oValue
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
return null;
}
/**
*
* @param oMinValue
* @param oMaxValue
*/
public final void updateAxisMinMax( Object oMinValue, Object oMaxValue )
{
if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
{
if ( ( iType & PERCENT ) == PERCENT )
{
oMaximum = new Double( 100 );
oMinimum = new Double( 1 );
oStep = new Double( 10 );
bMaximumFixed = true;
bMinimumFixed = true;
bStepFixed = true;
return;
}
final double dMinValue = asDouble( oMinValue ).doubleValue( );
final double dMaxValue = asDouble( oMaxValue ).doubleValue( );
final double dAbsMax = Math.abs( dMaxValue );
final double dAbsMin = Math.abs( dMinValue );
final double dStep = asDouble( oStep ).doubleValue( );
final double dStepLog = Math.log( dStep );
int iPow = (int) Math.floor( Math.log( dAbsMax ) / dStepLog ) + 1;
double dMaxAxis = Math.pow( dStep, iPow );
iPow = (int) Math.floor( Math.log( dAbsMin ) / dStepLog ) - 1;
double dMinAxis = Math.pow( dStep, iPow + 1 );
if ( !bMaximumFixed )
{
oMaximum = new Double( dMaxAxis );
}
if ( !bMinimumFixed )
{
oMinimum = new Double( dMinAxis );
}
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
int iUnit = asInteger( oUnit );
int iStep = asInteger( oStep );
CDateTime cdtMinValue = asDateTime( oMinValue );
CDateTime cdtMaxValue = asDateTime( oMaxValue );
if ( !bMinimumFixed )
{
oMinimum = cdtMinValue.backward( iUnit, iStep );
}
( (CDateTime) oMinimum ).clearBelow( iUnit );
if ( !bMaximumFixed )
{
oMaximum = cdtMaxValue.forward( iUnit, iStep );
}
( (CDateTime) oMaximum ).clearBelow( iUnit );
}
else
{
final double dMinValue = asDouble( oMinValue ).doubleValue( );
final double dMaxValue = asDouble( oMaxValue ).doubleValue( );
final double dAbsMax = Math.abs( dMaxValue );
final double dAbsMin = Math.abs( dMinValue );
final double dStep = asDouble( oStep ).doubleValue( );
double dMinAxis = ( dStep > 1 ) ? Math.floor( dAbsMin / dStep )
: Math.round( dAbsMin / dStep );
dMinAxis *= dStep;
if ( dMinAxis == dAbsMin )
{
dMinAxis += dStep;
if ( dMinValue < 0 )
{
dMinAxis = -dMinAxis;
}
else if ( dMinValue == 0 )
{
dMinAxis = 0;
}
}
else
{
if ( dMinValue < 0 )
{
dMinAxis = -( dMinAxis + dStep );
}
else if ( dMinAxis >= dMinValue && dMinAxis != 0 )
{
dMinAxis -= dStep;
}
}
double dMaxAxis = ( dStep > 1 ) ? Math.floor( dAbsMax / dStep )
: Math.round( dAbsMax / dStep );
dMaxAxis *= dStep;
if ( dMaxAxis == dAbsMax )
{
dMaxAxis += dStep;
if ( dMaxValue == 0 )
{
dMaxAxis = 0;
}
}
else if ( dMinAxis != dMaxValue )
{
if ( dMaxValue < 0 )
{
dMaxAxis = -( dMaxAxis - dStep );
}
else if ( dMaxValue > 0 )
{
if ( dMaxAxis < dMaxValue )
{
dMaxAxis += dStep;
}
}
}
if ( dMinValue < 0 && dMaxValue < 0 )
{
if ( dMaxAxis <= dMaxValue - dStep )
{
dMaxAxis += 2 * dStep;
}
}
if ( dMinValue > 0 && dMaxValue > 0 )
{
if ( dMinAxis >= dMinValue + dStep )
{
dMinAxis -= 2 * dStep;
}
}
// handle special case for min/max are both zero
if ( dMinValue == 0 && dMaxValue == 0 )
{
if ( dMinAxis >= 0 )
{
dMinAxis = -1;
}
if ( dMaxAxis <= 0 )
{
dMaxAxis = 1;
}
}
if ( !bMaximumFixed )
{
oMaximum = new Double( dMaxAxis );
}
if ( !bMinimumFixed )
{
oMinimum = new Double( dMinAxis );
}
}
}
/**
* Checks all labels for any overlap for a given axis' scale
*
* @param la
* @param iLabelLocation
*
* @return
*/
public final boolean checkFit( IDisplayServer xs, Label la,
int iLabelLocation ) throws ChartException
{
if ( iType == TEXT || bCategoryScale )
{
// not for text and category style
return true;
}
final double dAngleInDegrees = la.getCaption( )
.getFont( )
.getRotation( );
double x = 0, y = 0;
int iPointToCheck = 0;
if ( iLabelLocation == ABOVE || iLabelLocation == BELOW )
{
if ( iScaleDirection == BACKWARD )
{
iPointToCheck = ( dAngleInDegrees < 0 && dAngleInDegrees > -90 ) ? 1
: 2;
}
else
{
iPointToCheck = ( dAngleInDegrees < 0 && dAngleInDegrees > -90 ) ? 3
: 0;
}
}
else if ( iLabelLocation == LEFT || iLabelLocation == RIGHT )
{
if ( iScaleDirection == FORWARD )
{
iPointToCheck = ( dAngleInDegrees < 0 && dAngleInDegrees > -90 ) ? 0
: 1;
}
else
{
iPointToCheck = ( dAngleInDegrees < 0 && dAngleInDegrees > -90 ) ? 2
: 3;
}
}
double[] da = daTickCoordinates;
RotatedRectangle rrPrev = null, rrPrev2 = null, rr;
if ( ( iType & ( NUMERICAL | LINEAR ) ) == ( NUMERICAL | LINEAR ) )
{
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
final double dAxisStep = asDouble( getStep( ) ).doubleValue( );
String sText;
DecimalFormat df = null;
if ( fs == null ) // CREATE IF FORMAT SPECIFIER IS UNDEFINED
{
df = new DecimalFormat( getNumericPattern( ) );
}
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
for ( int i = 0; i < da.length; i++ )
{
// TODO special logic for last datapoint in non-equal scale unit
// case.
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = NULL_STRING;
}
if ( iLabelLocation == ABOVE || iLabelLocation == BELOW )
{
x = da[i] * dZoomFactor;
}
else if ( iLabelLocation == LEFT || iLabelLocation == RIGHT )
{
y = da[i] * dZoomFactor;
}
la.getCaption( ).setValue( sText );
try
{
rr = computePolygon( xs, iLabelLocation, la, x, y );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
Point p = rr.getPoint( iPointToCheck );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
if ( rrPrev2 != null
&& ( rrPrev2.contains( p )
|| rrPrev2.getPoint( iPointToCheck )
.equals( p ) || ChartUtil.intersects( rr,
rrPrev2 ) ) )
{
return false;
}
rrPrev2 = rr;
}
else
{
if ( rrPrev != null
&& ( rrPrev.contains( p )
|| rrPrev.getPoint( iPointToCheck )
.equals( p ) || ChartUtil.intersects( rr,
rrPrev ) ) )
{
return false;
}
rrPrev = rr;
}
dAxisValue += dAxisStep;
}
}
else if ( ( iType & ( NUMERICAL | LOGARITHMIC ) ) == ( NUMERICAL | LOGARITHMIC ) )
{
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
final double dAxisStep = asDouble( getStep( ) ).doubleValue( );
String sText;
NumberDataElement nde = NumberDataElementImpl.create( 0 );
DecimalFormat df = null;
for ( int i = 0; i < da.length; i++ )
{
nde.setValue( dAxisValue );
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( dAxisValue ) );
}
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = NULL_STRING;
}
if ( iLabelLocation == ABOVE || iLabelLocation == BELOW )
{
x = da[i] * dZoomFactor;
}
else if ( iLabelLocation == LEFT || iLabelLocation == RIGHT )
{
y = da[i] * dZoomFactor;
}
la.getCaption( ).setValue( sText );
try
{
rr = computePolygon( xs, iLabelLocation, la, x, y );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
Point p = rr.getPoint( iPointToCheck );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
if ( rrPrev2 != null
&& ( rrPrev2.contains( p )
|| rrPrev2.getPoint( iPointToCheck )
.equals( p ) || ChartUtil.intersects( rr,
rrPrev2 ) ) )
{
return false;
}
rrPrev2 = rr;
}
else
{
if ( rrPrev != null
&& ( rrPrev.contains( p ) || rrPrev.getPoint( iPointToCheck )
.equals( p ) ) )
{
return false;
}
rrPrev = rr;
}
dAxisValue *= dAxisStep;
}
}
else if ( iType == DATE_TIME )
{
CDateTime cdt, cdtAxisValue = asDateTime( oMinimum );
final int iUnit = asInteger( oUnit );
final int iStep = asInteger( oStep );
final IDateFormatWrapper sdf = DateFormatWrapperFactory.getPreferredDateFormat( iUnit,
rtc.getULocale( ) );
String sText;
cdt = cdtAxisValue;
for ( int i = 0; i < da.length; i++ )
{
sText = ValueFormatter.format( cdt, fs, rtc.getULocale( ), sdf );
if ( iLabelLocation == ABOVE || iLabelLocation == BELOW )
{
x = da[i] * dZoomFactor;
}
else if ( iLabelLocation == LEFT || iLabelLocation == RIGHT )
{
y = da[i] * dZoomFactor;
}
la.getCaption( ).setValue( sText );
try
{
rr = computePolygon( xs, iLabelLocation, la, x, y );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
Point p = rr.getPoint( iPointToCheck );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
if ( rrPrev2 != null
&& ( rrPrev2.contains( p )
|| rrPrev2.getPoint( iPointToCheck )
.equals( p ) || ChartUtil.intersects( rr,
rrPrev2 ) ) )
{
return false;
}
rrPrev2 = rr;
}
else
{
if ( rrPrev != null
&& ( rrPrev.contains( p ) || rrPrev.getPoint( iPointToCheck )
.equals( p ) ) )
{
return false;
}
rrPrev = rr;
}
cdt = cdtAxisValue.forward( iUnit, iStep * ( i + 1 ) ); // ALWAYS
// W.R.T
// START
// VALUE
}
}
return true;
}
/**
* Calculates visibility for axis labels.
*
* @param xs
* @param la
* @param iLabelLocation
* @return
* @throws ChartException
*/
final protected boolean[] checkTickLabelsVisibility( IDisplayServer xs,
Label la, int iLabelLocation ) throws ChartException
{
boolean[] ba = new boolean[daTickCoordinates.length];
boolean vis = la.isSetVisible( ) && la.isVisible( );
Arrays.fill( ba, vis );
// initialize stagger state.
baTickLabelStaggered = new boolean[daTickCoordinates.length];
boolean staggerEnabled = isAxisLabelStaggered( );
// all non-visible label, skip checking.
if ( !vis )
{
return ba;
}
// check with showing interval.
if ( iLabelShowingInterval >= 2 )
{
for ( int i = 0, c = 0; i < ba.length; i++ )
{
ba[i] = ( i % iLabelShowingInterval == 0 );
if ( staggerEnabled && ba[i] )
{
c++;
baTickLabelStaggered[i] = ( c % 2 == 0 );
}
}
}
else
{
// no interval applied.
if ( staggerEnabled )
{
for ( int i = 0; i < ba.length; i++ )
{
baTickLabelStaggered[i] = ( i % 2 != 0 );
}
}
}
if ( iType != TEXT && !bCategoryScale )
{
// This should be already auto scaled, just return here.
return ba;
}
final double dAngleInDegrees = la.getCaption( )
.getFont( )
.getRotation( );
double x = 0, y = 0;
int iNewPointToCheck = 0, iPrevPointToCheck = 0;
/*
* Rectangle points are layed out like this
*
* 0 1 NextLabel 3 2
*
* 0 1 0 1 PreviousLabel NextLabel 3 2 3 2
*
*
*/
boolean isNegativeRotation = ( dAngleInDegrees < 0 && dAngleInDegrees > -90 );
switch ( iLabelLocation )
{
case ABOVE :
iNewPointToCheck = isNegativeRotation ? 3 : 0;
iPrevPointToCheck = isNegativeRotation ? 1 : 3;
break;
case BELOW :
iNewPointToCheck = isNegativeRotation ? ( iScaleDirection == BACKWARD ? 1
: 3 )
: ( iScaleDirection == BACKWARD ? 2 : 0 );
iPrevPointToCheck = isNegativeRotation ? ( iScaleDirection == BACKWARD ? 2
: 0 )
: ( iScaleDirection == BACKWARD ? 0 : 2 );
break;
case LEFT :
iNewPointToCheck = iScaleDirection == FORWARD ? 1 : 2;
iPrevPointToCheck = iScaleDirection == FORWARD ? 2 : 1;
break;
case RIGHT :
iNewPointToCheck = iScaleDirection == FORWARD ? 0 : 3;
iPrevPointToCheck = iScaleDirection == FORWARD ? 3 : 0;
break;
}
RotatedRectangle rrPrev[] = new RotatedRectangle[2];
DataSetIterator dsi = getData( );
dsi.reset( );
final int iDateTimeUnit = ( iType == IConstants.DATE_TIME ) ? CDateTime.computeUnit( dsi )
: IConstants.UNDEFINED;
String sText = null;
dsi.reset( );
for ( int i = 0; i < daTickCoordinates.length - 1; i++ )
{
Object oValue = null;
if ( dsi.hasNext( ) )
{
oValue = dsi.next( );
}
// only check visible labels.
if ( ba[i] )
{
sText = formatCategoryValue( iType, oValue, iDateTimeUnit );
if ( iLabelLocation == ABOVE || iLabelLocation == BELOW )
{
x = this.daTickCoordinates[i] * dZoomFactor;
}
else if ( iLabelLocation == LEFT || iLabelLocation == RIGHT )
{
y = this.daTickCoordinates[i] * dZoomFactor;
}
la.getCaption( ).setValue( sText );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
// check second line.
ba[i] = checkOneTickLabelVisibility( rrPrev,
1,
iLabelLocation,
x,
y,
iNewPointToCheck,
iPrevPointToCheck,
la,
xs,
i );
}
else
{
// check first line.
ba[i] = checkOneTickLabelVisibility( rrPrev,
0,
iLabelLocation,
x,
y,
iNewPointToCheck,
iPrevPointToCheck,
la,
xs,
i );
}
}
}
return ba;
}
/**
* Check the label visibility for a given tick label
*
* @param rrPrev
* The 2-array of RotatedRectangle, to keep the previous label
* rectangle
* @param arrayIndex
* The index to use in rrPrev, 0 or 1, depending on staggering
* @param iLabelLocation
* The location of the label (left/right, or above/below)
* @param x
* The x location of the tick
* @param y
* The y location of the tick
* @param iNewPointToCheck
* the point to check for intersection in the new label
* @param iPrevPointToCheck
* the point to check for intersection in the previous label
* @param la
* The label
* @param xs
* the Display Server
* @param tickIndex
* The tick index, must start from 0.
* @return
*/
protected final boolean checkOneTickLabelVisibility(
final RotatedRectangle[] rrPrev, final int arrayIndex,
final int iLabelLocation, final double x, final double y,
final int iNewPointToCheck, final int iPrevPointToCheck,
final Label la, final IDisplayServer xs, final int tickIndex )
{
if ( rrPrev[arrayIndex] == null )
{
// Always show the first label.
rrPrev[arrayIndex] = computePolygon( xs, iLabelLocation, la, x, y );;
return true;
}
else
{
Point previousPoint = rrPrev[arrayIndex].getPoint( iPrevPointToCheck );
// quick check for false (fast)
if ( quickCheckVisibility( iLabelLocation, previousPoint, x, y ) )
{
// extensive check (expensive)
RotatedRectangle rr = computePolygon( xs,
iLabelLocation,
la,
x,
y );
Point p = rr.getPoint( iNewPointToCheck );
boolean visible = !( rrPrev[arrayIndex].contains( p ) || ChartUtil.intersects( rr,
rrPrev[arrayIndex] ) );
if ( visible )
{
rrPrev[arrayIndex] = rr;
return true;
}
}
return false;
}
}
protected boolean quickCheckVisibility( int iLabelLocation,
Point previousPoint, double x, double y )
{
// quick check first (fast)
if ( iLabelLocation == ABOVE || iLabelLocation == BELOW )
{
if ( ( iScaleDirection == BACKWARD && previousPoint.getX( ) < x )
|| ( iScaleDirection != BACKWARD && previousPoint.getX( ) > x ) )
{
return false;
}
}
else if ( iLabelLocation == LEFT || iLabelLocation == RIGHT )
{
if ( ( iScaleDirection == FORWARD && previousPoint.getY( ) > y )
|| ( iScaleDirection != FORWARD && previousPoint.getY( ) < y ) )
{
return false;
}
}
return true;
}
/**
*
*/
final void resetShifts( )
{
dStartShift = 0;
dEndShift = 0;
}
/**
*
* @return
*/
public final double getStart( )
{
return dStart;
}
/**
*
* @return
*/
public final double getEnd( )
{
return dEnd;
}
/**
*
* @return
*/
final double getStartShift( )
{
return dStartShift;
}
/**
*
* @return
*/
final double getEndShift( )
{
return dEndShift;
}
/**
*
* @param ax
* @param dsi
* @param iType
* @param dStart
* @param dEnd
*
* @return
*/
static final AutoScale computeScale( IDisplayServer xs, OneAxis ax,
DataSetIterator dsi, int iType, double dStart, double dEnd,
DataElement oMinimum, DataElement oMaximum, Double oStep,
FormatSpecifier fs, RunTimeContext rtc, int direction )
throws ChartException
{
return computeScale( xs,
ax,
dsi,
iType,
dStart,
dEnd,
oMinimum,
oMaximum,
oStep,
fs,
rtc,
direction,
1.0 );
}
/**
*
* @param ax
* @param dsi
* @param iType
* @param dStart
* @param dEnd
*
* @return
*/
static final AutoScale computeScale( IDisplayServer xs, OneAxis ax,
DataSetIterator dsi, int iType, double dStart, double dEnd,
DataElement oMinimum, DataElement oMaximum, Double oStep,
FormatSpecifier fs, RunTimeContext rtc, int direction,
double zoomFactor ) throws ChartException
{
final Label la = ax.getLabel( );
final int iLabelLocation = ax.getLabelPosition( );
final int iOrientation = ax.getOrientation( );
AutoScale sc = null;
AutoScale scCloned = null;
if ( ( iType & TEXT ) == TEXT || ax.isCategoryScale( ) )
{
sc = new AutoScale( iType );
sc.fs = fs;
sc.rtc = rtc;
sc.bCategoryScale = true;
sc.bAxisLabelStaggered = ax.isAxisLabelStaggered( );
sc.iLabelShowingInterval = ax.getLableShowingInterval( );
sc.dZoomFactor = zoomFactor;
sc.setData( dsi );
sc.setDirection( direction );
sc.computeTicks( xs,
ax.getLabel( ),
iLabelLocation,
iOrientation,
dStart,
dEnd,
false,
null );
}
else if ( ( iType & LINEAR ) == LINEAR )
{
Object oValue;
double dValue, dMinValue = Double.MAX_VALUE, dMaxValue = -Double.MAX_VALUE;
dsi.reset( );
while ( dsi.hasNext( ) )
{
oValue = dsi.next( );
if ( oValue == null ) // NULL VALUE CHECK
{
continue;
}
dValue = ( (Double) oValue ).doubleValue( );
if ( dValue < dMinValue )
dMinValue = dValue;
if ( dValue > dMaxValue )
dMaxValue = dValue;
}
double dDelta = dMaxValue - dMinValue;
if ( dDelta == 0 ) // PREVENT INFINITE LOOP DUE TO LOG(dDelta) IN
// AUTO SCALING ALGORITHM
{
dDelta = 1;
}
final double dAbsMax = Math.abs( dMaxValue );
final double dAbsMin = Math.abs( dMinValue );
double dStep = Math.max( dAbsMax, dAbsMin );
dStep = Math.floor( Math.log( dDelta ) / LOG_10 );
dStep = Math.pow( 10, dStep );
sc = new AutoScale( iType,
new Double( 0 ),
new Double( 0 ),
new Double( dStep ) );
sc.setData( dsi );
sc.setDirection( direction );
sc.fs = fs; // FORMAT SPECIFIER
sc.rtc = rtc; // LOCALE
sc.bAxisLabelStaggered = ax.isAxisLabelStaggered( );
sc.iLabelShowingInterval = ax.getLableShowingInterval( );
sc.dZoomFactor = zoomFactor;
// OVERRIDE MINIMUM IF SPECIFIED
if ( oMinimum != null )
{
if ( oMinimum instanceof NumberDataElement )
{
sc.oMinimum = new Double( ( (NumberDataElement) oMinimum ).getValue( ) );
}
/*
* else if (oMinimum instanceof DateTimeDataElement) {
* sc.oMinimum = ((DateTimeDataElement)
* oMinimum).getValueAsCDateTime(); }
*/
else
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.invalid.minimum.scale.value", //$NON-NLS-1$
new Object[]{
sc.oMinimum,
ax.getModelAxis( ).getType( ).getName( )
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
sc.bMinimumFixed = true;
}
// OVERRIDE MAXIMUM IF SPECIFIED
if ( oMaximum != null )
{
if ( oMaximum instanceof NumberDataElement )
{
sc.oMaximum = new Double( ( (NumberDataElement) oMaximum ).getValue( ) );
}
/*
* else if (oMaximum instanceof DateTimeDataElement) {
* sc.oMaximum = ((DateTimeDataElement)
* oMaximum).getValueAsCDateTime(); }
*/
else
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.invalid.maximum.scale.value", //$NON-NLS-1$
new Object[]{
sc.oMaximum,
ax.getModelAxis( ).getType( ).getName( )
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
sc.bMaximumFixed = true;
}
// OVERRIDE STEP IF SPECIFIED
if ( oStep != null )
{
sc.oStep = oStep;
sc.bStepFixed = true;
// VALIDATE OVERRIDDEN STEP
if ( ( (Double) sc.oStep ).doubleValue( ) <= 0 )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.invalid.step.value", //$NON-NLS-1$
new Object[]{
oStep
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
}
// VALIDATE OVERRIDDEN MIN/MAX
if ( sc.bMaximumFixed && sc.bMinimumFixed )
{
if ( ( (Double) sc.oMinimum ).doubleValue( ) > ( (Double) sc.oMaximum ).doubleValue( ) )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.min.largerthan.max", //$NON-NLS-1$
new Object[]{
sc.oMinimum, sc.oMaximum
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
}
final Object oMinValue = new Double( dMinValue );
final Object oMaxValue = new Double( dMaxValue );
sc.updateAxisMinMax( oMinValue, oMaxValue );
sc.computeTicks( xs,
ax.getLabel( ),
iLabelLocation,
iOrientation,
dStart,
dEnd,
false,
null );
dStart = sc.dStart;
dEnd = sc.dEnd;
boolean bFirstFit = sc.checkFit( xs, la, iLabelLocation );
boolean bFits = bFirstFit;
boolean bZoomSuccess = false;
// THE AUTO ZOOM LOOP
while ( bFits == bFirstFit )
{
bZoomSuccess = true;
scCloned = (AutoScale) sc.clone( );
if ( sc.bStepFixed ) // DO NOT AUTO ZOOM IF STEP IS FIXED
{
break;
}
if ( bFirstFit )
{
if ( !bFits )
{
break;
}
bZoomSuccess = sc.zoomIn( );
}
else
{
if ( !bFits && sc.getTickCordinates( ).length == 2 )
{
break;
}
bZoomSuccess = sc.zoomOut( );
}
if ( !bZoomSuccess )
break;
sc.updateAxisMinMax( oMinValue, oMaxValue );
sc.computeTicks( xs,
ax.getLabel( ),
iLabelLocation,
iOrientation,
dStart,
dEnd,
false,
null );
bFits = sc.checkFit( xs, la, iLabelLocation );
if ( !bFits && sc.getTickCordinates( ).length == 2 )
{
sc = scCloned;
break;
}
}
// RESTORE TO LAST SCALE BEFORE ZOOM
if ( scCloned != null && bFirstFit && bZoomSuccess )
{
sc = scCloned;
}
}
else if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
{
Object oValue;
double dValue, dMinValue = Double.MAX_VALUE, dMaxValue = -Double.MAX_VALUE;
if ( ( iType & PERCENT ) == PERCENT )
{
dMinValue = 0;
dMaxValue = 100;
}
else
{
dsi.reset( );
while ( dsi.hasNext( ) )
{
oValue = dsi.next( );
if ( oValue == null ) // NULL VALUE CHECK
{
continue;
}
dValue = ( (Double) oValue ).doubleValue( );
if ( dValue < dMinValue )
dMinValue = dValue;
if ( dValue > dMaxValue )
dMaxValue = dValue;
}
}
final Object oMinValue = new Double( dMinValue );
final Object oMaxValue = new Double( dMaxValue );
sc = new AutoScale( iType,
new Double( 0 ),
new Double( 0 ),
new Double( 10 ) );
sc.fs = fs; // FORMAT SPECIFIER
sc.rtc = rtc; // LOCALE
sc.bAxisLabelStaggered = ax.isAxisLabelStaggered( );
sc.iLabelShowingInterval = ax.getLableShowingInterval( );
sc.dZoomFactor = zoomFactor;
sc.setData( dsi );
sc.setDirection( direction );
sc.updateAxisMinMax( oMinValue, oMaxValue );
if ( ( iType & PERCENT ) == PERCENT )
{
sc.bStepFixed = true;
sc.bMaximumFixed = true;
sc.bMinimumFixed = true;
}
sc.computeTicks( xs,
ax.getLabel( ),
iLabelLocation,
iOrientation,
dStart,
dEnd,
false,
null );
if ( ( iType & PERCENT ) == PERCENT )
{
return sc;
}
dStart = sc.dStart;
dEnd = sc.dEnd;
boolean bFirstFit = sc.checkFit( xs, la, iLabelLocation );
boolean bFits = bFirstFit;
boolean bZoomSuccess = false;
// sc.oDebug = String.valueOf(bFirstFit);
while ( bFits == bFirstFit )
{
bZoomSuccess = true;
scCloned = (AutoScale) sc.clone( );
if ( sc.bStepFixed ) // DO NOT AUTO ZOOM IF STEP IS FIXED
{
break;
}
if ( bFirstFit )
{
if ( !bFits )
{
break;
}
bZoomSuccess = sc.zoomIn( );
}
else
{
if ( !bFits && sc.getTickCordinates( ).length == 2 )
{
break;
}
bZoomSuccess = sc.zoomOut( );
}
if ( !bZoomSuccess )
break;
sc.updateAxisMinMax( oMinValue, oMaxValue );
sc.computeTicks( xs,
ax.getLabel( ),
iLabelLocation,
iOrientation,
dStart,
dEnd,
false,
null );
bFits = sc.checkFit( xs, la, iLabelLocation );
if ( !bFits && sc.getTickCordinates( ).length == 2 )
{
sc = scCloned;
break;
}
}
// RESTORE TO LAST SCALE BEFORE ZOOM
if ( scCloned != null && bFirstFit && bZoomSuccess )
{
sc = scCloned;
}
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
Calendar cValue;
Calendar caMin = null, caMax = null;
dsi.reset( );
while ( dsi.hasNext( ) )
{
cValue = (Calendar) dsi.next( );
if ( cValue == null ) // NULL VALUE CHECK
{
continue;
}
if ( caMin == null )
{
caMin = cValue;
}
if ( caMax == null )
{
caMax = cValue;
}
if ( cValue.before( caMin ) )
caMin = cValue;
else if ( cValue.after( caMax ) )
caMax = cValue;
}
CDateTime cdtMinValue = new CDateTime( caMin );
CDateTime cdtMaxValue = new CDateTime( caMax );
int iUnit = CDateTime.getDifference( cdtMinValue, cdtMaxValue );
// Can't detect a difference, assume ms
if ( iUnit == 0 )
iUnit = CDateTime.SECOND;
CDateTime cdtMinAxis = cdtMinValue.backward( iUnit, 1 );
CDateTime cdtMaxAxis = cdtMaxValue.forward( iUnit, 1 );
cdtMinAxis.clearBelow( iUnit );
cdtMaxAxis.clearBelow( iUnit );
sc = new AutoScale( DATE_TIME,
cdtMinAxis,
cdtMaxAxis,
new Integer( iUnit ),
new Integer( 1 ) );
sc.setDirection( direction );
sc.fs = fs; // FORMAT SPECIFIER
sc.rtc = rtc; // LOCALE
sc.bAxisLabelStaggered = ax.isAxisLabelStaggered( );
sc.iLabelShowingInterval = ax.getLableShowingInterval( );
sc.dZoomFactor = zoomFactor;
sc.computeTicks( xs,
la,
iLabelLocation,
iOrientation,
dStart,
dEnd,
false,
null );
dStart = sc.dStart;
dEnd = sc.dEnd;
// OVERRIDE MINIMUM IF SPECIFIED
if ( oMinimum != null )
{
if ( oMinimum instanceof DateTimeDataElement )
{
sc.oMinimum = ( (DateTimeDataElement) oMinimum ).getValueAsCDateTime( );
}
else
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.invalid.minimum.scale.value", //$NON-NLS-1$
new Object[]{
oMinimum,
ax.getModelAxis( ).getType( ).getName( )
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
sc.bMinimumFixed = true;
}
// OVERRIDE MAXIMUM IF SPECIFIED
if ( oMaximum != null )
{
if ( oMaximum instanceof DateTimeDataElement )
{
sc.oMaximum = ( (DateTimeDataElement) oMaximum ).getValueAsCDateTime( );
}
else
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.invalid.maximum.scale.value", //$NON-NLS-1$
new Object[]{
sc.oMaximum,
ax.getModelAxis( ).getType( ).getName( )
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
sc.bMaximumFixed = true;
}
// TODO why is this commented out? Needs to be implemented
// OVERRIDE STEP IF SPECIFIED
/*
* if (oStep != null) { sc.oStep = oStep; sc.bStepFixed = true; //
* VALIDATE OVERRIDDEN STEP if (((Double) sc.oStep).doubleValue() <=
* 0) { throw new GenerationException("Invalid 'step({0}) <= 0'
* specified for axis scale" + oStep ); //
* i18n_CONCATENATIONS_REMOVED } }
*/
// VALIDATE OVERRIDDEN MIN/MAX
if ( sc.bMaximumFixed && sc.bMinimumFixed )
{
if ( ( (CDateTime) sc.oMinimum ).after( sc.oMaximum ) )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.min.largerthan.max", //$NON-NLS-1$
new Object[]{
sc.oMinimum, sc.oMaximum
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
}
boolean bFirstFit = sc.checkFit( xs, la, iLabelLocation );
boolean bFits = bFirstFit, bZoomSuccess = false;
while ( bFits == bFirstFit )
{
bZoomSuccess = true;
scCloned = (AutoScale) sc.clone( );
if ( sc.bStepFixed ) // DO NOT AUTO ZOOM IF STEP IS FIXED
{
break;
}
if ( bFirstFit )
{
if ( !bFits )
{
break;
}
bZoomSuccess = sc.zoomIn( );
}
else
{
if ( !bFits && sc.getTickCordinates( ).length == 2 )
{
break;
}
bZoomSuccess = sc.zoomOut( );
}
if ( !bZoomSuccess )
break;
sc.adjustAxisMinMax( cdtMinValue, cdtMaxValue );
sc.computeTicks( xs,
la,
iLabelLocation,
iOrientation,
dStart,
dEnd,
false,
null );
bFits = sc.checkFit( xs, la, iLabelLocation );
if ( !bFits && sc.getTickCordinates( ).length == 2 )
{
sc = scCloned;
break;
}
}
// RESTORE TO LAST SCALE BEFORE ZOOM
if ( scCloned != null && bFirstFit && bZoomSuccess )
{
sc = scCloned;
}
}
sc.setData( dsi );
return sc;
}
/**
*
* @param la
* @param iLabelLocation
* @param iOrientation
* @param dStart
* @param dEnd
* @param bConsiderStartEndLabels
* @param aax
*/
public final int computeTicks( IDisplayServer xs, Label la,
int iLabelLocation, int iOrientation, double dStart, double dEnd,
boolean bConsiderStartEndLabels, AllAxes aax )
throws ChartException
{
return computeTicks( xs,
la,
iLabelLocation,
iOrientation,
dStart,
dEnd,
bConsiderStartEndLabels,
bConsiderStartEndLabels,
aax );
}
/**
*
* @param la
* @param iLabelLocation
* @param iOrientation
* @param dStart
* @param dEnd
* @param bConsiderStartEndLabels
* @param aax
*/
public final int computeTicks( IDisplayServer xs, Label la,
int iLabelLocation, int iOrientation, double dStart, double dEnd,
boolean bConsiderStartLabel, boolean bConsiderEndLabel, AllAxes aax )
throws ChartException
{
int nTicks = 0;
double dLength = 0;
double dTickGap = 0;
int iDirection = ( iScaleDirection == AUTO ) ? ( ( iOrientation == HORIZONTAL ) ? FORWARD
: BACKWARD )
: iScaleDirection;
DataSetIterator dsi = getData( );
if ( bConsiderStartLabel || bConsiderEndLabel )
{
computeAxisStartEndShifts( xs,
la,
iOrientation,
iLabelLocation,
aax );
if ( bConsiderStartLabel )
{
dStart += dStartShift * iDirection;
}
if ( bConsiderEndLabel )
{
dEnd += dEndShift * -iDirection;
}
}
if ( ( iType & TEXT ) == TEXT || bCategoryScale )
{
nTicks = dsi.size( ) + 1;
dLength = Math.abs( dStart - dEnd );
}
else if ( ( iType & NUMERICAL ) == NUMERICAL )
{
nTicks = getTickCount( );
dLength = Math.abs( dStart - dEnd );
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
final CDateTime cdt1 = (CDateTime) oMinimum;
final CDateTime cdt2 = (CDateTime) oMaximum;
final double dNumberOfSteps = Math.ceil( CDateTime.computeDifference( cdt2,
cdt1,
asInteger( oUnit ) ) );
nTicks = (int) ( dNumberOfSteps / asInteger( oStep ) ) + 1;
dLength = Math.abs( dStart - dEnd );
}
else
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.unknown.axis.type.tick.computations", //$NON-NLS-1$
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
if ( !bCategoryScale
&& ( iType & NUMERICAL ) == NUMERICAL
&& ( iType & LINEAR ) == LINEAR )
{
double dMax = asDouble( oMaximum ).doubleValue( );
double dMin = asDouble( oMinimum ).doubleValue( );
double dStep = asDouble( oStep ).doubleValue( );
dTickGap = Math.min( Math.abs( dStep / ( dMax - dMin ) * dLength ),
dLength )
* iDirection;
}
else
{
dTickGap = dLength / ( nTicks - 1 ) * iDirection;
}
double d = dStart + dTickGap;
final double[] da = new double[nTicks];
for ( int i = 1; i < nTicks - 1; i++, d += dTickGap )
{
da[i] = d;
}
da[0] = dStart;
da[nTicks - 1] = dEnd;
setTickCordinates( null );
setEndPoints( dStart, dEnd );
setTickCordinates( da );
baTickLabelVisible = checkTickLabelsVisibility( xs, la, iLabelLocation );
return nTicks;
}
/**
* Returns the formatted value for given Axis type and value.
*
* @param iType
* @param oValue
* @return
*/
public final String formatCategoryValue( int iType, Object oValue,
int iDateTimeUnit )
{
if ( oValue == null )
{
return IConstants.NULL_STRING;
}
if ( iType == IConstants.TEXT ) // MOST LIKELY
{
return oValue.toString( );
}
else if ( iType == IConstants.DATE_TIME )
{
final Calendar ca = (Calendar) oValue;
IDateFormatWrapper sdf = null;
if ( fs == null ) // ONLY COMPUTE INTERNALLY IF FORMAT SPECIFIER
// ISN'T DEFINED
{
sdf = DateFormatWrapperFactory.getPreferredDateFormat( iDateTimeUnit,
rtc.getULocale( ) );
}
// ADJUST THE START POSITION
try
{
return ValueFormatter.format( ca, fs, rtc.getULocale( ), sdf );
}
catch ( ChartException dfex )
{
logger.log( dfex );
return IConstants.NULL_STRING;
}
}
else if ( ( iType & IConstants.NUMERICAL ) == IConstants.NUMERICAL )
{
DecimalFormat df = null;
if ( fs == null ) // ONLY COMPUTE INTERNALLY IF FORMAT SPECIFIER
// ISN'T DEFINED
{
df = new DecimalFormat( getNumericPattern( ) );
}
try
{
return ValueFormatter.format( oValue, fs, rtc.getULocale( ), df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
return IConstants.NULL_STRING;
}
}
return IConstants.NULL_STRING;
}
/**
* Computes the axis start/end shifts (due to start/end labels) and also
* takes into consideration all start/end shifts of any overlay axes in the
* same direction as the current scale.
*
* @param la
* @param iOrientation
* @param iLocation
* @param aax
*/
final void computeAxisStartEndShifts( IDisplayServer xs, Label la,
int iOrientation, int iLocation, AllAxes aax )
throws ChartException
{
final double dMaxSS = ( aax != null && iOrientation == aax.getOrientation( ) ) ? aax.getMaxStartShift( )
: 0;
final double dMaxES = ( aax != null && iOrientation == aax.getOrientation( ) ) ? aax.getMaxEndShift( )
: 0;
if ( !la.isVisible( ) )
{
dStartShift = dMaxSS;
dEndShift = dMaxES;
return;
}
if ( getType( ) == TEXT || bCategoryScale )
{
// COMPUTE THE BOUNDING BOXES FOR FIRST AND LAST LABEL TO ADJUST
// START/END OF X-AXIS
final double dUnitSize = getUnitSize( );
final DataSetIterator dsi = getData( );
final int iDateTimeUnit;
BoundingBox bb = null;
try
{
iDateTimeUnit = ( getType( ) == IConstants.DATE_TIME ) ? CDateTime.computeUnit( dsi )
: IConstants.UNDEFINED;
}
catch ( ClassCastException e )
{
// Happens when data in dsi is not of DateTime format
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.dataset.non.datetime", //$NON-NLS-1$
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
final double rotation = la.getCaption( ).getFont( ).getRotation( );
final boolean bCenter = rotation == 0
|| rotation == 90
|| rotation == -90;
// TODO check first visible label shift.
if ( !isTickLabelVisible( 0 ) )
{
dStartShift = dMaxSS;
}
else
{
// ADJUST THE START POSITION
la.getCaption( ).setValue( formatCategoryValue( getType( ),
dsi.first( ),
iDateTimeUnit ) );
try
{
bb = computeBox( xs, iLocation, la, 0, 0 );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
if ( iOrientation == VERTICAL ) // VERTICAL AXIS
{
if ( bCenter )
{
dStartShift = Math.max( dMaxSS,
( dUnitSize > bb.getHeight( ) ) ? 0
: ( bb.getHeight( ) - dUnitSize ) / 2 );
}
else if ( iScaleDirection == FORWARD )
{
dStartShift = Math.max( dMaxSS, bb.getHotPoint( )
- dUnitSize
/ 2 );
}
else
{
dStartShift = Math.max( dMaxSS, bb.getHeight( )
- bb.getHotPoint( )
- dUnitSize
/ 2 );
}
}
else if ( iOrientation == HORIZONTAL ) // HORIZONTAL AXIS
{
if ( bCenter )
{
dStartShift = Math.max( dMaxSS,
( dUnitSize > bb.getWidth( ) ) ? 0
: ( bb.getWidth( ) - dUnitSize ) / 2 );
}
else if ( iScaleDirection == BACKWARD )
{
dStartShift = Math.max( dMaxSS, bb.getWidth( )
- bb.getHotPoint( )
- dUnitSize
/ 2 );
}
else
{
dStartShift = Math.max( dMaxSS, bb.getHotPoint( )
- dUnitSize
/ 2 );
}
}
}
// TODO check last visible label shift.
if ( !isTickLabelVisible( dsi.size( ) - 1 ) )
{
dEndShift = dMaxES;
}
else
{
// ADJUST THE END POSITION
la.getCaption( ).setValue( formatCategoryValue( getType( ),
dsi.last( ),
iDateTimeUnit ) );
try
{
bb = computeBox( xs, iLocation, la, 0, dEnd );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
if ( iOrientation == VERTICAL ) // VERTICAL AXIS
{
if ( bCenter )
{
dEndShift = Math.max( dMaxES,
( dUnitSize > bb.getHeight( ) ) ? 0
: ( bb.getHeight( ) - dUnitSize ) / 2 );
}
else if ( iScaleDirection == FORWARD )
{
dEndShift = Math.max( dMaxES, bb.getHeight( )
- bb.getHotPoint( )
- dUnitSize
/ 2 );
}
else
{
dEndShift = Math.max( dMaxES, bb.getHotPoint( )
- dUnitSize
/ 2 );
}
}
else if ( iOrientation == HORIZONTAL ) // HORIZONTAL AXIS
{
if ( bCenter )
{
dEndShift = Math.max( dMaxES,
( dUnitSize > bb.getWidth( ) ) ? 0
: ( bb.getWidth( ) - dUnitSize ) / 2 );
}
else if ( iScaleDirection == BACKWARD )
{
dEndShift = Math.max( dMaxES, bb.getHotPoint( )
- dUnitSize
/ 2 );
}
else
{
dEndShift = Math.max( dMaxES, bb.getWidth( )
- bb.getHotPoint( )
- dUnitSize
/ 2 );
}
}
}
}
else if ( ( iType & NUMERICAL ) == NUMERICAL )
{
if ( ( iType & LINEAR ) == LINEAR )
{
// ADJUST THE START POSITION
DecimalFormat df = null;
if ( fs == null ) // ONLY COMPUTE INTERNALLY IF FORMAT
// SPECIFIER
// ISN'T DEFINED
{
df = new DecimalFormat( getNumericPattern( ) );
}
String sValue = null;
try
{
sValue = ValueFormatter.format( getMinimum( ),
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sValue = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sValue );
BoundingBox bb = null;
try
{
bb = computeBox( xs, iLocation, la, 0, 0 );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
if ( iOrientation == VERTICAL ) // VERTICAL AXIS
{
dStartShift = Math.max( dMaxSS,
( bb.getHeight( ) - bb.getHotPoint( ) ) );
}
else if ( iOrientation == HORIZONTAL )
{
dStartShift = Math.max( dMaxSS, bb.getHotPoint( ) );
}
// ADJUST THE END POSITION
try
{
sValue = ValueFormatter.format( getMaximum( ),
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sValue = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sValue );
try
{
bb = computeBox( xs, iLocation, la, 0, 0 );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
if ( iOrientation == VERTICAL ) // VERTICAL AXIS
{
dEndShift = Math.max( dMaxES, bb.getHotPoint( ) );
}
else if ( iOrientation == HORIZONTAL )
{
dEndShift = Math.max( dMaxES,
( bb.getWidth( ) - bb.getHotPoint( ) ) );
}
}
else if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
{
// ADJUST THE START POSITION
final double dMinimum = asDouble( getMinimum( ) ).doubleValue( );
DecimalFormat df = null;
if ( fs == null ) // ONLY COMPUTE INTERNALLY IF FORMAT
// SPECIFIER
// ISN'T DEFINED
{
df = new DecimalFormat( getNumericPattern( dMinimum ) );
}
String sValue = null;
try
{
sValue = ValueFormatter.format( getMinimum( ),
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sValue = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sValue );
BoundingBox bb = null;
try
{
bb = computeBox( xs, iLocation, la, 0, 0 );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
if ( iOrientation == VERTICAL ) // VERTICAL AXIS
{
dStartShift = Math.max( dMaxSS,
( bb.getHeight( ) - bb.getHotPoint( ) ) );
}
else if ( iOrientation == HORIZONTAL )
{
dStartShift = Math.max( dMaxSS, bb.getHotPoint( ) );
}
// ADJUST THE END POSITION
final double dMaximum = asDouble( getMaximum( ) ).doubleValue( );
if ( fs == null ) // ONLY COMPUTE INTERNALLY (DIFFERENT FROM
// MINIMUM) IF FORMAT SPECIFIER ISN'T DEFINED
{
df = new DecimalFormat( getNumericPattern( dMaximum ) );
}
try
{
sValue = ValueFormatter.format( getMaximum( ),
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sValue = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sValue );
try
{
bb = computeBox( xs, iLocation, la, 0, 0 );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
if ( iOrientation == VERTICAL ) // VERTICAL AXIS
{
dEndShift = Math.max( dMaxES, bb.getHotPoint( ) );
}
else if ( iOrientation == HORIZONTAL )
{
dEndShift = Math.max( dMaxES,
( bb.getWidth( ) - bb.getHotPoint( ) ) );
}
}
}
else if ( getType( ) == DATE_TIME )
{
// COMPUTE THE BOUNDING BOXES FOR FIRST AND LAST LABEL TO ADJUST
// START/END OF X-AXIS
CDateTime cdt = asDateTime( getMinimum( ) );
final int iUnit = asInteger( oUnit );
IDateFormatWrapper sdf = null;
String sText = null;
if ( fs == null ) // ONLY COMPUTE INTERNALLY IF FORMAT SPECIFIER
// ISN'T DEFINED
{
sdf = DateFormatWrapperFactory.getPreferredDateFormat( iUnit,
rtc.getULocale( ) );
}
// ADJUST THE START POSITION
try
{
sText = ValueFormatter.format( cdt, fs, rtc.getULocale( ), sdf );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
BoundingBox bb = null;
try
{
bb = computeBox( xs, iLocation, la, 0, 0 );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
if ( iOrientation == VERTICAL ) // VERTICAL AXIS
{
dStartShift = Math.max( dMaxSS,
( bb.getHeight( ) - bb.getHotPoint( ) ) );
}
else if ( iOrientation == HORIZONTAL )
{
dStartShift = Math.max( dMaxSS, bb.getHotPoint( ) );
}
// ADJUST THE END POSITION
cdt = asDateTime( getMaximum( ) );
try
{
sText = ValueFormatter.format( cdt, fs, rtc.getULocale( ), sdf );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
try
{
bb = computeBox( xs, iLocation, la, 0, dEnd );
}
catch ( IllegalArgumentException uiex )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
uiex );
}
if ( iOrientation == VERTICAL ) // VERTICAL AXIS
{
dEndShift = Math.max( dMaxES, bb.getHotPoint( ) );
}
else if ( iOrientation == HORIZONTAL )
{
dEndShift = Math.max( dMaxES,
( bb.getWidth( ) - bb.getHotPoint( ) ) );
}
}
}
final double computeAxisLabelThickness( IDisplayServer xs, Label la,
int iOrientation ) throws ChartException
{
if ( !la.isSetVisible( ) )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.unset.label.visibility", //$NON-NLS-1$
new Object[]{
la.getCaption( ).getValue( )
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
if ( !la.isVisible( ) )
{
return 0;
}
String sText;
double[] da = getTickCordinates( );
if ( iOrientation == VERTICAL )
{
double dW, dMaxW = 0, dMaxW2 = 0;
if ( ( getType( ) & TEXT ) == TEXT || bCategoryScale )
{
final DataSetIterator dsi = getData( );
final int iDateTimeUnit = ( getType( ) == IConstants.DATE_TIME ) ? CDateTime.computeUnit( dsi )
: IConstants.UNDEFINED;
dsi.reset( );
int i = 0;
while ( dsi.hasNext( ) )
{
la.getCaption( ).setValue( formatCategoryValue( getType( ),
dsi.next( ),
iDateTimeUnit ) );
dW = computeWidth( xs, la );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
dMaxW2 = Math.max( dW, dMaxW2 );
}
else if ( dW > dMaxW )
{
dMaxW = dW;
}
i++;
}
}
else if ( ( getType( ) & LINEAR ) == LINEAR )
{
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
double dAxisStep = asDouble( getStep( ) ).doubleValue( );
DecimalFormat df = null;
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( ) );
}
for ( int i = 0; i < da.length; i++ )
{
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
dW = computeWidth( xs, la );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
dMaxW2 = Math.max( dW, dMaxW2 );
}
else if ( dW > dMaxW )
{
dMaxW = dW;
}
dAxisValue += dAxisStep;
}
}
else if ( ( getType( ) & LOGARITHMIC ) == LOGARITHMIC )
{
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
double dAxisStep = asDouble( getStep( ) ).doubleValue( );
DecimalFormat df = null;
for ( int i = 0; i < da.length; i++ )
{
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( dAxisValue ) );
}
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
dW = computeWidth( xs, la );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
dMaxW2 = Math.max( dW, dMaxW2 );
}
else if ( dW > dMaxW )
{
dMaxW = dW;
}
dAxisValue *= dAxisStep;
}
}
else if ( ( getType( ) & DATE_TIME ) == DATE_TIME )
{
CDateTime cdtAxisValue = asDateTime( getMinimum( ) );
int iStep = asInteger( getStep( ) );
int iUnit = asInteger( getUnit( ) );
IDateFormatWrapper sdf = null;
if ( fs == null )
{
sdf = DateFormatWrapperFactory.getPreferredDateFormat( iUnit,
rtc.getULocale( ) );
}
for ( int i = 0; i < da.length; i++ )
{
try
{
sText = ValueFormatter.format( cdtAxisValue,
fs,
rtc.getULocale( ),
sdf );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
dW = computeWidth( xs, la );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
dMaxW2 = Math.max( dW, dMaxW2 );
}
else if ( dW > dMaxW )
{
dMaxW = dW;
}
cdtAxisValue = cdtAxisValue.forward( iUnit, iStep );
}
}
return dMaxW + dMaxW2;
}
else if ( iOrientation == HORIZONTAL )
{
double dH, dMaxH = 0, dMaxH2 = 0;
if ( ( getType( ) & TEXT ) == TEXT || bCategoryScale )
{
final DataSetIterator dsi = getData( );
final int iDateTimeUnit = ( getType( ) == IConstants.DATE_TIME ) ? CDateTime.computeUnit( dsi )
: IConstants.UNDEFINED;
dsi.reset( );
int i = 0;
while ( dsi.hasNext( ) )
{
la.getCaption( ).setValue( formatCategoryValue( getType( ),
dsi.next( ),
iDateTimeUnit ) );
dH = computeHeight( xs, la );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
dMaxH2 = Math.max( dH, dMaxH2 );
}
else if ( dH > dMaxH )
{
dMaxH = dH;
}
i++;
}
}
else if ( ( getType( ) & LINEAR ) == LINEAR )
{
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
final double dAxisStep = asDouble( getStep( ) ).doubleValue( );
DecimalFormat df = null;
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( ) );
}
for ( int i = 0; i < da.length; i++ )
{
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
dH = computeHeight( xs, la );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
dMaxH2 = Math.max( dH, dMaxH2 );
}
else if ( dH > dMaxH )
{
dMaxH = dH;
}
dAxisValue += dAxisStep;
}
}
else if ( ( getType( ) & LOGARITHMIC ) == LOGARITHMIC )
{
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
final double dAxisStep = asDouble( getStep( ) ).doubleValue( );
DecimalFormat df = null;
for ( int i = 0; i < da.length; i++ )
{
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( dAxisValue ) );
}
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
dH = computeHeight( xs, la );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
dMaxH2 = Math.max( dH, dMaxH2 );
}
else if ( dH > dMaxH )
{
dMaxH = dH;
}
dAxisValue *= dAxisStep;
}
}
else if ( ( getType( ) & DATE_TIME ) == DATE_TIME )
{
CDateTime cdtAxisValue = asDateTime( getMinimum( ) );
final int iStep = asInteger( getStep( ) );
final int iUnit = asInteger( getUnit( ) );
IDateFormatWrapper sdf = null;
if ( fs == null )
{
sdf = DateFormatWrapperFactory.getPreferredDateFormat( iUnit,
rtc.getULocale( ) );
}
for ( int i = 0; i < da.length; i++ )
{
try
{
sText = ValueFormatter.format( cdtAxisValue,
fs,
rtc.getULocale( ),
sdf );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
dH = computeHeight( xs, la );
if ( isAxisLabelStaggered( ) && baTickLabelStaggered[i] )
{
dMaxH2 = Math.max( dH, dMaxH2 );
}
else if ( dH > dMaxH )
{
dMaxH = dH;
}
cdtAxisValue.forward( iUnit, iStep );
}
}
return dMaxH + dMaxH2;
}
return 0;
}
/**
* @param xs
* @param la
* @param iOrientation
* @return
* @throws ChartException
*/
public final double computeStaggeredAxisLabelOffset( IDisplayServer xs,
Label la, int iOrientation ) throws ChartException
{
if ( !la.isSetVisible( ) )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.GENERATION,
"exception.unset.label.visibility", //$NON-NLS-1$
new Object[]{
la.getCaption( ).getValue( )
},
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
if ( !la.isVisible( ) || !isAxisLabelStaggered( ) )
{
return 0;
}
String sText;
double[] da = getTickCordinates( );
if ( iOrientation == VERTICAL )
{
double dW, dMaxW = 0;
if ( ( getType( ) & TEXT ) == TEXT || bCategoryScale )
{
final DataSetIterator dsi = getData( );
final int iDateTimeUnit = ( getType( ) == IConstants.DATE_TIME ) ? CDateTime.computeUnit( dsi )
: IConstants.UNDEFINED;
dsi.reset( );
int i = 0;
while ( dsi.hasNext( ) )
{
la.getCaption( ).setValue( formatCategoryValue( getType( ),
dsi.next( ),
iDateTimeUnit ) );
if ( !baTickLabelStaggered[i] )
{
dW = computeWidth( xs, la );
if ( dW > dMaxW )
{
dMaxW = dW;
}
}
i++;
}
}
else if ( ( getType( ) & LINEAR ) == LINEAR )
{
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
double dAxisStep = asDouble( getStep( ) ).doubleValue( );
DecimalFormat df = null;
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( ) );
}
for ( int i = 0; i < da.length; i++ )
{
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
if ( !baTickLabelStaggered[i] )
{
dW = computeWidth( xs, la );
if ( dW > dMaxW )
{
dMaxW = dW;
}
}
dAxisValue += dAxisStep;
}
}
else if ( ( getType( ) & LOGARITHMIC ) == LOGARITHMIC )
{
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
double dAxisStep = asDouble( getStep( ) ).doubleValue( );
DecimalFormat df = null;
for ( int i = 0; i < da.length; i++ )
{
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( dAxisValue ) );
}
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
if ( !baTickLabelStaggered[i] )
{
dW = computeWidth( xs, la );
if ( dW > dMaxW )
{
dMaxW = dW;
}
}
dAxisValue *= dAxisStep;
}
}
else if ( ( getType( ) & DATE_TIME ) == DATE_TIME )
{
CDateTime cdtAxisValue = asDateTime( getMinimum( ) );
int iStep = asInteger( getStep( ) );
int iUnit = asInteger( getUnit( ) );
IDateFormatWrapper sdf = null;
if ( fs == null )
{
sdf = DateFormatWrapperFactory.getPreferredDateFormat( iUnit,
rtc.getULocale( ) );
}
for ( int i = 0; i < da.length; i++ )
{
try
{
sText = ValueFormatter.format( cdtAxisValue,
fs,
rtc.getULocale( ),
sdf );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
if ( !baTickLabelStaggered[i] )
{
dW = computeWidth( xs, la );
if ( dW > dMaxW )
{
dMaxW = dW;
}
}
cdtAxisValue = cdtAxisValue.forward( iUnit, iStep );
}
}
return dMaxW;
}
else if ( iOrientation == HORIZONTAL )
{
double dH, dMaxH = 0;
if ( ( getType( ) & TEXT ) == TEXT || bCategoryScale )
{
final DataSetIterator dsi = getData( );
final int iDateTimeUnit = ( getType( ) == IConstants.DATE_TIME ) ? CDateTime.computeUnit( dsi )
: IConstants.UNDEFINED;
dsi.reset( );
int i = 0;
while ( dsi.hasNext( ) )
{
la.getCaption( ).setValue( formatCategoryValue( getType( ),
dsi.next( ),
iDateTimeUnit ) );
if ( !baTickLabelStaggered[i] )
{
dH = computeHeight( xs, la );
if ( dH > dMaxH )
{
dMaxH = dH;
}
}
i++;
}
}
else if ( ( getType( ) & LINEAR ) == LINEAR )
{
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
final double dAxisStep = asDouble( getStep( ) ).doubleValue( );
DecimalFormat df = null;
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( ) );
}
for ( int i = 0; i < da.length; i++ )
{
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
if ( !baTickLabelStaggered[i] )
{
dH = computeHeight( xs, la );
if ( dH > dMaxH )
{
dMaxH = dH;
}
}
dAxisValue += dAxisStep;
}
}
else if ( ( getType( ) & LOGARITHMIC ) == LOGARITHMIC )
{
final NumberDataElement nde = NumberDataElementImpl.create( 0 );
double dAxisValue = asDouble( getMinimum( ) ).doubleValue( );
final double dAxisStep = asDouble( getStep( ) ).doubleValue( );
DecimalFormat df = null;
for ( int i = 0; i < da.length; i++ )
{
if ( fs == null )
{
df = new DecimalFormat( getNumericPattern( dAxisValue ) );
}
nde.setValue( dAxisValue );
try
{
sText = ValueFormatter.format( nde,
fs,
rtc.getULocale( ),
df );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
if ( !baTickLabelStaggered[i] )
{
dH = computeHeight( xs, la );
if ( dH > dMaxH )
{
dMaxH = dH;
}
}
dAxisValue *= dAxisStep;
}
}
else if ( ( getType( ) & DATE_TIME ) == DATE_TIME )
{
CDateTime cdtAxisValue = asDateTime( getMinimum( ) );
final int iStep = asInteger( getStep( ) );
final int iUnit = asInteger( getUnit( ) );
IDateFormatWrapper sdf = null;
if ( fs == null )
{
sdf = DateFormatWrapperFactory.getPreferredDateFormat( iUnit,
rtc.getULocale( ) );
}
for ( int i = 0; i < da.length; i++ )
{
try
{
sText = ValueFormatter.format( cdtAxisValue,
fs,
rtc.getULocale( ),
sdf );
}
catch ( ChartException dfex )
{
logger.log( dfex );
sText = IConstants.NULL_STRING;
}
la.getCaption( ).setValue( sText );
if ( !baTickLabelStaggered[i] )
{
dH = computeHeight( xs, la );
if ( dH > dMaxH )
{
dMaxH = dH;
}
}
cdtAxisValue.forward( iUnit, iStep );
}
}
return dMaxH;
}
return 0;
}
/**
* @return
*/
public final boolean isStepFixed( )
{
return bStepFixed;
}
/**
* @param v
*/
public final void setStepFixed( boolean v )
{
this.bStepFixed = v;
}
/**
* @return
*/
public final boolean isMinimumFixed( )
{
return bMinimumFixed;
}
/**
* @param v
*/
public final void setMinimumFixed( boolean v )
{
this.bMinimumFixed = v;
}
/**
* @return
*/
public final boolean isMaximumFixed( )
{
return bMaximumFixed;
}
/**
* @param v
*/
public final void setMaximumFixed( boolean v )
{
this.bMaximumFixed = v;
}
/**
* @return
*/
public final boolean isCategoryScale( )
{
return bCategoryScale;
}
/**
*
* @param iMinorUnitsPerMajor
* @return
*/
public final double[] getMinorCoordinates( int iMinorUnitsPerMajor )
{
if ( daTickCoordinates == null || iMinorUnitsPerMajor <= 0 )
{
return null;
}
final double[] da = new double[iMinorUnitsPerMajor];
final double dUnit = getUnitSize( );
if ( ( iType & LOGARITHMIC ) != LOGARITHMIC )
{
final double dEach = dUnit / iMinorUnitsPerMajor;
for ( int i = 1; i < iMinorUnitsPerMajor; i++ )
{
da[i - 1] = dEach * i;
}
}
else
{
final double dCount = iMinorUnitsPerMajor;
final double dMax = Math.log( dCount );
for ( int i = 0; i < iMinorUnitsPerMajor; i++ )
{
da[i] = ( Math.log( i + 1 ) * dUnit ) / dMax;
}
}
da[iMinorUnitsPerMajor - 1] = dUnit;
return da;
}
/**
* @return
*/
public final RunTimeContext getRunTimeContext( )
{
return rtc;
}
/**
* @param context
*/
public final void setRunTimeContext( RunTimeContext context )
{
this.rtc = context;
}
}
See more files for this project here