001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * ---------------
028 * LabelBlock.java
029 * ---------------
030 * (C) Copyright 2004-present, by David Gilbert.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   Pierre-Marie Le Biot;
034 *                   Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier);
035 *
036 */
037
038package org.jfree.chart.block;
039
040import java.awt.Color;
041import java.awt.Font;
042import java.awt.Graphics2D;
043import java.awt.Paint;
044import java.awt.Shape;
045import java.awt.geom.Point2D;
046import java.awt.geom.Rectangle2D;
047import java.io.IOException;
048import java.io.ObjectInputStream;
049import java.io.ObjectOutputStream;
050import java.util.Objects;
051import org.jfree.chart.HashUtils;
052
053import org.jfree.chart.entity.ChartEntity;
054import org.jfree.chart.entity.StandardEntityCollection;
055import org.jfree.chart.text.TextBlock;
056import org.jfree.chart.text.TextBlockAnchor;
057import org.jfree.chart.text.TextUtils;
058import org.jfree.chart.ui.RectangleAnchor;
059import org.jfree.chart.ui.Size2D;
060import org.jfree.chart.util.PaintUtils;
061import org.jfree.chart.util.Args;
062import org.jfree.chart.util.PublicCloneable;
063import org.jfree.chart.util.SerialUtils;
064
065/**
066 * A block containing a label.
067 */
068public class LabelBlock extends AbstractBlock
069        implements Block, PublicCloneable {
070
071    /** For serialization. */
072    static final long serialVersionUID = 249626098864178017L;
073
074    /**
075     * The text for the label - retained in case the label needs
076     * regenerating (for example, to change the font).
077     */
078    private String text;
079
080    /** The label. */
081    private TextBlock label;
082
083    /** The font. */
084    private Font font;
085
086    /** The tool tip text (can be {@code null}). */
087    private String toolTipText;
088
089    /** The URL text (can be {@code null}). */
090    private String urlText;
091
092    /** The default color. */
093    public static final Paint DEFAULT_PAINT = Color.BLACK;
094
095    /** The paint. */
096    private transient Paint paint;
097
098    /**
099     * The content alignment point.
100     */
101    private TextBlockAnchor contentAlignmentPoint;
102
103    /**
104     * The anchor point for the text.
105     */
106    private RectangleAnchor textAnchor;
107
108    /**
109     * Creates a new label block.
110     *
111     * @param label  the label ({@code null} not permitted).
112     */
113    public LabelBlock(String label) {
114        this(label, new Font("SansSerif", Font.PLAIN, 10), DEFAULT_PAINT);
115    }
116
117    /**
118     * Creates a new label block.
119     *
120     * @param text  the text for the label ({@code null} not permitted).
121     * @param font  the font ({@code null} not permitted).
122     */
123    public LabelBlock(String text, Font font) {
124        this(text, font, DEFAULT_PAINT);
125    }
126
127    /**
128     * Creates a new label block.
129     *
130     * @param text  the text for the label ({@code null} not permitted).
131     * @param font  the font ({@code null} not permitted).
132     * @param paint the paint ({@code null} not permitted).
133     */
134    public LabelBlock(String text, Font font, Paint paint) {
135        this.text = text;
136        this.paint = paint;
137        this.label = TextUtils.createTextBlock(text, font, this.paint);
138        this.font = font;
139        this.toolTipText = null;
140        this.urlText = null;
141        this.contentAlignmentPoint = TextBlockAnchor.CENTER;
142        this.textAnchor = RectangleAnchor.CENTER;
143    }
144
145    /**
146     * Returns the font.
147     *
148     * @return The font (never {@code null}).
149     *
150     * @see #setFont(Font)
151     */
152    public Font getFont() {
153        return this.font;
154    }
155
156    /**
157     * Sets the font and regenerates the label.
158     *
159     * @param font  the font ({@code null} not permitted).
160     *
161     * @see #getFont()
162     */
163    public void setFont(Font font) {
164        Args.nullNotPermitted(font, "font");
165        this.font = font;
166        this.label = TextUtils.createTextBlock(this.text, font, this.paint);
167    }
168
169    /**
170     * Returns the paint.
171     *
172     * @return The paint (never {@code null}).
173     *
174     * @see #setPaint(Paint)
175     */
176    public Paint getPaint() {
177        return this.paint;
178    }
179
180    /**
181     * Sets the paint and regenerates the label.
182     *
183     * @param paint  the paint ({@code null} not permitted).
184     *
185     * @see #getPaint()
186     */
187    public void setPaint(Paint paint) {
188        Args.nullNotPermitted(paint, "paint");
189        this.paint = paint;
190        this.label = TextUtils.createTextBlock(this.text, this.font,
191                this.paint);
192    }
193
194    /**
195     * Returns the tool tip text.
196     *
197     * @return The tool tip text (possibly {@code null}).
198     *
199     * @see #setToolTipText(String)
200     */
201    public String getToolTipText() {
202        return this.toolTipText;
203    }
204
205    /**
206     * Sets the tool tip text.
207     *
208     * @param text  the text ({@code null} permitted).
209     *
210     * @see #getToolTipText()
211     */
212    public void setToolTipText(String text) {
213        this.toolTipText = text;
214    }
215
216    /**
217     * Returns the URL text.
218     *
219     * @return The URL text (possibly {@code null}).
220     *
221     * @see #setURLText(String)
222     */
223    public String getURLText() {
224        return this.urlText;
225    }
226
227    /**
228     * Sets the URL text.
229     *
230     * @param text  the text ({@code null} permitted).
231     *
232     * @see #getURLText()
233     */
234    public void setURLText(String text) {
235        this.urlText = text;
236    }
237
238    /**
239     * Returns the content alignment point.
240     *
241     * @return The content alignment point (never {@code null}).
242     */
243    public TextBlockAnchor getContentAlignmentPoint() {
244        return this.contentAlignmentPoint;
245    }
246
247    /**
248     * Sets the content alignment point.
249     *
250     * @param anchor  the anchor used to determine the alignment point (never
251     *         {@code null}).
252     */
253    public void setContentAlignmentPoint(TextBlockAnchor anchor) {
254        Args.nullNotPermitted(anchor, "anchor");
255        this.contentAlignmentPoint = anchor;
256    }
257
258    /**
259     * Returns the text anchor (never {@code null}).
260     *
261     * @return The text anchor.
262     */
263    public RectangleAnchor getTextAnchor() {
264        return this.textAnchor;
265    }
266
267    /**
268     * Sets the text anchor.
269     *
270     * @param anchor  the anchor ({@code null} not permitted).
271     */
272    public void setTextAnchor(RectangleAnchor anchor) {
273        this.textAnchor = anchor;
274    }
275
276    /**
277     * Arranges the contents of the block, within the given constraints, and
278     * returns the block size.
279     *
280     * @param g2  the graphics device.
281     * @param constraint  the constraint ({@code null} not permitted).
282     *
283     * @return The block size (in Java2D units, never {@code null}).
284     */
285    @Override
286    public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
287        g2.setFont(this.font);
288        Size2D s = this.label.calculateDimensions(g2);
289        return new Size2D(calculateTotalWidth(s.getWidth()),
290                calculateTotalHeight(s.getHeight()));
291    }
292
293    /**
294     * Draws the block.
295     *
296     * @param g2  the graphics device.
297     * @param area  the area.
298     */
299    @Override
300    public void draw(Graphics2D g2, Rectangle2D area) {
301        draw(g2, area, null);
302    }
303
304    /**
305     * Draws the block within the specified area.
306     *
307     * @param g2  the graphics device.
308     * @param area  the area.
309     * @param params  ignored ({@code null} permitted).
310     *
311     * @return Always {@code null}.
312     */
313    @Override
314    public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
315        area = trimMargin(area);
316        drawBorder(g2, area);
317        area = trimBorder(area);
318        area = trimPadding(area);
319
320        // check if we need to collect chart entities from the container
321        EntityBlockParams ebp = null;
322        StandardEntityCollection sec = null;
323        Shape entityArea = null;
324        if (params instanceof EntityBlockParams) {
325            ebp = (EntityBlockParams) params;
326            if (ebp.getGenerateEntities()) {
327                sec = new StandardEntityCollection();
328                entityArea = (Shape) area.clone();
329            }
330        }
331        g2.setPaint(this.paint);
332        g2.setFont(this.font);
333        Point2D pt = this.textAnchor.getAnchorPoint(area);
334        this.label.draw(g2, (float) pt.getX(), (float) pt.getY(),
335                this.contentAlignmentPoint);
336        BlockResult result = null;
337        if (ebp != null && sec != null) {
338            if (this.toolTipText != null || this.urlText != null) {
339                ChartEntity entity = new ChartEntity(entityArea,
340                        this.toolTipText, this.urlText);
341                sec.add(entity);
342                result = new BlockResult();
343                result.setEntityCollection(sec);
344            }
345        }
346        return result;
347    }
348
349    /**
350     * Tests this {@code LabelBlock} for equality with an arbitrary object.
351     *
352     * @param obj  the object ({@code null} permitted).
353     *
354     * @return A boolean.
355     */
356    @Override
357    public boolean equals(Object obj) {
358        if (obj == this) {
359            return true;
360        }
361        if (!(obj instanceof LabelBlock)) {
362            return false;
363        }
364        LabelBlock that = (LabelBlock) obj;
365        if (!Objects.equals(this.text, that.text)) {
366            return false;
367        }
368        if (!Objects.equals(this.label, that.label)) {
369            return false;
370        }
371        if (!Objects.equals(this.font, that.font)) {
372            return false;
373        }
374        if (!PaintUtils.equal(this.paint, that.paint)) {
375            return false;
376        }
377        if (!Objects.equals(this.toolTipText, that.toolTipText)) {
378            return false;
379        }
380        if (!Objects.equals(this.urlText, that.urlText)) {
381            return false;
382        }
383        if (!Objects.equals(this.contentAlignmentPoint, that.contentAlignmentPoint)) {
384            return false;
385        }
386        if (!Objects.equals(this.textAnchor, that.textAnchor)) {
387            return false;
388        }
389        if (!that.canEqual(this)) {
390            return false;
391        }
392        return super.equals(obj);
393    }
394
395    /**
396     * Ensures symmetry between super/subclass implementations of equals. For
397     * more detail, see http://jqno.nl/equalsverifier/manual/inheritance.
398     *
399     * @param other Object
400     * 
401     * @return true ONLY if the parameter is THIS class type
402     */
403    @Override
404    public boolean canEqual(Object other) {
405        // fix the "equals not symmetric" problem
406        return (other instanceof LabelBlock);
407    }
408
409    @Override
410    public int hashCode() {
411        int hash = super.hashCode(); // equals calls superclass, hashCode must also
412        hash = 71 * hash + Objects.hashCode(this.text);
413        hash = 71 * hash + Objects.hashCode(this.label);
414        hash = 71 * hash + Objects.hashCode(this.font);
415        hash = 71 * hash + Objects.hashCode(this.toolTipText);
416        hash = 71 * hash + Objects.hashCode(this.urlText);
417        hash = 71 * hash + HashUtils.hashCodeForPaint(this.paint);
418        hash = 71 * hash + Objects.hashCode(this.contentAlignmentPoint);
419        hash = 71 * hash + Objects.hashCode(this.textAnchor);
420        return hash;
421    }
422
423    /**
424     * Returns a clone of this {@code LabelBlock} instance.
425     *
426     * @return A clone.
427     *
428     * @throws CloneNotSupportedException if there is a problem cloning.
429     */
430    @Override
431    public Object clone() throws CloneNotSupportedException {
432        return super.clone();
433    }
434
435    /**
436     * Provides serialization support.
437     *
438     * @param stream  the output stream.
439     *
440     * @throws IOException if there is an I/O error.
441     */
442    private void writeObject(ObjectOutputStream stream) throws IOException {
443        stream.defaultWriteObject();
444        SerialUtils.writePaint(this.paint, stream);
445    }
446
447    /**
448     * Provides serialization support.
449     *
450     * @param stream  the input stream.
451     *
452     * @throws IOException  if there is an I/O error.
453     * @throws ClassNotFoundException  if there is a classpath problem.
454     */
455    private void readObject(ObjectInputStream stream)
456        throws IOException, ClassNotFoundException {
457        stream.defaultReadObject();
458        this.paint = SerialUtils.readPaint(stream);
459    }
460
461}