001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------------- 028 * NumberTickUnitSource.java 029 * ------------------------- 030 * (C) Copyright 2014, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 18-Mar-2014 : Version 1 (DG); 038 * 039 */ 040 041package org.jfree.chart.axis; 042 043import java.io.Serializable; 044import java.text.DecimalFormat; 045import java.text.NumberFormat; 046import org.jfree.util.ObjectUtilities; 047 048/** 049 * A tick unit source implementation that returns NumberTickUnit instances 050 * that are multiples of 1, 2 or 5 times some power of 10. 051 * 052 * @since 1.0.18 053 */ 054public class NumberTickUnitSource implements TickUnitSource, Serializable { 055 056 private boolean integers; 057 058 private int power = 0; 059 060 private int factor = 1; 061 062 /** The number formatter to use (an override, it can be null). */ 063 private NumberFormat formatter; 064 065 /** 066 * Creates a new instance. 067 */ 068 public NumberTickUnitSource() { 069 this(false); 070 } 071 072 /** 073 * Creates a new instance. 074 * 075 * @param integers show integers only. 076 */ 077 public NumberTickUnitSource(boolean integers) { 078 this(integers, null); 079 } 080 081 /** 082 * Creates a new instance. 083 * 084 * @param integers show integers only? 085 * @param formatter a formatter for the axis tick labels ({@code null} 086 * permitted). 087 */ 088 public NumberTickUnitSource(boolean integers, NumberFormat formatter) { 089 this.integers = integers; 090 this.formatter = formatter; 091 this.power = 0; 092 this.factor = 1; 093 } 094 095 @Override 096 public TickUnit getLargerTickUnit(TickUnit unit) { 097 TickUnit t = getCeilingTickUnit(unit); 098 if (t.equals(unit)) { 099 next(); 100 t = new NumberTickUnit(getTickSize(), getTickLabelFormat(), 101 getMinorTickCount()); 102 } 103 return t; 104 } 105 106 @Override 107 public TickUnit getCeilingTickUnit(TickUnit unit) { 108 return getCeilingTickUnit(unit.getSize()); 109 } 110 111 @Override 112 public TickUnit getCeilingTickUnit(double size) { 113 if (Double.isInfinite(size)) { 114 throw new IllegalArgumentException("Must be finite."); 115 } 116 this.power = (int) Math.ceil(Math.log10(size)); 117 if (this.integers) { 118 power = Math.max(this.power, 0); 119 } 120 this.factor = 1; 121 boolean done = false; 122 // step down in size until the current size is too small or there are 123 // no more units 124 while (!done) { 125 done = !previous(); 126 if (getTickSize() < size) { 127 next(); 128 done = true; 129 } 130 } 131 return new NumberTickUnit(getTickSize(), getTickLabelFormat(), 132 getMinorTickCount()); 133 } 134 135 private boolean next() { 136 if (factor == 1) { 137 factor = 2; 138 return true; 139 } 140 if (factor == 2) { 141 factor = 5; 142 return true; 143 } 144 if (factor == 5) { 145 if (power == 300) { 146 return false; 147 } 148 power++; 149 factor = 1; 150 return true; 151 } 152 throw new IllegalStateException("We should never get here."); 153 } 154 155 private boolean previous() { 156 if (factor == 1) { 157 if (this.integers && power == 0 || power == -300) { 158 return false; 159 } 160 factor = 5; 161 power--; 162 return true; 163 } 164 if (factor == 2) { 165 factor = 1; 166 return true; 167 } 168 if (factor == 5) { 169 factor = 2; 170 return true; 171 } 172 throw new IllegalStateException("We should never get here."); 173 } 174 175 private double getTickSize() { 176 return this.factor * Math.pow(10.0, this.power); 177 } 178 179 private DecimalFormat dfNeg4 = new DecimalFormat("0.0000"); 180 private DecimalFormat dfNeg3 = new DecimalFormat("0.000"); 181 private DecimalFormat dfNeg2 = new DecimalFormat("0.00"); 182 private DecimalFormat dfNeg1 = new DecimalFormat("0.0"); 183 private DecimalFormat df0 = new DecimalFormat("#,##0"); 184 private DecimalFormat df = new DecimalFormat("#.######E0"); 185 186 private NumberFormat getTickLabelFormat() { 187 if (this.formatter != null) { 188 return this.formatter; 189 } 190 if (power == -4) { 191 return dfNeg4; 192 } 193 if (power == -3) { 194 return dfNeg3; 195 } 196 if (power == -2) { 197 return dfNeg2; 198 } 199 if (power == -1) { 200 return dfNeg1; 201 } 202 if (power >= 0 && power <= 6) { 203 return df0; 204 } 205 return df; 206 } 207 208 private int getMinorTickCount() { 209 if (factor == 1) { 210 return 10; 211 } else if (factor == 5) { 212 return 5; 213 } 214 return 0; 215 } 216 217 @Override 218 public boolean equals(Object obj) { 219 if (obj == this) { 220 return true; 221 } 222 if (!(obj instanceof NumberTickUnitSource)) { 223 return false; 224 } 225 NumberTickUnitSource that = (NumberTickUnitSource) obj; 226 if (this.integers != that.integers) { 227 return false; 228 } 229 if (!ObjectUtilities.equal(this.formatter, that.formatter)) { 230 return false; 231 } 232 if (this.power != that.power) { 233 return false; 234 } 235 if (this.factor != that.factor) { 236 return false; 237 } 238 return true; 239 } 240}