/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 1999-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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. */ package org.geotools.axis; import java.awt.RenderingHints; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.Serializable; import java.util.Locale; import javax.measure.unit.Unit; import javax.measure.converter.ConversionException; import org.geotools.util.Utilities; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; /** * Base class for graduation. * * @since 2.0 * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (PMO, IRD) */ public abstract class AbstractGraduation implements Graduation, Serializable { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = 5215728323932315112L; /** * The axis's units, or {@code null} if unknow. */ private Unit<?> unit; /** * The axis title for this graduation. */ private String title; /** * The locale for formatting labels. */ private Locale locale = Locale.getDefault(); /** * A list of event listeners for this component. */ protected final PropertyChangeSupport listenerList; /** * Constructs a graduation with the supplied units. * * @param unit The axis's units, or {@code null} if unknow. */ public AbstractGraduation(final Unit<?> unit) { listenerList = new PropertyChangeSupport(this); this.unit = unit; } /** * Sets the minimum value for this graduation. If the new minimum is greater than the current * maximum, then the maximum will also be set to a value greater than or equals to the minimum. * * @param value The new minimum in {@link #getUnit} units. * @return {@code true} if the state of this graduation changed as a result of this call, or * {@code false} if the new value is identical to the previous one. * @throws IllegalArgumentException If {@code value} is NaN ou infinite. * * @see #getMinimum * @see #setMaximum(double) */ public abstract boolean setMinimum(final double value) throws IllegalArgumentException; /** * Sets the maximum value for this graduation. If the new maximum is less than the current * minimum, then the minimum will also be set to a value less than or equals to the maximum. * * @param value The new maximum in {@link #getUnit} units. * @return {@code true} if the state of this graduation changed as a result of this call, or * {@code false} if the new value is identical to the previous one. * @throws IllegalArgumentException If {@code value} is NaN ou infinite. * * @see #getMaximum * @see #setMinimum(double) */ public abstract boolean setMaximum(final double value) throws IllegalArgumentException; /** * Returns the axis title. If {@code includeUnits} is {@code true}, then the returned string * will includes units as in "Temperature (°C)". The exact formatting is local-dependent. * * @param includeSymbol {@code true} to format unit symbol after the name. * @return The graduation name (also to be use as axis title). */ public synchronized String getTitle(final boolean includeSymbol) { if (includeSymbol) { final String symbol = getSymbol(); if (symbol!=null && symbol.length()!=0) { // TODO: localize if needed. return (title!=null) ? title+" ("+symbol+')' : symbol; } } return title; } /** * Sets the axis title, not including unit symbol. This method will fire a * property change event with the {@code "title"} property name. * * @param title New axis title, or {@code null} to remove any previous setting. */ public void setTitle(final String title) { final String old; synchronized (this) { old = this.title; this.title = title; } listenerList.firePropertyChange("title", old, title); } /** * Returns a string representation of axis's units, or {@code null} * if there is none. The default implementation returns the string * representation of {@link #getUnit}. */ String getSymbol() { final Unit<?> unit = getUnit(); return (unit != null) ? unit.toString() : null; } /** * Returns the graduation's units, or {@code null} if unknow. */ public Unit<?> getUnit() { return unit; } /** * Changes the graduation's units. Subclasses will automatically convert minimum and maximum * values from the old units to the new one. This method fires a property change event with the * {@code "unit"} property name. * * @param unit The new units, or {@code null} if unknow. If null, minimum and maximum values * are not converted. * @throws ConversionException if units are not convertible, or if the * specified units is illegal for this graduation. */ public void setUnit(final Unit<?> unit) throws ConversionException { final Unit<?> oldUnit; synchronized (this) { oldUnit = this.unit; this.unit = unit; } listenerList.firePropertyChange("unit", oldUnit, unit); } /** * Returns the locale to use for formatting labels. */ public Locale getLocale() { return locale; } /** * Sets the locale to use for formatting labels. * This will fire a property change event with the {@code "locale"} property name. * * @param locale The new labels format. */ public synchronized void setLocale(final Locale locale) { final Locale old; synchronized (this) { old = this.locale; this.locale = locale; } listenerList.firePropertyChange("locale", old, locale); } /** * Adds a {@link PropertyChangeListener} to the listener list. The listener is registered * for all properties. A {@link java.beans.PropertyChangeEvent} will get fired in response * to setting a property, such as {@link #setTitle} or {@link #setLocale}. */ public void addPropertyChangeListener(final PropertyChangeListener listener) { listenerList.addPropertyChangeListener(listener); } /** * Removes a {@link PropertyChangeListener} from the listener list. */ public void removePropertyChangeListener(final PropertyChangeListener listener) { listenerList.removePropertyChangeListener(listener); } /** * Retourne la longueur de l'axe, en pixels ou en points (1/72 de pouce). */ static float getVisualAxisLength(final RenderingHints hints) { return getValue(hints, VISUAL_AXIS_LENGTH, 600); } /** * Retourne l'espace approximatif (en pixels ou en points) à laisser entre les * graduations principales. L'espace réel entre les graduations peut être légèrement * différent, par exemple pour avoir des étiquettes qui correspondent à des valeurs * arrondies. */ static float getVisualTickSpacing(final RenderingHints hints) { return getValue(hints, VISUAL_TICK_SPACING, 48); } /** * Retourne une valeur sous forme de nombre réel. */ private static float getValue(final RenderingHints hints, final RenderingHints.Key key, final float defaultValue) { if (hints != null) { final Object object = hints.get(key); if (object instanceof Number) { final float value = ((Number) object).floatValue(); if (value!=0 && !Float.isInfinite(value)) { return value; } } } return defaultValue; } /** * Vérifie que le nombre spécifié est non-nul. S'il * est 0, NaN ou infini, une exception sera lancée. * * @param name Nom de l'argument. * @param n Nombre à vérifier. * @throws IllegalArgumentException Si <var>n</var> est NaN ou infini. */ static void ensureNonNull(final String name, final double n) throws IllegalArgumentException { if (Double.isNaN(n) || Double.isInfinite(n) || n==0) { throw new IllegalArgumentException(Errors.format( ErrorKeys.ILLEGAL_ARGUMENT_$2, name, new Double(n))); } } /** * Vérifie que le nombre spécifié est réel. S'il est NaN ou infini, une exception sera lancée. * * @param name Nom de l'argument. * @param n Nombre à vérifier. * @throws IllegalArgumentException Si <var>n</var> est NaN ou infini. */ static void ensureFinite(final String name, final double n) throws IllegalArgumentException { if (Double.isNaN(n) || Double.isInfinite(n)) { throw new IllegalArgumentException(Errors.format( ErrorKeys.ILLEGAL_ARGUMENT_$2, name, new Double(n))); } } /** * Vérifie que le nombre spécifié est réel. S'il est NaN ou infini, une exception sera lancée. * * @param name Nom de l'argument. * @param n Nombre à vérifier. * @throws IllegalArgumentException Si <var>n</var> est NaN ou infini. */ static void ensureFinite(final String name, final float n) throws IllegalArgumentException { if (Float.isNaN(n) || Float.isInfinite(n)) { throw new IllegalArgumentException(Errors.format( ErrorKeys.ILLEGAL_ARGUMENT_$2, name, new Float(n))); } } /** * Compares this graduation with the specified object for equality. * This method do not compare listeners registered in {@link #listenerList}. * * @param object The object to compare with. * @return {@code true} if this graduation is equals to the given object. */ @Override public boolean equals(final Object object) { if (object!=null && object.getClass().equals(getClass())) { final AbstractGraduation that = (AbstractGraduation) object; return Utilities.equals(this.unit, that.unit ) && Utilities.equals(this.title, that.title ) && Utilities.equals(this.locale, that.locale); } return false; } /** * Returns a hash value for this graduation. */ @Override public int hashCode() { int code = (int) serialVersionUID; if (title != null) { code ^= title.hashCode(); } if (unit != null) { code ^= unit.hashCode(); } return code; } }