1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jfreechart/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU Lesser General Public License as published by
  11. * the Free Software Foundation; either version 2.1 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17. * License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public License
  20. * along with this library; if not, write to the Free Software Foundation,
  21. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  22. *
  23. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  24. * in the United States and other countries.]
  25. *
  26. * ---------------------------
  27. * AbstractXYItemRenderer.java
  28. * ---------------------------
  29. * (C) Copyright 2002-2005, by Object Refinery Limited and Contributors.
  30. *
  31. * Original Author: David Gilbert (for Object Refinery Limited);
  32. * Contributor(s): Richard Atkinson;
  33. * Focus Computer Services Limited;
  34. * Tim Bardzil;
  35. *
  36. * $Id: AbstractXYItemRenderer.java,v 1.20 2005/03/08 15:55:04 mungady Exp $
  37. *
  38. * Changes:
  39. * --------
  40. * 15-Mar-2002 : Version 1 (DG);
  41. * 09-Apr-2002 : Added a getToolTipGenerator() method reflecting the change in
  42. * the XYItemRenderer interface (DG);
  43. * 05-Aug-2002 : Added a urlGenerator member variable to support HTML image
  44. * maps (RA);
  45. * 20-Aug-2002 : Added property change events for the tooltip and URL
  46. * generators (DG);
  47. * 22-Aug-2002 : Moved property change support into AbstractRenderer class (DG);
  48. * 23-Sep-2002 : Fixed errors reported by Checkstyle tool (DG);
  49. * 18-Nov-2002 : Added methods for drawing grid lines (DG);
  50. * 17-Jan-2003 : Moved plot classes into a separate package (DG);
  51. * 25-Mar-2003 : Implemented Serializable (DG);
  52. * 01-May-2003 : Modified initialise() return type and drawItem() method
  53. * signature (DG);
  54. * 15-May-2003 : Modified to take into account the plot orientation (DG);
  55. * 21-May-2003 : Added labels to markers (DG);
  56. * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
  57. * Services Ltd) (DG);
  58. * 27-Jul-2003 : Added getRangeType() to support stacked XY area charts (RA);
  59. * 31-Jul-2003 : Deprecated all but the default constructor (DG);
  60. * 13-Aug-2003 : Implemented Cloneable (DG);
  61. * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
  62. * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
  63. * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG);
  64. * 11-Feb-2004 : Updated labelling for markers (DG);
  65. * 25-Feb-2004 : Added updateCrosshairValues() method. Moved deprecated code
  66. * to bottom of source file (DG);
  67. * 16-Apr-2004 : Added support for IntervalMarker in drawRangeMarker() method
  68. * - thanks to Tim Bardzil (DG);
  69. * 05-May-2004 : Fixed bug (948310) where interval markers extend beyond axis
  70. * range (DG);
  71. * 03-Jun-2004 : Fixed more bugs in drawing interval markers (DG);
  72. * 26-Aug-2004 : Added the addEntity() method (DG);
  73. * 29-Sep-2004 : Added annotation support (with layers) (DG);
  74. * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities -->
  75. * TextUtilities (DG);
  76. * 06-Oct-2004 : Added findDomainBounds() method and renamed
  77. * getRangeExtent() --> findRangeBounds() (DG);
  78. * 07-Jan-2005 : Removed deprecated code (DG);
  79. * 27-Jan-2005 : Modified getLegendItem() to omit hidden series (DG);
  80. * 24-Feb-2005 : Added getLegendItems() method (DG);
  81. * 08-Mar-2005 : Fixed positioning of marker labels (DG);
  82. *
  83. */
  84. package org.jfree.chart.renderer.xy;
  85. import java.awt.Font;
  86. import java.awt.GradientPaint;
  87. import java.awt.Graphics2D;
  88. import java.awt.Paint;
  89. import java.awt.Shape;
  90. import java.awt.Stroke;
  91. import java.awt.geom.Ellipse2D;
  92. import java.awt.geom.Line2D;
  93. import java.awt.geom.Point2D;
  94. import java.awt.geom.Rectangle2D;
  95. import java.io.Serializable;
  96. import java.util.Iterator;
  97. import java.util.List;
  98. import org.jfree.chart.LegendItem;
  99. import org.jfree.chart.LegendItemCollection;
  100. import org.jfree.chart.annotations.XYAnnotation;
  101. import org.jfree.chart.axis.ValueAxis;
  102. import org.jfree.chart.entity.EntityCollection;
  103. import org.jfree.chart.entity.XYItemEntity;
  104. import org.jfree.chart.event.RendererChangeEvent;
  105. import org.jfree.chart.labels.ItemLabelPosition;
  106. import org.jfree.chart.labels.StandardXYSeriesLabelGenerator;
  107. import org.jfree.chart.labels.XYLabelGenerator;
  108. import org.jfree.chart.labels.XYSeriesLabelGenerator;
  109. import org.jfree.chart.labels.XYToolTipGenerator;
  110. import org.jfree.chart.plot.CrosshairState;
  111. import org.jfree.chart.plot.DrawingSupplier;
  112. import org.jfree.chart.plot.IntervalMarker;
  113. import org.jfree.chart.plot.Marker;
  114. import org.jfree.chart.plot.Plot;
  115. import org.jfree.chart.plot.PlotOrientation;
  116. import org.jfree.chart.plot.PlotRenderingInfo;
  117. import org.jfree.chart.plot.ValueMarker;
  118. import org.jfree.chart.plot.XYPlot;
  119. import org.jfree.chart.renderer.AbstractRenderer;
  120. import org.jfree.chart.urls.XYURLGenerator;
  121. import org.jfree.data.Range;
  122. import org.jfree.data.general.DatasetUtilities;
  123. import org.jfree.data.xy.XYDataset;
  124. import org.jfree.text.TextUtilities;
  125. import org.jfree.ui.GradientPaintTransformer;
  126. import org.jfree.ui.Layer;
  127. import org.jfree.ui.LengthAdjustmentType;
  128. import org.jfree.ui.RectangleAnchor;
  129. import org.jfree.ui.RectangleInsets;
  130. import org.jfree.util.Log;
  131. import org.jfree.util.LogContext;
  132. import org.jfree.util.ObjectList;
  133. import org.jfree.util.ObjectUtilities;
  134. import org.jfree.util.PublicCloneable;
  135. /**
  136. * A base class that can be used to create new {@link XYItemRenderer}
  137. * implementations.
  138. */
  139. public abstract class AbstractXYItemRenderer extends AbstractRenderer
  140. implements XYItemRenderer,
  141. Cloneable,
  142. Serializable {
  143. /** The plot. */
  144. private XYPlot plot;
  145. /** The item label generator for ALL series. */
  146. private XYLabelGenerator itemLabelGenerator;
  147. /** A list of item label generators (one per series). */
  148. private ObjectList itemLabelGeneratorList;
  149. /** The base item label generator. */
  150. private XYLabelGenerator baseItemLabelGenerator;
  151. /** The tool tip generator for ALL series. */
  152. private XYToolTipGenerator toolTipGenerator;
  153. /** A list of tool tip generators (one per series). */
  154. private ObjectList toolTipGeneratorList;
  155. /** The base tool tip generator. */
  156. private XYToolTipGenerator baseToolTipGenerator;
  157. /** The URL text generator. */
  158. private XYURLGenerator urlGenerator;
  159. /**
  160. * Annotations to be drawn in the background layer ('underneath' the data
  161. * items).
  162. */
  163. private List backgroundAnnotations;
  164. /**
  165. * Annotations to be drawn in the foreground layer ('on top' of the data
  166. * items).
  167. */
  168. private List foregroundAnnotations;
  169. private int defaultEntityRadius;
  170. private XYSeriesLabelGenerator legendItemLabelGenerator;
  171. /** Access to logging facilities. */
  172. private static final LogContext LOGGER
  173. = Log.createContext(AbstractXYItemRenderer.class);
  174. /**
  175. * Creates a renderer where the tooltip generator and the URL generator are
  176. * both <code>null</code>.
  177. */
  178. protected AbstractXYItemRenderer() {
  179. this.itemLabelGenerator = null;
  180. this.itemLabelGeneratorList = new ObjectList();
  181. this.toolTipGenerator = null;
  182. this.toolTipGeneratorList = new ObjectList();
  183. this.urlGenerator = null;
  184. this.backgroundAnnotations = new java.util.ArrayList();
  185. this.foregroundAnnotations = new java.util.ArrayList();
  186. this.defaultEntityRadius = 3;
  187. this.legendItemLabelGenerator
  188. = new StandardXYSeriesLabelGenerator("{0}");
  189. }
  190. /**
  191. * Returns the number of passes through the data that the renderer requires
  192. * in order to draw the chart. Most charts will require a single pass, but
  193. * some require two passes.
  194. *
  195. * @return The pass count.
  196. */
  197. public int getPassCount() {
  198. return 1;
  199. }
  200. /**
  201. * Returns the plot that the renderer is assigned to.
  202. *
  203. * @return The plot.
  204. */
  205. public XYPlot getPlot() {
  206. return this.plot;
  207. }
  208. /**
  209. * Sets the plot that the renderer is assigned to.
  210. *
  211. * @param plot the plot.
  212. */
  213. public void setPlot(XYPlot plot) {
  214. this.plot = plot;
  215. }
  216. /**
  217. * Initialises the renderer and returns a state object that should be
  218. * passed to all subsequent calls to the drawItem() method.
  219. * <P>
  220. * This method will be called before the first item is rendered, giving the
  221. * renderer an opportunity to initialise any state information it wants to
  222. * maintain. The renderer can do nothing if it chooses.
  223. *
  224. * @param g2 the graphics device.
  225. * @param dataArea the area inside the axes.
  226. * @param plot the plot.
  227. * @param data the data.
  228. * @param info an optional info collection object to return data back to
  229. * the caller.
  230. *
  231. * @return The renderer state (never <code>null</code>).
  232. */
  233. public XYItemRendererState initialise(Graphics2D g2,
  234. Rectangle2D dataArea,
  235. XYPlot plot,
  236. XYDataset data,
  237. PlotRenderingInfo info) {
  238. XYItemRendererState state = new XYItemRendererState(info);
  239. return state;
  240. }
  241. // LABEL GENERATOR
  242. /**
  243. * Returns the label generator for a data item. This implementation simply
  244. * passes control to the {@link #getSeriesLabelGenerator(int)} method. If,
  245. * for some reason, you want a different generator for individual items,
  246. * you can override this method.
  247. *
  248. * @param row the row index (zero based).
  249. * @param column the column index (zero based).
  250. *
  251. * @return The generator (possibly <code>null</code>).
  252. */
  253. public XYLabelGenerator getLabelGenerator(int row, int column) {
  254. return getSeriesLabelGenerator(row);
  255. }
  256. /**
  257. * Returns the label generator for a series.
  258. *
  259. * @param series the series index (zero based).
  260. *
  261. * @return the generator (possibly <code>null</code>).
  262. */
  263. public XYLabelGenerator getSeriesLabelGenerator(int series) {
  264. // return the generator for ALL series, if there is one...
  265. if (this.itemLabelGenerator != null) {
  266. return this.itemLabelGenerator;
  267. }
  268. // otherwise look up the generator table
  269. XYLabelGenerator generator
  270. = (XYLabelGenerator) this.itemLabelGeneratorList.get(series);
  271. if (generator == null) {
  272. generator = this.baseItemLabelGenerator;
  273. }
  274. return generator;
  275. }
  276. /**
  277. * Sets the item label generator for ALL series and sends a
  278. * {@link RendererChangeEvent} to all registered listeners.
  279. *
  280. * @param generator the generator (<code>null</code> permitted).
  281. */
  282. public void setLabelGenerator(XYLabelGenerator generator) {
  283. this.itemLabelGenerator = generator;
  284. notifyListeners(new RendererChangeEvent(this));
  285. }
  286. /**
  287. * Sets the label generator for a series and sends a
  288. * {@link RendererChangeEvent} to all registered listeners.
  289. *
  290. * @param series the series index (zero based).
  291. * @param generator the generator (<code>null</code> permitted).
  292. */
  293. public void setSeriesLabelGenerator(int series,
  294. XYLabelGenerator generator) {
  295. this.itemLabelGeneratorList.set(series, generator);
  296. notifyListeners(new RendererChangeEvent(this));
  297. }
  298. /**
  299. * Returns the base item label generator.
  300. *
  301. * @return the generator (possibly <code>null</code>).
  302. */
  303. public XYLabelGenerator getBaseLabelGenerator() {
  304. return this.baseItemLabelGenerator;
  305. }
  306. /**
  307. * Sets the base item label generator and sends a
  308. * {@link RendererChangeEvent} to all registered listeners.
  309. *
  310. * @param generator the generator (<code>null</code> permitted).
  311. */
  312. public void setBaseLabelGenerator(XYLabelGenerator generator) {
  313. this.baseItemLabelGenerator = generator;
  314. notifyListeners(new RendererChangeEvent(this));
  315. }
  316. // TOOL TIP GENERATOR
  317. /**
  318. * Returns the tool tip generator for a data item. This implementation
  319. * simply passes control to the getSeriesToolTipGenerator() method. If,
  320. * for some reason, you want a different generator for individual items,
  321. * you can override this method.
  322. *
  323. * @param row the row index (zero based).
  324. * @param column the column index (zero based).
  325. *
  326. * @return The generator (possibly <code>null</code>).
  327. */
  328. public XYToolTipGenerator getToolTipGenerator(int row, int column) {
  329. return getSeriesToolTipGenerator(row);
  330. }
  331. /**
  332. * Returns the tool tip generator for a series.
  333. *
  334. * @param series the series index (zero based).
  335. *
  336. * @return The generator (possibly <code>null</code>).
  337. */
  338. public XYToolTipGenerator getSeriesToolTipGenerator(int series) {
  339. // return the generator for ALL series, if there is one...
  340. if (this.toolTipGenerator != null) {
  341. return this.toolTipGenerator;
  342. }
  343. // otherwise look up the generator table
  344. XYToolTipGenerator generator
  345. = (XYToolTipGenerator) this.toolTipGeneratorList.get(series);
  346. if (generator == null) {
  347. generator = this.baseToolTipGenerator;
  348. }
  349. return generator;
  350. }
  351. /**
  352. * Sets the tool tip generator for ALL series and sends a
  353. * {@link RendererChangeEvent} to all registered listeners.
  354. *
  355. * @param generator the generator (<code>null</code> permitted).
  356. */
  357. public void setToolTipGenerator(XYToolTipGenerator generator) {
  358. this.toolTipGenerator = generator;
  359. notifyListeners(new RendererChangeEvent(this));
  360. }
  361. /**
  362. * Sets the tool tip generator for a series and sends a
  363. * {@link RendererChangeEvent} to all registered listeners.
  364. *
  365. * @param series the series index (zero based).
  366. * @param generator the generator (<code>null</code> permitted).
  367. */
  368. public void setSeriesToolTipGenerator(int series,
  369. XYToolTipGenerator generator) {
  370. this.toolTipGeneratorList.set(series, generator);
  371. notifyListeners(new RendererChangeEvent(this));
  372. }
  373. /**
  374. * Returns the base tool tip generator.
  375. *
  376. * @return The generator (possibly <code>null</code>).
  377. */
  378. public XYToolTipGenerator getBaseToolTipGenerator() {
  379. return this.baseToolTipGenerator;
  380. }
  381. /**
  382. * Sets the base tool tip generator and sends a {@link RendererChangeEvent}
  383. * to all registered listeners.
  384. *
  385. * @param generator the generator (<code>null</code> permitted).
  386. */
  387. public void setBaseToolTipGenerator(XYToolTipGenerator generator) {
  388. this.baseToolTipGenerator = generator;
  389. notifyListeners(new RendererChangeEvent(this));
  390. }
  391. // URL GENERATOR
  392. /**
  393. * Returns the URL generator for HTML image maps.
  394. *
  395. * @return the URL generator (possibly <code>null</code>).
  396. */
  397. public XYURLGenerator getURLGenerator() {
  398. return this.urlGenerator;
  399. }
  400. /**
  401. * Sets the URL generator for HTML image maps.
  402. *
  403. * @param urlGenerator the URL generator (<code>null</code> permitted).
  404. */
  405. public void setURLGenerator(XYURLGenerator urlGenerator) {
  406. this.urlGenerator = urlGenerator;
  407. notifyListeners(new RendererChangeEvent(this));
  408. }
  409. /**
  410. * Adds an annotation and sends a {@link RendererChangeEvent} to all
  411. * registered listeners. The annotation is added to the foreground
  412. * layer.
  413. *
  414. * @param annotation the annotation (<code>null</code> not permitted).
  415. */
  416. public void addAnnotation(XYAnnotation annotation) {
  417. // defer argument checking
  418. addAnnotation(annotation, Layer.FOREGROUND);
  419. }
  420. /**
  421. * Adds an annotation to the specified layer.
  422. *
  423. * @param annotation the annotation (<code>null</code> not permitted).
  424. * @param layer the layer (<code>null</code> not permitted).
  425. */
  426. public void addAnnotation(XYAnnotation annotation, Layer layer) {
  427. if (annotation == null) {
  428. throw new IllegalArgumentException("Null 'annotation' argument.");
  429. }
  430. if (layer.equals(Layer.FOREGROUND)) {
  431. this.foregroundAnnotations.add(annotation);
  432. notifyListeners(new RendererChangeEvent(this));
  433. }
  434. else if (layer.equals(Layer.BACKGROUND)) {
  435. this.backgroundAnnotations.add(annotation);
  436. notifyListeners(new RendererChangeEvent(this));
  437. }
  438. else {
  439. // should never get here
  440. throw new RuntimeException("Unknown layer.");
  441. }
  442. }
  443. /**
  444. * Removes the specified annotation and sends a {@link RendererChangeEvent}
  445. * to all registered listeners.
  446. *
  447. * @param annotation the annotation to remove (<code>null</code> not
  448. * permitted).
  449. *
  450. * @return A boolean to indicate whether or not the annotation was
  451. * successfully removed.
  452. */
  453. public boolean removeAnnotation(XYAnnotation annotation) {
  454. boolean removed = this.foregroundAnnotations.remove(annotation);
  455. removed = removed & this.backgroundAnnotations.remove(annotation);
  456. notifyListeners(new RendererChangeEvent(this));
  457. return removed;
  458. }
  459. /**
  460. * Removes all annotations and sends a {@link RendererChangeEvent}
  461. * to all registered listeners.
  462. */
  463. public void removeAnnotations() {
  464. this.foregroundAnnotations.clear();
  465. this.backgroundAnnotations.clear();
  466. notifyListeners(new RendererChangeEvent(this));
  467. }
  468. /**
  469. * Returns the radius of the circle used for the default entity area
  470. * when no area is specified.
  471. *
  472. * @return A radius.
  473. */
  474. public int getDefaultEntityRadius() {
  475. return this.defaultEntityRadius;
  476. }
  477. /**
  478. * Sets the radius of the circle used for the default entity area
  479. * when no area is specified.
  480. *
  481. * @param radius the radius.
  482. */
  483. public void setDefaultEntityRadius(int radius) {
  484. this.defaultEntityRadius = radius;
  485. }
  486. /**
  487. * Returns the legend item label generator.
  488. *
  489. * @return The label generator (never <code>null</code>).
  490. */
  491. public XYSeriesLabelGenerator getLegendItemLabelGenerator() {
  492. return this.legendItemLabelGenerator;
  493. }
  494. /**
  495. * Sets the legend item label generator.
  496. *
  497. * @param generator the generator (<code>null</code> not permitted).
  498. */
  499. public void setLegendItemLabelGenerator(XYSeriesLabelGenerator generator) {
  500. if (generator == null) {
  501. throw new IllegalArgumentException("Null 'generator' argument.");
  502. }
  503. this.legendItemLabelGenerator = generator;
  504. }
  505. /**
  506. * Returns the lower and upper bounds (range) of the x-values in the
  507. * specified dataset.
  508. *
  509. * @param dataset the dataset (<code>null</code> permitted).
  510. *
  511. * @return The range (<code>null</code> if the dataset is <code>null</code>
  512. * or empty).
  513. */
  514. public Range findDomainBounds(XYDataset dataset) {
  515. if (dataset != null) {
  516. return DatasetUtilities.findDomainBounds(dataset, false);
  517. }
  518. else {
  519. return null;
  520. }
  521. }
  522. /**
  523. * Returns the range of values the renderer requires to display all the
  524. * items from the specified dataset.
  525. *
  526. * @param dataset the dataset (<code>null</code> permitted).
  527. *
  528. * @return The range (<code>null</code> if the dataset is <code>null</code>
  529. * or empty).
  530. */
  531. public Range findRangeBounds(XYDataset dataset) {
  532. if (dataset != null) {
  533. return DatasetUtilities.findRangeBounds(dataset, false);
  534. }
  535. else {
  536. return null;
  537. }
  538. }
  539. /**
  540. * Returns a (possibly empty) collection of legend items for the series
  541. * that this renderer is responsible for drawing.
  542. *
  543. * @return The legend item collection (never <code>null</code>).
  544. */
  545. public LegendItemCollection getLegendItems() {
  546. if (this.plot == null) {
  547. return new LegendItemCollection();
  548. }
  549. LegendItemCollection result = new LegendItemCollection();
  550. int index = this.plot.getIndexOf(this);
  551. XYDataset dataset = this.plot.getDataset(index);
  552. if (dataset != null) {
  553. int seriesCount = dataset.getSeriesCount();
  554. for (int i = 0; i < seriesCount; i++) {
  555. if (isSeriesVisibleInLegend(i)) {
  556. LegendItem item = getLegendItem(index, i);
  557. if (item != null) {
  558. result.add(item);
  559. }
  560. }
  561. }
  562. }
  563. return result;
  564. }
  565. /**
  566. * Returns a default legend item for the specified series. Subclasses
  567. * should override this method to generate customised items.
  568. *
  569. * @param datasetIndex the dataset index (zero-based).
  570. * @param series the series index (zero-based).
  571. *
  572. * @return A legend item for the series.
  573. */
  574. public LegendItem getLegendItem(int datasetIndex, int series) {
  575. LegendItem result = null;
  576. XYPlot xyplot = getPlot();
  577. if (xyplot != null) {
  578. XYDataset dataset = xyplot.getDataset(datasetIndex);
  579. if (dataset != null) {
  580. if (getItemVisible(series, 0)) {
  581. String label = this.legendItemLabelGenerator.generateLabel(
  582. dataset, series
  583. );
  584. String description = label;
  585. Shape shape = getSeriesShape(series);
  586. Paint paint = getSeriesPaint(series);
  587. Paint outlinePaint = getSeriesOutlinePaint(series);
  588. Stroke outlineStroke = getSeriesOutlineStroke(series);
  589. result = new LegendItem(
  590. label, description, shape, paint,
  591. outlineStroke, outlinePaint
  592. );
  593. }
  594. }
  595. }
  596. return result;
  597. }
  598. /**
  599. * Fills a band between two values on the axis. This can be used to color
  600. * bands between the grid lines.
  601. *
  602. * @param g2 the graphics device.
  603. * @param plot the plot.
  604. * @param axis the domain axis.
  605. * @param dataArea the data area.
  606. * @param start the start value.
  607. * @param end the end value.
  608. */
  609. public void fillDomainGridBand(Graphics2D g2,
  610. XYPlot plot,
  611. ValueAxis axis,
  612. Rectangle2D dataArea,
  613. double start, double end) {
  614. double x1 = axis.valueToJava2D(
  615. start, dataArea, plot.getDomainAxisEdge()
  616. );
  617. double x2 = axis.valueToJava2D(
  618. end, dataArea, plot.getDomainAxisEdge()
  619. );
  620. // TODO: need to change the next line to take account of plot
  621. // orientation...
  622. Rectangle2D band = new Rectangle2D.Double(
  623. x1, dataArea.getMinY(),
  624. x2 - x1, dataArea.getMaxY() - dataArea.getMinY()
  625. );
  626. Paint paint = plot.getDomainTickBandPaint();
  627. if (paint != null) {
  628. g2.setPaint(paint);
  629. g2.fill(band);
  630. }
  631. }
  632. /**
  633. * Fills a band between two values on the range axis. This can be used to
  634. * color bands between the grid lines.
  635. *
  636. * @param g2 the graphics device.
  637. * @param plot the plot.
  638. * @param axis the range axis.
  639. * @param dataArea the data area.
  640. * @param start the start value.
  641. * @param end the end value.
  642. */
  643. public void fillRangeGridBand(Graphics2D g2,
  644. XYPlot plot,
  645. ValueAxis axis,
  646. Rectangle2D dataArea,
  647. double start, double end) {
  648. double y1 = axis.valueToJava2D(
  649. start, dataArea, plot.getRangeAxisEdge()
  650. );
  651. double y2 = axis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge());
  652. // TODO: need to change the next line to take account of the plot
  653. // orientation
  654. Rectangle2D band = new Rectangle2D.Double(
  655. dataArea.getMinX(), y2, dataArea.getWidth(), y1 - y2
  656. );
  657. Paint paint = plot.getRangeTickBandPaint();
  658. if (paint != null) {
  659. g2.setPaint(paint);
  660. g2.fill(band);
  661. }
  662. }
  663. /**
  664. * Draws a grid line against the range axis.
  665. *
  666. * @param g2 the graphics device.
  667. * @param plot the plot.
  668. * @param axis the value axis.
  669. * @param dataArea the area for plotting data (not yet adjusted for any
  670. * 3D effect).
  671. * @param value the value at which the grid line should be drawn.
  672. *
  673. */
  674. public void drawDomainGridLine(Graphics2D g2,
  675. XYPlot plot,
  676. ValueAxis axis,
  677. Rectangle2D dataArea,
  678. double value) {
  679. Range range = axis.getRange();
  680. if (!range.contains(value)) {
  681. return;
  682. }
  683. PlotOrientation orientation = plot.getOrientation();
  684. double v = axis.valueToJava2D(
  685. value, dataArea, plot.getDomainAxisEdge()
  686. );
  687. Line2D line = null;
  688. if (orientation == PlotOrientation.HORIZONTAL) {
  689. line = new Line2D.Double(
  690. dataArea.getMinX(), v, dataArea.getMaxX(), v
  691. );
  692. }
  693. else if (orientation == PlotOrientation.VERTICAL) {
  694. line = new Line2D.Double(
  695. v, dataArea.getMinY(), v, dataArea.getMaxY()
  696. );
  697. }
  698. Paint paint = plot.getDomainGridlinePaint();
  699. Stroke stroke = plot.getDomainGridlineStroke();
  700. g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
  701. g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
  702. g2.draw(line);
  703. }
  704. /**
  705. * Draws a line perpendicular to the range axis.
  706. *
  707. * @param g2 the graphics device.
  708. * @param plot the plot.
  709. * @param axis the value axis.
  710. * @param dataArea the area for plotting data (not yet adjusted for any 3D
  711. * effect).
  712. * @param value the value at which the grid line should be drawn.
  713. * @param paint the paint.
  714. * @param stroke the stroke.
  715. */
  716. public void drawRangeLine(Graphics2D g2,
  717. XYPlot plot,
  718. ValueAxis axis,
  719. Rectangle2D dataArea,
  720. double value,
  721. Paint paint,
  722. Stroke stroke) {
  723. Range range = axis.getRange();
  724. if (!range.contains(value)) {
  725. return;
  726. }
  727. PlotOrientation orientation = plot.getOrientation();
  728. Line2D line = null;
  729. double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge());
  730. if (orientation == PlotOrientation.HORIZONTAL) {
  731. line = new Line2D.Double(
  732. v, dataArea.getMinY(), v, dataArea.getMaxY()
  733. );
  734. }
  735. else if (orientation == PlotOrientation.VERTICAL) {
  736. line = new Line2D.Double(
  737. dataArea.getMinX(), v, dataArea.getMaxX(), v
  738. );
  739. }
  740. g2.setPaint(paint);
  741. g2.setStroke(stroke);
  742. g2.draw(line);
  743. }
  744. /**
  745. * Draws a vertical line on the chart to represent a 'range marker'.
  746. *
  747. * @param g2 the graphics device.
  748. * @param plot the plot.
  749. * @param domainAxis the domain axis.
  750. * @param marker the marker line.
  751. * @param dataArea the axis data area.
  752. */
  753. public void drawDomainMarker(Graphics2D g2,
  754. XYPlot plot,
  755. ValueAxis domainAxis,
  756. Marker marker,
  757. Rectangle2D dataArea) {
  758. if (marker instanceof ValueMarker) {
  759. ValueMarker vm = (ValueMarker) marker;
  760. double value = vm.getValue();
  761. Range range = domainAxis.getRange();
  762. if (!range.contains(value)) {
  763. return;
  764. }
  765. double v = domainAxis.valueToJava2D(
  766. value, dataArea, plot.getDomainAxisEdge()
  767. );
  768. PlotOrientation orientation = plot.getOrientation();
  769. Line2D line = null;
  770. if (orientation == PlotOrientation.HORIZONTAL) {
  771. line = new Line2D.Double(
  772. dataArea.getMinX(), v, dataArea.getMaxX(), v
  773. );
  774. }
  775. else if (orientation == PlotOrientation.VERTICAL) {
  776. line = new Line2D.Double(
  777. v, dataArea.getMinY(), v, dataArea.getMaxY()
  778. );
  779. }
  780. g2.setPaint(marker.getPaint());
  781. g2.setStroke(marker.getStroke());
  782. g2.draw(line);
  783. String label = marker.getLabel();
  784. RectangleAnchor anchor = marker.getLabelAnchor();
  785. if (label != null) {
  786. Font labelFont = marker.getLabelFont();
  787. g2.setFont(labelFont);
  788. g2.setPaint(marker.getLabelPaint());
  789. Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
  790. g2, orientation, dataArea, line.getBounds2D(),
  791. marker.getLabelOffset(),
  792. marker.getLabelOffsetTypeForDomain(),
  793. marker.getLabelOffsetTypeForRange(), anchor
  794. );
  795. TextUtilities.drawAlignedString(
  796. label, g2,
  797. (float) coordinates.getX(), (float) coordinates.getY(),
  798. marker.getLabelTextAnchor()
  799. );
  800. }
  801. }
  802. else if (marker instanceof IntervalMarker) {
  803. IntervalMarker im = (IntervalMarker) marker;
  804. double start = im.getStartValue();
  805. double end = im.getEndValue();
  806. Range range = domainAxis.getRange();
  807. if (!(range.intersects(start, end))) {
  808. return;
  809. }
  810. // don't draw beyond the axis range...
  811. start = range.constrain(start);
  812. end = range.constrain(end);
  813. double v0 = domainAxis.valueToJava2D(
  814. start, dataArea, plot.getDomainAxisEdge()
  815. );
  816. double v1 = domainAxis.valueToJava2D(
  817. end, dataArea, plot.getDomainAxisEdge()
  818. );
  819. PlotOrientation orientation = plot.getOrientation();
  820. Rectangle2D rect = null;
  821. if (orientation == PlotOrientation.HORIZONTAL) {
  822. rect = new Rectangle2D.Double(
  823. dataArea.getMinX(), Math.min(v0, v1),
  824. dataArea.getWidth(), Math.abs(v1 - v0)
  825. );
  826. }
  827. else if (orientation == PlotOrientation.VERTICAL) {
  828. rect = new Rectangle2D.Double(
  829. Math.min(v0, v1), dataArea.getMinY(),
  830. Math.abs(v1 - v0), dataArea.getHeight()
  831. );
  832. }
  833. Paint p = marker.getPaint();
  834. if (p instanceof GradientPaint) {
  835. GradientPaint gp = (GradientPaint) p;
  836. GradientPaintTransformer t = im.getGradientPaintTransformer();
  837. if (t != null) {
  838. gp = t.transform(gp, rect);
  839. }
  840. g2.setPaint(gp);
  841. }
  842. else {
  843. g2.setPaint(p);
  844. }
  845. g2.fill(rect);
  846. String label = marker.getLabel();
  847. RectangleAnchor anchor = marker.getLabelAnchor();
  848. if (label != null) {
  849. Font labelFont = marker.getLabelFont();
  850. g2.setFont(labelFont);
  851. g2.setPaint(marker.getLabelPaint());
  852. Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
  853. g2, orientation, dataArea, rect, marker.getLabelOffset(),
  854. marker.getLabelOffsetTypeForDomain(),
  855. marker.getLabelOffsetTypeForRange(), anchor
  856. );
  857. TextUtilities.drawAlignedString(
  858. label, g2, (float) coordinates.getX(),
  859. (float) coordinates.getY(),
  860. marker.getLabelTextAnchor()
  861. );
  862. }
  863. }
  864. }
  865. /**
  866. * Calculates the (x, y) coordinates for drawing a marker label.
  867. *
  868. * @param g2 the graphics device.
  869. * @param orientation the plot orientation.
  870. * @param dataArea the data area.
  871. * @param markerArea the rectangle surrounding the marker area.
  872. * @param markerOffset the marker label offset.
  873. * @param labelOffsetForDomain the domain offset type.
  874. * @param labelOffsetForRange the range offset type.
  875. * @param anchor the label anchor.
  876. *
  877. * @return The coordinates for drawing the marker label.
  878. */
  879. protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2,
  880. PlotOrientation orientation,
  881. Rectangle2D dataArea,
  882. Rectangle2D markerArea,
  883. RectangleInsets markerOffset,
  884. LengthAdjustmentType labelOffsetForDomain,
  885. LengthAdjustmentType labelOffsetForRange,
  886. RectangleAnchor anchor) {
  887. Rectangle2D anchorRect = null;
  888. if (orientation == PlotOrientation.HORIZONTAL) {
  889. anchorRect = markerOffset.createAdjustedRectangle(
  890. markerArea, labelOffsetForRange, labelOffsetForDomain
  891. );
  892. }
  893. else if (orientation == PlotOrientation.VERTICAL) {
  894. anchorRect = markerOffset.createAdjustedRectangle(
  895. markerArea, labelOffsetForDomain, labelOffsetForRange
  896. );
  897. }
  898. return RectangleAnchor.coordinates(anchorRect, anchor);
  899. }
  900. /**
  901. * Draws a horizontal line across the chart to represent a 'range marker'.
  902. *
  903. * @param g2 the graphics device.
  904. * @param plot the plot.
  905. * @param rangeAxis the range axis.
  906. * @param marker the marker line.
  907. * @param dataArea the axis data area.
  908. */
  909. public void drawRangeMarker(Graphics2D g2,
  910. XYPlot plot,
  911. ValueAxis rangeAxis,
  912. Marker marker,
  913. Rectangle2D dataArea) {
  914. if (marker instanceof ValueMarker) {
  915. ValueMarker vm = (ValueMarker) marker;
  916. double value = vm.getValue();
  917. Range range = rangeAxis.getRange();
  918. if (!range.contains(value)) {
  919. return;
  920. }
  921. double v = rangeAxis.valueToJava2D(
  922. value, dataArea, plot.getRangeAxisEdge()
  923. );
  924. PlotOrientation orientation = plot.getOrientation();
  925. Line2D line = null;
  926. if (orientation == PlotOrientation.HORIZONTAL) {
  927. line = new Line2D.Double(
  928. v, dataArea.getMinY(), v, dataArea.getMaxY()
  929. );
  930. }
  931. else if (orientation == PlotOrientation.VERTICAL) {
  932. line = new Line2D.Double(
  933. dataArea.getMinX(), v, dataArea.getMaxX(), v
  934. );
  935. }
  936. g2.setPaint(marker.getPaint());
  937. g2.setStroke(marker.getStroke());
  938. g2.draw(line);
  939. String label = marker.getLabel();
  940. RectangleAnchor anchor = marker.getLabelAnchor();
  941. if (label != null) {
  942. Font labelFont = marker.getLabelFont();
  943. g2.setFont(labelFont);
  944. g2.setPaint(marker.getLabelPaint());
  945. Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
  946. g2, orientation, dataArea, line.getBounds2D(),
  947. marker.getLabelOffset(),
  948. marker.getLabelOffsetTypeForDomain(),
  949. marker.getLabelOffsetTypeForRange(), anchor
  950. );
  951. TextUtilities.drawAlignedString(
  952. label, g2, (float) coordinates.getX(),
  953. (float) coordinates.getY(),
  954. marker.getLabelTextAnchor()
  955. );
  956. }
  957. }
  958. else if (marker instanceof IntervalMarker) {
  959. IntervalMarker im = (IntervalMarker) marker;
  960. double start = im.getStartValue();
  961. double end = im.getEndValue();
  962. Range range = rangeAxis.getRange();
  963. if (!(range.intersects(start, end))) {
  964. return;
  965. }
  966. // don't draw beyond the axis range...
  967. start = range.constrain(start);
  968. end = range.constrain(end);
  969. double v0 = rangeAxis.valueToJava2D(
  970. start, dataArea, plot.getRangeAxisEdge()
  971. );
  972. double v1 = rangeAxis.valueToJava2D(
  973. end, dataArea, plot.getRangeAxisEdge()
  974. );
  975. PlotOrientation orientation = plot.getOrientation();
  976. Rectangle2D rect = null;
  977. if (orientation == PlotOrientation.HORIZONTAL) {
  978. rect = new Rectangle2D.Double(
  979. Math.min(v0, v1), dataArea.getMinY(),
  980. Math.abs(v1 - v0), dataArea.getHeight()
  981. );
  982. }
  983. else if (orientation == PlotOrientation.VERTICAL) {
  984. rect = new Rectangle2D.Double(
  985. dataArea.getMinX(), Math.min(v0, v1),
  986. dataArea.getWidth(), Math.abs(v0 - v1)
  987. );
  988. }
  989. Paint p = marker.getPaint();
  990. if (p instanceof GradientPaint) {
  991. GradientPaint gp = (GradientPaint) p;
  992. GradientPaintTransformer t = im.getGradientPaintTransformer();
  993. if (t != null) {
  994. gp = t.transform(gp, rect);
  995. }
  996. g2.setPaint(gp);
  997. }
  998. else {
  999. g2.setPaint(p);
  1000. }
  1001. g2.fill(rect);
  1002. String label = marker.getLabel();
  1003. RectangleAnchor anchor = marker.getLabelAnchor();
  1004. if (label != null) {
  1005. Font labelFont = marker.getLabelFont();
  1006. g2.setFont(labelFont);
  1007. g2.setPaint(marker.getLabelPaint());
  1008. Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
  1009. g2, orientation, dataArea, rect, marker.getLabelOffset(),
  1010. marker.getLabelOffsetTypeForDomain(),
  1011. marker.getLabelOffsetTypeForRange(), anchor
  1012. );
  1013. TextUtilities.drawAlignedString(
  1014. label, g2, (float) coordinates.getX(),
  1015. (float) coordinates.getY(),
  1016. marker.getLabelTextAnchor()
  1017. );
  1018. }
  1019. }
  1020. }
  1021. /**
  1022. * Calculates the (x, y) coordinates for drawing a marker label.
  1023. *
  1024. * @param g2 the graphics device.
  1025. * @param orientation the plot orientation.
  1026. * @param dataArea the data area.
  1027. * @param markerArea the marker area.
  1028. * @param markerOffset the marker offset.
  1029. * @param anchor the label anchor.
  1030. *
  1031. * @return The coordinates for drawing the marker label.
  1032. */
  1033. private Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2,
  1034. PlotOrientation orientation,
  1035. Rectangle2D dataArea,
  1036. Rectangle2D markerArea,
  1037. RectangleInsets markerOffset,
  1038. LengthAdjustmentType labelOffsetForDomain,
  1039. LengthAdjustmentType labelOffsetForRange,
  1040. RectangleAnchor anchor) {
  1041. Rectangle2D anchorRect = null;
  1042. if (orientation == PlotOrientation.HORIZONTAL) {
  1043. anchorRect = markerOffset.createAdjustedRectangle(
  1044. markerArea, labelOffsetForRange, labelOffsetForDomain
  1045. );
  1046. }
  1047. else if (orientation == PlotOrientation.VERTICAL) {
  1048. anchorRect = markerOffset.createAdjustedRectangle(
  1049. markerArea, labelOffsetForDomain, labelOffsetForRange
  1050. );
  1051. }
  1052. return RectangleAnchor.coordinates(anchorRect, anchor);
  1053. }
  1054. /**
  1055. * Returns a clone of the renderer.
  1056. *
  1057. * @return A clone.
  1058. *
  1059. * @throws CloneNotSupportedException if the renderer does not support
  1060. * cloning.
  1061. */
  1062. protected Object clone() throws CloneNotSupportedException {
  1063. AbstractXYItemRenderer clone = (AbstractXYItemRenderer) super.clone();
  1064. // 'plot' : just retain reference, not a deep copy
  1065. if (this.itemLabelGenerator != null
  1066. && this.itemLabelGenerator instanceof PublicCloneable) {
  1067. PublicCloneable pc = (PublicCloneable) this.itemLabelGenerator;
  1068. clone.itemLabelGenerator = (XYLabelGenerator) pc.clone();
  1069. }
  1070. return clone;
  1071. }
  1072. /**
  1073. * Tests this renderer for equality with another object.
  1074. *
  1075. * @param obj the object.
  1076. *
  1077. * @return <code>true</code> or <code>false</code>.
  1078. */
  1079. public boolean equals(Object obj) {
  1080. if (obj == null) {
  1081. return false;
  1082. }
  1083. if (obj == this) {
  1084. return true;
  1085. }
  1086. if (!(obj instanceof AbstractXYItemRenderer)) {
  1087. return false;
  1088. }
  1089. AbstractXYItemRenderer renderer = (AbstractXYItemRenderer) obj;
  1090. if (!super.equals(obj)) {
  1091. return false;
  1092. }
  1093. if (!ObjectUtilities.equal(
  1094. this.itemLabelGenerator, renderer.itemLabelGenerator
  1095. )) {
  1096. return false;
  1097. }
  1098. if (!ObjectUtilities.equal(this.urlGenerator, renderer.urlGenerator)) {
  1099. return false;
  1100. }
  1101. return true;
  1102. }
  1103. /**
  1104. * Returns the drawing supplier from the plot.
  1105. *
  1106. * @return The drawing supplier (possibly <code>null</code>).
  1107. */
  1108. public DrawingSupplier getDrawingSupplier() {
  1109. DrawingSupplier result = null;
  1110. XYPlot p = getPlot();
  1111. if (p != null) {
  1112. result = p.getDrawingSupplier();
  1113. }
  1114. return result;
  1115. }
  1116. /**
  1117. * Considers the current (x, y) coordinate and updates the crosshair point
  1118. * if it meets the criteria (usually means the (x, y) coordinate is the
  1119. * closest to the anchor point so far).
  1120. *
  1121. * @param crosshairState the crosshair state (<code>null</code> permitted,
  1122. * but the method does nothing in that case).
  1123. * @param x the x-value (in data space).
  1124. * @param y the y-value (in data space).
  1125. * @param transX the x-value translated to Java2D space.
  1126. * @param transY the y-value translated to Java2D space.
  1127. * @param orientation the plot orientation (<code>null</code> not
  1128. * permitted).
  1129. */
  1130. protected void updateCrosshairValues(CrosshairState crosshairState,
  1131. double x, double y, double transX,
  1132. double transY,
  1133. PlotOrientation orientation) {
  1134. if (orientation == null) {
  1135. throw new IllegalArgumentException("Null 'orientation' argument.");
  1136. }
  1137. if (crosshairState != null) {
  1138. // do we need to update the crosshair values?
  1139. if (this.plot.isDomainCrosshairLockedOnData()) {
  1140. if (this.plot.isRangeCrosshairLockedOnData()) {
  1141. // both axes
  1142. crosshairState.updateCrosshairPoint(
  1143. x, y, transX, transY, orientation
  1144. );
  1145. }
  1146. else {
  1147. // just the domain axis...
  1148. crosshairState.updateCrosshairX(x);
  1149. }
  1150. }
  1151. else {
  1152. if (this.plot.isRangeCrosshairLockedOnData()) {
  1153. // just the range axis...
  1154. crosshairState.updateCrosshairY(y);
  1155. }
  1156. }
  1157. }
  1158. }
  1159. /**
  1160. * Draws an item label.
  1161. *
  1162. * @param g2 the graphics device.
  1163. * @param orientation the orientation.
  1164. * @param dataset the dataset.
  1165. * @param series the series index (zero-based).
  1166. * @param item the item index (zero-based).
  1167. * @param x the x coordinate (in Java2D space).
  1168. * @param y the y coordinate (in Java2D space).
  1169. * @param negative indicates a negative value (which affects the item
  1170. * label position).
  1171. */
  1172. protected void drawItemLabel(Graphics2D g2,
  1173. PlotOrientation orientation,
  1174. XYDataset dataset,
  1175. int series,
  1176. int item,
  1177. double x,
  1178. double y,
  1179. boolean negative) {
  1180. XYLabelGenerator generator = getLabelGenerator(series, item);
  1181. if (generator != null) {
  1182. Font labelFont = getItemLabelFont(series, item);
  1183. Paint paint = getItemLabelPaint(series, item);
  1184. g2.setFont(labelFont);
  1185. g2.setPaint(paint);
  1186. String label = generator.generateLabel(dataset, series, item);
  1187. // get the label position..
  1188. ItemLabelPosition position = null;
  1189. if (!negative) {
  1190. position = getPositiveItemLabelPosition(series, item);
  1191. }
  1192. else {
  1193. position = getNegativeItemLabelPosition(series, item);
  1194. }
  1195. // work out the label anchor point...
  1196. Point2D anchorPoint = calculateLabelAnchorPoint(
  1197. position.getItemLabelAnchor(), x, y, orientation
  1198. );
  1199. TextUtilities.drawRotatedString(
  1200. label, g2, (float) anchorPoint.getX(),
  1201. (float) anchorPoint.getY(),
  1202. position.getTextAnchor(), position.getAngle(),
  1203. position.getRotationAnchor()
  1204. );
  1205. }
  1206. }
  1207. /**
  1208. * Draws all the annotations for the specified layer.
  1209. *
  1210. * @param g2 the graphics device.
  1211. * @param dataArea the data area.
  1212. * @param domainAxis the domain axis.
  1213. * @param rangeAxis the range axis.
  1214. * @param layer the layer.
  1215. * @param info the plot rendering info.
  1216. */
  1217. public void drawAnnotations(Graphics2D g2,
  1218. Rectangle2D dataArea,
  1219. ValueAxis domainAxis,
  1220. ValueAxis rangeAxis,
  1221. Layer layer,
  1222. PlotRenderingInfo info) {
  1223. Iterator iterator = null;
  1224. if (layer.equals(Layer.FOREGROUND)) {
  1225. iterator = this.foregroundAnnotations.iterator();
  1226. }
  1227. else if (layer.equals(Layer.BACKGROUND)) {
  1228. iterator = this.backgroundAnnotations.iterator();
  1229. }
  1230. else {
  1231. // should not get here
  1232. throw new RuntimeException("Unknown layer.");
  1233. }
  1234. while (iterator.hasNext()) {
  1235. XYAnnotation annotation = (XYAnnotation) iterator.next();
  1236. annotation.draw(
  1237. g2, this.plot, dataArea, domainAxis, rangeAxis, 0, info
  1238. );
  1239. }
  1240. }
  1241. /**
  1242. * Adds an entity to the collection.
  1243. *
  1244. * @param entities the entity collection being populated.
  1245. * @param area the entity area (if <code>null</code> a default will be
  1246. * used).
  1247. * @param dataset the dataset.
  1248. * @param series the series.
  1249. * @param item the item.
  1250. * @param entityX the entity's center x-coordinate in user space.
  1251. * @param entityY the entity's center y-coordinate in user space.
  1252. */
  1253. protected void addEntity(EntityCollection entities, Shape area,
  1254. XYDataset dataset, int series, int item,
  1255. double entityX, double entityY) {
  1256. if (!this.getItemCreateEntity(series, item)) {
  1257. return;
  1258. }
  1259. if (area == null) {
  1260. area = new Ellipse2D.Double(
  1261. entityX - this.defaultEntityRadius,
  1262. entityY - this.defaultEntityRadius,
  1263. this.defaultEntityRadius * 2, this.defaultEntityRadius * 2
  1264. );
  1265. }
  1266. String tip = null;
  1267. XYToolTipGenerator generator = getToolTipGenerator(series, item);
  1268. if (generator != null) {
  1269. tip = generator.generateToolTip(dataset, series, item);
  1270. }
  1271. String url = null;
  1272. if (getURLGenerator() != null) {
  1273. url = getURLGenerator().generateURL(dataset, series, item);
  1274. }
  1275. XYItemEntity entity = new