View Javadoc

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          // don't draw the annotation if symbolic names date is out of axis' bounds.
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 }