001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2008, 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     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025     * in the United States and other countries.]
026     *
027     * --------------------
028     * XYBoxAnnotation.java
029     * --------------------
030     * (C) Copyright 2005-2008, by Object Refinery Limited and Contributors.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * Changes:
036     * --------
037     * 19-Jan-2005 : Version 1 (DG);
038     * 06-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
039     *
040     */
041    
042    package org.jfree.chart.annotations;
043    
044    import java.awt.BasicStroke;
045    import java.awt.Color;
046    import java.awt.Graphics2D;
047    import java.awt.Paint;
048    import java.awt.Stroke;
049    import java.awt.geom.Rectangle2D;
050    import java.io.IOException;
051    import java.io.ObjectInputStream;
052    import java.io.ObjectOutputStream;
053    import java.io.Serializable;
054    
055    import org.jfree.chart.axis.ValueAxis;
056    import org.jfree.chart.plot.Plot;
057    import org.jfree.chart.plot.PlotOrientation;
058    import org.jfree.chart.plot.PlotRenderingInfo;
059    import org.jfree.chart.plot.XYPlot;
060    import org.jfree.io.SerialUtilities;
061    import org.jfree.ui.RectangleEdge;
062    import org.jfree.util.ObjectUtilities;
063    import org.jfree.util.PaintUtilities;
064    import org.jfree.util.PublicCloneable;
065    
066    /**
067     * A box annotation that can be placed on an {@link XYPlot}.  The
068     * box coordinates are specified in data space.
069     */
070    public class XYBoxAnnotation extends AbstractXYAnnotation
071            implements Cloneable, PublicCloneable, Serializable {
072    
073        /** For serialization. */
074        private static final long serialVersionUID = 6764703772526757457L;
075    
076        /** The lower x-coordinate. */
077        private double x0;
078    
079        /** The lower y-coordinate. */
080        private double y0;
081    
082        /** The upper x-coordinate. */
083        private double x1;
084    
085        /** The upper y-coordinate. */
086        private double y1;
087    
088        /** The stroke used to draw the box outline. */
089        private transient Stroke stroke;
090    
091        /** The paint used to draw the box outline. */
092        private transient Paint outlinePaint;
093    
094        /** The paint used to fill the box. */
095        private transient Paint fillPaint;
096    
097        /**
098         * Creates a new annotation (where, by default, the box is drawn
099         * with a black outline).
100         *
101         * @param x0  the lower x-coordinate of the box (in data space).
102         * @param y0  the lower y-coordinate of the box (in data space).
103         * @param x1  the upper x-coordinate of the box (in data space).
104         * @param y1  the upper y-coordinate of the box (in data space).
105         */
106        public XYBoxAnnotation(double x0, double y0, double x1, double y1) {
107            this(x0, y0, x1, y1, new BasicStroke(1.0f), Color.black);
108        }
109    
110        /**
111         * Creates a new annotation where the box is drawn as an outline using
112         * the specified <code>stroke</code> and <code>outlinePaint</code>.
113         *
114         * @param x0  the lower x-coordinate of the box (in data space).
115         * @param y0  the lower y-coordinate of the box (in data space).
116         * @param x1  the upper x-coordinate of the box (in data space).
117         * @param y1  the upper y-coordinate of the box (in data space).
118         * @param stroke  the shape stroke (<code>null</code> permitted).
119         * @param outlinePaint  the shape color (<code>null</code> permitted).
120         */
121        public XYBoxAnnotation(double x0, double y0, double x1, double y1,
122                               Stroke stroke, Paint outlinePaint) {
123            this(x0, y0, x1, y1, stroke, outlinePaint, null);
124        }
125    
126        /**
127         * Creates a new annotation.
128         *
129         * @param x0  the lower x-coordinate of the box (in data space).
130         * @param y0  the lower y-coordinate of the box (in data space).
131         * @param x1  the upper x-coordinate of the box (in data space).
132         * @param y1  the upper y-coordinate of the box (in data space).
133         * @param stroke  the shape stroke (<code>null</code> permitted).
134         * @param outlinePaint  the shape color (<code>null</code> permitted).
135         * @param fillPaint  the paint used to fill the shape (<code>null</code>
136         *                   permitted).
137         */
138        public XYBoxAnnotation(double x0, double y0, double x1, double y1,
139                               Stroke stroke, Paint outlinePaint, Paint fillPaint) {
140            this.x0 = x0;
141            this.y0 = y0;
142            this.x1 = x1;
143            this.y1 = y1;
144            this.stroke = stroke;
145            this.outlinePaint = outlinePaint;
146            this.fillPaint = fillPaint;
147        }
148    
149        /**
150         * Draws the annotation.  This method is usually called by the
151         * {@link XYPlot} class, you shouldn't need to call it directly.
152         *
153         * @param g2  the graphics device.
154         * @param plot  the plot.
155         * @param dataArea  the data area.
156         * @param domainAxis  the domain axis.
157         * @param rangeAxis  the range axis.
158         * @param rendererIndex  the renderer index.
159         * @param info  the plot rendering info.
160         */
161        public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
162                         ValueAxis domainAxis, ValueAxis rangeAxis,
163                         int rendererIndex, PlotRenderingInfo info) {
164    
165            PlotOrientation orientation = plot.getOrientation();
166            RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
167                    plot.getDomainAxisLocation(), orientation);
168            RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
169                    plot.getRangeAxisLocation(), orientation);
170    
171            double transX0 = domainAxis.valueToJava2D(this.x0, dataArea,
172                    domainEdge);
173            double transY0 = rangeAxis.valueToJava2D(this.y0, dataArea, rangeEdge);
174            double transX1 = domainAxis.valueToJava2D(this.x1, dataArea,
175                    domainEdge);
176            double transY1 = rangeAxis.valueToJava2D(this.y1, dataArea, rangeEdge);
177    
178            Rectangle2D box = null;
179            if (orientation == PlotOrientation.HORIZONTAL) {
180                box = new Rectangle2D.Double(transY0, transX1, transY1 - transY0,
181                        transX0 - transX1);
182            }
183            else if (orientation == PlotOrientation.VERTICAL) {
184                box = new Rectangle2D.Double(transX0, transY1, transX1 - transX0,
185                        transY0 - transY1);
186            }
187    
188            if (this.fillPaint != null) {
189                g2.setPaint(this.fillPaint);
190                g2.fill(box);
191            }
192    
193            if (this.stroke != null && this.outlinePaint != null) {
194                g2.setPaint(this.outlinePaint);
195                g2.setStroke(this.stroke);
196                g2.draw(box);
197            }
198            addEntity(info, box, rendererIndex, getToolTipText(), getURL());
199    
200        }
201    
202        /**
203         * Tests this annotation for equality with an arbitrary object.
204         *
205         * @param obj  the object (<code>null</code> permitted).
206         *
207         * @return A boolean.
208         */
209        public boolean equals(Object obj) {
210            if (obj == this) {
211                return true;
212            }
213            // now try to reject equality
214            if (!super.equals(obj)) {
215                return false;
216            }
217            if (!(obj instanceof XYBoxAnnotation)) {
218                return false;
219            }
220            XYBoxAnnotation that = (XYBoxAnnotation) obj;
221            if (!(this.x0 == that.x0)) {
222                return false;
223            }
224            if (!(this.y0 == that.y0)) {
225                return false;
226            }
227            if (!(this.x1 == that.x1)) {
228                return false;
229            }
230            if (!(this.y1 == that.y1)) {
231                return false;
232            }
233            if (!ObjectUtilities.equal(this.stroke, that.stroke)) {
234                return false;
235            }
236            if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) {
237                return false;
238            }
239            if (!PaintUtilities.equal(this.fillPaint, that.fillPaint)) {
240                return false;
241            }
242            // seem to be the same
243            return true;
244        }
245    
246        /**
247         * Returns a hash code.
248         *
249         * @return A hash code.
250         */
251        public int hashCode() {
252            int result;
253            long temp;
254            temp = Double.doubleToLongBits(this.x0);
255            result = (int) (temp ^ (temp >>> 32));
256            temp = Double.doubleToLongBits(this.x1);
257            result = 29 * result + (int) (temp ^ (temp >>> 32));
258            temp = Double.doubleToLongBits(this.y0);
259            result = 29 * result + (int) (temp ^ (temp >>> 32));
260            temp = Double.doubleToLongBits(this.y1);
261            result = 29 * result + (int) (temp ^ (temp >>> 32));
262            return result;
263        }
264    
265        /**
266         * Returns a clone.
267         *
268         * @return A clone.
269         *
270         * @throws CloneNotSupportedException not thrown by this class, but may be
271         *                                    by subclasses.
272         */
273        public Object clone() throws CloneNotSupportedException {
274            return super.clone();
275        }
276    
277        /**
278         * Provides serialization support.
279         *
280         * @param stream  the output stream (<code>null</code> not permitted).
281         *
282         * @throws IOException if there is an I/O error.
283         */
284        private void writeObject(ObjectOutputStream stream) throws IOException {
285            stream.defaultWriteObject();
286            SerialUtilities.writeStroke(this.stroke, stream);
287            SerialUtilities.writePaint(this.outlinePaint, stream);
288            SerialUtilities.writePaint(this.fillPaint, stream);
289        }
290    
291        /**
292         * Provides serialization support.
293         *
294         * @param stream  the input stream (<code>null</code> not permitted).
295         *
296         * @throws IOException  if there is an I/O error.
297         * @throws ClassNotFoundException  if there is a classpath problem.
298         */
299        private void readObject(ObjectInputStream stream)
300            throws IOException, ClassNotFoundException {
301    
302            stream.defaultReadObject();
303            this.stroke = SerialUtilities.readStroke(stream);
304            this.outlinePaint = SerialUtilities.readPaint(stream);
305            this.fillPaint = SerialUtilities.readPaint(stream);
306        }
307    
308    }