1 package net.sf.statcvs.charts;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Font;
6 import java.awt.Graphics2D;
7 import java.awt.Stroke;
8 import java.awt.geom.Line2D;
9 import java.awt.geom.Rectangle2D;
10 import java.util.ArrayList;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.Iterator;
14 import java.util.List;
15
16 import net.sf.statcvs.model.SymbolicName;
17
18 import org.jfree.chart.annotations.XYAnnotation;
19 import org.jfree.chart.axis.ValueAxis;
20 import org.jfree.chart.plot.Plot;
21 import org.jfree.chart.plot.PlotOrientation;
22 import org.jfree.chart.plot.PlotRenderingInfo;
23 import org.jfree.chart.plot.XYPlot;
24 import org.jfree.text.TextUtilities;
25 import org.jfree.ui.RectangleEdge;
26 import org.jfree.ui.TextAnchor;
27
28 /**
29 * SymbolicNameAnnotation
30 *
31 * Provides symbolic name annotations for XYPlots with java.util.Date
32 * objects on the domain axis.
33 *
34 * TODO: Move vertically to accommodate annotations that are close together
35 *
36 * @author Tammo van Lessen
37 * @version $Id: SymbolicNameAnnotation.java,v 1.6 2008/04/02 11:22:15 benoitx Exp $
38 */
39 public class SymbolicNameAnnotation implements XYAnnotation {
40 public static final int STYLE_DEFAULT = 0;
41 public static final int STYLE_NO_LABELS = 1;
42 public static final float MIN_LABEL_X_SPACING = 7.0f;
43
44 public static List createAnnotations(final Collection symbolicNames) {
45 return createAnnotations(symbolicNames, STYLE_DEFAULT);
46 }
47
48 public static List createAnnotations(final Collection symbolicNames, final int style) {
49 final List annotations = new ArrayList();
50 for (final Iterator it = symbolicNames.iterator(); it.hasNext();) {
51 final SymbolicName sn = (SymbolicName) it.next();
52 annotations.add(new SymbolicNameAnnotation(sn, symbolicNames, style));
53 }
54 return annotations;
55 }
56
57 private final Color linePaint = Color.GRAY;
58 private final Color textPaint = Color.DARK_GRAY;
59 private final Font font = new Font("Dialog", Font.PLAIN, 9);
60 private final static Stroke STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[] { 3.5f }, 0.0f);
61
62 private final SymbolicName symbolicName;
63 private final List allSymbolicNames;
64 private final int style;
65
66 /**
67 * Creates an annotation for a symbolic name.
68 * Paints a gray dashed vertical line at the symbolic names
69 * date position and draws its name at the top left.
70 *
71 * @param symbolicName
72 * @param style {@link #STYLE_HEAVY} or {@link #STYLE_LIGHT}
73 */
74 public SymbolicNameAnnotation(final SymbolicName symbolicName, final Collection all, final int style) {
75 this.symbolicName = symbolicName;
76 this.allSymbolicNames = new ArrayList(all);
77 Collections.sort(this.allSymbolicNames);
78 this.style = style;
79 }
80
81 /**
82 * @see org.jfree.chart.annotations.XYAnnotation#draw(java.awt.Graphics2D, org.jfree.chart.plot.XYPlot, java.awt.geom.Rectangle2D, org.jfree.chart.axis.ValueAxis, org.jfree.chart.axis.ValueAxis, int, org.jfree.chart.plot.PlotRenderingInfo)
83 */
84 public void draw(final Graphics2D g2d, final XYPlot xyPlot, final Rectangle2D dataArea, final ValueAxis domainAxis, final ValueAxis rangeAxis,
85 final int rendererIndex, final PlotRenderingInfo info) {
86 final PlotOrientation orientation = xyPlot.getOrientation();
87
88
89 if (domainAxis.getUpperBound() < symbolicName.getDate().getTime() || domainAxis.getLowerBound() > symbolicName.getDate().getTime()) {
90
91 return;
92 }
93
94 final RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(xyPlot.getDomainAxisLocation(), orientation);
95 final RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(xyPlot.getRangeAxisLocation(), orientation);
96
97 final float x = getNaturalX(symbolicName, dataArea, domainAxis, domainEdge);
98
99 final float y1 = (float) rangeAxis.valueToJava2D(rangeAxis.getUpperBound(), dataArea, rangeEdge);
100 final float y2 = (float) rangeAxis.valueToJava2D(rangeAxis.getLowerBound(), dataArea, rangeEdge);
101
102 g2d.setPaint(linePaint);
103 g2d.setStroke(STROKE);
104
105 final Line2D line = new Line2D.Float(x, y1, x, y2);
106 g2d.draw(line);
107
108 if (this.style == STYLE_NO_LABELS) {
109 return;
110 }
111 g2d.setFont(font);
112 g2d.setPaint(textPaint);
113 TextUtilities.drawRotatedString(symbolicName.getName(), g2d, getArrangedLabelX(symbolicName, dataArea, domainAxis, domainEdge), y1 + 2,
114 TextAnchor.BOTTOM_RIGHT, -Math.PI / 2, TextAnchor.BOTTOM_RIGHT);
115 }
116
117 private float getArrangedLabelX(final SymbolicName tag, final Rectangle2D dataArea, final ValueAxis domainAxis, final RectangleEdge domainEdge) {
118 final float naturalX = getNaturalX(tag, dataArea, domainAxis, domainEdge);
119 final int offset = this.allSymbolicNames.indexOf(tag);
120 if (offset == this.allSymbolicNames.size() - 1) {
121 return naturalX;
122 }
123 final SymbolicName next = (SymbolicName) this.allSymbolicNames.get(offset + 1);
124 final float nextX = getArrangedLabelX(next, dataArea, domainAxis, domainEdge);
125 if (nextX > naturalX + MIN_LABEL_X_SPACING) {
126 return naturalX;
127 }
128 return nextX - MIN_LABEL_X_SPACING;
129 }
130
131 private float getNaturalX(final SymbolicName tag, final Rectangle2D dataArea, final ValueAxis domainAxis, final RectangleEdge domainEdge) {
132 return (float) domainAxis.valueToJava2D(tag.getDate().getTime(), dataArea, domainEdge);
133 }
134 }