    001/* ===========================================================
    002 * Orson Charts : a 3D chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C)opyright 2013-2016, by Object Refinery Limited.  All rights reserved.
    006 *
    007 * http://www.object-refinery.com/orsoncharts/index.html
    008 *
    009 * This program is free software: you can redistribute it and/or modify
    010 * it under the terms of the GNU General Public License as published by
    011 * the Free Software Foundation, either version 3 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This program is distributed in the hope that it will be useful,
    015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
    016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    017 * GNU General Public License for more details.
    018 *
    019 * You should have received a copy of the GNU General Public License
    020 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    021 *
    022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    023 * Other names may be trademarks of their respective owners.]
    024 *
    025 * If you do not wish to be bound by the terms of the GPL, an alternative
    026 * commercial license can be purchased.  For details, please see visit the
    027 * Orson Charts home page:
    028 *
    029 * http://www.object-refinery.com/orsoncharts/index.html
    030 *
    031 */
    032
    033package com.orsoncharts.marker;
    034
    035import java.awt.Color;
    036import java.awt.Font;
    037import java.awt.Graphics2D;
    038import java.awt.Stroke;
    039import java.awt.geom.Line2D;
    040import java.awt.geom.Point2D;
    041import java.io.IOException;
    042import java.io.ObjectInputStream;
    043import java.io.ObjectOutputStream;
    044import java.io.Serializable;
    045
    046import com.orsoncharts.Range;
    047import com.orsoncharts.util.Anchor2D;
    048import com.orsoncharts.util.ArgChecks;
    049import com.orsoncharts.util.ObjectUtils;
    050import com.orsoncharts.util.SerialUtils;
    051
    052/**
    053 * A marker used to mark one value on an axis.
    054 * <br><br>
    055 * NOTE: This class is serializable, but the serialization format is subject
    056 * to change in future releases and should not be relied upon for persisting
    057 * instances of this class.
    058 *
    059 * @since 1.2
    060 */
    061@SuppressWarnings("serial")
    062public class NumberMarker extends AbstractMarker implements ValueMarker,
    063        Serializable {
    064
    065    /** The data value to be marked. */
    066    private double value;
    067
    068    /** The label for the marker (optional). */
    069    private String label;
    070
    071    /** The font for the label. */
    072    private Font font;
    073
    074    /** The color for the label. */
    075    private Color labelColor;
    076
    077    /** The anchor for the label. */
    078    private Anchor2D labelAnchor;
    079
    080    /** The stroke for the marker line. */
    081    private transient Stroke stroke;
    082
    083    /** The color for the marker line. */
    084    private Color lineColor;
    085
    086    /**
    087     * Creates a new marker.
    088     *
    089     * @param value  the value.
    090     */
    091    public NumberMarker(double value) {
    092        super();
    093        this.value = value;
    094        this.label = null;
    095        this.font = DEFAULT_MARKER_FONT;
    096        this.labelColor = DEFAULT_LABEL_COLOR;
    097        this.stroke = DEFAULT_LINE_STROKE;
    098        this.lineColor = DEFAULT_LINE_COLOR;
    099        this.labelAnchor = Anchor2D.CENTER;
    100    }
    101
    102    /**
    103     * Returns the value for the marker (the initial value comes from the
    104     * constructor).
    105     *
    106     * @return The value.
    107     */
    108    public double getValue() {
    109        return this.value;
    110    }
    111
    112    /**
    113     * Sets the value for the marker and sends a change event to all registered
    114     * listeners.
    115     *
    116     * @param value  the value.
    117     */
    118    public void setValue(double value) {
    119        this.value = value;
    120        fireChangeEvent();
    121    }
    122
    123    /**
    124     * Returns the range for the marker (in this case, a single value range).
    125     * This method is used by the axis to filter out markers that do not touch
    126     * the current axis range.
    127     *
    128     * @return The range for the marker (never {@code null}).
    129     */
    130    @Override
    131    public Range getRange() {
    132        return new Range(this.value, this.value);
    133    }
    134
    135    /**
    136     * Returns the label for the marker (if this is {@code null} then no
    137     * label is displayed).  The default value is {@code null}.
    138     *
    139     * @return The label (possibly {@code null}).
    140     */
    141    public String getLabel() {
    142        return this.label;
    143    }
    144
    145    /**
    146     * Sets the label and sends a change event to all registered listeners.
    147     *
    148     * @param label  the label ({@code null} permitted).
    149     */
    150    public void setLabel(String label) {
    151        this.label = label;
    152        fireChangeEvent();
    153    }
    154
    155    /**
    156     * Returns the font for the label.  The default value is
    157     * {@link Marker#DEFAULT_MARKER_FONT}.
    158     *
    159     * @return The font (never {@code null}).
    160     */
    161    public Font getFont() {
    162        return this.font;
    163    }
    164
    165    /**
    166     * Sets the font for the marker label and sends a change event to all
    167     * registered listeners.
    168     *
    169     * @param font  the font ({@code null} not permitted).
    170     */
    171    public void setFont(Font font) {
    172        ArgChecks.nullNotPermitted(font, "font");
    173        this.font = font;
    174        fireChangeEvent();
    175    }
    176
    177    /**
    178     * Returns the label color.  The default value is
    179     * {@link Marker#DEFAULT_LABEL_COLOR}.
    180     *
    181     * @return The label color (never {@code null}).
    182     */
    183    public Color getLabelColor() {
    184        return this.labelColor;
    185    }
    186
    187    /**
    188     * Sets the label color and sends a change event to all registered
    189     * listeners.
    190     *
    191     * @param color  the color ({@code null} not permitted).
    192     */
    193    public void setLabelColor(Color color) {
    194        ArgChecks.nullNotPermitted(color, "color");
    195        this.labelColor = color;
    196        fireChangeEvent();
    197    }
    198
    199    /**
    200     * Returns the anchor for the label.  The default value is
    201     * {@link Anchor2D#CENTER}.
    202     *
    203     * @return The anchor for the label.
    204     */
    205    public Anchor2D getLabelAnchor() {
    206        return this.labelAnchor;
    207    }
    208
    209    /**
    210     * Sets the anchor for the label and sends a change event to all registered
    211     * listeners.
    212     *
    213     * @param anchor  the anchor ({@code null} not permitted).
    214     */
    215    public void setLabelAnchor(Anchor2D anchor) {
    216        ArgChecks.nullNotPermitted(anchor, "anchor");
    217        this.labelAnchor = anchor;
    218        fireChangeEvent();
    219    }
    220
    221    /**
    222     * Returns the stroke for the marker line.  The default value is
    223     * {@link Marker#DEFAULT_LINE_STROKE}.
    224     *
    225     * @return The stroke for the marker line (never {@code null}).
    226     */
    227    public Stroke getLineStroke() {
    228        return this.stroke;
    229    }
    230
    231    /**
    232     * Sets the stroke for the marker line and sends a change event to all
    233     * registered listeners.
    234     *
    235     * @param stroke  the stroke ({@code null} not permitted).
    236     */
    237    public void setLineStroke(Stroke stroke) {
    238        ArgChecks.nullNotPermitted(stroke, "stroke");
    239        this.stroke = stroke;
    240        fireChangeEvent();
    241    }
    242
    243    /**
    244     * Returns the color for the marker line.  The default value is
    245     * {@link Marker#DEFAULT_LINE_COLOR}.
    246     *
    247     * @return The color for the marker line (never {@code null}).
    248     */
    249    public Color getLineColor() {
    250        return this.lineColor;
    251    }
    252
    253    /**
    254     * Sets the color for the marker line and sends a change event to all
    255     * registered listeners.
    256     *
    257     * @param color  the color ({@code null} not permitted).
    258     */
    259    public void setLineColor(Color color) {
    260        ArgChecks.nullNotPermitted(color, "color");
    261        this.lineColor = color;
    262        fireChangeEvent();
    263    }
    264
    265    /**
    266     * Draws the marker.  This method is called by the library, you won't
    267     * normally call it directly.
    268     *
    269     * @param g2  the graphics target ({@code null} not permitted).
    270     * @param markerData  transient marker data ({@code null} not
    271     *     permitted).
    272     */
    273    @Override
    274    public void draw(Graphics2D g2, MarkerData markerData, boolean reverse) {
    275        MarkerLine line = markerData.getValueLine();
    276        g2.setPaint(this.lineColor);
    277        g2.setStroke(this.stroke);
    278        Line2D l = new Line2D.Double(line.getStartPoint(), line.getEndPoint());
    279        g2.draw(l);
    280        Point2D labelPoint = markerData.getLabelPoint();
    281        if (labelPoint != null) {
    282            g2.setFont(this.font);
    283            g2.setColor(this.labelColor);
    284            drawMarkerLabel(g2, this.label, labelPoint.getX(),
    285                    labelPoint.getY(), this.labelAnchor, l, reverse);
    286        }
    287    }
    288
    289    @Override
    290    public int hashCode() {
    291        int hash = 7;
    292        hash = 19 * hash + (int) (Double.doubleToLongBits(this.value)
    293                ^ (Double.doubleToLongBits(this.value) >>> 32));
    294        hash = 19 * hash + ObjectUtils.hashCode(this.label);
    295        return hash;
    296    }
    297
    298    @Override
    299    public boolean equals(Object obj) {
    300        if (obj == null) {
    301            return false;
    302        }
    303        if (getClass() != obj.getClass()) {
    304            return false;
    305        }
    306        final NumberMarker other = (NumberMarker) obj;
    307        if (Double.doubleToLongBits(this.value)
    308                != Double.doubleToLongBits(other.value)) {
    309            return false;
    310        }
    311        if (!ObjectUtils.equals(this.label, other.label)) {
    312            return false;
    313        }
    314        if (!ObjectUtils.equals(this.font, other.font)) {
    315            return false;
    316        }
    317        if (!ObjectUtils.equals(this.labelColor, other.labelColor)) {
    318            return false;
    319        }
    320        if (!ObjectUtils.equals(this.labelAnchor, other.labelAnchor)) {
    321            return false;
    322        }
    323        if (!ObjectUtils.equals(this.stroke, other.stroke)) {
    324            return false;
    325        }
    326        if (!ObjectUtils.equals(this.lineColor, other.lineColor)) {
    327            return false;
    328        }
    329        return true;
    330    }
    331
    332    /**
    333     * Provides serialization support.
    334     *
    335     * @param stream  the output stream.
    336     *
    337     * @throws IOException  if there is an I/O error.
    338     */
    339    private void writeObject(ObjectOutputStream stream) throws IOException {
    340        stream.defaultWriteObject();
    341        SerialUtils.writeStroke(this.stroke, stream);
    342    }
    343
    344    /**
    345     * Provides serialization support.
    346     *
    347     * @param stream  the input stream.
    348     *
    349     * @throws IOException  if there is an I/O error.
    350     * @throws ClassNotFoundException  if there is a classpath problem.
    351     */
    352    private void readObject(ObjectInputStream stream)
    353        throws IOException, ClassNotFoundException {
    354        stream.defaultReadObject();
    355        this.stroke = SerialUtils.readStroke(stream);
    356    }
    357
    358}