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     * XYLineAnnotation.java
029     * ---------------------
030     * (C) Copyright 2003-2007, by Object Refinery Limited.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * Changes:
036     * --------
037     * 02-Apr-2003 : Version 1 (DG);
038     * 19-Aug-2003 : Added equals method, implemented Cloneable, and applied 
039     *               serialization fixes (DG);
040     * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
041     * 14-Apr-2004 : Fixed draw() method to handle plot orientation correctly (DG);
042     * 29-Sep-2004 : Added support for tool tips and URLS, now extends 
043     *               AbstractXYAnnotation (DG);
044     * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG);
045     * 08-Jun-2005 : Fixed equals() method to handle GradientPaint() (DG);
046     * 
047     */
048    
049    package org.jfree.chart.annotations;
050    
051    import java.awt.BasicStroke;
052    import java.awt.Color;
053    import java.awt.Graphics2D;
054    import java.awt.Paint;
055    import java.awt.Stroke;
056    import java.awt.geom.Line2D;
057    import java.awt.geom.Rectangle2D;
058    import java.io.IOException;
059    import java.io.ObjectInputStream;
060    import java.io.ObjectOutputStream;
061    import java.io.Serializable;
062    
063    import org.jfree.chart.axis.ValueAxis;
064    import org.jfree.chart.plot.Plot;
065    import org.jfree.chart.plot.PlotOrientation;
066    import org.jfree.chart.plot.PlotRenderingInfo;
067    import org.jfree.chart.plot.XYPlot;
068    import org.jfree.io.SerialUtilities;
069    import org.jfree.ui.RectangleEdge;
070    import org.jfree.util.ObjectUtilities;
071    import org.jfree.util.PaintUtilities;
072    import org.jfree.util.PublicCloneable;
073    import org.jfree.util.ShapeUtilities;
074    
075    /**
076     * A simple line annotation that can be placed on an {@link XYPlot}.
077     */
078    public class XYLineAnnotation extends AbstractXYAnnotation
079                                  implements Cloneable, PublicCloneable, 
080                                             Serializable {
081    
082        /** For serialization. */
083        private static final long serialVersionUID = -80535465244091334L;
084        
085        /** The x-coordinate. */
086        private double x1;
087    
088        /** The y-coordinate. */
089        private double y1;
090    
091        /** The x-coordinate. */
092        private double x2;
093    
094        /** The y-coordinate. */
095        private double y2;
096    
097        /** The line stroke. */
098        private transient Stroke stroke;
099    
100        /** The line color. */
101        private transient Paint paint;
102    
103        /**
104         * Creates a new annotation that draws a line from (x1, y1) to (x2, y2) 
105         * where the coordinates are measured in data space (that is, against the 
106         * plot's axes).
107         * 
108         * @param x1  the x-coordinate for the start of the line.
109         * @param y1  the y-coordinate for the start of the line.
110         * @param x2  the x-coordinate for the end of the line.
111         * @param y2  the y-coordinate for the end of the line.
112         */
113        public XYLineAnnotation(double x1, double y1, double x2, double y2) {
114            this(x1, y1, x2, y2, new BasicStroke(1.0f), Color.black);
115        }
116        
117        /**
118         * Creates a new annotation that draws a line from (x1, y1) to (x2, y2) 
119         * where the coordinates are measured in data space (that is, against the 
120         * plot's axes).
121         *
122         * @param x1  the x-coordinate for the start of the line.
123         * @param y1  the y-coordinate for the start of the line.
124         * @param x2  the x-coordinate for the end of the line.
125         * @param y2  the y-coordinate for the end of the line.
126         * @param stroke  the line stroke (<code>null</code> not permitted).
127         * @param paint  the line color (<code>null</code> not permitted).
128         */
129        public XYLineAnnotation(double x1, double y1, double x2, double y2,
130                                Stroke stroke, Paint paint) {
131    
132            if (stroke == null) {
133                throw new IllegalArgumentException("Null 'stroke' argument.");   
134            }
135            if (paint == null) {
136                throw new IllegalArgumentException("Null 'paint' argument.");   
137            }
138            this.x1 = x1;
139            this.y1 = y1;
140            this.x2 = x2;
141            this.y2 = y2;
142            this.stroke = stroke;
143            this.paint = paint;
144    
145        }
146    
147        /**
148         * Draws the annotation.  This method is called by the {@link XYPlot} 
149         * class, you won't normally need to call it yourself.
150         *
151         * @param g2  the graphics device.
152         * @param plot  the plot.
153         * @param dataArea  the data area.
154         * @param domainAxis  the domain axis.
155         * @param rangeAxis  the range axis.
156         * @param rendererIndex  the renderer index.
157         * @param info  if supplied, this info object will be populated with
158         *              entity information.
159         */
160        public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
161                         ValueAxis domainAxis, ValueAxis rangeAxis, 
162                         int rendererIndex,
163                         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            float j2DX1 = 0.0f;
171            float j2DX2 = 0.0f;
172            float j2DY1 = 0.0f;
173            float j2DY2 = 0.0f;
174            if (orientation == PlotOrientation.VERTICAL) {
175                j2DX1 = (float) domainAxis.valueToJava2D(this.x1, dataArea, 
176                        domainEdge);
177                j2DY1 = (float) rangeAxis.valueToJava2D(this.y1, dataArea, 
178                        rangeEdge);
179                j2DX2 = (float) domainAxis.valueToJava2D(this.x2, dataArea, 
180                        domainEdge);
181                j2DY2 = (float) rangeAxis.valueToJava2D(this.y2, dataArea, 
182                        rangeEdge);
183            }
184            else if (orientation == PlotOrientation.HORIZONTAL) {
185                j2DY1 = (float) domainAxis.valueToJava2D(this.x1, dataArea, 
186                        domainEdge);
187                j2DX1 = (float) rangeAxis.valueToJava2D(this.y1, dataArea, 
188                        rangeEdge);
189                j2DY2 = (float) domainAxis.valueToJava2D(this.x2, dataArea, 
190                        domainEdge);
191                j2DX2 = (float) rangeAxis.valueToJava2D(this.y2, dataArea, 
192                        rangeEdge);                
193            }
194            g2.setPaint(this.paint);
195            g2.setStroke(this.stroke);
196            Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2);
197            g2.draw(line);
198    
199            String toolTip = getToolTipText();
200            String url = getURL();
201            if (toolTip != null || url != null) {
202                addEntity(info, ShapeUtilities.createLineRegion(line, 1.0f), 
203                        rendererIndex, toolTip, url);
204            }
205        }
206    
207        /**
208         * Tests this object for equality with an arbitrary object.
209         * 
210         * @param obj  the object to test against (<code>null</code> permitted).
211         * 
212         * @return <code>true</code> or <code>false</code>.
213         */
214        public boolean equals(Object obj) {
215            if (obj == this) {
216                return true;
217            }
218            if (!super.equals(obj)) {
219                return false;
220            }
221            if (!(obj instanceof XYLineAnnotation)) {
222                return false;
223            }
224            XYLineAnnotation that = (XYLineAnnotation) obj;
225            if (this.x1 != that.x1) {
226                return false;
227            }
228            if (this.y1 != that.y1) {
229                return false;
230            }
231            if (this.x2 != that.x2) {
232                return false;
233            }
234            if (this.y2 != that.y2) {
235                return false;
236            }
237            if (!PaintUtilities.equal(this.paint, that.paint)) {
238                return false;
239            }
240            if (!ObjectUtilities.equal(this.stroke, that.stroke)) {
241                return false;
242            }
243            // seems to be the same...
244            return true;
245        }
246        
247        /**
248         * Returns a hash code.
249         * 
250         * @return A hash code.
251         */
252        public int hashCode() {
253            int result;
254            long temp;
255            temp = Double.doubleToLongBits(this.x1);
256            result = (int) (temp ^ (temp >>> 32));
257            temp = Double.doubleToLongBits(this.x2);
258            result = 29 * result + (int) (temp ^ (temp >>> 32));
259            temp = Double.doubleToLongBits(this.y1);
260            result = 29 * result + (int) (temp ^ (temp >>> 32));
261            temp = Double.doubleToLongBits(this.y2);
262            result = 29 * result + (int) (temp ^ (temp >>> 32));
263            return result;
264        }
265    
266        /**
267         * Returns a clone of the annotation.
268         * 
269         * @return A clone.
270         * 
271         * @throws CloneNotSupportedException  if the annotation can't be cloned.
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.
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.writePaint(this.paint, stream);
287            SerialUtilities.writeStroke(this.stroke, stream);
288        }
289    
290        /**
291         * Provides serialization support.
292         *
293         * @param stream  the input stream.
294         *
295         * @throws IOException  if there is an I/O error.
296         * @throws ClassNotFoundException  if there is a classpath problem.
297         */
298        private void readObject(ObjectInputStream stream) 
299            throws IOException, ClassNotFoundException {
300            stream.defaultReadObject();
301            this.paint = SerialUtilities.readPaint(stream);
302            this.stroke = SerialUtilities.readStroke(stream);
303        }
304    
305    }