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