diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..7de0b7241 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: java +sudo: false +script: + - travis_wait mvn compile diff --git a/pom.xml b/pom.xml index c4b1e71a9..10406cd8e 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,17 @@ 4.11 test + + + org.checkerframework + checker-qual + 2.5.1 + + + org.checkerframework + jdk8 + 2.5.1 + @@ -66,24 +77,60 @@ ossrh https://oss.sonatype.org/content/repositories/snapshots - - + + src/test/java - org.apache.maven.plugins maven-compiler-plugin 3.7.0 - ${project.source.level} - ${project.target.level} - ${project.build.sourceEncoding} + 1.8 + 1.8 + + 10000 + 10000 + + + + org.checkerframework + checker + 2.5.1 + + + + + ${checkerframework.checkers} + + + -AprintErrorStack + + -Xbootclasspath/p:${annotatedJdk} + -AskipDefs=Test + ${checkerframework.extraargs} + ${checkerframework.extraargs2} + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + properties + + + + + org.apache.maven.plugins maven-surefire-plugin @@ -155,9 +202,15 @@ + 1.8 + 1.8 UTF-8 - 1.6 - 1.6 + + ${org.checkerframework:jdk8:jar} + + org.checkerframework.checker.index.IndexChecker + + @@ -225,6 +278,19 @@ + + + org.apache.maven.plugins + maven-dependency-plugin + + + + properties + + + + + diff --git a/src/main/java/org/jfree/chart/ChartColor.java b/src/main/java/org/jfree/chart/ChartColor.java index d77e00a1a..7b903d106 100644 --- a/src/main/java/org/jfree/chart/ChartColor.java +++ b/src/main/java/org/jfree/chart/ChartColor.java @@ -45,6 +45,9 @@ package org.jfree.chart; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.awt.Color; import java.awt.Paint; @@ -146,7 +149,7 @@ public ChartColor(int r, int g, int b) { * * @return An array of objects with the {@code Paint} interface. */ - public static Paint[] createDefaultPaintArray() { + public static Paint @ArrayLen(34) [] createDefaultPaintArray() { return new Paint[] { new Color(0xFF, 0x55, 0x55), diff --git a/src/main/java/org/jfree/chart/ChartFrame.java b/src/main/java/org/jfree/chart/ChartFrame.java index cbd07b751..d1579a589 100644 --- a/src/main/java/org/jfree/chart/ChartFrame.java +++ b/src/main/java/org/jfree/chart/ChartFrame.java @@ -2,7 +2,7 @@ * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * - * (C) Copyright 2000-2016, by Object Refinery Limited and Contributors. + * (C) Copyright 2000-2018, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * @@ -27,17 +27,11 @@ * --------------- * ChartFrame.java * --------------- - * (C) Copyright 2001-2008, by Object Refinery Limited and Contributors. + * (C) Copyright 2001-2018, by Object Refinery Limited and Contributors. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -; * - * Changes - * ------- - * 22-Nov-2001 : Version 1 (DG); - * 08-Jan-2001 : Added chartPanel attribute (DG); - * 24-May-2002 : Renamed JFreeChartFrame --> ChartFrame (DG); - * */ package org.jfree.chart; @@ -75,7 +69,7 @@ public ChartFrame(String title, JFreeChart chart) { public ChartFrame(String title, JFreeChart chart, boolean scrollPane) { super(title); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - this.chartPanel = new ChartPanel(chart); + this.chartPanel = new ChartPanel(chart, false); if (scrollPane) { setContentPane(new JScrollPane(this.chartPanel)); } diff --git a/src/main/java/org/jfree/chart/JFreeChart.java b/src/main/java/org/jfree/chart/JFreeChart.java index 0a6201898..eff632e4b 100644 --- a/src/main/java/org/jfree/chart/JFreeChart.java +++ b/src/main/java/org/jfree/chart/JFreeChart.java @@ -2,7 +2,7 @@ * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * - * (C) Copyright 2000-2017, by Object Refinery Limited and Contributors. + * (C) Copyright 2000-2018, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * @@ -27,7 +27,7 @@ * --------------- * JFreeChart.java * --------------- - * (C) Copyright 2000-2016, by Object Refinery Limited and Contributors. + * (C) Copyright 2000-2018, by Object Refinery Limited and Contributors. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): Andrzej Porebski; @@ -42,119 +42,12 @@ * contributed to this source file (JFreeChart.java) - for a list of ALL * contributors to the project, please see the README.txt file. * - * Changes (from 20-Jun-2001) - * -------------------------- - * 20-Jun-2001 : Modifications submitted by Andrzej Porebski for legend - * placement; - * 21-Jun-2001 : Removed JFreeChart parameter from Plot constructors (DG); - * 22-Jun-2001 : Multiple titles added (original code by David Berry, with - * reworkings by DG); - * 18-Sep-2001 : Updated header (DG); - * 15-Oct-2001 : Moved data source classes into new package - * com.jrefinery.data.* (DG); - * 18-Oct-2001 : New factory method for creating VerticalXYBarChart (DG); - * 19-Oct-2001 : Moved series paint and stroke methods to the Plot class (DG); - * Moved static chart creation methods to new ChartFactory - * class (DG); - * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG); - * Fixed bug where chart isn't registered with the dataset (DG); - * 07-Nov-2001 : Fixed bug where null title in constructor causes - * exception (DG); - * Tidied up event notification code (DG); - * 17-Nov-2001 : Added getLegendItemCount() method (DG); - * 21-Nov-2001 : Set clipping in draw method to ensure that nothing gets drawn - * outside the chart area (DG); - * 11-Dec-2001 : Added the createBufferedImage() method, taken from the - * JFreeChartServletDemo class (DG); - * 13-Dec-2001 : Added tooltips (DG); - * 16-Jan-2002 : Added handleClick() method (DG); - * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG); - * 05-Feb-2002 : Removed redundant tooltips code (DG); - * 19-Feb-2002 : Added accessor methods for the backgroundImage and - * backgroundImageAlpha attributes (DG); - * 21-Feb-2002 : Added static fields for INFO, COPYRIGHT, LICENCE, CONTRIBUTORS - * and LIBRARIES. These can be used to display information about - * JFreeChart (DG); - * 06-Mar-2002 : Moved constants to JFreeChartConstants interface (DG); - * 18-Apr-2002 : PieDataset is no longer sorted (oldman); - * 23-Apr-2002 : Moved dataset to the Plot class (DG); - * 13-Jun-2002 : Added an extra draw() method (DG); - * 25-Jun-2002 : Implemented the Drawable interface and removed redundant - * imports (DG); - * 26-Jun-2002 : Added another createBufferedImage() method (DG); - * 18-Sep-2002 : Fixed issues reported by Checkstyle (DG); - * 23-Sep-2002 : Added new contributor (DG); - * 28-Oct-2002 : Created main title and subtitle list to replace existing title - * list (DG); - * 08-Jan-2003 : Added contributor (DG); - * 17-Jan-2003 : Added new constructor (DG); - * 22-Jan-2003 : Added ChartColor class by Cameron Riley, and background image - * alignment code by Christian W. Zuckschwerdt (DG); - * 11-Feb-2003 : Added flag to allow suppression of chart change events, based - * on a suggestion by Klaus Rheinwald (DG); - * 04-Mar-2003 : Added small fix for suppressed chart change events (see bug id - * 690865) (DG); - * 10-Mar-2003 : Added Benoit Xhenseval to contributors (DG); - * 26-Mar-2003 : Implemented Serializable (DG); - * 15-Jul-2003 : Added an optional border for the chart (DG); - * 11-Sep-2003 : Took care of listeners while cloning (NB); - * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); - * 22-Sep-2003 : Added nullpointer checks. - * 25-Sep-2003 : Added nullpointer checks too (NB). - * 03-Dec-2003 : Legends are now registered by this class instead of using the - * old constructor way (TM); - * 03-Dec-2003 : Added anchorPoint to draw() method (DG); - * 08-Jan-2004 : Reworked title code, introducing line wrapping (DG); - * 09-Feb-2004 : Created additional createBufferedImage() method (DG); - * 05-Apr-2004 : Added new createBufferedImage() method (DG); - * 27-May-2004 : Moved constants from JFreeChartConstants.java back to this - * class (DG); - * 25-Nov-2004 : Updates for changes to Title class (DG); - * 06-Jan-2005 : Change lookup for default background color (DG); - * 31-Jan-2005 : Added Don Elliott to contributors (DG); - * 02-Feb-2005 : Added clearSubtitles() method (DG); - * 03-Feb-2005 : Added Mofeed Shahin to contributors (DG); - * 08-Feb-2005 : Updated for RectangleConstraint changes (DG); - * 28-Mar-2005 : Renamed Legend --> OldLegend (DG); - * 12-Apr-2005 : Added methods to access legend(s) in subtitle list (DG); - * 13-Apr-2005 : Added removeLegend() and removeSubtitle() methods (DG); - * 20-Apr-2005 : Modified to collect chart entities from titles and - * subtitles (DG); - * 26-Apr-2005 : Removed LOGGER (DG); - * 06-Jun-2005 : Added addLegend() method and padding attribute, fixed equals() - * method (DG); - * 24-Nov-2005 : Removed OldLegend and related code - don't want to support - * this in 1.0.0 final (DG); - * ------------- JFREECHART 1.0.x --------------------------------------------- - * 27-Jan-2006 : Updated version number (DG); - * 07-Dec-2006 : Added some missing credits (DG); - * 17-Jan-2007 : Added Darren Jung to contributor list (DG); - * 05-Mar-2007 : Added Sergei Ivanov to the contributor list (DG); - * 16-Mar-2007 : Modified initial legend border (DG); - * 22-Mar-2007 : New methods for text anti-aliasing (DG); - * 16-May-2007 : Fixed argument check in getSubtitle(), copy list in - * get/setSubtitles(), and added new addSubtitle(int, Title) - * method (DG); - * 05-Jun-2007 : Add change listener to default legend (DG); - * 04-Dec-2007 : In createBufferedImage() methods, make the default image type - * BufferedImage.TYPE_INT_ARGB (thanks to Klaus Rheinwald) (DG); - * 05-Dec-2007 : Fixed bug 1749124 (not registering as listener with - * TextTitle) (DG); - * 23-Apr-2008 : Added new contributor (Diego Pierangeli) (DG); - * 16-May-2008 : Added new contributor (Michael Siemer) (DG); - * 19-Sep-2008 : Check for title visibility (DG); - * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by - * Jess Thrysoee (DG); - * 19-Mar-2009 : Added entity support - see patch 2603321 by Peter Kolb (DG); - * 19-May-2009 : Fixed FindBugs warnings, patch by Michal Wozniak (DG); - * 29-Jun-2009 : Check visibility flag in main title (DG); - * 02-Jul-2013 : Use ParamChecks class (DG); - * 21-Jun-2014 : Set default hint value for stroke control (DG); - * */ package org.jfree.chart; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -176,7 +69,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import javax.swing.UIManager; @@ -185,7 +77,6 @@ import org.jfree.chart.block.BlockParams; import org.jfree.chart.block.EntityBlockResult; import org.jfree.chart.block.LengthConstraintType; -import org.jfree.chart.block.LineBorder; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.JFreeChartEntity; @@ -304,7 +195,7 @@ public boolean isCompatibleValue(Object val) { * The chart subtitles (zero, one or many). This field should never be * {@code null}. */ - private List subtitles; + private List subtitles; /** Draws the visual representation of the data. */ private Plot plot; @@ -419,7 +310,7 @@ public JFreeChart(String title, Font titleFont, Plot plot, this.plot = plot; plot.addChangeListener(this); - this.subtitles = new ArrayList(); + this.subtitles = new ArrayList<Title>(); // create a legend, if requested... if (createLegend) { @@ -714,11 +605,9 @@ public LegendTitle getLegend() { * * @see #addLegend(LegendTitle) */ - public LegendTitle getLegend(int index) { + public LegendTitle getLegend(@NonNegative int index) { int seen = 0; - Iterator iterator = this.subtitles.iterator(); - while (iterator.hasNext()) { - Title subtitle = (Title) iterator.next(); + for (Title subtitle : this.subtitles) { if (subtitle instanceof LegendTitle) { if (seen == index) { return (LegendTitle) subtitle; @@ -742,13 +631,13 @@ public void removeLegend() { } /** - * Returns the list of subtitles for the chart. + * Returns a new list containing all the subtitles for the chart. * * @return The subtitle list (possibly empty, but never {@code null}). * * @see #setSubtitles(List) */ - public List getSubtitles() { + public List<Title> getSubtitles() { return new ArrayList(this.subtitles); } @@ -762,15 +651,11 @@ public List getSubtitles() { * * @see #getSubtitles() */ - public void setSubtitles(List subtitles) { - if (subtitles == null) { - throw new NullPointerException("Null 'subtitles' argument."); - } + public void setSubtitles(List<Title> subtitles) { + Args.nullNotPermitted(subtitles, "subtitles"); setNotify(false); clearSubtitles(); - Iterator iterator = subtitles.iterator(); - while (iterator.hasNext()) { - Title t = (Title) iterator.next(); + for (Title t: subtitles) { if (t != null) { addSubtitle(t); } @@ -798,11 +683,11 @@ public int getSubtitleCount() { * * @see #addSubtitle(Title) */ - public Title getSubtitle(int index) { + public Title getSubtitle(@NonNegative int index) { if ((index < 0) || (index >= getSubtitleCount())) { throw new IllegalArgumentException("Index out of range."); } - return (Title) this.subtitles.get(index); + return this.subtitles.get(index); } /** @@ -829,7 +714,7 @@ public void addSubtitle(Title subtitle) { * * @since 1.0.6 */ - public void addSubtitle(int index, Title subtitle) { + public void addSubtitle(@NonNegative int index, Title subtitle) { if (index < 0 || index > getSubtitleCount()) { throw new IllegalArgumentException( "The 'index' argument is out of range."); @@ -847,9 +732,7 @@ public void addSubtitle(int index, Title subtitle) { * @see #addSubtitle(Title) */ public void clearSubtitles() { - Iterator iterator = this.subtitles.iterator(); - while (iterator.hasNext()) { - Title t = (Title) iterator.next(); + for (Title t : this.subtitles) { t.removeChangeListener(this); } this.subtitles.clear(); @@ -1264,9 +1147,7 @@ public void draw(Graphics2D g2, Rectangle2D chartArea, Point2D anchor, } } - Iterator iterator = this.subtitles.iterator(); - while (iterator.hasNext()) { - Title currentTitle = (Title) iterator.next(); + for (Title currentTitle : this.subtitles) { if (currentTitle.isVisible()) { EntityCollection e = drawTitle(currentTitle, g2, nonTitleArea, (entities != null)); @@ -1750,7 +1631,7 @@ public Object clone() throws CloneNotSupportedException { chart.title.addChangeListener(chart); } - chart.subtitles = new ArrayList(); + chart.subtitles = new ArrayList<Title>(); for (int i = 0; i < getSubtitleCount(); i++) { Title subtitle = (Title) getSubtitle(i).clone(); chart.subtitles.add(subtitle); diff --git a/src/main/java/org/jfree/chart/LegendItem.java b/src/main/java/org/jfree/chart/LegendItem.java index 0144d9003..fb47b21c5 100644 --- a/src/main/java/org/jfree/chart/LegendItem.java +++ b/src/main/java/org/jfree/chart/LegendItem.java @@ -64,6 +64,8 @@ package org.jfree.chart; +import org.checkerframework.checker.index.qual.*; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -114,10 +116,10 @@ public class LegendItem implements Cloneable, Serializable { private Comparable seriesKey; /** The dataset index. */ - private int datasetIndex; + private @NonNegative int datasetIndex; /** The series index. */ - private int series; + private @NonNegative int series; /** The label. */ private String label; @@ -514,7 +516,9 @@ private String characterIteratorToString(CharacterIterator iterator) { int i = 0; char c = iterator.first(); while (c != CharacterIterator.DONE) { - chars[i] = c; + @SuppressWarnings("index") // Correctness depends on the Character Iterator having count elements before supplying CharacterIterator.DONE + @IndexFor("chars") int index = i; + chars[index] = c; i++; c = iterator.next(); } @@ -555,7 +559,7 @@ public void setDataset(Dataset dataset) { * @see #setDatasetIndex(int) * @see #getDataset() */ - public int getDatasetIndex() { + public @NonNegative int getDatasetIndex() { return this.datasetIndex; } @@ -568,7 +572,7 @@ public int getDatasetIndex() { * * @see #getDatasetIndex() */ - public void setDatasetIndex(int index) { + public void setDatasetIndex(@NonNegative int index) { this.datasetIndex = index; } @@ -603,7 +607,7 @@ public void setSeriesKey(Comparable key) { * * @since 1.0.2 */ - public int getSeriesIndex() { + public @NonNegative int getSeriesIndex() { return this.series; } @@ -614,7 +618,7 @@ public int getSeriesIndex() { * * @since 1.0.2 */ - public void setSeriesIndex(int index) { + public void setSeriesIndex(@NonNegative int index) { this.series = index; } diff --git a/src/main/java/org/jfree/chart/LegendItemCollection.java b/src/main/java/org/jfree/chart/LegendItemCollection.java index 84dd2b96b..44215c266 100644 --- a/src/main/java/org/jfree/chart/LegendItemCollection.java +++ b/src/main/java/org/jfree/chart/LegendItemCollection.java @@ -45,6 +45,8 @@ package org.jfree.chart; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Iterator; import java.util.List; @@ -94,7 +96,7 @@ public void addAll(LegendItemCollection collection) { * * @return The legend item. */ - public LegendItem get(int index) { + public LegendItem get(@NonNegative int index) { return (LegendItem) this.items.get(index); } @@ -103,7 +105,7 @@ public LegendItem get(int index) { * * @return The item count. */ - public int getItemCount() { + public @NonNegative int getItemCount() { return this.items.size(); } diff --git a/src/main/java/org/jfree/chart/PolarChartPanel.java b/src/main/java/org/jfree/chart/PolarChartPanel.java index d5b333353..ee331d6dd 100644 --- a/src/main/java/org/jfree/chart/PolarChartPanel.java +++ b/src/main/java/org/jfree/chart/PolarChartPanel.java @@ -44,6 +44,9 @@ package org.jfree.chart; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.awt.Component; import java.awt.event.ActionEvent; @@ -246,7 +249,7 @@ private void checkChart(JFreeChart chart) { * * @return The item index. */ - private int getPopupMenuItem(JPopupMenu menu, String text) { + private @GTENegativeOne int getPopupMenuItem(JPopupMenu menu, String text) { int index = -1; for (int i = 0; (index == -1) && (i < menu.getComponentCount()); i++) { Component comp = menu.getComponent(i); diff --git a/src/main/java/org/jfree/chart/annotations/AbstractXYAnnotation.java b/src/main/java/org/jfree/chart/annotations/AbstractXYAnnotation.java index 703960283..d7027f99b 100644 --- a/src/main/java/org/jfree/chart/annotations/AbstractXYAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/AbstractXYAnnotation.java @@ -43,6 +43,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Rectangle2D; @@ -138,7 +140,7 @@ public void setURL(String url) { @Override public abstract void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, + @NonNegative int rendererIndex, PlotRenderingInfo info); /** @@ -152,7 +154,7 @@ public abstract void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, * @param urlText the URL text. */ protected void addEntity(PlotRenderingInfo info, - Shape hotspot, int rendererIndex, + Shape hotspot, @NonNegative int rendererIndex, String toolTipText, String urlText) { if (info == null) { return; diff --git a/src/main/java/org/jfree/chart/annotations/XYAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYAnnotation.java index 44dd3659c..ac023d4a8 100644 --- a/src/main/java/org/jfree/chart/annotations/XYAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYAnnotation.java @@ -47,6 +47,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; @@ -76,6 +78,6 @@ public interface XYAnnotation extends Annotation { */ public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, PlotRenderingInfo info); + @NonNegative int rendererIndex, PlotRenderingInfo info); } diff --git a/src/main/java/org/jfree/chart/annotations/XYBoxAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYBoxAnnotation.java index 7fcc4788c..4d5d1eb3a 100644 --- a/src/main/java/org/jfree/chart/annotations/XYBoxAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYBoxAnnotation.java @@ -41,6 +41,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -162,7 +164,7 @@ public XYBoxAnnotation(double x0, double y0, double x1, double y1, @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, PlotRenderingInfo info) { + @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( diff --git a/src/main/java/org/jfree/chart/annotations/XYDataImageAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYDataImageAnnotation.java index efaca762e..ea55064c3 100644 --- a/src/main/java/org/jfree/chart/annotations/XYDataImageAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYDataImageAnnotation.java @@ -41,6 +41,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.Rectangle2D; @@ -243,7 +245,7 @@ public Range getYRange() { @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, + @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); diff --git a/src/main/java/org/jfree/chart/annotations/XYDrawableAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYDrawableAnnotation.java index 19794173b..171978355 100644 --- a/src/main/java/org/jfree/chart/annotations/XYDrawableAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYDrawableAnnotation.java @@ -44,6 +44,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; @@ -145,7 +147,7 @@ public XYDrawableAnnotation(double x, double y, double displayWidth, @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, + @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); diff --git a/src/main/java/org/jfree/chart/annotations/XYImageAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYImageAnnotation.java index 17ffc580e..93274ce8a 100644 --- a/src/main/java/org/jfree/chart/annotations/XYImageAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYImageAnnotation.java @@ -49,6 +49,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.Point2D; @@ -193,7 +195,7 @@ public RectangleAnchor getImageAnchor() { @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, + @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); diff --git a/src/main/java/org/jfree/chart/annotations/XYLineAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYLineAnnotation.java index 59ea96ee3..a3b949ede 100644 --- a/src/main/java/org/jfree/chart/annotations/XYLineAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYLineAnnotation.java @@ -51,6 +51,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -161,7 +163,7 @@ public XYLineAnnotation(double x1, double y1, double x2, double y2, @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, + @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); diff --git a/src/main/java/org/jfree/chart/annotations/XYPointerAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYPointerAnnotation.java index 12580a4fe..d80b57f31 100644 --- a/src/main/java/org/jfree/chart/annotations/XYPointerAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYPointerAnnotation.java @@ -55,6 +55,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -376,7 +378,7 @@ public void setArrowPaint(Paint paint) { */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, - ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, + ValueAxis domainAxis, ValueAxis rangeAxis, @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); diff --git a/src/main/java/org/jfree/chart/annotations/XYPolygonAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYPolygonAnnotation.java index 2738417cd..8782ff40e 100644 --- a/src/main/java/org/jfree/chart/annotations/XYPolygonAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYPolygonAnnotation.java @@ -41,6 +41,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -207,7 +209,7 @@ public Paint getOutlinePaint() { @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, PlotRenderingInfo info) { + @NonNegative int rendererIndex, PlotRenderingInfo info) { // if we don't have at least 2 (x, y) coordinates, just return if (this.polygon.length < 4) { @@ -219,17 +221,21 @@ public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); - GeneralPath area = new GeneralPath(); double x = domainAxis.valueToJava2D(this.polygon[0], dataArea, domainEdge); double y = rangeAxis.valueToJava2D(this.polygon[1], dataArea, rangeEdge); + + GeneralPath area = new GeneralPath(); + if (orientation == PlotOrientation.HORIZONTAL) { area.moveTo((float) y, (float) x); for (int i = 2; i < this.polygon.length; i += 2) { x = domainAxis.valueToJava2D(this.polygon[i], dataArea, domainEdge); - y = rangeAxis.valueToJava2D(this.polygon[i + 1], dataArea, + @SuppressWarnings("index") // polygon always has an even number of elements, and i is even and less than the length of polygon, so i + 1 must be an index + double y1 = this.polygon[i + 1]; + y = rangeAxis.valueToJava2D(y1, dataArea, rangeEdge); area.lineTo((float) y, (float) x); } @@ -240,7 +246,9 @@ else if (orientation == PlotOrientation.VERTICAL) { for (int i = 2; i < this.polygon.length; i += 2) { x = domainAxis.valueToJava2D(this.polygon[i], dataArea, domainEdge); - y = rangeAxis.valueToJava2D(this.polygon[i + 1], dataArea, + @SuppressWarnings("index") // polygon always has an even number of elements, and i is even and less than the length of polygon, so i + 1 must be an index + double y1 = this.polygon[i + 1]; + y = rangeAxis.valueToJava2D(y1, dataArea, rangeEdge); area.lineTo((float) x, (float) y); } diff --git a/src/main/java/org/jfree/chart/annotations/XYShapeAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYShapeAnnotation.java index 0320bdda2..6ee39edf9 100644 --- a/src/main/java/org/jfree/chart/annotations/XYShapeAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYShapeAnnotation.java @@ -55,6 +55,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -160,7 +162,7 @@ public XYShapeAnnotation(Shape shape, Stroke stroke, Paint outlinePaint, @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, + @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); diff --git a/src/main/java/org/jfree/chart/annotations/XYTextAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYTextAnnotation.java index ef15c07e5..55d1da00c 100644 --- a/src/main/java/org/jfree/chart/annotations/XYTextAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYTextAnnotation.java @@ -55,6 +55,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -515,7 +517,7 @@ public void setOutlineVisible(boolean visible) { @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, PlotRenderingInfo info) { + @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( diff --git a/src/main/java/org/jfree/chart/annotations/XYTitleAnnotation.java b/src/main/java/org/jfree/chart/annotations/XYTitleAnnotation.java index 96b2e2585..aee8687b1 100644 --- a/src/main/java/org/jfree/chart/annotations/XYTitleAnnotation.java +++ b/src/main/java/org/jfree/chart/annotations/XYTitleAnnotation.java @@ -46,6 +46,8 @@ package org.jfree.chart.annotations; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -244,7 +246,7 @@ public void setMaxHeight(double max) { @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, - int rendererIndex, PlotRenderingInfo info) { + @NonNegative int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); AxisLocation domainAxisLocation = plot.getDomainAxisLocation(); diff --git a/src/main/java/org/jfree/chart/axis/CompassFormat.java b/src/main/java/org/jfree/chart/axis/CompassFormat.java index 647d1ea6c..d45802424 100644 --- a/src/main/java/org/jfree/chart/axis/CompassFormat.java +++ b/src/main/java/org/jfree/chart/axis/CompassFormat.java @@ -42,6 +42,9 @@ package org.jfree.chart.axis; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.common.value.qual.IntRange; + import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; @@ -53,7 +56,7 @@ public class CompassFormat extends NumberFormat { /** The directions. */ - public final String[] directions; + public final String @ArrayLen(16) [] directions; /** * Creates a new formatter using English identifiers. @@ -88,7 +91,7 @@ public CompassFormat(String n, String e, String s, String w) { * * @since 1.0.18 */ - public CompassFormat(String[] directions) { + public CompassFormat(String @ArrayLen(16) [] directions) { super(); Args.nullNotPermitted(directions, "directions"); if (directions.length != 16) { @@ -110,7 +113,8 @@ public String getDirectionCode(double direction) { if (direction < 0.0) { direction = direction + 360.0; } - int index = ((int) Math.floor(direction / 11.25) + 1) / 2; + @SuppressWarnings({"index", "value"}) // Needs @DoubleRange + @IntRange(from = 0, to = 15) int index = ((int) Math.floor(direction / 11.25) + 1) / 2; return directions[index]; } diff --git a/src/main/java/org/jfree/chart/axis/CyclicNumberAxis.java b/src/main/java/org/jfree/chart/axis/CyclicNumberAxis.java index eacc73118..8602a4a5f 100644 --- a/src/main/java/org/jfree/chart/axis/CyclicNumberAxis.java +++ b/src/main/java/org/jfree/chart/axis/CyclicNumberAxis.java @@ -50,6 +50,9 @@ package org.jfree.chart.axis; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -384,7 +387,7 @@ public CycleBoundTick(boolean mapToLastCycle, Number number, * @return The anchor point. */ @Override - protected float[] calculateAnchorPoint(ValueTick tick, double cursor, + protected float @ArrayLen(2) [] calculateAnchorPoint(ValueTick tick, double cursor, Rectangle2D dataArea, RectangleEdge edge) { if (tick instanceof CycleBoundTick) { boolean mapsav = this.boundMappedToLastCycle; @@ -476,7 +479,7 @@ protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.TOP_RIGHT : TextAnchor.BOTTOM_RIGHT; - result.remove(result.size() - 1); + removeLast(result); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, @@ -495,7 +498,7 @@ protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_LEFT : TextAnchor.BOTTOM_RIGHT; - result.remove(result.size() - 1); + removeLast(result); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, @@ -513,7 +516,7 @@ protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.TOP_LEFT : TextAnchor.TOP_RIGHT; - result.remove(result.size() - 1); + removeLast(result); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, @@ -554,6 +557,14 @@ protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, } + /** + * Removes the last element of a list. The list must have at least one element. + */ + @SuppressWarnings("index") // Because the list must have at least one element, result.size() - 1 must be non-negative + private void removeLast(List result) { + result.remove(result.size() - 1); + } + /** * Builds a list of ticks for the axis. This method is called when the * axis is at the left or right of the chart (so the axis is "vertical"). @@ -624,7 +635,7 @@ protected List refreshVerticalTicks(Graphics2D g2, Rectangle2D dataArea, && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_LEFT : TextAnchor.BOTTOM_RIGHT; - result.remove(result.size() - 1); + removeLast(result); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, @@ -643,7 +654,7 @@ protected List refreshVerticalTicks(Graphics2D g2, Rectangle2D dataArea, && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_RIGHT : TextAnchor.BOTTOM_LEFT; - result.remove(result.size() - 1); + removeLast(result); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, @@ -664,7 +675,7 @@ protected List refreshVerticalTicks(Graphics2D g2, Rectangle2D dataArea, && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_RIGHT : TextAnchor.TOP_RIGHT; - result.remove(result.size() - 1); + removeLast(result); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, @@ -682,7 +693,7 @@ protected List refreshVerticalTicks(Graphics2D g2, Rectangle2D dataArea, && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_LEFT : TextAnchor.TOP_LEFT; - result.remove(result.size() - 1); + removeLast(result); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, diff --git a/src/main/java/org/jfree/chart/axis/DateAxis.java b/src/main/java/org/jfree/chart/axis/DateAxis.java index eeafdd4d6..64758b02b 100644 --- a/src/main/java/org/jfree/chart/axis/DateAxis.java +++ b/src/main/java/org/jfree/chart/axis/DateAxis.java @@ -138,6 +138,8 @@ package org.jfree.chart.axis; +import org.checkerframework.checker.index.qual.*; + import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; @@ -1005,7 +1007,9 @@ else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) { return d2; } else if (DateTickUnitType.MONTH.equals(unit.getUnitType())) { - value = count * ((current + 1) / count) - 1; + @SuppressWarnings("index") // if this is a month, then current >= 1, so the below is safe + @NonNegative int valueTmp = count * ((current + 1) / count) - 1; + value = valueTmp; years = calendar.get(Calendar.YEAR); calendar.clear(Calendar.MILLISECOND); calendar.set(years, value, 1, 0, 0, 0); diff --git a/src/main/java/org/jfree/chart/axis/DateTickUnit.java b/src/main/java/org/jfree/chart/axis/DateTickUnit.java index 3c9e671c3..f4c254890 100644 --- a/src/main/java/org/jfree/chart/axis/DateTickUnit.java +++ b/src/main/java/org/jfree/chart/axis/DateTickUnit.java @@ -56,6 +56,8 @@ package org.jfree.chart.axis; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import java.text.DateFormat; import java.util.Calendar; @@ -81,7 +83,7 @@ public class DateTickUnit extends TickUnit implements Serializable { private DateTickUnitType unitType; /** The unit count. */ - private int count; + private @Positive int count; /** * The roll unit type. @@ -91,7 +93,7 @@ public class DateTickUnit extends TickUnit implements Serializable { private DateTickUnitType rollUnitType; /** The roll count. */ - private int rollCount; + private @Positive int rollCount; /** The date formatter. */ private DateFormat formatter; @@ -104,7 +106,7 @@ public class DateTickUnit extends TickUnit implements Serializable { * * @since 1.0.13 */ - public DateTickUnit(DateTickUnitType unitType, int multiple) { + public DateTickUnit(DateTickUnitType unitType, @Positive int multiple) { this(unitType, multiple, DateFormat.getDateInstance(DateFormat.SHORT)); } @@ -117,7 +119,7 @@ public DateTickUnit(DateTickUnitType unitType, int multiple) { * * @since 1.0.13 */ - public DateTickUnit(DateTickUnitType unitType, int multiple, + public DateTickUnit(DateTickUnitType unitType, @Positive int multiple, DateFormat formatter) { this(unitType, multiple, unitType, multiple, formatter); } @@ -133,8 +135,8 @@ public DateTickUnit(DateTickUnitType unitType, int multiple, * * @since 1.0.13 */ - public DateTickUnit(DateTickUnitType unitType, int multiple, - DateTickUnitType rollUnitType, int rollMultiple, + public DateTickUnit(DateTickUnitType unitType, @Positive int multiple, + DateTickUnitType rollUnitType, @Positive int rollMultiple, DateFormat formatter) { super(DateTickUnit.getMillisecondCount(unitType, multiple)); Args.nullNotPermitted(formatter, "formatter"); @@ -167,7 +169,7 @@ public DateTickUnitType getUnitType() { * * @return The unit multiple (always > 0). */ - public int getMultiple() { + public @Positive int getMultiple() { return this.count; } @@ -189,7 +191,7 @@ public DateTickUnitType getRollUnitType() { * * @since 1.0.13 */ - public int getRollMultiple() { + public @Positive int getRollMultiple() { return this.rollCount; } @@ -279,7 +281,7 @@ public Date rollDate(Date base, TimeZone zone) { * * @return The field code. */ - public int getCalendarField() { + public @NonNegative int getCalendarField() { return this.unitType.getCalendarField(); } diff --git a/src/main/java/org/jfree/chart/axis/DateTickUnitType.java b/src/main/java/org/jfree/chart/axis/DateTickUnitType.java index 794055f2d..000b3e5b2 100644 --- a/src/main/java/org/jfree/chart/axis/DateTickUnitType.java +++ b/src/main/java/org/jfree/chart/axis/DateTickUnitType.java @@ -40,6 +40,8 @@ package org.jfree.chart.axis; +import org.checkerframework.checker.index.qual.*; + import java.io.ObjectStreamException; import java.io.Serializable; import java.util.Calendar; @@ -86,7 +88,7 @@ public class DateTickUnitType implements Serializable { private String name; /** The corresponding field value in Java's Calendar class. */ - private int calendarField; + private @NonNegative int calendarField; /** * Private constructor. @@ -94,7 +96,7 @@ public class DateTickUnitType implements Serializable { * @param name the name. * @param calendarField the calendar field. */ - private DateTickUnitType(String name, int calendarField) { + private DateTickUnitType(String name, @NonNegative int calendarField) { this.name = name; this.calendarField = calendarField; } @@ -104,7 +106,7 @@ private DateTickUnitType(String name, int calendarField) { * * @return The calendar field. */ - public int getCalendarField() { + public @NonNegative int getCalendarField() { return this.calendarField; } diff --git a/src/main/java/org/jfree/chart/axis/MonthDateFormat.java b/src/main/java/org/jfree/chart/axis/MonthDateFormat.java index ee7a10688..19647e95d 100644 --- a/src/main/java/org/jfree/chart/axis/MonthDateFormat.java +++ b/src/main/java/org/jfree/chart/axis/MonthDateFormat.java @@ -41,6 +41,9 @@ package org.jfree.chart.axis; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.common.value.qual.IntRange; + import java.text.DateFormat; import java.text.DateFormatSymbols; import java.text.FieldPosition; @@ -64,10 +67,10 @@ public class MonthDateFormat extends DateFormat { /** The symbols used for the months. */ - private String[] months; + private String @ArrayLen(12) [] months; /** Flags that control which months will have the year appended. */ - private boolean[] showYear; + private boolean @ArrayLen(13) [] showYear; /** The year formatter. */ private DateFormat yearFormatter; @@ -163,18 +166,20 @@ public MonthDateFormat(TimeZone zone, Locale locale, int chars, * @param yearFormatter the year formatter. */ public MonthDateFormat(TimeZone zone, Locale locale, int chars, - boolean[] showYear, DateFormat yearFormatter) { + boolean @ArrayLen(13) [] showYear, DateFormat yearFormatter) { Args.nullNotPermitted(locale, "locale"); DateFormatSymbols dfs = new DateFormatSymbols(locale); String[] monthsFromLocale = dfs.getMonths(); this.months = new String[12]; for (int i = 0; i < 12; i++) { + @SuppressWarnings({"index", "value"}) // loop index is bounded by loop https://github.com/typetools/checker-framework/issues/1669 + @IntRange(from = 0, to = 11) int i1 = i; if (chars > 0) { - this.months[i] = monthsFromLocale[i].substring(0, - Math.min(chars, monthsFromLocale[i].length())); + this.months[i1] = monthsFromLocale[i1].substring(0, + Math.min(chars, monthsFromLocale[i1].length())); } else { - this.months[i] = monthsFromLocale[i]; + this.months[i1] = monthsFromLocale[i1]; } } this.calendar = new GregorianCalendar(zone); @@ -200,7 +205,8 @@ public MonthDateFormat(TimeZone zone, Locale locale, int chars, public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { this.calendar.setTime(date); - int month = this.calendar.get(Calendar.MONTH); + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation + @IntRange(from=0, to=11) int month = this.calendar.get(Calendar.MONTH); toAppendTo.append(this.months[month]); if (this.showYear[month]) { toAppendTo.append(this.yearFormatter.format(date)); diff --git a/src/main/java/org/jfree/chart/axis/PeriodAxis.java b/src/main/java/org/jfree/chart/axis/PeriodAxis.java index eedd72155..2d26d858c 100644 --- a/src/main/java/org/jfree/chart/axis/PeriodAxis.java +++ b/src/main/java/org/jfree/chart/axis/PeriodAxis.java @@ -65,6 +65,9 @@ package org.jfree.chart.axis; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.common.value.qual.IntVal; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.FontMetrics; @@ -179,7 +182,7 @@ public class PeriodAxis extends ValueAxis private transient Paint minorTickMarkPaint = Color.BLACK; /** Info for each labeling band. */ - private PeriodAxisLabelInfo[] labelInfo; + private PeriodAxisLabelInfo @ArrayLen(2) [] labelInfo; /** * Creates a new axis. @@ -510,7 +513,7 @@ public void setMinorTickMarkOutsideLength(float length) { * * @return An array. */ - public PeriodAxisLabelInfo[] getLabelInfo() { + public PeriodAxisLabelInfo @ArrayLen(2) [] getLabelInfo() { return this.labelInfo; } @@ -520,7 +523,7 @@ public PeriodAxisLabelInfo[] getLabelInfo() { * * @param info the info. */ - public void setLabelInfo(PeriodAxisLabelInfo[] info) { + public void setLabelInfo(PeriodAxisLabelInfo @ArrayLen(2) [] info) { this.labelInfo = info; fireChangeEvent(); } @@ -800,7 +803,7 @@ protected void drawTickMarksVertical(Graphics2D g2, AxisState state, * * @return The updated axis state. */ - protected AxisState drawTickLabels(int band, Graphics2D g2, AxisState state, + protected AxisState drawTickLabels(@IntVal({0, 1}) int band, Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { // work out the initial gap @@ -1142,7 +1145,7 @@ public int hashCode() { public Object clone() throws CloneNotSupportedException { PeriodAxis clone = (PeriodAxis) super.clone(); clone.timeZone = (TimeZone) this.timeZone.clone(); - clone.labelInfo = (PeriodAxisLabelInfo[]) this.labelInfo.clone(); + clone.labelInfo = (PeriodAxisLabelInfo @ArrayLen(2) []) this.labelInfo.clone(); return clone; } diff --git a/src/main/java/org/jfree/chart/axis/QuarterDateFormat.java b/src/main/java/org/jfree/chart/axis/QuarterDateFormat.java index 05df205a4..280c3e078 100644 --- a/src/main/java/org/jfree/chart/axis/QuarterDateFormat.java +++ b/src/main/java/org/jfree/chart/axis/QuarterDateFormat.java @@ -46,6 +46,9 @@ package org.jfree.chart.axis; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.common.value.qual.IntRange; + import java.io.Serializable; import java.text.DateFormat; import java.text.FieldPosition; @@ -69,11 +72,11 @@ public class QuarterDateFormat extends DateFormat private static final long serialVersionUID = -6738465248529797176L; /** Symbols for regular quarters. */ - public static final String[] REGULAR_QUARTERS = new String[] {"1", "2", + public static final String @ArrayLen(4) [] REGULAR_QUARTERS = new String[] {"1", "2", "3", "4"}; /** Symbols for roman numbered quarters. */ - public static final String[] ROMAN_QUARTERS = new String[] {"I", "II", + public static final String @ArrayLen(4) [] ROMAN_QUARTERS = new String[] {"I", "II", "III", "IV"}; /** @@ -81,11 +84,11 @@ public class QuarterDateFormat extends DateFormat * * @since 1.0.6 */ - public static final String[] GREEK_QUARTERS = new String[] {"\u0391", + public static final String @ArrayLen(4) [] GREEK_QUARTERS = new String[] {"\u0391", "\u0392", "\u0393", "\u0394"}; /** The strings. */ - private String[] quarters = REGULAR_QUARTERS; + private String @ArrayLen(4) [] quarters = REGULAR_QUARTERS; /** A flag that controls whether the quarter or the year goes first. */ private boolean quarterFirst; @@ -112,7 +115,7 @@ public QuarterDateFormat(TimeZone zone) { * @param zone the time zone ({@code null} not permitted). * @param quarterSymbols the quarter symbols. */ - public QuarterDateFormat(TimeZone zone, String[] quarterSymbols) { + public QuarterDateFormat(TimeZone zone, String @ArrayLen(4) [] quarterSymbols) { this(zone, quarterSymbols, false); } @@ -126,7 +129,7 @@ public QuarterDateFormat(TimeZone zone, String[] quarterSymbols) { * * @since 1.0.6 */ - public QuarterDateFormat(TimeZone zone, String[] quarterSymbols, + public QuarterDateFormat(TimeZone zone, String @ArrayLen(4) [] quarterSymbols, boolean quarterFirst) { Args.nullNotPermitted(zone, "zone"); this.calendar = new GregorianCalendar(zone); @@ -154,7 +157,8 @@ public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { this.calendar.setTime(date); int year = this.calendar.get(Calendar.YEAR); - int month = this.calendar.get(Calendar.MONTH); + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation + @IntRange(from=0, to=11) int month = this.calendar.get(Calendar.MONTH); int quarter = month / 3; if (this.quarterFirst) { toAppendTo.append(this.quarters[quarter]); diff --git a/src/main/java/org/jfree/chart/axis/SymbolAxis.java b/src/main/java/org/jfree/chart/axis/SymbolAxis.java index d2a4d3b52..b2ac04d29 100644 --- a/src/main/java/org/jfree/chart/axis/SymbolAxis.java +++ b/src/main/java/org/jfree/chart/axis/SymbolAxis.java @@ -724,6 +724,7 @@ protected List refreshTicksVertical(Graphics2D g2, Rectangle2D dataArea, * * @return The symbol. */ + @SuppressWarnings("index") // this method intentionally throws and catches an indexoutofboundsexception public String valueToString(double value) { String strToReturn; try { diff --git a/src/main/java/org/jfree/chart/axis/TickUnits.java b/src/main/java/org/jfree/chart/axis/TickUnits.java index 727705ee3..470af2693 100644 --- a/src/main/java/org/jfree/chart/axis/TickUnits.java +++ b/src/main/java/org/jfree/chart/axis/TickUnits.java @@ -55,6 +55,8 @@ package org.jfree.chart.axis; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import java.text.NumberFormat; import java.util.ArrayList; @@ -114,7 +116,7 @@ public int size() { * * @return The tickunit. */ - public TickUnit get(int pos) { + public TickUnit get(@NonNegative int pos) { return (TickUnit) this.tickUnits.get(pos); } @@ -126,6 +128,7 @@ public TickUnit get(int pos) { * @return A tick unit that is larger than the supplied unit. */ @Override + @SuppressWarnings("index") // binary search on list, which SearchIndex does not handle because lists are not fixed-size data structure public TickUnit getLargerTickUnit(TickUnit unit) { int index = Collections.binarySearch(this.tickUnits, unit); if (index >= 0) { @@ -148,6 +151,7 @@ public TickUnit getLargerTickUnit(TickUnit unit) { * @return A unit from the collection. */ @Override + @SuppressWarnings("index") // binary search on list, which SearchIndex does not handle because lists are not fixed-size data structure public TickUnit getCeilingTickUnit(TickUnit unit) { int index = Collections.binarySearch(this.tickUnits, unit); if (index >= 0) { diff --git a/src/main/java/org/jfree/chart/axis/ValueAxis.java b/src/main/java/org/jfree/chart/axis/ValueAxis.java index f5c685484..f94715d7a 100644 --- a/src/main/java/org/jfree/chart/axis/ValueAxis.java +++ b/src/main/java/org/jfree/chart/axis/ValueAxis.java @@ -114,6 +114,11 @@ package org.jfree.chart.axis; +import org.checkerframework.checker.index.qual.NonNegative; + +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.dataflow.qual.Pure; + import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; @@ -628,7 +633,7 @@ protected void drawAxisLine(Graphics2D g2, double cursor, * * @return The x and y coordinates of the anchor point. */ - protected float[] calculateAnchorPoint(ValueTick tick, double cursor, + protected float @ArrayLen(2) [] calculateAnchorPoint(ValueTick tick, double cursor, Rectangle2D dataArea, RectangleEdge edge) { RectangleInsets insets = getTickLabelInsets(); @@ -1471,6 +1476,7 @@ public void setMinorTickCount(int count) { * * @see #java2DToValue(double, Rectangle2D, RectangleEdge) */ + @Pure public abstract double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge); @@ -1653,7 +1659,7 @@ protected int getAutoTickIndex() { * * @see #getAutoTickIndex() */ - protected void setAutoTickIndex(int index) { + protected void setAutoTickIndex(@NonNegative int index) { this.autoTickIndex = index; } diff --git a/src/main/java/org/jfree/chart/block/ColumnArrangement.java b/src/main/java/org/jfree/chart/block/ColumnArrangement.java index 7813f7d6e..40a594730 100644 --- a/src/main/java/org/jfree/chart/block/ColumnArrangement.java +++ b/src/main/java/org/jfree/chart/block/ColumnArrangement.java @@ -41,6 +41,8 @@ package org.jfree.chart.block; +import org.checkerframework.checker.index.qual.SameLen; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; @@ -308,6 +310,7 @@ protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, * * @return The size after the arrangement. */ + @SuppressWarnings("index") // @LengthIs https://github.com/kelloggm/checker-framework/issues/152 protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { double y = 0.0; double height = 0.0; diff --git a/src/main/java/org/jfree/chart/block/FlowArrangement.java b/src/main/java/org/jfree/chart/block/FlowArrangement.java index a47d79e5d..122f2f981 100644 --- a/src/main/java/org/jfree/chart/block/FlowArrangement.java +++ b/src/main/java/org/jfree/chart/block/FlowArrangement.java @@ -42,6 +42,8 @@ package org.jfree.chart.block; +import org.checkerframework.checker.index.qual.SameLen; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; @@ -353,6 +355,7 @@ protected Size2D arrangeRN(BlockContainer container, Graphics2D g2, * * @return The size after the arrangement. */ + @SuppressWarnings("index") // @LengthIs https://github.com/kelloggm/checker-framework/issues/152 protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { double x = 0.0; double width = 0.0; diff --git a/src/main/java/org/jfree/chart/block/GridArrangement.java b/src/main/java/org/jfree/chart/block/GridArrangement.java index 64e5279f5..50757800d 100644 --- a/src/main/java/org/jfree/chart/block/GridArrangement.java +++ b/src/main/java/org/jfree/chart/block/GridArrangement.java @@ -42,6 +42,8 @@ package org.jfree.chart.block; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; @@ -58,10 +60,10 @@ public class GridArrangement implements Arrangement, Serializable { private static final long serialVersionUID = -2563758090144655938L; /** The rows. */ - private int rows; + private @NonNegative int rows; /** The columns. */ - private int columns; + private @NonNegative int columns; /** * Creates a new grid arrangement. @@ -69,7 +71,7 @@ public class GridArrangement implements Arrangement, Serializable { * @param rows the row count. * @param columns the column count. */ - public GridArrangement(int rows, int columns) { + public GridArrangement(@NonNegative int rows, @NonNegative int columns) { this.rows = rows; this.columns = columns; } diff --git a/src/main/java/org/jfree/chart/date/SerialDate.java b/src/main/java/org/jfree/chart/date/SerialDate.java index b27524865..61c4c7585 100644 --- a/src/main/java/org/jfree/chart/date/SerialDate.java +++ b/src/main/java/org/jfree/chart/date/SerialDate.java @@ -28,6 +28,11 @@ package org.jfree.chart.date; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.common.value.qual.IntRange; +import org.checkerframework.common.value.qual.IntVal; +import org.checkerframework.checker.index.qual.SameLen; + import java.io.Serializable; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; @@ -104,25 +109,25 @@ public abstract class SerialDate implements Comparable, Serializable, public static final int SUNDAY = Calendar.SUNDAY; /** The number of days in each month in non leap years. */ - static final int[] LAST_DAY_OF_MONTH = + static final @IntRange(from = 0, to = 31) int @ArrayLen(13) [] LAST_DAY_OF_MONTH = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /** The number of days in a (non-leap) year up to the end of each month. */ - static final int[] AGGREGATE_DAYS_TO_END_OF_MONTH = + static final int @ArrayLen(13) [] AGGREGATE_DAYS_TO_END_OF_MONTH = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; /** The number of days in a year up to the end of the preceding month. */ - static final int[] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = + static final @IntRange(from = 0, to = 365) int @ArrayLen(14) [] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; /** The number of days in a leap year up to the end of each month. */ - static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH = + static final int @ArrayLen(13) [] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; /** * The number of days in a leap year up to the end of the preceding month. */ - static final int[] + static final @IntRange(from = 0, to = 366) int @ArrayLen(14) [] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; @@ -218,7 +223,8 @@ public static int stringToWeekdayCode(String s) { final String[] shortWeekdayNames = DATE_FORMAT_SYMBOLS.getShortWeekdays(); - final String[] weekDayNames = DATE_FORMAT_SYMBOLS.getWeekdays(); + @SuppressWarnings("index") // SameLen checker could use the Value Checker https://github.com/kelloggm/checker-framework/issues/179 + final String @SameLen("shortWeekdayNames") [] weekDayNames = DATE_FORMAT_SYMBOLS.getWeekdays(); int result = -1; s = s.trim(); @@ -245,8 +251,8 @@ public static int stringToWeekdayCode(String s) { * * @return a string representing the supplied day-of-the-week. */ - public static String weekdayCodeToString(int weekday) { - final String[] weekdays = DATE_FORMAT_SYMBOLS.getWeekdays(); + public static String weekdayCodeToString(@IntRange(from=0, to=7) int weekday) { + final String @ArrayLen(8) [] weekdays = DATE_FORMAT_SYMBOLS.getWeekdays(); return weekdays[weekday]; } @@ -255,7 +261,7 @@ public static String weekdayCodeToString(int weekday) { * * @return an array of month names. */ - public static String[] getMonths() { + public static String @ArrayLen(13) [] getMonths() { return getMonths(false); @@ -269,7 +275,7 @@ public static String[] getMonths() { * * @return an array of month names. */ - public static String[] getMonths(boolean shortened) { + public static String @ArrayLen(13) [] getMonths(boolean shortened) { if (shortened) { return DATE_FORMAT_SYMBOLS.getShortMonths(); } @@ -315,7 +321,7 @@ public static boolean isValidMonthCode(int code) { * * @return the quarter that the month belongs to. */ - public static int monthCodeToQuarter(int code) { + public static @IntVal({1,2,3,4}) int monthCodeToQuarter(@IntRange(from = 1, to = 12) int code) { switch(code) { case JANUARY: @@ -346,7 +352,7 @@ public static int monthCodeToQuarter(int code) { * * @return a string representing the supplied month. */ - public static String monthCodeToString(int month) { + public static String monthCodeToString(@IntRange(from = 1, to = 12) int month) { return monthCodeToString(month, false); } @@ -361,7 +367,7 @@ public static String monthCodeToString(int month) { * * @return a string representing the supplied month. */ - public static String monthCodeToString(int month, boolean shortened) { + public static String monthCodeToString(@IntRange(from = 1, to = 12) int month, boolean shortened) { // check arguments... if (!isValidMonthCode(month)) { @@ -369,7 +375,7 @@ public static String monthCodeToString(int month, boolean shortened) { "SerialDate.monthCodeToString: month outside valid range."); } - final String[] months; + final String @ArrayLen(13) [] months; if (shortened) { months = DATE_FORMAT_SYMBOLS.getShortMonths(); @@ -394,10 +400,11 @@ public static String monthCodeToString(int month, boolean shortened) { * @return {@code -1} if the string is not parseable, the month of the * year otherwise. */ - public static int stringToMonthCode(String s) { + public static @IntRange(from = -1, to = 12) int stringToMonthCode(String s) { final String[] shortMonthNames = DATE_FORMAT_SYMBOLS.getShortMonths(); - final String[] monthNames = DATE_FORMAT_SYMBOLS.getMonths(); + @SuppressWarnings("index") // DateFormatSymbols can't be annotated correctly because of https://github.com/kelloggm/checker-framework/issues/223 + final String @SameLen("shortMonthNames") [] monthNames = DATE_FORMAT_SYMBOLS.getMonths(); int result = -1; s = s.trim(); @@ -423,8 +430,10 @@ public static int stringToMonthCode(String s) { } } } + @SuppressWarnings({"index", "value"}) // the loop above only ensures that 1 <= result <= 12 if s is an actual month name + @IntRange(from = 1, to =12) int toReturn = result; - return result; + return toReturn; } @@ -454,7 +463,7 @@ public static boolean isValidWeekInMonthCode(int code) { * * @return {@code true} if the specified year is a leap year. */ - public static boolean isLeapYear(int yyyy) { + public static boolean isLeapYear(@IntRange(from = 1900, to = 9999) int yyyy) { if ((yyyy % 4) != 0) { return false; @@ -479,9 +488,10 @@ else if ((yyyy % 100) == 0) { * * @param yyyy the year (in the range 1900 to 9999). * - * @return the number of leap years from 1900 to the specified year. + * @return the number of leap years from 1900 to the specified year (if year is 9999, 1964 leap years). */ - public static int leapYearCount(int yyyy) { + @SuppressWarnings({"index", "value"}) // The Value Checker loses some precision here, I believe due to imprecise estimation of division on ranges + public static @IntRange(from = 0, to = 1964) int leapYearCount(@IntRange(from = 1900, to = 9999) int yyyy) { int leap4 = (yyyy - 1896) / 4; int leap100 = (yyyy - 1800) / 100; int leap400 = (yyyy - 1600) / 400; @@ -497,7 +507,8 @@ public static int leapYearCount(int yyyy) { * * @return the number of the last day of the month. */ - public static int lastDayOfMonth(int month, int yyyy) { + @SuppressWarnings({"index", "value"}) // February has 28 days, so 1 is only added to 28, not 31. + public static @IntRange(from = 28, to = 31) int lastDayOfMonth(@IntRange(from = 1, to = 12) int month, int yyyy) { final int result = LAST_DAY_OF_MONTH[month]; if (month != FEBRUARY) { @@ -521,6 +532,7 @@ else if (isLeapYear(yyyy)) { * * @return a new date. */ + @SuppressWarnings({"index", "value"}) // days could take the serial day number out of range, if it is sufficiently large, but no restriction on days public static SerialDate addDays(int days, SerialDate base) { int serialDayNumber = base.toSerial() + days; return SerialDate.createInstance(serialDayNumber); @@ -543,9 +555,10 @@ public static SerialDate addMonths(int months, SerialDate base) { if (yy < MINIMUM_YEAR_SUPPORTED || yy > MAXIMUM_YEAR_SUPPORTED) { throw new IllegalArgumentException("Call to addMonths resulted in unsupported year"); } - int mm = (12 * base.getYYYY() + base.getMonth() + months - 1) % 12 + 1; - int dd = Math.min(base.getDayOfMonth(), - SerialDate.lastDayOfMonth(mm, yy)); + @SuppressWarnings({"index", "value"}) // if months is negative, then yy would be out of range and the exception above is thrown. + @IntRange(from = 1, to = 12) int mm = (12 * base.getYYYY() + base.getMonth() + months - 1) % 12 + 1; + @SuppressWarnings({"index", "value"}) // Value Checker support for Math#min and Math#max https://github.com/typetools/checker-framework/issues/1687 + @IntRange(from = 1, to = 31) int dd = Math.min(base.getDayOfMonth(), SerialDate.lastDayOfMonth(mm, yy)); return SerialDate.createInstance(dd, mm, yy); } @@ -567,7 +580,8 @@ public static SerialDate addYears(int years, SerialDate base) { if (targetY < MINIMUM_YEAR_SUPPORTED || targetY > MAXIMUM_YEAR_SUPPORTED) { throw new IllegalArgumentException("Call to addYears resulted in unsupported year"); } - int targetD = Math.min(baseD, SerialDate.lastDayOfMonth(baseM, targetY)); + @SuppressWarnings({"index", "value"}) // Value Checker support for Math#min and Math#max https://github.com/typetools/checker-framework/issues/1687 + @IntRange(from = 1, to = 31) int targetD = Math.min(baseD, SerialDate.lastDayOfMonth(baseM, targetY)); return SerialDate.createInstance(targetD, baseM, targetY); } @@ -581,7 +595,7 @@ public static SerialDate addYears(int years, SerialDate base) { * @return the latest date that falls on the specified day-of-the-week and * is BEFORE the base date. */ - public static SerialDate getPreviousDayOfWeek(int targetWeekday, + public static SerialDate getPreviousDayOfWeek(@IntRange(from = 1, to = 7) int targetWeekday, SerialDate base) { // check arguments... @@ -612,7 +626,7 @@ public static SerialDate getPreviousDayOfWeek(int targetWeekday, * @return the earliest date that falls on the specified day-of-the-week * and is AFTER the base date. */ - public static SerialDate getFollowingDayOfWeek(int targetWeekday, + public static SerialDate getFollowingDayOfWeek(@IntRange(from = 1, to = 7) int targetWeekday, SerialDate base) { // check arguments... @@ -644,7 +658,7 @@ public static SerialDate getFollowingDayOfWeek(int targetWeekday, * @return the date that falls on the specified day-of-the-week and is * CLOSEST to the base date. */ - public static SerialDate getNearestDayOfWeek(int targetDOW, SerialDate base) { + public static SerialDate getNearestDayOfWeek(@IntRange(from = 1, to = 7) int targetDOW, SerialDate base) { // check arguments... if (!SerialDate.isValidWeekdayCode(targetDOW)) { @@ -729,7 +743,7 @@ public static String relativeToString(int relative) { * * @return An instance of {@link SerialDate}. */ - public static SerialDate createInstance(int day, int month, int yyyy) { + public static SerialDate createInstance(@IntRange(from = 1, to = 31) int day, @IntRange(from = 1, to = 12) int month, @IntRange(from = 1900, to = 9999) int yyyy) { return new SpreadsheetDate(day, month, yyyy); } @@ -741,7 +755,7 @@ public static SerialDate createInstance(int day, int month, int yyyy) { * * @return a instance of SerialDate. */ - public static SerialDate createInstance(int serial) { + public static SerialDate createInstance(@IntRange(from = 2, to = 2958465) int serial) { return new SpreadsheetDate(serial); } @@ -752,6 +766,7 @@ public static SerialDate createInstance(int serial) { * * @return a instance of SerialDate. */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public static SerialDate createInstance(java.util.Date date) { GregorianCalendar calendar = new GregorianCalendar(); @@ -768,7 +783,7 @@ public static SerialDate createInstance(java.util.Date date) { * * @return the serial number for the date. */ - public abstract int toSerial(); + public abstract @IntRange(from = 2, to = 2958465) int toSerial(); /** * Returns a java.util.Date. Since java.util.Date has more precision than @@ -814,28 +829,28 @@ public String toString() { * * @return the year. */ - public abstract int getYYYY(); + public abstract @IntRange(from=1900, to=9999) int getYYYY(); /** * Returns the month (January = 1, February = 2, March = 3). * * @return the month of the year. */ - public abstract int getMonth(); + public abstract @IntRange(from = 1, to = 12) int getMonth(); /** * Returns the day of the month. * * @return the day of the month. */ - public abstract int getDayOfMonth(); + public abstract @IntRange(from = 1, to = 31) int getDayOfMonth(); /** * Returns the day of the week. * * @return the day of the week. */ - public abstract int getDayOfWeek(); + public abstract @IntRange(from = 1, to = 7) int getDayOfWeek(); /** * Returns the difference (in days) between this date and the specified @@ -941,7 +956,7 @@ public abstract boolean isInRange(SerialDate d1, SerialDate d2, * @return the latest date that falls on the specified day-of-the-week and * is BEFORE this date. */ - public SerialDate getPreviousDayOfWeek(int targetDOW) { + public SerialDate getPreviousDayOfWeek(@IntRange(from = 1, to = 7) int targetDOW) { return getPreviousDayOfWeek(targetDOW, this); } @@ -954,7 +969,7 @@ public SerialDate getPreviousDayOfWeek(int targetDOW) { * @return the earliest date that falls on the specified day-of-the-week * and is AFTER this date. */ - public SerialDate getFollowingDayOfWeek(int targetDOW) { + public SerialDate getFollowingDayOfWeek(@IntRange(from = 1, to = 7) int targetDOW) { return getFollowingDayOfWeek(targetDOW, this); } @@ -965,7 +980,7 @@ public SerialDate getFollowingDayOfWeek(int targetDOW) { * * @return the nearest date that falls on the specified day-of-the-week. */ - public SerialDate getNearestDayOfWeek(int targetDOW) { + public SerialDate getNearestDayOfWeek(@IntRange(from = 1, to = 7) int targetDOW) { return getNearestDayOfWeek(targetDOW, this); } diff --git a/src/main/java/org/jfree/chart/date/SpreadsheetDate.java b/src/main/java/org/jfree/chart/date/SpreadsheetDate.java index 862f2ee9d..f315e8cd8 100644 --- a/src/main/java/org/jfree/chart/date/SpreadsheetDate.java +++ b/src/main/java/org/jfree/chart/date/SpreadsheetDate.java @@ -28,6 +28,10 @@ package org.jfree.chart.date; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.common.value.qual.IntRange; +import org.checkerframework.checker.index.qual.*; + import java.util.Calendar; import java.util.Date; @@ -58,16 +62,16 @@ public class SpreadsheetDate extends SerialDate { * The day number (1-Jan-1900 = 2, 2-Jan-1900 = 3, ..., 31-Dec-9999 = * 2958465). */ - private final int serial; + private final @IntRange(from = 2, to = 2958465) int serial; /** The day of the month (1 to 28, 29, 30 or 31 depending on the month). */ - private final int day; + private final @IntRange(from = 1, to = 31) int day; /** The month of the year (1 to 12). */ - private final int month; + private final @IntRange(from = 1, to = 12) int month; /** The year (1900 to 9999). */ - private final int year; + private final @IntRange(from = 1900, to = 9999) int year; /** * Creates a new date instance. @@ -76,7 +80,7 @@ public class SpreadsheetDate extends SerialDate { * @param month the month (in the range 1 to 12). * @param year the year (in the range 1900 to 9999). */ - public SpreadsheetDate(int day, int month, int year) { + public SpreadsheetDate(@IntRange(from = 1, to = 31) int day, @IntRange(from = 1, to = 12) int month, @IntRange(from = 1, to = 9999) int year) { if ((year >= 1900) && (year <= 9999)) { this.year = year; @@ -111,7 +115,7 @@ public SpreadsheetDate(int day, int month, int year) { * * @param serial the serial number for the day (range: 2 to 2958465). */ - public SpreadsheetDate(int serial) { + public SpreadsheetDate(@IntRange(from = 2, to = 2958465) int serial) { if ((serial >= SERIAL_LOWER_BOUND) && (serial <= SERIAL_UPPER_BOUND)) { this.serial = serial; @@ -125,26 +129,34 @@ public SpreadsheetDate(int serial) { // get the year from the serial date final int days = this.serial - SERIAL_LOWER_BOUND; // overestimated because we ignored leap days - final int overestimatedYYYY = 1900 + (days / 365); + @SuppressWarnings({"index", "value"}) // not really true, but gets this to typecheck. Safety is assured by checks later in this method + final @IntRange(from = 1900, to = 9999) int overestimatedYYYY = 1900 + (days / 365); final int leaps = SerialDate.leapYearCount(overestimatedYYYY); final int nonleapdays = days - leaps; // underestimated because we overestimated years - int underestimatedYYYY = 1900 + (nonleapdays / 365); + @SuppressWarnings({"index", "value"}) // not really true, but gets this to typecheck. Safety is assured by checks later in this method + @IntRange(from = 1900, to = 9999) int underestimatedYYYY = 1900 + (nonleapdays / 365); if (underestimatedYYYY == overestimatedYYYY) { this.year = underestimatedYYYY; } else { int ss1 = calcSerial(1, 1, underestimatedYYYY); + @SuppressWarnings({"index", "value"}) // Necessary for typechecking the modified code below. + @IntRange(from = 1901, to = 10000) int underestimatedYYYY2 = underestimatedYYYY; while (ss1 <= this.serial) { - underestimatedYYYY = underestimatedYYYY + 1; - ss1 = calcSerial(1, 1, underestimatedYYYY); + @SuppressWarnings({"index", "value"}) // This code rolls the year up to one higher than the correct one. + @IntRange(from = 1901, to = 10000) int underestimatedYYYYTmp = underestimatedYYYY2 + 1; + underestimatedYYYY2 = underestimatedYYYYTmp; + @SuppressWarnings({"index", "value"}) // this call to calcSerial could technically happen with a year that's 10000. The result would still be correct. + int ss1tmp = calcSerial(1, 1, underestimatedYYYY2); + ss1 = ss1tmp; } - this.year = underestimatedYYYY - 1; + this.year = underestimatedYYYY2 - 1; } final int ss2 = calcSerial(1, 1, this.year); - int[] daysToEndOfPrecedingMonth + @IntRange(from = 0, to = 366) int @ArrayLen(14) [] daysToEndOfPrecedingMonth = AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH; if (isLeapYear(this.year)) { @@ -153,17 +165,23 @@ public SpreadsheetDate(int serial) { } // get the month from the serial date - int mm = 1; + @IntRange(from = 1, to = 13) int mm = 1; int sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1; while (sss < this.serial) { - mm = mm + 1; + @SuppressWarnings({"index", "value"}) // this loop cannot go around more than 12 times + @IntRange(from=2, to=13) int mm1 = mm + 1; + mm = mm1; sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1; } - this.month = mm - 1; + @SuppressWarnings({"index", "value"}) // previous loop always goes around at least once + @IntRange(from = 1, to = 12) int newMonth = mm - 1; + this.month = newMonth; // what's left is d(+1); - this.day = this.serial - ss2 + @SuppressWarnings({"index", "value"}) // date math + @IntRange(from = 1, to = 31) int newDay = this.serial - ss2 - daysToEndOfPrecedingMonth[this.month] + 1; + this.day = newDay; } @@ -175,7 +193,7 @@ public SpreadsheetDate(int serial) { * @return The serial number of this date. */ @Override - public int toSerial() { + public @IntRange(from = 2, to = 2958465) int toSerial() { return this.serial; } @@ -197,7 +215,7 @@ public Date toDate() { * @return The year. */ @Override - public int getYYYY() { + public @IntRange(from = 1900, to = 9999) int getYYYY() { return this.year; } @@ -207,7 +225,7 @@ public int getYYYY() { * @return The month of the year. */ @Override - public int getMonth() { + public @IntRange(from = 1, to = 12) int getMonth() { return this.month; } @@ -217,7 +235,7 @@ public int getMonth() { * @return The day of the month. */ @Override - public int getDayOfMonth() { + public @IntRange(from = 1, to = 31) int getDayOfMonth() { return this.day; } @@ -232,7 +250,7 @@ public int getDayOfMonth() { * @return A code representing the day of the week. */ @Override - public int getDayOfWeek() { + public @IntRange(from = 1, to = 7) int getDayOfWeek() { return (this.serial + 6) % 7 + 1; } @@ -425,16 +443,22 @@ else if (include == SerialDate.INCLUDE_SECOND) { * * @return the serial number from the day, month and year. */ - private int calcSerial(int d, int m, int y) { + private @IntRange(from = 2, to = 2958465) int calcSerial(@IntRange(from = 1, to = 31) int d, /*@IntRange(from=1,to=12)*/ int m, @IntRange(from = 1900, to = 9999) int y) { + @SuppressWarnings({"index", "value"}) // Though passing y - 1 is technically incorrect iff y = 1900, leapYearCount returns zero if passed 1899 (same as 1900). int yy = ((y - 1900) * 365) + SerialDate.leapYearCount(y - 1); - int mm = SerialDate.AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[m]; + @SuppressWarnings({"index", "value"}) // Preceding month can't be December - month must be 1 - 12 (12 -> current month is december). + @IntRange(from = 0, to = 336) int mm = SerialDate.AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[m]; if (m > MonthConstants.FEBRUARY) { if (SerialDate.isLeapYear(y)) { - mm = mm + 1; + @SuppressWarnings({"index", "value"}) // Adding leap day doesn't change this value's range + @IntRange(from = 0, to = 336) int mm1 = mm + 1; + mm = mm1; } } int dd = d; - return yy + mm + dd + 1; + @SuppressWarnings("value") // Imprecision due to presence or absence of leap days (checker thinks it may be two higher) + @IntRange(from = 2, to = 2958465) int result = yy + mm + dd + 1; + return result; } } diff --git a/src/main/java/org/jfree/chart/entity/EntityCollection.java b/src/main/java/org/jfree/chart/entity/EntityCollection.java index a923881a0..45188fff3 100644 --- a/src/main/java/org/jfree/chart/entity/EntityCollection.java +++ b/src/main/java/org/jfree/chart/entity/EntityCollection.java @@ -47,6 +47,8 @@ package org.jfree.chart.entity; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Collection; import java.util.Iterator; @@ -92,7 +94,7 @@ public interface EntityCollection { * * @return An entity. */ - public ChartEntity getEntity(int index); + public ChartEntity getEntity(@NonNegative int index); /** * Returns the entity count. diff --git a/src/main/java/org/jfree/chart/entity/PieSectionEntity.java b/src/main/java/org/jfree/chart/entity/PieSectionEntity.java index 9551c185a..f18263a8c 100644 --- a/src/main/java/org/jfree/chart/entity/PieSectionEntity.java +++ b/src/main/java/org/jfree/chart/entity/PieSectionEntity.java @@ -54,6 +54,8 @@ package org.jfree.chart.entity; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Shape; import java.io.Serializable; @@ -149,7 +151,7 @@ public int getPieIndex() { * * @see #getPieIndex() */ - public void setPieIndex(int index) { + public void setPieIndex(@NonNegative int index) { this.pieIndex = index; } @@ -171,7 +173,7 @@ public int getSectionIndex() { * * @see #getSectionIndex() */ - public void setSectionIndex(int index) { + public void setSectionIndex(@NonNegative int index) { this.sectionIndex = index; } diff --git a/src/main/java/org/jfree/chart/entity/StandardEntityCollection.java b/src/main/java/org/jfree/chart/entity/StandardEntityCollection.java index 683bd2a14..6ce642866 100644 --- a/src/main/java/org/jfree/chart/entity/StandardEntityCollection.java +++ b/src/main/java/org/jfree/chart/entity/StandardEntityCollection.java @@ -50,6 +50,8 @@ package org.jfree.chart.entity; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Collection; import java.util.Collections; @@ -98,7 +100,7 @@ public int getEntityCount() { * @see #add(ChartEntity) */ @Override - public ChartEntity getEntity(int index) { + public ChartEntity getEntity(@NonNegative int index) { return (ChartEntity) this.entities.get(index); } diff --git a/src/main/java/org/jfree/chart/entity/XYAnnotationEntity.java b/src/main/java/org/jfree/chart/entity/XYAnnotationEntity.java index d1e29cead..b05ef3233 100644 --- a/src/main/java/org/jfree/chart/entity/XYAnnotationEntity.java +++ b/src/main/java/org/jfree/chart/entity/XYAnnotationEntity.java @@ -40,6 +40,8 @@ package org.jfree.chart.entity; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Shape; import java.io.Serializable; @@ -64,7 +66,7 @@ public class XYAnnotationEntity extends ChartEntity * @param toolTipText the tool tip text. * @param urlText the URL text for HTML image maps. */ - public XYAnnotationEntity(Shape hotspot, int rendererIndex, + public XYAnnotationEntity(Shape hotspot, @NonNegative int rendererIndex, String toolTipText, String urlText) { super(hotspot, toolTipText, urlText); this.rendererIndex = rendererIndex; @@ -84,7 +86,7 @@ public int getRendererIndex() { * * @param index the item index (zero-based). */ - public void setRendererIndex(int index) { + public void setRendererIndex(@NonNegative int index) { this.rendererIndex = index; } diff --git a/src/main/java/org/jfree/chart/entity/XYItemEntity.java b/src/main/java/org/jfree/chart/entity/XYItemEntity.java index f55f1d675..84cc62ead 100644 --- a/src/main/java/org/jfree/chart/entity/XYItemEntity.java +++ b/src/main/java/org/jfree/chart/entity/XYItemEntity.java @@ -50,6 +50,8 @@ package org.jfree.chart.entity; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Shape; import org.jfree.data.xy.XYDataset; @@ -67,7 +69,7 @@ public class XYItemEntity extends ChartEntity { private transient XYDataset dataset; /** The series. */ - private int series; + private @NonNegative int series; /** The item. */ private int item; @@ -83,7 +85,7 @@ public class XYItemEntity extends ChartEntity { * @param urlText the URL text for HTML image maps. */ public XYItemEntity(Shape area, - XYDataset dataset, int series, int item, + XYDataset dataset, @NonNegative int series, @NonNegative int item, String toolTipText, String urlText) { super(area, toolTipText, urlText); this.dataset = dataset; @@ -114,7 +116,7 @@ public void setDataset(XYDataset dataset) { * * @return The series index. */ - public int getSeriesIndex() { + public @NonNegative int getSeriesIndex() { return this.series; } @@ -123,7 +125,7 @@ public int getSeriesIndex() { * * @param series the series index (zero-based). */ - public void setSeriesIndex(int series) { + public void setSeriesIndex(@NonNegative int series) { this.series = series; } @@ -141,7 +143,7 @@ public int getItem() { * * @param item the item index (zero-based). */ - public void setItem(int item) { + public void setItem(@NonNegative int item) { this.item = item; } diff --git a/src/main/java/org/jfree/chart/labels/AbstractCategoryItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/AbstractCategoryItemLabelGenerator.java index 2622e0295..f6024f622 100644 --- a/src/main/java/org/jfree/chart/labels/AbstractCategoryItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/AbstractCategoryItemLabelGenerator.java @@ -46,6 +46,9 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; @@ -158,7 +161,7 @@ protected AbstractCategoryItemLabelGenerator(String labelFormat, * * @return The label. */ - public String generateRowLabel(CategoryDataset dataset, int row) { + public String generateRowLabel(CategoryDataset dataset, @NonNegative int row) { return dataset.getRowKey(row).toString(); } @@ -170,7 +173,7 @@ public String generateRowLabel(CategoryDataset dataset, int row) { * * @return The label. */ - public String generateColumnLabel(CategoryDataset dataset, int column) { + public String generateColumnLabel(CategoryDataset dataset, @NonNegative int column) { return dataset.getColumnKey(column).toString(); } @@ -211,7 +214,7 @@ public DateFormat getDateFormat() { * @return The label (possibly {@code null}). */ protected String generateLabelString(CategoryDataset dataset, - int row, int column) { + @NonNegative int row, @NonNegative int column) { Args.nullNotPermitted(dataset, "dataset"); String result; Object[] items = createItemArray(dataset, row, column); @@ -230,8 +233,8 @@ protected String generateLabelString(CategoryDataset dataset, * * @return The items (never {@code null}). */ - protected Object[] createItemArray(CategoryDataset dataset, - int row, int column) { + protected Object @MinLen(4) [] createItemArray(CategoryDataset dataset, + @NonNegative int row, @NonNegative int column) { Object[] result = new Object[4]; result[0] = dataset.getRowKey(row).toString(); result[1] = dataset.getColumnKey(column).toString(); diff --git a/src/main/java/org/jfree/chart/labels/AbstractPieItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/AbstractPieItemLabelGenerator.java index 325687d7d..71c482483 100644 --- a/src/main/java/org/jfree/chart/labels/AbstractPieItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/AbstractPieItemLabelGenerator.java @@ -45,6 +45,11 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.MinLen; + import java.io.Serializable; import java.text.MessageFormat; import java.text.NumberFormat; @@ -134,7 +139,7 @@ public NumberFormat getPercentFormat() { * * @return The items (never {@code null}). */ - protected Object[] createItemArray(PieDataset dataset, Comparable key) { + protected Object @MinLen(4) [] createItemArray(PieDataset dataset, Comparable key) { Object[] result = new Object[4]; double total = DatasetUtils.calculatePieDatasetTotal(dataset); result[0] = key.toString(); diff --git a/src/main/java/org/jfree/chart/labels/AbstractXYItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/AbstractXYItemLabelGenerator.java index ddd11105e..566359bcd 100644 --- a/src/main/java/org/jfree/chart/labels/AbstractXYItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/AbstractXYItemLabelGenerator.java @@ -55,6 +55,11 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.MinLen; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; @@ -233,7 +238,7 @@ public DateFormat getYDateFormat() { * * @return The label (possibly {@code null}). */ - public String generateLabelString(XYDataset dataset, int series, int item) { + public String generateLabelString(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { String result; Object[] items = createItemArray(dataset, series, item); result = MessageFormat.format(this.formatString, items); @@ -262,8 +267,8 @@ public String getNullYString() { * @return An array of three items from the dataset formatted as * {@code String} objects (never {@code null}). */ - protected Object[] createItemArray(XYDataset dataset, int series, - int item) { + protected Object @MinLen(3) [] createItemArray(XYDataset dataset, @NonNegative int series, + @IndexFor("#1.getSeries(#2)") int item) { Object[] result = new Object[3]; result[0] = dataset.getSeriesKey(series).toString(); diff --git a/src/main/java/org/jfree/chart/labels/BoxAndWhiskerToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/BoxAndWhiskerToolTipGenerator.java index 29e8dff2a..ec764ea98 100644 --- a/src/main/java/org/jfree/chart/labels/BoxAndWhiskerToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/BoxAndWhiskerToolTipGenerator.java @@ -41,6 +41,10 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.MinLen; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.MessageFormat; import java.text.NumberFormat; @@ -108,8 +112,8 @@ public BoxAndWhiskerToolTipGenerator(String format, * @return The items (never {@code null}). */ @Override - protected Object[] createItemArray(CategoryDataset dataset, int series, - int item) { + protected Object @MinLen(8) [] createItemArray(CategoryDataset dataset, @NonNegative int series, + @NonNegative int item) { Object[] result = new Object[8]; result[0] = dataset.getRowKey(series); Number y = dataset.getValue(series, item); diff --git a/src/main/java/org/jfree/chart/labels/BoxAndWhiskerXYToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/BoxAndWhiskerXYToolTipGenerator.java index 4a287ecb5..b5d231413 100644 --- a/src/main/java/org/jfree/chart/labels/BoxAndWhiskerXYToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/BoxAndWhiskerXYToolTipGenerator.java @@ -49,6 +49,12 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; @@ -123,8 +129,8 @@ public BoxAndWhiskerXYToolTipGenerator(String toolTipFormat, * @return The items (never {@code null}). */ @Override - protected Object[] createItemArray(XYDataset dataset, int series, - int item) { + protected Object @MinLen(8) [] createItemArray(XYDataset dataset, @NonNegative int series, + @IndexFor("#1.getSeries(#2)") int item) { Object[] result = new Object[8]; result[0] = dataset.getSeriesKey(series).toString(); Number x = dataset.getX(series, item); @@ -138,12 +144,14 @@ protected Object[] createItemArray(XYDataset dataset, int series, if (dataset instanceof BoxAndWhiskerXYDataset) { BoxAndWhiskerXYDataset d = (BoxAndWhiskerXYDataset) dataset; - result[2] = formatter.format(d.getMeanValue(series, item)); - result[3] = formatter.format(d.getMedianValue(series, item)); - result[4] = formatter.format(d.getMinRegularValue(series, item)); - result[5] = formatter.format(d.getMaxRegularValue(series, item)); - result[6] = formatter.format(d.getQ1Value(series, item)); - result[7] = formatter.format(d.getQ3Value(series, item)); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("d.getSeries(series)") int itemD = item; + result[2] = formatter.format(d.getMeanValue(series, itemD)); + result[3] = formatter.format(d.getMedianValue(series, itemD)); + result[4] = formatter.format(d.getMinRegularValue(series, itemD)); + result[5] = formatter.format(d.getMaxRegularValue(series, itemD)); + result[6] = formatter.format(d.getQ1Value(series, itemD)); + result[7] = formatter.format(d.getQ3Value(series, itemD)); } return result; } diff --git a/src/main/java/org/jfree/chart/labels/BubbleXYItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/BubbleXYItemLabelGenerator.java index bfd78e994..b2ca4b8bb 100644 --- a/src/main/java/org/jfree/chart/labels/BubbleXYItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/BubbleXYItemLabelGenerator.java @@ -45,6 +45,12 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; @@ -161,7 +167,7 @@ public DateFormat getZDateFormat() { * @return The item label (possibly {@code null}). */ @Override - public String generateLabel(XYDataset dataset, int series, int item) { + public String generateLabel(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { return generateLabelString(dataset, series, item); } @@ -175,11 +181,14 @@ public String generateLabel(XYDataset dataset, int series, int item) { * @return The label (possibly {@code null}). */ @Override - public String generateLabelString(XYDataset dataset, int series, int item) { + public String generateLabelString(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { String result; Object[] items; if (dataset instanceof XYZDataset) { - items = createItemArray((XYZDataset) dataset, series, item); + XYZDataset xyzDataset = (XYZDataset) dataset; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("xyzDataset.getSeries(series)") int item1 = item; + items = createItemArray(xyzDataset, series, item1); } else { items = createItemArray(dataset, series, item); @@ -198,8 +207,8 @@ public String generateLabelString(XYDataset dataset, int series, int item) { * * @return The items (never {@code null}). */ - protected Object[] createItemArray(XYZDataset dataset, - int series, int item) { + protected Object @MinLen(4) [] createItemArray(XYZDataset dataset, + @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { Object[] result = new Object[4]; result[0] = dataset.getSeriesKey(series).toString(); diff --git a/src/main/java/org/jfree/chart/labels/CategoryItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/CategoryItemLabelGenerator.java index 8e4a2b178..dbbc8d49c 100644 --- a/src/main/java/org/jfree/chart/labels/CategoryItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/CategoryItemLabelGenerator.java @@ -59,6 +59,8 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.category.CategoryDataset; /** @@ -80,7 +82,7 @@ public interface CategoryItemLabelGenerator { * * @return The label. */ - public String generateRowLabel(CategoryDataset dataset, int row); + public String generateRowLabel(CategoryDataset dataset, @NonNegative int row); /** * Generates a label for the specified row. @@ -90,7 +92,7 @@ public interface CategoryItemLabelGenerator { * * @return The label. */ - public String generateColumnLabel(CategoryDataset dataset, int column); + public String generateColumnLabel(CategoryDataset dataset, @NonNegative int column); /** * Generates a label for the specified item. The label is typically a @@ -102,6 +104,6 @@ public interface CategoryItemLabelGenerator { * * @return The label (possibly {@code null}). */ - public String generateLabel(CategoryDataset dataset, int row, int column); + public String generateLabel(CategoryDataset dataset, @NonNegative int row, @NonNegative int column); } diff --git a/src/main/java/org/jfree/chart/labels/CategorySeriesLabelGenerator.java b/src/main/java/org/jfree/chart/labels/CategorySeriesLabelGenerator.java index 56e958813..15b3429ee 100644 --- a/src/main/java/org/jfree/chart/labels/CategorySeriesLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/CategorySeriesLabelGenerator.java @@ -42,6 +42,8 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.category.CategoryDataset; /** @@ -62,6 +64,6 @@ public interface CategorySeriesLabelGenerator { * * @return A series label. */ - public String generateLabel(CategoryDataset dataset, int series); + public String generateLabel(CategoryDataset dataset, @NonNegative int series); } diff --git a/src/main/java/org/jfree/chart/labels/CategoryToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/CategoryToolTipGenerator.java index dd92e7fdf..7ab4cf2c9 100644 --- a/src/main/java/org/jfree/chart/labels/CategoryToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/CategoryToolTipGenerator.java @@ -41,6 +41,8 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.category.CategoryDataset; /** @@ -66,6 +68,6 @@ public interface CategoryToolTipGenerator { * * @return The tooltip text (possibly {@code null}). */ - public String generateToolTip(CategoryDataset dataset, int row, int column); + public String generateToolTip(CategoryDataset dataset, @NonNegative int row, @NonNegative int column); } diff --git a/src/main/java/org/jfree/chart/labels/CustomXYToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/CustomXYToolTipGenerator.java index 36794e636..1b64b8ee6 100644 --- a/src/main/java/org/jfree/chart/labels/CustomXYToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/CustomXYToolTipGenerator.java @@ -46,6 +46,8 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.List; import org.jfree.chart.util.PublicCloneable; @@ -77,7 +79,7 @@ public CustomXYToolTipGenerator() { * * @return The list count. */ - public int getListCount() { + public @NonNegative int getListCount() { return this.toolTipSeries.size(); } @@ -88,7 +90,7 @@ public int getListCount() { * * @return The tooltip count. */ - public int getToolTipCount(int list) { + public @NonNegative int getToolTipCount(@NonNegative int list) { int result = 0; List tooltips = (List) this.toolTipSeries.get(list); @@ -106,7 +108,7 @@ public int getToolTipCount(int list) { * * @return The tool tip text. */ - public String getToolTipText(int series, int item) { + public String getToolTipText(@NonNegative int series, @NonNegative int item) { String result = null; @@ -141,7 +143,7 @@ public void addToolTipSeries(List toolTips) { * @return The tooltip text. */ @Override - public String generateToolTip(XYDataset data, int series, int item) { + public String generateToolTip(XYDataset data, @NonNegative int series, @NonNegative int item) { return getToolTipText(series, item); } diff --git a/src/main/java/org/jfree/chart/labels/HighLowItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/HighLowItemLabelGenerator.java index 00bb845dd..82eb7de10 100644 --- a/src/main/java/org/jfree/chart/labels/HighLowItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/HighLowItemLabelGenerator.java @@ -52,6 +52,10 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; @@ -118,17 +122,19 @@ public HighLowItemLabelGenerator(DateFormat dateFormatter, * @return The tooltip text. */ @Override - public String generateToolTip(XYDataset dataset, int series, int item) { + public String generateToolTip(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { if (!(dataset instanceof OHLCDataset)) { return null; } StringBuilder sb = new StringBuilder(); OHLCDataset d = (OHLCDataset) dataset; - Number high = d.getHigh(series, item); - Number low = d.getLow(series, item); - Number open = d.getOpen(series, item); - Number close = d.getClose(series, item); - Number x = d.getX(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("d.getSeries(series)") int itemD = item; + Number high = d.getHigh(series, itemD); + Number low = d.getLow(series, itemD); + Number open = d.getOpen(series, itemD); + Number close = d.getClose(series, itemD); + Number x = d.getX(series, itemD); sb.append(d.getSeriesKey(series).toString()); if (x != null) { Date date = new Date(x.longValue()); @@ -164,7 +170,7 @@ public String generateToolTip(XYDataset dataset, int series, int item) { * @return The label (possibly {@code null}). */ @Override - public String generateLabel(XYDataset dataset, int series, int category) { + public String generateLabel(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int category) { return null; //TODO: implement this method properly } diff --git a/src/main/java/org/jfree/chart/labels/IntervalCategoryItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/IntervalCategoryItemLabelGenerator.java index a0d081fcb..4a67b0ab3 100644 --- a/src/main/java/org/jfree/chart/labels/IntervalCategoryItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/IntervalCategoryItemLabelGenerator.java @@ -40,6 +40,9 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; @@ -106,8 +109,8 @@ public IntervalCategoryItemLabelGenerator(String labelFormat, * @return The items (never {@code null}). */ @Override - protected Object[] createItemArray(CategoryDataset dataset, - int row, int column) { + protected Object @MinLen(5) [] createItemArray(CategoryDataset dataset, + @NonNegative int row, @NonNegative int column) { Object[] result = new Object[5]; result[0] = dataset.getRowKey(row).toString(); result[1] = dataset.getColumnKey(column).toString(); diff --git a/src/main/java/org/jfree/chart/labels/IntervalCategoryToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/IntervalCategoryToolTipGenerator.java index da51a5e41..d89edcd29 100644 --- a/src/main/java/org/jfree/chart/labels/IntervalCategoryToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/IntervalCategoryToolTipGenerator.java @@ -41,6 +41,9 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.text.DateFormat; import java.text.NumberFormat; @@ -103,8 +106,8 @@ public IntervalCategoryToolTipGenerator(String labelFormat, * @return The items (never {@code null}). */ @Override - protected Object[] createItemArray(CategoryDataset dataset, - int row, int column) { + protected Object @MinLen(5) [] createItemArray(CategoryDataset dataset, + @NonNegative int row, @NonNegative int column) { Object[] result = new Object[5]; result[0] = dataset.getRowKey(row).toString(); result[1] = dataset.getColumnKey(column).toString(); diff --git a/src/main/java/org/jfree/chart/labels/IntervalXYItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/IntervalXYItemLabelGenerator.java index 7d39a2293..f47ff5cc1 100644 --- a/src/main/java/org/jfree/chart/labels/IntervalXYItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/IntervalXYItemLabelGenerator.java @@ -40,6 +40,12 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; @@ -149,10 +155,12 @@ public IntervalXYItemLabelGenerator(String formatString, * {@code String} objects (never {@code null}). */ @Override - protected Object[] createItemArray(XYDataset dataset, int series, - int item) { + protected Object @MinLen(7) [] createItemArray(XYDataset dataset, @NonNegative int series, + @IndexFor("#1.getSeries(#2)") int item) { IntervalXYDataset intervalDataset = null; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("intervalDataset.getSeries(series)") int itemIntervalDataset = item; if (dataset instanceof IntervalXYDataset) { intervalDataset = (IntervalXYDataset) dataset; } @@ -166,10 +174,10 @@ protected Object[] createItemArray(XYDataset dataset, int series, double ys = y; double ye = y; if (intervalDataset != null) { - xs = intervalDataset.getStartXValue(series, item); - xe = intervalDataset.getEndXValue(series, item); - ys = intervalDataset.getStartYValue(series, item); - ye = intervalDataset.getEndYValue(series, item); + xs = intervalDataset.getStartXValue(series, itemIntervalDataset); + xe = intervalDataset.getEndXValue(series, itemIntervalDataset); + ys = intervalDataset.getStartYValue(series, itemIntervalDataset); + ye = intervalDataset.getEndYValue(series, itemIntervalDataset); } DateFormat xdf = getXDateFormat(); @@ -199,7 +207,7 @@ protected Object[] createItemArray(XYDataset dataset, int series, } } if (Double.isNaN(ys) && intervalDataset != null - && intervalDataset.getStartY(series, item) == null) { + && intervalDataset.getStartY(series, itemIntervalDataset) == null) { result[5] = getNullYString(); } else { @@ -211,7 +219,7 @@ protected Object[] createItemArray(XYDataset dataset, int series, } } if (Double.isNaN(ye) && intervalDataset != null - && intervalDataset.getEndY(series, item) == null) { + && intervalDataset.getEndY(series, itemIntervalDataset) == null) { result[6] = getNullYString(); } else { @@ -235,7 +243,7 @@ protected Object[] createItemArray(XYDataset dataset, int series, * @return The label text (possibly {@code null}). */ @Override - public String generateLabel(XYDataset dataset, int series, int item) { + public String generateLabel(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { return generateLabelString(dataset, series, item); } diff --git a/src/main/java/org/jfree/chart/labels/IntervalXYToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/IntervalXYToolTipGenerator.java index a3c09ba75..01be4eef4 100644 --- a/src/main/java/org/jfree/chart/labels/IntervalXYToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/IntervalXYToolTipGenerator.java @@ -40,6 +40,11 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.MinLen; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; @@ -146,9 +151,11 @@ public IntervalXYToolTipGenerator(String formatString, * {@code String} objects (never {@code null}). */ @Override - protected Object[] createItemArray(XYDataset dataset, int series, - int item) { + protected Object@MinLen(7) [] createItemArray(XYDataset dataset, @NonNegative int series, + @IndexFor("#1.getSeries(#2)") int item) { IntervalXYDataset intervalDataset = null; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("intervalDataset.getSeries(series)") int itemIntervalDataset = item; if (dataset instanceof IntervalXYDataset) { intervalDataset = (IntervalXYDataset) dataset; } @@ -162,10 +169,10 @@ protected Object[] createItemArray(XYDataset dataset, int series, double ys = y; double ye = y; if (intervalDataset != null) { - xs = intervalDataset.getStartXValue(series, item); - xe = intervalDataset.getEndXValue(series, item); - ys = intervalDataset.getStartYValue(series, item); - ye = intervalDataset.getEndYValue(series, item); + xs = intervalDataset.getStartXValue(series, itemIntervalDataset); + xe = intervalDataset.getEndXValue(series, itemIntervalDataset); + ys = intervalDataset.getStartYValue(series, itemIntervalDataset); + ye = intervalDataset.getEndYValue(series, itemIntervalDataset); } DateFormat xdf = getXDateFormat(); @@ -193,7 +200,7 @@ protected Object[] createItemArray(XYDataset dataset, int series, } } if (Double.isNaN(ys) && intervalDataset != null - && intervalDataset.getStartY(series, item) == null) { + && intervalDataset.getStartY(series, itemIntervalDataset) == null) { result[5] = getNullYString(); } else { if (ydf != null) { @@ -204,7 +211,7 @@ protected Object[] createItemArray(XYDataset dataset, int series, } } if (Double.isNaN(ye) && intervalDataset != null - && intervalDataset.getEndY(series, item) == null) { + && intervalDataset.getEndY(series, itemIntervalDataset) == null) { result[6] = getNullYString(); } else { if (ydf != null) { @@ -227,7 +234,7 @@ protected Object[] createItemArray(XYDataset dataset, int series, * @return The tool tip text (possibly {@code null}). */ @Override - public String generateToolTip(XYDataset dataset, int series, int item) { + public String generateToolTip(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { return generateLabelString(dataset, series, item); } diff --git a/src/main/java/org/jfree/chart/labels/MultipleXYSeriesLabelGenerator.java b/src/main/java/org/jfree/chart/labels/MultipleXYSeriesLabelGenerator.java index 8309feb23..b99ed519b 100644 --- a/src/main/java/org/jfree/chart/labels/MultipleXYSeriesLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/MultipleXYSeriesLabelGenerator.java @@ -44,6 +44,12 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.MessageFormat; import java.util.HashMap; @@ -104,7 +110,7 @@ public MultipleXYSeriesLabelGenerator(String format) { * @param series the series index. * @param label the label. */ - public void addSeriesLabel(int series, String label) { + public void addSeriesLabel(@NonNegative int series, String label) { Integer key = new Integer(series); List labelList = (List) this.seriesLabelLists.get(key); if (labelList == null) { @@ -119,7 +125,7 @@ public void addSeriesLabel(int series, String label) { * * @param series the series index. */ - public void clearSeriesLabels(int series) { + public void clearSeriesLabels(@NonNegative int series) { Integer key = new Integer(series); this.seriesLabelLists.put(key, null); } @@ -134,7 +140,7 @@ public void clearSeriesLabels(int series) { * @return A series label. */ @Override - public String generateLabel(XYDataset dataset, int series) { + public String generateLabel(XYDataset dataset, @NonNegative int series) { Args.nullNotPermitted(dataset, "dataset"); StringBuilder label = new StringBuilder(); label.append(MessageFormat.format(this.formatPattern, @@ -162,7 +168,7 @@ public String generateLabel(XYDataset dataset, int series) { * * @return The items (never {@code null}). */ - protected Object[] createItemArray(XYDataset dataset, int series) { + protected Object @MinLen(1) [] createItemArray(XYDataset dataset, @NonNegative int series) { Object[] result = new Object[1]; result[0] = dataset.getSeriesKey(series).toString(); return result; diff --git a/src/main/java/org/jfree/chart/labels/StandardCategoryItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/StandardCategoryItemLabelGenerator.java index b7da96d12..c8f1ae9cf 100644 --- a/src/main/java/org/jfree/chart/labels/StandardCategoryItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/StandardCategoryItemLabelGenerator.java @@ -43,6 +43,8 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; @@ -124,7 +126,7 @@ public StandardCategoryItemLabelGenerator(String labelFormat, * @return The label (possibly {@code null}). */ @Override - public String generateLabel(CategoryDataset dataset, int row, int column) { + public String generateLabel(CategoryDataset dataset, @NonNegative int row, @NonNegative int column) { return generateLabelString(dataset, row, column); } diff --git a/src/main/java/org/jfree/chart/labels/StandardCategorySeriesLabelGenerator.java b/src/main/java/org/jfree/chart/labels/StandardCategorySeriesLabelGenerator.java index 1a560129f..2db497224 100644 --- a/src/main/java/org/jfree/chart/labels/StandardCategorySeriesLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/StandardCategorySeriesLabelGenerator.java @@ -44,6 +44,12 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.MessageFormat; @@ -95,7 +101,7 @@ public StandardCategorySeriesLabelGenerator(String format) { * @return A series label. */ @Override - public String generateLabel(CategoryDataset dataset, int series) { + public String generateLabel(CategoryDataset dataset, @NonNegative int series) { Args.nullNotPermitted(dataset, "dataset"); String label = MessageFormat.format(this.formatPattern, createItemArray(dataset, series)); @@ -111,7 +117,7 @@ public String generateLabel(CategoryDataset dataset, int series) { * * @return The items (never {@code null}). */ - protected Object[] createItemArray(CategoryDataset dataset, int series) { + protected Object @MinLen(1) [] createItemArray(CategoryDataset dataset, @NonNegative int series) { Object[] result = new Object[1]; result[0] = dataset.getRowKey(series).toString(); return result; diff --git a/src/main/java/org/jfree/chart/labels/StandardCategoryToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/StandardCategoryToolTipGenerator.java index af6901b2e..41c36c1ca 100644 --- a/src/main/java/org/jfree/chart/labels/StandardCategoryToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/StandardCategoryToolTipGenerator.java @@ -42,6 +42,8 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; @@ -107,7 +109,7 @@ public StandardCategoryToolTipGenerator(String labelFormat, */ @Override public String generateToolTip(CategoryDataset dataset, - int row, int column) { + @NonNegative int row, @NonNegative int column) { return generateLabelString(dataset, row, column); } diff --git a/src/main/java/org/jfree/chart/labels/StandardXYItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/StandardXYItemLabelGenerator.java index 7830b10ee..d4d8f529d 100644 --- a/src/main/java/org/jfree/chart/labels/StandardXYItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/StandardXYItemLabelGenerator.java @@ -60,6 +60,10 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; @@ -180,7 +184,7 @@ public StandardXYItemLabelGenerator(String formatString, * @return The label text (possibly {@code null}). */ @Override - public String generateLabel(XYDataset dataset, int series, int item) { + public String generateLabel(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { return generateLabelString(dataset, series, item); } diff --git a/src/main/java/org/jfree/chart/labels/StandardXYSeriesLabelGenerator.java b/src/main/java/org/jfree/chart/labels/StandardXYSeriesLabelGenerator.java index baa55d202..044160369 100644 --- a/src/main/java/org/jfree/chart/labels/StandardXYSeriesLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/StandardXYSeriesLabelGenerator.java @@ -44,6 +44,12 @@ package org.jfree.chart.labels; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.MessageFormat; @@ -99,7 +105,7 @@ public StandardXYSeriesLabelGenerator(String format) { * @return A series label. */ @Override - public String generateLabel(XYDataset dataset, int series) { + public String generateLabel(XYDataset dataset, @NonNegative int series) { Args.nullNotPermitted(dataset, "dataset"); String label = MessageFormat.format( this.formatPattern, createItemArray(dataset, series) @@ -116,7 +122,7 @@ this.formatPattern, createItemArray(dataset, series) * * @return The items (never {@code null}). */ - protected Object[] createItemArray(XYDataset dataset, int series) { + protected Object @MinLen(1) [] createItemArray(XYDataset dataset, @NonNegative int series) { Object[] result = new Object[1]; result[0] = dataset.getSeriesKey(series).toString(); return result; diff --git a/src/main/java/org/jfree/chart/labels/StandardXYToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/StandardXYToolTipGenerator.java index 3fe021d3a..daa230526 100644 --- a/src/main/java/org/jfree/chart/labels/StandardXYToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/StandardXYToolTipGenerator.java @@ -42,6 +42,10 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; @@ -162,7 +166,7 @@ public StandardXYToolTipGenerator(String formatString, * @return The tooltip text (possibly {@code null}). */ @Override - public String generateToolTip(XYDataset dataset, int series, int item) { + public String generateToolTip(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { return generateLabelString(dataset, series, item); } diff --git a/src/main/java/org/jfree/chart/labels/StandardXYZToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/StandardXYZToolTipGenerator.java index ec111e336..15c43287f 100644 --- a/src/main/java/org/jfree/chart/labels/StandardXYZToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/StandardXYZToolTipGenerator.java @@ -42,6 +42,11 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.MinLen; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; @@ -157,7 +162,7 @@ public DateFormat getZDateFormat() { * @return The tooltip text (possibly {@code null}). */ @Override - public String generateToolTip(XYZDataset dataset, int series, int item) { + public String generateToolTip(XYZDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { return generateLabelString(dataset, series, item); } @@ -171,8 +176,9 @@ public String generateToolTip(XYZDataset dataset, int series, int item) { * @return The label (possibly {@code null}). */ @Override - public String generateLabelString(XYDataset dataset, int series, int item) { + public String generateLabelString(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { String result; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 Object[] items = createItemArray((XYZDataset) dataset, series, item); result = MessageFormat.format(getFormatString(), items); return result; @@ -188,8 +194,8 @@ public String generateLabelString(XYDataset dataset, int series, int item) { * * @return The items (never {@code null}). */ - protected Object[] createItemArray(XYZDataset dataset, - int series, int item) { + protected Object @MinLen(4) [] createItemArray(XYZDataset dataset, + @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { Object[] result = new Object[4]; result[0] = dataset.getSeriesKey(series).toString(); diff --git a/src/main/java/org/jfree/chart/labels/SymbolicXYItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/SymbolicXYItemLabelGenerator.java index ed0c004db..8304f70c3 100644 --- a/src/main/java/org/jfree/chart/labels/SymbolicXYItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/SymbolicXYItemLabelGenerator.java @@ -49,6 +49,10 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import org.jfree.chart.util.PublicCloneable; @@ -78,7 +82,7 @@ public class SymbolicXYItemLabelGenerator implements XYItemLabelGenerator, * @return The tool tip text (possibly {@code null}). */ @Override - public String generateToolTip(XYDataset data, int series, int item) { + public String generateToolTip(XYDataset data, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { String xStr, yStr; if (data instanceof YisSymbolic) { @@ -115,7 +119,7 @@ else if (data instanceof TimeSeriesCollection) { * @return The label (possibly {@code null}). */ @Override - public String generateLabel(XYDataset dataset, int series, int category) { + public String generateLabel(XYDataset dataset, @NonNegative int series, @NonNegative int category) { return null; //TODO: implement this method properly } diff --git a/src/main/java/org/jfree/chart/labels/XYItemLabelGenerator.java b/src/main/java/org/jfree/chart/labels/XYItemLabelGenerator.java index a5481722c..8a93d252f 100644 --- a/src/main/java/org/jfree/chart/labels/XYItemLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/XYItemLabelGenerator.java @@ -48,6 +48,10 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.xy.XYDataset; /** @@ -66,6 +70,6 @@ public interface XYItemLabelGenerator { * * @return The label (possibly {@code null}). */ - public String generateLabel(XYDataset dataset, int series, int item); + public String generateLabel(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item); } diff --git a/src/main/java/org/jfree/chart/labels/XYSeriesLabelGenerator.java b/src/main/java/org/jfree/chart/labels/XYSeriesLabelGenerator.java index 43c7f626a..f32d8c8e7 100644 --- a/src/main/java/org/jfree/chart/labels/XYSeriesLabelGenerator.java +++ b/src/main/java/org/jfree/chart/labels/XYSeriesLabelGenerator.java @@ -42,6 +42,8 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.xy.XYDataset; /** @@ -63,6 +65,6 @@ public interface XYSeriesLabelGenerator { * * @return A series label. */ - public String generateLabel(XYDataset dataset, int series); + public String generateLabel(XYDataset dataset, @NonNegative int series); } diff --git a/src/main/java/org/jfree/chart/labels/XYToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/XYToolTipGenerator.java index 17c2322c4..32816139d 100644 --- a/src/main/java/org/jfree/chart/labels/XYToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/XYToolTipGenerator.java @@ -42,6 +42,10 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.xy.XYDataset; /** @@ -59,6 +63,6 @@ public interface XYToolTipGenerator { * * @return The tooltip text (possibly {@code null}). */ - public String generateToolTip(XYDataset dataset, int series, int item); + public String generateToolTip(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item); } diff --git a/src/main/java/org/jfree/chart/labels/XYZToolTipGenerator.java b/src/main/java/org/jfree/chart/labels/XYZToolTipGenerator.java index 5f22f35b5..8a13907d9 100644 --- a/src/main/java/org/jfree/chart/labels/XYZToolTipGenerator.java +++ b/src/main/java/org/jfree/chart/labels/XYZToolTipGenerator.java @@ -42,6 +42,10 @@ package org.jfree.chart.labels; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.xy.XYZDataset; /** @@ -59,6 +63,6 @@ public interface XYZToolTipGenerator extends XYToolTipGenerator { * * @return The tooltip text (possibly {@code null}). */ - public String generateToolTip(XYZDataset dataset, int series, int item); + public String generateToolTip(XYZDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item); } diff --git a/src/main/java/org/jfree/chart/panel/AbstractOverlay.java b/src/main/java/org/jfree/chart/panel/AbstractOverlay.java index 07aba8729..87111c870 100644 --- a/src/main/java/org/jfree/chart/panel/AbstractOverlay.java +++ b/src/main/java/org/jfree/chart/panel/AbstractOverlay.java @@ -2,7 +2,7 @@ * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * - * (C) Copyright 2000-2016, by Object Refinery Limited and Contributors. + * (C) Copyright 2000-2018, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * @@ -27,16 +27,11 @@ * -------------------- * AbstractOverlay.java * -------------------- - * (C) Copyright 2009-2016, by Object Refinery Limited. + * (C) Copyright 2009-2018, by Object Refinery Limited. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -; * - * Changes: - * -------- - * 09-Apr-2009 : Version 1 (DG); - * 02-Jul-2013 : Use ParamChecks (DG); - * */ package org.jfree.chart.panel; diff --git a/src/main/java/org/jfree/chart/panel/CrosshairOverlay.java b/src/main/java/org/jfree/chart/panel/CrosshairOverlay.java index 738f8bff3..dea1edd3a 100644 --- a/src/main/java/org/jfree/chart/panel/CrosshairOverlay.java +++ b/src/main/java/org/jfree/chart/panel/CrosshairOverlay.java @@ -32,13 +32,6 @@ * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): John Matthews; * - * Changes: - * -------- - * 09-Apr-2009 : Version 1 (DG); - * 19-May-2009 : Fixed FindBugs warnings, patch by Michal Wozniak (DG); - * 02-Jul-2013 : Use ParamChecks (DG); - * 05-Mar-2016 : Fix label outline stroke (DG); - * */ package org.jfree.chart.panel; @@ -56,7 +49,6 @@ import java.beans.PropertyChangeListener; import java.io.Serializable; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; @@ -74,7 +66,9 @@ import org.jfree.chart.util.PublicCloneable; /** - * An overlay for a {@link ChartPanel} that draws crosshairs on a plot. + * An overlay for a {@link ChartPanel} that draws crosshairs on a chart. If + * you are using the JavaFX extensions for JFreeChart, then you should use + * the {@code CrosshairOverlayFX} class. * * @since 1.0.13 */ @@ -88,7 +82,7 @@ public class CrosshairOverlay extends AbstractOverlay implements Overlay, protected List<Crosshair> yCrosshairs; /** - * Default constructor. + * Creates a new overlay that initially contains no crosshairs. */ public CrosshairOverlay() { super(); @@ -97,7 +91,7 @@ public CrosshairOverlay() { } /** - * Adds a crosshair against the domain axis and sends an + * Adds a crosshair against the domain axis (x-axis) and sends an * {@link OverlayChangeEvent} to all registered listeners. * * @param crosshair the crosshair ({@code null} not permitted). @@ -217,7 +211,10 @@ public void propertyChange(PropertyChangeEvent e) { } /** - * Paints the crosshairs in the layer. + * Renders the crosshairs in the overlay on top of the chart that has just + * been rendered in the specified {@code chartPanel}. This method is + * called by the JFreeChart framework, you won't normally call it from + * user code. * * @param g2 the graphics target. * @param chartPanel the chart panel. diff --git a/src/main/java/org/jfree/chart/panel/Overlay.java b/src/main/java/org/jfree/chart/panel/Overlay.java index c4d4163f5..191e36192 100644 --- a/src/main/java/org/jfree/chart/panel/Overlay.java +++ b/src/main/java/org/jfree/chart/panel/Overlay.java @@ -2,7 +2,7 @@ * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * - * (C) Copyright 2000-2016, by Object Refinery Limited and Contributors. + * (C) Copyright 2000-2018, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * @@ -27,16 +27,13 @@ * ------------ * Overlay.java * ------------ - * (C) Copyright 2009, by Object Refinery Limited. + * (C) Copyright 2009-2018, by Object Refinery Limited. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -; * - * Changes: - * -------- - * 09-Apr-2009 : Version 1 (DG); - * */ + package org.jfree.chart.panel; import java.awt.Graphics2D; @@ -44,32 +41,48 @@ import org.jfree.chart.event.OverlayChangeListener; /** - * Defines the interface for an overlay that can be added to a - * {@link ChartPanel}. + * An {@code Overlay} is anything that can be drawn over top of a chart to add + * additional information to the chart. This interface defines the operations + * that must be supported for an overlay that can be added to a + * {@link ChartPanel} in Swing. + * <br><br> + * Note: if you are using JavaFX rather than Swing, then you need to look at + * the {@code OverlayFX} interface in the <b>JFreeChart-FX</b> project. * * @since 1.0.13 */ public interface Overlay { /** - * Paints the crosshairs in the layer. + * Paints the visual representation of the overlay. This method will be + * called by the {@link ChartPanel} after the underlying chart has been + * fully rendered. When implementing this method, the {@code chartPanel} + * argument can be used to get state information from the chart (you can, + * for example, extract the axis ranges for the chart). * - * @param g2 the graphics target. - * @param chartPanel the chart panel. + * @param g2 the graphics target (never {@code null}). + * @param chartPanel the chart panel (never {@code null}). */ public void paintOverlay(Graphics2D g2, ChartPanel chartPanel); /** - * Registers a change listener with the overlay. + * Registers a change listener with the overlay. Typically this method + * not be called by user code, it exists so that the {@link ChartPanel} + * can register and receive notification of changes to the overlay (such + * changes will trigger an automatic repaint of the chart). * - * @param listener the listener. + * @param listener the listener ({@code null} not permitted). + * + * @see #removeChangeListener(org.jfree.chart.event.OverlayChangeListener) */ public void addChangeListener(OverlayChangeListener listener); /** * Deregisters a listener from the overlay. * - * @param listener the listener. + * @param listener the listener ({@code null} not permitted). + * + * @see #addChangeListener(org.jfree.chart.event.OverlayChangeListener) */ public void removeChangeListener(OverlayChangeListener listener); diff --git a/src/main/java/org/jfree/chart/panel/package.html b/src/main/java/org/jfree/chart/panel/package.html index 550eadb30..1538e8aa9 100644 --- a/src/main/java/org/jfree/chart/panel/package.html +++ b/src/main/java/org/jfree/chart/panel/package.html @@ -1,6 +1,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <html> <body bgcolor="white"> -Classes related to the {@link org.jfree.chart.ChartPanel} class. +Classes that provide Swing-specific support related to the +{@link org.jfree.chart.ChartPanel} class. </body> </html> diff --git a/src/main/java/org/jfree/chart/plot/AbstractPieLabelDistributor.java b/src/main/java/org/jfree/chart/plot/AbstractPieLabelDistributor.java index 20bb0dd4d..67744ccd5 100644 --- a/src/main/java/org/jfree/chart/plot/AbstractPieLabelDistributor.java +++ b/src/main/java/org/jfree/chart/plot/AbstractPieLabelDistributor.java @@ -41,6 +41,8 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.List; import org.jfree.chart.util.Args; @@ -70,7 +72,7 @@ public AbstractPieLabelDistributor() { * * @return The label record. */ - public PieLabelRecord getPieLabelRecord(int index) { + public PieLabelRecord getPieLabelRecord(@NonNegative int index) { return (PieLabelRecord) this.labels.get(index); } @@ -89,7 +91,7 @@ public void addPieLabelRecord(PieLabelRecord record) { * * @return The item count. */ - public int getItemCount() { + public @NonNegative int getItemCount() { return this.labels.size(); } diff --git a/src/main/java/org/jfree/chart/plot/CategoryCrosshairState.java b/src/main/java/org/jfree/chart/plot/CategoryCrosshairState.java index 19358c2a8..51576b3ba 100644 --- a/src/main/java/org/jfree/chart/plot/CategoryCrosshairState.java +++ b/src/main/java/org/jfree/chart/plot/CategoryCrosshairState.java @@ -40,6 +40,8 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.*; + import java.awt.geom.Point2D; import org.jfree.chart.renderer.category.CategoryItemRenderer; @@ -122,7 +124,7 @@ public void setColumnKey(Comparable key) { * @param orientation the plot orientation. */ public void updateCrosshairPoint(Comparable rowKey, Comparable columnKey, - double value, int datasetIndex, double transX, double transY, + double value, @NonNegative int datasetIndex, double transX, double transY, PlotOrientation orientation) { Point2D anchor = getAnchor(); @@ -159,7 +161,7 @@ public void updateCrosshairPoint(Comparable rowKey, Comparable columnKey, * @param orientation the plot orientation. */ public void updateCrosshairX(Comparable rowKey, Comparable columnKey, - int datasetIndex, double transX, PlotOrientation orientation) { + @NonNegative int datasetIndex, double transX, PlotOrientation orientation) { Point2D anchor = getAnchor(); if (anchor != null) { diff --git a/src/main/java/org/jfree/chart/plot/CategoryPlot.java b/src/main/java/org/jfree/chart/plot/CategoryPlot.java index 5a215df0d..b84253c03 100644 --- a/src/main/java/org/jfree/chart/plot/CategoryPlot.java +++ b/src/main/java/org/jfree/chart/plot/CategoryPlot.java @@ -187,6 +187,9 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -327,10 +330,10 @@ public class CategoryPlot extends Plot implements ValueAxisPlot, Pannable, private RectangleInsets axisOffset; /** Storage for the domain axes. */ - private Map<Integer, CategoryAxis> domainAxes; + private Map<@NonNegative Integer, CategoryAxis> domainAxes; /** Storage for the domain axis locations. */ - private Map<Integer, AxisLocation> domainAxisLocations; + private Map<@NonNegative Integer, AxisLocation> domainAxisLocations; /** * A flag that controls whether or not the shared domain axis is drawn @@ -339,30 +342,30 @@ public class CategoryPlot extends Plot implements ValueAxisPlot, Pannable, private boolean drawSharedDomainAxis; /** Storage for the range axes. */ - private Map<Integer, ValueAxis> rangeAxes; + private Map<@NonNegative Integer, ValueAxis> rangeAxes; /** Storage for the range axis locations. */ - private Map<Integer, AxisLocation> rangeAxisLocations; + private Map<@NonNegative Integer, AxisLocation> rangeAxisLocations; /** Storage for the datasets. */ - private Map<Integer, CategoryDataset> datasets; + private Map<@NonNegative Integer, CategoryDataset> datasets; /** * Storage for keys that map each dataset to one or more domain axes. * Typically a dataset is rendered using the scale of a single axis, but * a dataset can contribute to the "auto-range" of any number of axes. */ - private TreeMap<Integer, List<Integer>> datasetToDomainAxesMap; + private TreeMap<@NonNegative Integer, List<Integer>> datasetToDomainAxesMap; /** * Storage for keys that map each dataset to one or more range axes. * Typically a dataset is rendered using the scale of a single axis, but * a dataset can contribute to the "auto-range" of any number of axes. */ - private TreeMap<Integer, List<Integer>> datasetToRangeAxesMap; + private TreeMap<@NonNegative Integer, List<Integer>> datasetToRangeAxesMap; /** Storage for the renderers. */ - private Map<Integer, CategoryItemRenderer> renderers; + private Map<@NonNegative Integer, CategoryItemRenderer> renderers; /** The dataset rendering order. */ private DatasetRenderingOrder renderingOrder @@ -516,16 +519,16 @@ public class CategoryPlot extends Plot implements ValueAxisPlot, Pannable, private boolean rangeCrosshairLockedOnData = true; /** A map containing lists of markers for the domain axes. */ - private Map foregroundDomainMarkers; + private Map<@NonNegative Integer, Collection> foregroundDomainMarkers; /** A map containing lists of markers for the domain axes. */ - private Map backgroundDomainMarkers; + private Map<@NonNegative Integer, Collection> backgroundDomainMarkers; /** A map containing lists of markers for the range axes. */ - private Map foregroundRangeMarkers; + private Map<@NonNegative Integer, Collection> foregroundRangeMarkers; /** A map containing lists of markers for the range axes. */ - private Map backgroundRangeMarkers; + private Map<@NonNegative Integer, Collection> backgroundRangeMarkers; /** * A (possibly empty) list of annotations for the plot. The list should @@ -591,17 +594,23 @@ public CategoryPlot(CategoryDataset dataset, CategoryAxis domainAxis, this.orientation = PlotOrientation.VERTICAL; // allocate storage for dataset, axes and renderers - this.domainAxes = new HashMap<Integer, CategoryAxis>(); - this.domainAxisLocations = new HashMap<Integer, AxisLocation>(); - this.rangeAxes = new HashMap<Integer, ValueAxis>(); - this.rangeAxisLocations = new HashMap<Integer, AxisLocation>(); + HashMap domainAxesTmp = new HashMap<@NonNegative Integer, CategoryAxis>(); + this.domainAxes = domainAxesTmp; + HashMap domainAxisLocationsTmp = new HashMap<@NonNegative Integer, AxisLocation>(); + this.domainAxisLocations = domainAxisLocationsTmp; + HashMap rangeAxesTmp = new HashMap<@NonNegative Integer, ValueAxis>(); + this.rangeAxes = rangeAxesTmp; + HashMap rangeAxisLocationsTmp = new HashMap<@NonNegative Integer, AxisLocation>(); + this.rangeAxisLocations = rangeAxisLocationsTmp; this.datasetToDomainAxesMap = new TreeMap(); this.datasetToRangeAxesMap = new TreeMap(); - this.renderers = new HashMap<Integer, CategoryItemRenderer>(); + HashMap renderersTmp = new HashMap<@NonNegative Integer, CategoryItemRenderer>(); + this.renderers = renderersTmp; - this.datasets = new HashMap<Integer, CategoryDataset>(); + HashMap datasetsTmp = new HashMap<@NonNegative Integer, CategoryDataset>(); + this.datasets = datasetsTmp; this.datasets.put(0, dataset); if (dataset != null) { dataset.addChangeListener(this); @@ -757,7 +766,7 @@ public CategoryAxis getDomainAxis() { * * @see #setDomainAxis(int, CategoryAxis) */ - public CategoryAxis getDomainAxis(int index) { + public CategoryAxis getDomainAxis(@NonNegative int index) { CategoryAxis result = (CategoryAxis) this.domainAxes.get(index); if (result == null) { Plot parent = getParent(); @@ -790,7 +799,7 @@ public void setDomainAxis(CategoryAxis axis) { * * @see #getDomainAxis(int) */ - public void setDomainAxis(int index, CategoryAxis axis) { + public void setDomainAxis(@NonNegative int index, CategoryAxis axis) { setDomainAxis(index, axis, true); } @@ -802,7 +811,7 @@ public void setDomainAxis(int index, CategoryAxis axis) { * @param axis the axis ({@code null} permitted). * @param notify notify listeners? */ - public void setDomainAxis(int index, CategoryAxis axis, boolean notify) { + public void setDomainAxis(@NonNegative int index, CategoryAxis axis, boolean notify) { CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index); if (existing != null) { existing.removeChangeListener(this); @@ -848,9 +857,9 @@ public void setDomainAxes(CategoryAxis[] axes) { * * @since 1.0.3 */ - public int getDomainAxisIndex(CategoryAxis axis) { + public @GTENegativeOne int getDomainAxisIndex(CategoryAxis axis) { Args.nullNotPermitted(axis, "axis"); - for (Entry<Integer, CategoryAxis> entry : this.domainAxes.entrySet()) { + for (Entry<@NonNegative Integer, CategoryAxis> entry : this.domainAxes.entrySet()) { if (entry.getValue() == axis) { return entry.getKey(); } @@ -878,7 +887,7 @@ public AxisLocation getDomainAxisLocation() { * * @see #setDomainAxisLocation(int, AxisLocation) */ - public AxisLocation getDomainAxisLocation(int index) { + public AxisLocation getDomainAxisLocation(@NonNegative int index) { AxisLocation result = this.domainAxisLocations.get(index); if (result == null) { result = AxisLocation.getOpposite(getDomainAxisLocation(0)); @@ -922,7 +931,7 @@ public void setDomainAxisLocation(AxisLocation location, boolean notify) { * @see #getDomainAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation) */ - public void setDomainAxisLocation(int index, AxisLocation location) { + public void setDomainAxisLocation(@NonNegative int index, AxisLocation location) { // delegate... setDomainAxisLocation(index, location, true); } @@ -940,7 +949,7 @@ public void setDomainAxisLocation(int index, AxisLocation location) { * @see #getDomainAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation, boolean) */ - public void setDomainAxisLocation(int index, AxisLocation location, + public void setDomainAxisLocation(@NonNegative int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException( @@ -969,7 +978,7 @@ public RectangleEdge getDomainAxisEdge() { * * @return The edge (never {@code null}). */ - public RectangleEdge getDomainAxisEdge(int index) { + public RectangleEdge getDomainAxisEdge(@NonNegative int index) { RectangleEdge result; AxisLocation location = getDomainAxisLocation(index); if (location != null) { @@ -985,7 +994,7 @@ public RectangleEdge getDomainAxisEdge(int index) { * * @return The axis count. */ - public int getDomainAxisCount() { + public @NonNegative int getDomainAxisCount() { return this.domainAxes.size(); } @@ -1032,7 +1041,7 @@ public ValueAxis getRangeAxis() { * * @return The axis ({@code null} possible). */ - public ValueAxis getRangeAxis(int index) { + public ValueAxis getRangeAxis(@NonNegative int index) { ValueAxis result = this.rangeAxes.get(index); if (result == null) { Plot parent = getParent(); @@ -1061,7 +1070,7 @@ public void setRangeAxis(ValueAxis axis) { * @param index the axis index. * @param axis the axis. */ - public void setRangeAxis(int index, ValueAxis axis) { + public void setRangeAxis(@NonNegative int index, ValueAxis axis) { setRangeAxis(index, axis, true); } @@ -1073,7 +1082,7 @@ public void setRangeAxis(int index, ValueAxis axis) { * @param axis the axis. * @param notify notify listeners? */ - public void setRangeAxis(int index, ValueAxis axis, boolean notify) { + public void setRangeAxis(@NonNegative int index, ValueAxis axis, boolean notify) { ValueAxis existing = this.rangeAxes.get(index); if (existing != null) { existing.removeChangeListener(this); @@ -1119,7 +1128,7 @@ public void setRangeAxes(ValueAxis[] axes) { * * @since 1.0.7 */ - public int getRangeAxisIndex(ValueAxis axis) { + public @GTENegativeOne int getRangeAxisIndex(ValueAxis axis) { Args.nullNotPermitted(axis, "axis"); int result = findRangeAxisIndex(axis); if (result < 0) { // try the parent plot @@ -1132,8 +1141,8 @@ public int getRangeAxisIndex(ValueAxis axis) { return result; } - private int findRangeAxisIndex(ValueAxis axis) { - for (Entry<Integer, ValueAxis> entry : this.rangeAxes.entrySet()) { + private @GTENegativeOne int findRangeAxisIndex(ValueAxis axis) { + for (Entry<@NonNegative Integer, ValueAxis> entry : this.rangeAxes.entrySet()) { if (entry.getValue() == axis) { return entry.getKey(); } @@ -1159,7 +1168,7 @@ public AxisLocation getRangeAxisLocation() { * * @see #setRangeAxisLocation(int, AxisLocation) */ - public AxisLocation getRangeAxisLocation(int index) { + public AxisLocation getRangeAxisLocation(@NonNegative int index) { AxisLocation result = this.rangeAxisLocations.get(index); if (result == null) { result = AxisLocation.getOpposite(getRangeAxisLocation(0)); @@ -1204,7 +1213,7 @@ public void setRangeAxisLocation(AxisLocation location, boolean notify) { * @see #getRangeAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation, boolean) */ - public void setRangeAxisLocation(int index, AxisLocation location) { + public void setRangeAxisLocation(@NonNegative int index, AxisLocation location) { setRangeAxisLocation(index, location, true); } @@ -1219,7 +1228,7 @@ public void setRangeAxisLocation(int index, AxisLocation location) { * @see #getRangeAxisLocation(int) * @see #setDomainAxisLocation(int, AxisLocation, boolean) */ - public void setRangeAxisLocation(int index, AxisLocation location, + public void setRangeAxisLocation(@NonNegative int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException( @@ -1247,7 +1256,7 @@ public RectangleEdge getRangeAxisEdge() { * * @return The edge. */ - public RectangleEdge getRangeAxisEdge(int index) { + public RectangleEdge getRangeAxisEdge(@NonNegative int index) { AxisLocation location = getRangeAxisLocation(index); return Plot.resolveRangeAxisLocation(location, this.orientation); } @@ -1257,7 +1266,7 @@ public RectangleEdge getRangeAxisEdge(int index) { * * @return The axis count. */ - public int getRangeAxisCount() { + public @NonNegative int getRangeAxisCount() { return this.rangeAxes.size(); } @@ -1307,7 +1316,7 @@ public CategoryDataset getDataset() { * * @see #setDataset(int, CategoryDataset) */ - public CategoryDataset getDataset(int index) { + public CategoryDataset getDataset(@NonNegative int index) { return this.datasets.get(index); } @@ -1335,7 +1344,7 @@ public void setDataset(CategoryDataset dataset) { * * @see #getDataset(int) */ - public void setDataset(int index, CategoryDataset dataset) { + public void setDataset(@NonNegative int index, CategoryDataset dataset) { CategoryDataset existing = (CategoryDataset) this.datasets.get(index); if (existing != null) { existing.removeChangeListener(this); @@ -1356,7 +1365,7 @@ public void setDataset(int index, CategoryDataset dataset) { * * @since 1.0.2 */ - public int getDatasetCount() { + public @NonNegative int getDatasetCount() { return this.datasets.size(); } @@ -1370,8 +1379,8 @@ public int getDatasetCount() { * * @since 1.0.11 */ - public int indexOf(CategoryDataset dataset) { - for (Entry<Integer, CategoryDataset> entry: this.datasets.entrySet()) { + public @GTENegativeOne int indexOf(CategoryDataset dataset) { + for (Entry<@NonNegative Integer, CategoryDataset> entry: this.datasets.entrySet()) { if (entry.getValue() == dataset) { return entry.getKey(); } @@ -1387,7 +1396,7 @@ public int indexOf(CategoryDataset dataset) { * * @see #getDomainAxisForDataset(int) */ - public void mapDatasetToDomainAxis(int index, int axisIndex) { + public void mapDatasetToDomainAxis(@NonNegative int index, int axisIndex) { List<Integer> axisIndices = new java.util.ArrayList<Integer>(1); axisIndices.add(axisIndex); mapDatasetToDomainAxes(index, axisIndices); @@ -1403,10 +1412,10 @@ public void mapDatasetToDomainAxis(int index, int axisIndex) { * * @since 1.0.12 */ - public void mapDatasetToDomainAxes(int index, List axisIndices) { + public void mapDatasetToDomainAxes(@NonNegative int index, List<Integer> axisIndices) { Args.requireNonNegative(index, "index"); checkAxisIndices(axisIndices); - this.datasetToDomainAxesMap.put(index, new ArrayList(axisIndices)); + this.datasetToDomainAxesMap.put(index, new ArrayList<Integer>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } @@ -1453,14 +1462,14 @@ private void checkAxisIndices(List indices) { * * @see #mapDatasetToDomainAxis(int, int) */ - public CategoryAxis getDomainAxisForDataset(int index) { + public CategoryAxis getDomainAxisForDataset(@NonNegative int index) { Args.requireNonNegative(index, "index"); CategoryAxis axis; List axisIndices = (List) this.datasetToDomainAxesMap.get( new Integer(index)); if (axisIndices != null) { - // the first axis in the list is used for data <--> Java2D - Integer axisIndex = (Integer) axisIndices.get(0); + @SuppressWarnings("index") // the first axis in the list is used for data <--> Java2D + @NonNegative Integer axisIndex = (Integer) axisIndices.get(0); axis = getDomainAxis(axisIndex.intValue()); } else { axis = getDomainAxis(0); @@ -1476,7 +1485,7 @@ public CategoryAxis getDomainAxisForDataset(int index) { * * @see #getRangeAxisForDataset(int) */ - public void mapDatasetToRangeAxis(int index, int axisIndex) { + public void mapDatasetToRangeAxis(@NonNegative int index, int axisIndex) { List axisIndices = new java.util.ArrayList(1); axisIndices.add(new Integer(axisIndex)); mapDatasetToRangeAxes(index, axisIndices); @@ -1492,10 +1501,10 @@ public void mapDatasetToRangeAxis(int index, int axisIndex) { * * @since 1.0.12 */ - public void mapDatasetToRangeAxes(int index, List axisIndices) { + public void mapDatasetToRangeAxes(@NonNegative int index, List<Integer> axisIndices) { Args.requireNonNegative(index, "index"); checkAxisIndices(axisIndices); - this.datasetToRangeAxesMap.put(index, new ArrayList(axisIndices)); + this.datasetToRangeAxesMap.put(index, new ArrayList<Integer>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } @@ -1510,14 +1519,14 @@ public void mapDatasetToRangeAxes(int index, List axisIndices) { * * @see #mapDatasetToRangeAxis(int, int) */ - public ValueAxis getRangeAxisForDataset(int index) { + public ValueAxis getRangeAxisForDataset(@NonNegative int index) { Args.requireNonNegative(index, "index"); ValueAxis axis; List axisIndices = (List) this.datasetToRangeAxesMap.get( new Integer(index)); if (axisIndices != null) { - // the first axis in the list is used for data <--> Java2D - Integer axisIndex = (Integer) axisIndices.get(0); + @SuppressWarnings("index") // the first axis in the list is used for data <--> Java2D + @NonNegative Integer axisIndex = (Integer) axisIndices.get(0); axis = getRangeAxis(axisIndex.intValue()); } else { axis = getRangeAxis(0); @@ -1532,7 +1541,7 @@ public ValueAxis getRangeAxisForDataset(int index) { * * @since 1.0.11 */ - public int getRendererCount() { + public @NonNegative int getRendererCount() { return this.renderers.size(); } @@ -1556,7 +1565,7 @@ public CategoryItemRenderer getRenderer() { * * @see #setRenderer(int, CategoryItemRenderer) */ - public CategoryItemRenderer getRenderer(int index) { + public CategoryItemRenderer getRenderer(@NonNegative int index) { CategoryItemRenderer renderer = this.renderers.get(index); if (renderer == null) { return this.renderers.get(0); @@ -1609,7 +1618,7 @@ public void setRenderer(CategoryItemRenderer renderer, boolean notify) { * @see #getRenderer(int) * @see #setRenderer(int, CategoryItemRenderer, boolean) */ - public void setRenderer(int index, CategoryItemRenderer renderer) { + public void setRenderer(@NonNegative int index, CategoryItemRenderer renderer) { setRenderer(index, renderer, true); } @@ -1625,7 +1634,7 @@ public void setRenderer(int index, CategoryItemRenderer renderer) { * * @see #getRenderer(int) */ - public void setRenderer(int index, CategoryItemRenderer renderer, + public void setRenderer(@NonNegative int index, CategoryItemRenderer renderer, boolean notify) { CategoryItemRenderer existing = this.renderers.get(index); if (existing != null) { @@ -1684,8 +1693,8 @@ public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) { * * @return The renderer index. */ - public int getIndexOf(CategoryItemRenderer renderer) { - for (Entry<Integer, CategoryItemRenderer> entry + public @GTENegativeOne int getIndexOf(CategoryItemRenderer renderer) { + for (Entry<@NonNegative Integer, CategoryItemRenderer> entry : this.renderers.entrySet()) { if (entry.getValue() == renderer) { return entry.getKey(); @@ -2184,7 +2193,8 @@ public LegendItemCollection getLegendItems() { // get the legend items for the datasets... for (CategoryDataset dataset: this.datasets.values()) { if (dataset != null) { - int datasetIndex = indexOf(dataset); + @SuppressWarnings("index") // rewrite guaranteed index: indexOf cannot return -1 here because we're looking up the indexOf an element we already know is in the dataset + @NonNegative int datasetIndex = indexOf(dataset); CategoryItemRenderer renderer = getRenderer(datasetIndex); if (renderer != null) { result.addAll(renderer.getLegendItems()); @@ -2356,7 +2366,7 @@ public void addDomainMarker(CategoryMarker marker, Layer layer) { * * @see #removeDomainMarker(int, Marker, Layer) */ - public void addDomainMarker(int index, CategoryMarker marker, Layer layer) { + public void addDomainMarker(@NonNegative int index, CategoryMarker marker, Layer layer) { addDomainMarker(index, marker, layer, true); } @@ -2376,7 +2386,7 @@ public void addDomainMarker(int index, CategoryMarker marker, Layer layer) { * * @see #removeDomainMarker(int, Marker, Layer, boolean) */ - public void addDomainMarker(int index, CategoryMarker marker, Layer layer, + public void addDomainMarker(@NonNegative int index, CategoryMarker marker, Layer layer, boolean notify) { Args.nullNotPermitted(marker, "marker"); Args.nullNotPermitted(layer, "layer"); @@ -2386,7 +2396,8 @@ public void addDomainMarker(int index, CategoryMarker marker, Layer layer, new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); - this.foregroundDomainMarkers.put(new Integer(index), markers); + @NonNegative Integer i = index; + this.foregroundDomainMarkers.put(i, markers); } markers.add(marker); } else if (layer == Layer.BACKGROUND) { @@ -2394,7 +2405,8 @@ public void addDomainMarker(int index, CategoryMarker marker, Layer layer, new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); - this.backgroundDomainMarkers.put(new Integer(index), markers); + @NonNegative Integer i = index; + this.backgroundDomainMarkers.put(i, markers); } markers.add(marker); } @@ -2412,19 +2424,21 @@ public void addDomainMarker(int index, CategoryMarker marker, Layer layer, */ public void clearDomainMarkers() { if (this.backgroundDomainMarkers != null) { - Set keys = this.backgroundDomainMarkers.keySet(); - Iterator iterator = keys.iterator(); + + Set<@NonNegative Integer> keys = this.backgroundDomainMarkers.keySet(); + Iterator<@NonNegative Integer> iterator = keys.iterator(); while (iterator.hasNext()) { - Integer key = (Integer) iterator.next(); + @NonNegative Integer key = (Integer) iterator.next(); clearDomainMarkers(key.intValue()); } this.backgroundDomainMarkers.clear(); } if (this.foregroundDomainMarkers != null) { - Set keys = this.foregroundDomainMarkers.keySet(); - Iterator iterator = keys.iterator(); + + Set<@NonNegative Integer> keys = this.foregroundDomainMarkers.keySet(); + Iterator<@NonNegative Integer> iterator = keys.iterator(); while (iterator.hasNext()) { - Integer key = (Integer) iterator.next(); + @NonNegative Integer key = (Integer) iterator.next(); clearDomainMarkers(key.intValue()); } this.foregroundDomainMarkers.clear(); @@ -2452,7 +2466,7 @@ public Collection getDomainMarkers(Layer layer) { * * @return A collection of markers (possibly {@code null}). */ - public Collection getDomainMarkers(int index, Layer layer) { + public Collection getDomainMarkers(@NonNegative int index, Layer layer) { Collection result = null; Integer key = new Integer(index); if (layer == Layer.FOREGROUND) { @@ -2474,7 +2488,7 @@ else if (layer == Layer.BACKGROUND) { * * @see #clearRangeMarkers(int) */ - public void clearDomainMarkers(int index) { + public void clearDomainMarkers(@NonNegative int index) { Integer key = new Integer(index); if (this.backgroundDomainMarkers != null) { Collection markers @@ -2547,7 +2561,7 @@ public boolean removeDomainMarker(Marker marker, Layer layer) { * * @since 1.0.7 */ - public boolean removeDomainMarker(int index, Marker marker, Layer layer) { + public boolean removeDomainMarker(@NonNegative int index, Marker marker, Layer layer) { return removeDomainMarker(index, marker, layer, true); } @@ -2565,7 +2579,7 @@ public boolean removeDomainMarker(int index, Marker marker, Layer layer) { * * @since 1.0.10 */ - public boolean removeDomainMarker(int index, Marker marker, Layer layer, + public boolean removeDomainMarker(@NonNegative int index, Marker marker, Layer layer, boolean notify) { ArrayList markers; if (layer == Layer.FOREGROUND) { @@ -2628,7 +2642,7 @@ public void addRangeMarker(Marker marker, Layer layer) { * * @see #removeRangeMarker(int, Marker, Layer) */ - public void addRangeMarker(int index, Marker marker, Layer layer) { + public void addRangeMarker(@NonNegative int index, Marker marker, Layer layer) { addRangeMarker(index, marker, layer, true); } @@ -2648,7 +2662,7 @@ public void addRangeMarker(int index, Marker marker, Layer layer) { * * @see #removeRangeMarker(int, Marker, Layer, boolean) */ - public void addRangeMarker(int index, Marker marker, Layer layer, + public void addRangeMarker(@NonNegative int index, Marker marker, Layer layer, boolean notify) { Collection markers; if (layer == Layer.FOREGROUND) { @@ -2656,7 +2670,8 @@ public void addRangeMarker(int index, Marker marker, Layer layer, new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); - this.foregroundRangeMarkers.put(new Integer(index), markers); + @NonNegative Integer i = index; + this.foregroundRangeMarkers.put(i, markers); } markers.add(marker); } else if (layer == Layer.BACKGROUND) { @@ -2664,7 +2679,8 @@ public void addRangeMarker(int index, Marker marker, Layer layer, new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); - this.backgroundRangeMarkers.put(new Integer(index), markers); + @NonNegative Integer i = index; + this.backgroundRangeMarkers.put(i, markers); } markers.add(marker); } @@ -2682,19 +2698,21 @@ public void addRangeMarker(int index, Marker marker, Layer layer, */ public void clearRangeMarkers() { if (this.backgroundRangeMarkers != null) { - Set keys = this.backgroundRangeMarkers.keySet(); - Iterator iterator = keys.iterator(); + + Set<@NonNegative Integer> keys = this.backgroundRangeMarkers.keySet(); + Iterator<@NonNegative Integer> iterator = keys.iterator(); while (iterator.hasNext()) { - Integer key = (Integer) iterator.next(); + @NonNegative Integer key = (Integer) iterator.next(); clearRangeMarkers(key.intValue()); } this.backgroundRangeMarkers.clear(); } if (this.foregroundRangeMarkers != null) { - Set keys = this.foregroundRangeMarkers.keySet(); - Iterator iterator = keys.iterator(); + + Set<@NonNegative Integer> keys = this.foregroundRangeMarkers.keySet(); + Iterator<@NonNegative Integer> iterator = keys.iterator(); while (iterator.hasNext()) { - Integer key = (Integer) iterator.next(); + @NonNegative Integer key = (Integer) iterator.next(); clearRangeMarkers(key.intValue()); } this.foregroundRangeMarkers.clear(); @@ -2724,7 +2742,7 @@ public Collection getRangeMarkers(Layer layer) { * * @return A collection of markers (possibly {@code null}). */ - public Collection getRangeMarkers(int index, Layer layer) { + public Collection getRangeMarkers(@NonNegative int index, Layer layer) { Collection result = null; Integer key = new Integer(index); if (layer == Layer.FOREGROUND) { @@ -2746,7 +2764,7 @@ else if (layer == Layer.BACKGROUND) { * * @see #clearDomainMarkers(int) */ - public void clearRangeMarkers(int index) { + public void clearRangeMarkers(@NonNegative int index) { Integer key = new Integer(index); if (this.backgroundRangeMarkers != null) { Collection markers @@ -2825,7 +2843,7 @@ public boolean removeRangeMarker(Marker marker, Layer layer) { * * @see #addRangeMarker(int, Marker, Layer) */ - public boolean removeRangeMarker(int index, Marker marker, Layer layer) { + public boolean removeRangeMarker(@NonNegative int index, Marker marker, Layer layer) { return removeRangeMarker(index, marker, layer, true); } @@ -2845,7 +2863,7 @@ public boolean removeRangeMarker(int index, Marker marker, Layer layer) { * * @see #addRangeMarker(int, Marker, Layer, boolean) */ - public boolean removeRangeMarker(int index, Marker marker, Layer layer, + public boolean removeRangeMarker(@NonNegative int index, Marker marker, Layer layer, boolean notify) { Args.nullNotPermitted(marker, "marker"); ArrayList markers; @@ -2996,7 +3014,7 @@ public int getCrosshairDatasetIndex() { * * @since 1.0.11 */ - public void setCrosshairDatasetIndex(int index) { + public void setCrosshairDatasetIndex(@NonNegative int index) { setCrosshairDatasetIndex(index, true); } @@ -3009,7 +3027,7 @@ public void setCrosshairDatasetIndex(int index) { * * @since 1.0.11 */ - public void setCrosshairDatasetIndex(int index, boolean notify) { + public void setCrosshairDatasetIndex(@NonNegative int index, boolean notify) { this.crosshairDatasetIndex = index; if (notify) { fireChangeEvent(); @@ -3383,7 +3401,8 @@ protected AxisSpace calculateDomainAxisSpace(Graphics2D g2, // reserve space for any domain axes... for (CategoryAxis xAxis : this.domainAxes.values()) { if (xAxis != null) { - int i = getDomainAxisIndex(xAxis); + @SuppressWarnings("index") // rewrite guaranteed index: xAxis is guaranteed to be in the set, so IndexOf returns NN + @NonNegative int i = getDomainAxisIndex(xAxis); RectangleEdge edge = getDomainAxisEdge(i); space = xAxis.reserveSpace(g2, this, plotArea, edge, space); } @@ -3427,7 +3446,8 @@ protected AxisSpace calculateRangeAxisSpace(Graphics2D g2, // reserve space for the range axes (if any)... for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { - int i = findRangeAxisIndex(yAxis); + @SuppressWarnings("index") // rewrite guaranteed index: yAxis is guaranteed to be in the set, so indexOf returns NN + @NonNegative int i = findRangeAxisIndex(yAxis); RectangleEdge edge = getRangeAxisEdge(i); space = yAxis.reserveSpace(g2, this, plotArea, edge, space); } @@ -3595,11 +3615,13 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, // draw the markers... for (CategoryItemRenderer renderer : this.renderers.values()) { - int i = getIndexOf(renderer); + @SuppressWarnings("index") // rewrite guaranteed index: renderer is one of the renderers of this object, so getIndexOf must return nonnegative + @NonNegative int i = getIndexOf(renderer); drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND); } for (CategoryItemRenderer renderer : this.renderers.values()) { - int i = getIndexOf(renderer); + @SuppressWarnings("index") // rewrite guaranteed index: renderer is one of the renderers of this object, so getIndexOf must return nonnegative + @NonNegative int i = getIndexOf(renderer); drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND); } @@ -3612,14 +3634,14 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, AlphaComposite.SRC_OVER, getForegroundAlpha())); DatasetRenderingOrder order = getDatasetRenderingOrder(); - List<Integer> datasetIndices = getDatasetIndices(order); + List<@NonNegative Integer> datasetIndices = getDatasetIndices(order); for (int i : datasetIndices) { foundData = render(g2, dataArea, i, state, crosshairState) || foundData; } // draw the foreground markers... - List<Integer> rendererIndices = getRendererIndices(order); + List<@NonNegative Integer> rendererIndices = getRendererIndices(order); for (int i : rendererIndices) { drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND); } @@ -3704,9 +3726,9 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, * * @return The list of indices. */ - private List<Integer> getDatasetIndices(DatasetRenderingOrder order) { - List<Integer> result = new ArrayList<Integer>(); - for (Map.Entry<Integer, CategoryDataset> entry : + private List<@NonNegative Integer> getDatasetIndices(DatasetRenderingOrder order) { + List<@NonNegative Integer> result = new ArrayList<@NonNegative Integer>(); + for (Map.Entry<@NonNegative Integer, CategoryDataset> entry : this.datasets.entrySet()) { if (entry.getValue() != null) { result.add(entry.getKey()); @@ -3727,9 +3749,9 @@ private List<Integer> getDatasetIndices(DatasetRenderingOrder order) { * * @return A list of indices. */ - private List<Integer> getRendererIndices(DatasetRenderingOrder order) { - List<Integer> result = new ArrayList<Integer>(); - for (Map.Entry<Integer, CategoryItemRenderer> entry: + private List<@NonNegative Integer> getRendererIndices(DatasetRenderingOrder order) { + List<@NonNegative Integer> result = new ArrayList<@NonNegative Integer>(); + for (Map.Entry<@NonNegative Integer, CategoryItemRenderer> entry: this.renderers.entrySet()) { if (entry.getValue() != null) { result.add(entry.getKey()); @@ -3777,7 +3799,8 @@ protected Map drawAxes(Graphics2D g2, Rectangle2D plotArea, // add domain axes to lists... for (CategoryAxis xAxis : this.domainAxes.values()) { if (xAxis != null) { - int index = getDomainAxisIndex(xAxis); + @SuppressWarnings("index") // rewrite guaranteed index: xAxis is guaranteed to have an index, so getDomainAxisIndex return NN + @NonNegative int index = getDomainAxisIndex(xAxis); axisCollection.add(xAxis, getDomainAxisEdge(index)); } } @@ -3785,7 +3808,8 @@ protected Map drawAxes(Graphics2D g2, Rectangle2D plotArea, // add range axes to lists... for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { - int index = findRangeAxisIndex(yAxis); + @SuppressWarnings("index") // rewrite guaranteed index: yAxis is guaranteed to have an index, so getRangeAxisIndex return NN + @NonNegative int index = findRangeAxisIndex(yAxis); axisCollection.add(yAxis, getRangeAxisEdge(index)); } } @@ -3867,7 +3891,7 @@ protected Map drawAxes(Graphics2D g2, Rectangle2D plotArea, * * @since 1.0.11 */ - public boolean render(Graphics2D g2, Rectangle2D dataArea, int index, + public boolean render(Graphics2D g2, Rectangle2D dataArea, @NonNegative int index, PlotRenderingInfo info, CategoryCrosshairState crosshairState) { boolean foundData = false; @@ -4065,7 +4089,7 @@ protected void drawAnnotations(Graphics2D g2, Rectangle2D dataArea) { * @see #drawRangeMarkers(Graphics2D, Rectangle2D, int, Layer) */ protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea, - int index, Layer layer) { + @NonNegative int index, Layer layer) { CategoryItemRenderer r = getRenderer(index); if (r == null) { @@ -4096,7 +4120,7 @@ protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea, * @see #drawDomainMarkers(Graphics2D, Rectangle2D, int, Layer) */ protected void drawRangeMarkers(Graphics2D g2, Rectangle2D dataArea, - int index, Layer layer) { + @NonNegative int index, Layer layer) { CategoryItemRenderer r = getRenderer(index); if (r == null) { @@ -4163,7 +4187,7 @@ else if (this.orientation == PlotOrientation.VERTICAL) { * @since 1.0.11 */ protected void drawDomainCrosshair(Graphics2D g2, Rectangle2D dataArea, - PlotOrientation orientation, int datasetIndex, + PlotOrientation orientation, @NonNegative int datasetIndex, Comparable rowKey, Comparable columnKey, Stroke stroke, Paint paint) { @@ -4981,12 +5005,18 @@ public Object clone() throws CloneNotSupportedException { } // AxisLocation is immutable, so we can just copy the maps - clone.domainAxisLocations = new HashMap<Integer, AxisLocation>( + + HashMap clonedomainAxisLocations = new HashMap<@NonNegative Integer, AxisLocation>( this.domainAxisLocations); - clone.rangeAxisLocations = new HashMap<Integer, AxisLocation>( + clone.domainAxisLocations = clonedomainAxisLocations; + + HashMap clonerangeAxisLocations = new HashMap<@NonNegative Integer, AxisLocation>( this.rangeAxisLocations); + clone.rangeAxisLocations = clonerangeAxisLocations; + - clone.datasets = new HashMap<Integer, CategoryDataset>(this.datasets); + HashMap clonedatasets = new HashMap<@NonNegative Integer, CategoryDataset>(this.datasets); + clone.datasets = clonedatasets; for (CategoryDataset dataset : clone.datasets.values()) { if (dataset != null) { dataset.addChangeListener(clone); diff --git a/src/main/java/org/jfree/chart/plot/CombinedDomainCategoryPlot.java b/src/main/java/org/jfree/chart/plot/CombinedDomainCategoryPlot.java index 25704cc9e..ca7c1feb3 100644 --- a/src/main/java/org/jfree/chart/plot/CombinedDomainCategoryPlot.java +++ b/src/main/java/org/jfree/chart/plot/CombinedDomainCategoryPlot.java @@ -463,7 +463,8 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, parentState.getSharedAxisStates().put(axis, axisState); // draw all the subplots - for (int i = 0; i < this.subplots.size(); i++) { + // this.subplotAreas and this.subplots always have the same length + for (int i = 0; i < this.subplotAreas.length; i++) { CategoryPlot plot = (CategoryPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = null; if (info != null) { diff --git a/src/main/java/org/jfree/chart/plot/CombinedDomainXYPlot.java b/src/main/java/org/jfree/chart/plot/CombinedDomainXYPlot.java index b53f551c6..b25125951 100644 --- a/src/main/java/org/jfree/chart/plot/CombinedDomainXYPlot.java +++ b/src/main/java/org/jfree/chart/plot/CombinedDomainXYPlot.java @@ -496,7 +496,8 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, parentState.getSharedAxisStates().put(axis, axisState); // draw all the subplots - for (int i = 0; i < this.subplots.size(); i++) { + // this.subplotAreas and this.subplots always have the same length + for (int i = 0; i < this.subplotAreas.length; i++) { XYPlot plot = (XYPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = null; if (info != null) { @@ -615,6 +616,7 @@ public void panRangeAxes(double panRange, PlotRenderingInfo info, if (!subplot.isRangePannable()) { return; } + @SuppressWarnings("index") // guaranteed index: getSubplotIndex is guaranteed to return non-negative because if the subplot isn't found, then subplot would have been null above and the function would have returned. PlotRenderingInfo subplotInfo = info.getSubplotInfo( info.getSubplotIndex(source)); if (subplotInfo == null) { diff --git a/src/main/java/org/jfree/chart/plot/CombinedRangeCategoryPlot.java b/src/main/java/org/jfree/chart/plot/CombinedRangeCategoryPlot.java index bacc32389..633d11111 100644 --- a/src/main/java/org/jfree/chart/plot/CombinedRangeCategoryPlot.java +++ b/src/main/java/org/jfree/chart/plot/CombinedRangeCategoryPlot.java @@ -365,7 +365,8 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, parentState.getSharedAxisStates().put(axis, state); // draw all the charts - for (int i = 0; i < this.subplots.size(); i++) { + // this.subplotArea and this.subplots always have the same length + for (int i = 0; i < this.subplotArea.length; i++) { CategoryPlot plot = (CategoryPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = null; if (info != null) { diff --git a/src/main/java/org/jfree/chart/plot/CombinedRangeXYPlot.java b/src/main/java/org/jfree/chart/plot/CombinedRangeXYPlot.java index a68ae118e..4296ff27f 100644 --- a/src/main/java/org/jfree/chart/plot/CombinedRangeXYPlot.java +++ b/src/main/java/org/jfree/chart/plot/CombinedRangeXYPlot.java @@ -435,7 +435,8 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, parentState.getSharedAxisStates().put(axis, axisState); // draw all the charts - for (int i = 0; i < this.subplots.size(); i++) { + // this.subplotAreas and this.subplots always have the same length + for (int i = 0; i < this.subplotAreas.length; i++) { XYPlot plot = (XYPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = null; if (info != null) { @@ -563,7 +564,8 @@ public void panDomainAxes(double panRange, PlotRenderingInfo info, if (!subplot.isDomainPannable()) { return; } - PlotRenderingInfo subplotInfo = info.getSubplotInfo( + @SuppressWarnings({"value", "index"}) // this function would already have returned if the subplot was not actually a subplot of this plot + PlotRenderingInfo subplotInfo = info.getSubplotInfo( info.getSubplotIndex(source)); if (subplotInfo == null) { return; diff --git a/src/main/java/org/jfree/chart/plot/CompassPlot.java b/src/main/java/org/jfree/chart/plot/CompassPlot.java index 3f88d9ec6..6e5a22c95 100644 --- a/src/main/java/org/jfree/chart/plot/CompassPlot.java +++ b/src/main/java/org/jfree/chart/plot/CompassPlot.java @@ -67,6 +67,12 @@ package org.jfree.chart.plot; +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.SameLen; +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.index.qual.LTLengthOf; +import org.checkerframework.checker.index.qual.IndexFor; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -162,10 +168,12 @@ public class CompassPlot extends Plot implements Cloneable, Serializable { private transient Rectangle2D rect1; /** An array of value datasets. */ - private ValueDataset[] datasets = new ValueDataset[1]; + @SuppressWarnings("index") // synced update: this.datasets and this.seriesNeedle are always updated in tandem + private ValueDataset @MinLen(1) @SameLen("this.seriesNeedle") [] datasets = new ValueDataset[1]; /** An array of needles. */ - private MeterNeedle[] seriesNeedle = new MeterNeedle[1]; + @SuppressWarnings("index") // synced update: this.datasets and this.seriesNeedle are always updated in tandem + private MeterNeedle @MinLen(1) @SameLen("this.datasets") [] seriesNeedle = new MeterNeedle[1]; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources @@ -369,7 +377,7 @@ public void setDrawBorder(boolean status) { * * @see #setSeriesOutlinePaint(int, Paint) */ - public void setSeriesPaint(int series, Paint paint) { + public void setSeriesPaint(@NonNegative int series, Paint paint) { // super.setSeriesPaint(series, paint); if ((series >= 0) && (series < this.seriesNeedle.length)) { this.seriesNeedle[series].setFillPaint(paint); @@ -384,7 +392,7 @@ public void setSeriesPaint(int series, Paint paint) { * * @see #setSeriesPaint(int, Paint) */ - public void setSeriesOutlinePaint(int series, Paint p) { + public void setSeriesOutlinePaint(@NonNegative int series, Paint p) { if ((series >= 0) && (series < this.seriesNeedle.length)) { this.seriesNeedle[series].setOutlinePaint(p); @@ -400,7 +408,7 @@ public void setSeriesOutlinePaint(int series, Paint p) { * * @see #setSeriesOutlinePaint(int, Paint) */ - public void setSeriesOutlineStroke(int series, Stroke stroke) { + public void setSeriesOutlineStroke(@NonNegative int series, Stroke stroke) { if ((series >= 0) && (series < this.seriesNeedle.length)) { this.seriesNeedle[series].setOutlineStroke(stroke); @@ -438,12 +446,14 @@ public void setSeriesNeedle(int type) { * * @see #setSeriesNeedle(int) */ - public void setSeriesNeedle(int index, int type) { + public void setSeriesNeedle(@NonNegative int index, int type) { switch (type) { case 0: setSeriesNeedle(index, new ArrowNeedle(true)); setSeriesPaint(index, Color.RED); - this.seriesNeedle[index].setHighlightPaint(Color.WHITE); + @SuppressWarnings("index") // documentation says this is okay + MeterNeedle seriesNeedOfIndex = this.seriesNeedle[index]; + seriesNeedOfIndex.setHighlightPaint(Color.WHITE); break; case 1: setSeriesNeedle(index, new LineNeedle()); @@ -491,7 +501,7 @@ public void setSeriesNeedle(int index, int type) { * @param index the series index. * @param needle the needle. */ - public void setSeriesNeedle(int index, MeterNeedle needle) { + public void setSeriesNeedle(@NonNegative int index, MeterNeedle needle) { if ((needle != null) && (index < this.seriesNeedle.length)) { this.seriesNeedle[index] = needle; } @@ -537,14 +547,24 @@ public void addDataset(ValueDataset dataset, MeterNeedle needle) { t[i] = this.datasets[i]; p[i] = this.seriesNeedle[i]; } - i = this.datasets.length; + + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/202: arrays have correlated but non-equal length + @LTLengthOf(value = {"t", "p", "p", "this.datasets"}, offset = {"0", "0", "-1", "-1"}) int newI = this.datasets.length; + i = newI; t[i] = dataset; p[i] = ((needle != null) ? needle : p[i - 1]); ValueDataset[] a = this.datasets; MeterNeedle[] b = this.seriesNeedle; - this.datasets = t; - this.seriesNeedle = p; + + @SuppressWarnings({"index", "value"}) // synced update: these two values are being changed together, but they go out of sync as the change occurs + ValueDataset @MinLen(1) @SameLen("this.seriesNeedle") [] t0 = t; + + @SuppressWarnings({"index", "value"}) // synced update: these two values are being changed together, but they go out of sync as the change occurs + MeterNeedle @MinLen(1) @SameLen("this.datasets") [] p0 = p; + + this.datasets = t0; + this.seriesNeedle = p0; for (--i; i >= 0; --i) { a[i] = null; @@ -801,6 +821,7 @@ public boolean equals(Object obj) { * exception, but subclasses (if any) might. */ @Override + @SuppressWarnings({"value", "index"}) // clone results in identical types public Object clone() throws CloneNotSupportedException { CompassPlot clone = (CompassPlot) super.clone(); diff --git a/src/main/java/org/jfree/chart/plot/Crosshair.java b/src/main/java/org/jfree/chart/plot/Crosshair.java index 9125bb159..25a18dd9e 100644 --- a/src/main/java/org/jfree/chart/plot/Crosshair.java +++ b/src/main/java/org/jfree/chart/plot/Crosshair.java @@ -2,7 +2,7 @@ * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * - * (C) Copyright 2000-2017, by Object Refinery Limited and Contributors. + * (C) Copyright 2000-2018, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * @@ -27,16 +27,11 @@ * -------------- * Crosshair.java * -------------- - * (C) Copyright 2009-2017, by Object Refinery Limited. + * (C) Copyright 2009-2018, by Object Refinery Limited. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -; * - * Changes: - * -------- - * 13-Feb-2009 : Version 1 (DG); - * 02-Jul-2013 : Use ParamChecks (DG); - * */ package org.jfree.chart.plot; @@ -55,6 +50,7 @@ import org.jfree.chart.HashUtils; import org.jfree.chart.labels.CrosshairLabelGenerator; import org.jfree.chart.labels.StandardCrosshairLabelGenerator; +import org.jfree.chart.panel.CrosshairOverlay; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; @@ -62,7 +58,17 @@ import org.jfree.chart.util.SerialUtils; /** - * A crosshair for display on a plot. + * A {@code Crosshair} represents a value on a chart and is usually displayed + * as a line perpendicular to the x or y-axis (and sometimes including a label + * that shows the crosshair value as text). Instances of this class are used + * to store the cross hair value plus the visual characteristics of the line + * that will be rendered once the instance is added to a + * {@link CrosshairOverlay} (or {@code CrosshairOverlaydFX} if you are using + * the JavaFX extensions for JFreeChart). + * <br><br> + * Crosshairs support a property change mechanism which is used by JFreeChart + * to automatically repaint the overlay whenever a crosshair attribute is + * updated. * * @since 1.0.13 */ diff --git a/src/main/java/org/jfree/chart/plot/CrosshairState.java b/src/main/java/org/jfree/chart/plot/CrosshairState.java index effa5437c..71e84ca49 100644 --- a/src/main/java/org/jfree/chart/plot/CrosshairState.java +++ b/src/main/java/org/jfree/chart/plot/CrosshairState.java @@ -53,6 +53,8 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.geom.Point2D; /** @@ -89,7 +91,7 @@ public class CrosshairState { * * @since 1.0.11 */ - private int datasetIndex; + private @NonNegative int datasetIndex; /** * The smallest distance (so far) between the anchor point and a data @@ -156,7 +158,7 @@ public void setCrosshairDistance(double distance) { * @param transY the y-value in Java2D space. * @param orientation the plot orientation ({@code null} not permitted). */ - public void updateCrosshairPoint(double x, double y, int datasetIndex, + public void updateCrosshairPoint(double x, double y, @NonNegative int datasetIndex, double transX, double transY, PlotOrientation orientation) { if (this.anchor != null) { @@ -198,7 +200,7 @@ public void updateCrosshairPoint(double x, double y, int datasetIndex, * * @since 1.0.20 */ - public void updateCrosshairX(double x, double transX, int datasetIndex) { + public void updateCrosshairX(double x, double transX, @NonNegative int datasetIndex) { if (this.anchor == null) { return; } @@ -223,7 +225,7 @@ public void updateCrosshairX(double x, double transX, int datasetIndex) { * * @since 1.0.20 */ - public void updateCrosshairY(double candidateY, double transY, int datasetIndex) { + public void updateCrosshairY(double candidateY, double transY, @NonNegative int datasetIndex) { if (this.anchor == null) { return; } @@ -377,7 +379,7 @@ public void setCrosshairY(double y) { * * @since 1.0.11 */ - public int getDatasetIndex() { + public @NonNegative int getDatasetIndex() { return this.datasetIndex; } @@ -390,7 +392,7 @@ public int getDatasetIndex() { * * @since 1.0.11 */ - public void setDatasetIndex(int index) { + public void setDatasetIndex(@NonNegative int index) { this.datasetIndex = index; } } diff --git a/src/main/java/org/jfree/chart/plot/DefaultDrawingSupplier.java b/src/main/java/org/jfree/chart/plot/DefaultDrawingSupplier.java index 009532943..c5ca29c1c 100644 --- a/src/main/java/org/jfree/chart/plot/DefaultDrawingSupplier.java +++ b/src/main/java/org/jfree/chart/plot/DefaultDrawingSupplier.java @@ -48,6 +48,9 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.index.qual.IndexFor; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Paint; @@ -108,37 +111,37 @@ public class DefaultDrawingSupplier implements DrawingSupplier, Cloneable, private transient Paint[] paintSequence; /** The current paint index. */ - private int paintIndex; + private @NonNegative int paintIndex; /** The outline paint sequence. */ private transient Paint[] outlinePaintSequence; /** The current outline paint index. */ - private int outlinePaintIndex; + private @NonNegative int outlinePaintIndex; /** The fill paint sequence. */ private transient Paint[] fillPaintSequence; /** The current fill paint index. */ - private int fillPaintIndex; + private @NonNegative int fillPaintIndex; /** The stroke sequence. */ private transient Stroke[] strokeSequence; /** The current stroke index. */ - private int strokeIndex; + private @NonNegative int strokeIndex; /** The outline stroke sequence. */ private transient Stroke[] outlineStrokeSequence; /** The current outline stroke index. */ - private int outlineStrokeIndex; + private @NonNegative int outlineStrokeIndex; /** The shape sequence. */ private transient Shape[] shapeSequence; /** The current shape index. */ - private int shapeIndex; + private @NonNegative int shapeIndex; /** * Creates a new supplier, with default sequences for fill paint, outline @@ -468,6 +471,7 @@ private void writeObject(ObjectOutputStream stream) throws IOException { * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ + @SuppressWarnings("index") // stream must have been serialized from an object of this class private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); diff --git a/src/main/java/org/jfree/chart/plot/FastScatterPlot.java b/src/main/java/org/jfree/chart/plot/FastScatterPlot.java index 682c8efff..e3e365c97 100644 --- a/src/main/java/org/jfree/chart/plot/FastScatterPlot.java +++ b/src/main/java/org/jfree/chart/plot/FastScatterPlot.java @@ -68,6 +68,10 @@ package org.jfree.chart.plot; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.checker.index.qual.SameLen; +import org.checkerframework.checker.index.qual.PolySameLen; + import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -122,7 +126,7 @@ public class FastScatterPlot extends Plot implements ValueAxisPlot, Pannable, public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray; /** The data. */ - private float[][] data; + private float @ArrayLen(2) [] @SameLen({"this.data[0]", "this.data[1]"}) [] data; /** The x data range. */ private Range xDataRange; @@ -195,7 +199,7 @@ public FastScatterPlot() { * @param domainAxis the domain (x) axis ({@code null} not permitted). * @param rangeAxis the range (y) axis ({@code null} not permitted). */ - public FastScatterPlot(float[][] data, + public FastScatterPlot(float @ArrayLen(2) [] @SameLen({"data[0]", "data[1]"}) [] data, ValueAxis domainAxis, ValueAxis rangeAxis) { super(); @@ -252,7 +256,7 @@ public float[][] getData() { * * @see #getData() */ - public void setData(float[][] data) { + public void setData(float @ArrayLen(2) [] @SameLen({"data[0]", "data[1]"}) [] data) { this.data = data; fireChangeEvent(); } @@ -701,7 +705,7 @@ else if (axis == this.rangeAxis) { * * @return The range. */ - private Range calculateXDataRange(float[][] data) { + private Range calculateXDataRange(float @ArrayLen(2) [] @SameLen({"data[0]", "data[1]"}) [] data) { Range result = null; @@ -733,7 +737,7 @@ private Range calculateXDataRange(float[][] data) { * * @return The range. */ - private Range calculateYDataRange(float[][] data) { + private Range calculateYDataRange(float @ArrayLen(2) [] @SameLen({"data[0]", "data[1]"}) [] data) { Range result = null; if (data != null) { @@ -1066,6 +1070,7 @@ public boolean equals(Object obj) { * not support cloning. */ @Override + @SuppressWarnings({"index", "value"}) // clone always results in the same types public Object clone() throws CloneNotSupportedException { FastScatterPlot clone = (FastScatterPlot) super.clone(); diff --git a/src/main/java/org/jfree/chart/plot/PiePlot.java b/src/main/java/org/jfree/chart/plot/PiePlot.java index 12e0bd5b5..f0a2a44d2 100644 --- a/src/main/java/org/jfree/chart/plot/PiePlot.java +++ b/src/main/java/org/jfree/chart/plot/PiePlot.java @@ -175,6 +175,8 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -302,7 +304,7 @@ public class PiePlot extends Plot implements Cloneable, Serializable { private PieDataset dataset; /** The pie index (used by the {@link MultiplePiePlot} class). */ - private int pieIndex; + private @NonNegative int pieIndex; /** * The amount of space left around the outside of the pie plot, expressed @@ -661,7 +663,7 @@ public void setDataset(PieDataset dataset) { * * @see #setPieIndex(int) */ - public int getPieIndex() { + public @NonNegative int getPieIndex() { return this.pieIndex; } @@ -673,7 +675,7 @@ public int getPieIndex() { * * @see #getPieIndex() */ - public void setPieIndex(int index) { + public void setPieIndex(@NonNegative int index) { this.pieIndex = index; } @@ -2538,7 +2540,7 @@ protected void drawPie(Graphics2D g2, Rectangle2D plotArea, * @param state state information for one chart. * @param currentPass the current pass index. */ - protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea, + protected void drawItem(Graphics2D g2, @NonNegative int section, Rectangle2D dataArea, PiePlotState state, int currentPass) { Number n = this.dataset.getValue(section); @@ -2965,7 +2967,9 @@ public LegendItemCollection getLegendItems() { false, // line not visible new Line2D.Float(), new BasicStroke(), Color.BLACK); item.setDataset(getDataset()); - item.setSeriesIndex(this.dataset.getIndex(key)); + @SuppressWarnings("index") // guaranteed index: key came from the list of keys for this dataset, so getIndex has to return non-negative + @NonNegative int newSeriesIndex = this.dataset.getIndex(key); + item.setSeriesIndex(newSeriesIndex); item.setSeriesKey(key); result.add(item); } diff --git a/src/main/java/org/jfree/chart/plot/Plot.java b/src/main/java/org/jfree/chart/plot/Plot.java index 072a27e49..9d6968315 100644 --- a/src/main/java/org/jfree/chart/plot/Plot.java +++ b/src/main/java/org/jfree/chart/plot/Plot.java @@ -135,6 +135,8 @@ package org.jfree.chart.plot; +import org.checkerframework.dataflow.qual.Pure; + import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -1486,6 +1488,7 @@ private void readObject(ObjectInputStream stream) * * @return The edge (never {@code null}). */ + @Pure public static RectangleEdge resolveDomainAxisLocation( AxisLocation location, PlotOrientation orientation) { @@ -1541,6 +1544,7 @@ else if (orientation == PlotOrientation.VERTICAL) { * * @return The edge (never {@code null}). */ + @Pure public static RectangleEdge resolveRangeAxisLocation( AxisLocation location, PlotOrientation orientation) { diff --git a/src/main/java/org/jfree/chart/plot/PlotRenderingInfo.java b/src/main/java/org/jfree/chart/plot/PlotRenderingInfo.java index 787fc3fbe..e6af0dfdd 100644 --- a/src/main/java/org/jfree/chart/plot/PlotRenderingInfo.java +++ b/src/main/java/org/jfree/chart/plot/PlotRenderingInfo.java @@ -46,6 +46,9 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; @@ -176,7 +179,7 @@ public void addSubplotInfo(PlotRenderingInfo info) { * * @see #addSubplotInfo(PlotRenderingInfo) */ - public PlotRenderingInfo getSubplotInfo(int index) { + public PlotRenderingInfo getSubplotInfo(@NonNegative int index) { return (PlotRenderingInfo) this.subplotInfo.get(index); } @@ -192,7 +195,7 @@ public PlotRenderingInfo getSubplotInfo(int index) { * * @return The subplot index (or -1 if no subplot contains {@code source}). */ - public int getSubplotIndex(Point2D source) { + public @GTENegativeOne int getSubplotIndex(Point2D source) { Args.nullNotPermitted(source, "source"); int subplotCount = getSubplotCount(); for (int i = 0; i < subplotCount; i++) { diff --git a/src/main/java/org/jfree/chart/plot/PolarPlot.java b/src/main/java/org/jfree/chart/plot/PolarPlot.java index 529d3bf2e..43940aefb 100644 --- a/src/main/java/org/jfree/chart/plot/PolarPlot.java +++ b/src/main/java/org/jfree/chart/plot/PolarPlot.java @@ -62,6 +62,10 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -255,7 +259,7 @@ public class PolarPlot extends Plot implements ValueAxisPlot, Zoomable, * entry for a dataset, it is assumed to map to the primary domain axis * (index = 0). */ - private Map datasetToAxesMap; + private Map<@NonNegative Integer, List<@NonNegative Integer>> datasetToAxesMap; /** * Default constructor. @@ -284,7 +288,7 @@ public PolarPlot(XYDataset dataset, ValueAxis radiusAxis, this.angleTickUnit = new NumberTickUnit(DEFAULT_ANGLE_TICK_UNIT_SIZE); this.axes = new ObjectList(); - this.datasetToAxesMap = new TreeMap(); + this.datasetToAxesMap = new TreeMap<>(); this.axes.set(0, radiusAxis); if (radiusAxis != null) { radiusAxis.setPlot(this); @@ -354,7 +358,7 @@ public ValueAxis getAxis() { * * @since 1.0.14 */ - public ValueAxis getAxis(int index) { + public ValueAxis getAxis(@NonNegative int index) { ValueAxis result = null; if (index < this.axes.size()) { result = (ValueAxis) this.axes.get(index); @@ -383,7 +387,7 @@ public void setAxis(ValueAxis axis) { * * @since 1.0.14 */ - public void setAxis(int index, ValueAxis axis) { + public void setAxis(@NonNegative int index, ValueAxis axis) { setAxis(index, axis, true); } @@ -399,7 +403,7 @@ public void setAxis(int index, ValueAxis axis) { * * @since 1.0.14 */ - public void setAxis(int index, ValueAxis axis, boolean notify) { + public void setAxis(@NonNegative int index, ValueAxis axis, boolean notify) { ValueAxis existing = getAxis(index); if (existing != null) { existing.removeChangeListener(this); @@ -441,7 +445,7 @@ public PolarAxisLocation getAxisLocation() { * * @since 1.0.14 */ - public PolarAxisLocation getAxisLocation(int index) { + public PolarAxisLocation getAxisLocation(@NonNegative int index) { PolarAxisLocation result = null; if (index < this.axisLocations.size()) { result = (PolarAxisLocation) this.axisLocations.get(index); @@ -491,7 +495,7 @@ public void setAxisLocation(PolarAxisLocation location, boolean notify) { * * @since 1.0.14 */ - public void setAxisLocation(int index, PolarAxisLocation location) { + public void setAxisLocation(@NonNegative int index, PolarAxisLocation location) { // delegate... setAxisLocation(index, location, true); } @@ -506,7 +510,7 @@ public void setAxisLocation(int index, PolarAxisLocation location) { * * @since 1.0.14 */ - public void setAxisLocation(int index, PolarAxisLocation location, + public void setAxisLocation(@NonNegative int index, PolarAxisLocation location, boolean notify) { Args.nullNotPermitted(location, "location"); this.axisLocations.set(index, location); @@ -548,7 +552,7 @@ public XYDataset getDataset() { * * @since 1.0.14 */ - public XYDataset getDataset(int index) { + public XYDataset getDataset(@NonNegative int index) { XYDataset result = null; if (index < this.datasets.size()) { result = (XYDataset) this.datasets.get(index); @@ -581,7 +585,7 @@ public void setDataset(XYDataset dataset) { * * @since 1.0.14 */ - public void setDataset(int index, XYDataset dataset) { + public void setDataset(@NonNegative int index, XYDataset dataset) { XYDataset existing = getDataset(index); if (existing != null) { existing.removeChangeListener(this); @@ -617,7 +621,7 @@ public int getDatasetCount() { * * @since 1.0.14 */ - public int indexOf(XYDataset dataset) { + public @GTENegativeOne int indexOf(XYDataset dataset) { int result = -1; for (int i = 0; i < this.datasets.size(); i++) { if (dataset == this.datasets.get(i)) { @@ -650,7 +654,7 @@ public PolarItemRenderer getRenderer() { * * @since 1.0.14 */ - public PolarItemRenderer getRenderer(int index) { + public PolarItemRenderer getRenderer(@NonNegative int index) { PolarItemRenderer result = null; if (index < this.renderers.size()) { result = (PolarItemRenderer) this.renderers.get(index); @@ -682,7 +686,7 @@ public void setRenderer(PolarItemRenderer renderer) { * * @since 1.0.14 */ - public void setRenderer(int index, PolarItemRenderer renderer) { + public void setRenderer(@NonNegative int index, PolarItemRenderer renderer) { setRenderer(index, renderer, true); } @@ -698,7 +702,7 @@ public void setRenderer(int index, PolarItemRenderer renderer) { * * @since 1.0.14 */ - public void setRenderer(int index, PolarItemRenderer renderer, + public void setRenderer(@NonNegative int index, PolarItemRenderer renderer, boolean notify) { PolarItemRenderer existing = getRenderer(index); if (existing != null) { @@ -1230,7 +1234,7 @@ else if (normalizedAngle > 270.0 && normalizedAngle < 360.0) { * * @since 1.0.14 */ - public void mapDatasetToAxis(int index, int axisIndex) { + public void mapDatasetToAxis(@NonNegative int index, int axisIndex) { List axisIndices = new java.util.ArrayList(1); axisIndices.add(new Integer(axisIndex)); mapDatasetToAxes(index, axisIndices); @@ -1246,13 +1250,12 @@ public void mapDatasetToAxis(int index, int axisIndex) { * * @since 1.0.14 */ - public void mapDatasetToAxes(int index, List axisIndices) { + public void mapDatasetToAxes(@NonNegative int index, List<@NonNegative Integer> axisIndices) { if (index < 0) { throw new IllegalArgumentException("Requires 'index' >= 0."); } checkAxisIndices(axisIndices); - Integer key = new Integer(index); - this.datasetToAxesMap.put(key, new ArrayList(axisIndices)); + this.datasetToAxesMap.put(index, new ArrayList<>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } @@ -1297,14 +1300,13 @@ private void checkAxisIndices(List indices) { * * @since 1.0.14 */ - public ValueAxis getAxisForDataset(int index) { + public ValueAxis getAxisForDataset(@NonNegative int index) { ValueAxis valueAxis; - List axisIndices = (List) this.datasetToAxesMap.get( - new Integer(index)); + List<@NonNegative Integer> axisIndices = this.datasetToAxesMap.get(index); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D - Integer axisIndex = (Integer) axisIndices.get(0); - valueAxis = getAxis(axisIndex.intValue()); + @NonNegative int axisIndex = axisIndices.get(0); + valueAxis = getAxis(axisIndex); } else { valueAxis = getAxis(0); diff --git a/src/main/java/org/jfree/chart/plot/RingPlot.java b/src/main/java/org/jfree/chart/plot/RingPlot.java index c6aceb55b..28d655f09 100644 --- a/src/main/java/org/jfree/chart/plot/RingPlot.java +++ b/src/main/java/org/jfree/chart/plot/RingPlot.java @@ -53,6 +53,8 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.*; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -499,7 +501,7 @@ public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea, * @param currentPass the current pass index. */ @Override - protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea, + protected void drawItem(Graphics2D g2, @NonNegative int section, Rectangle2D dataArea, PiePlotState state, int currentPass) { PieDataset dataset = getDataset(); diff --git a/src/main/java/org/jfree/chart/plot/SpiderWebPlot.java b/src/main/java/org/jfree/chart/plot/SpiderWebPlot.java index 2ce74b6c2..f3664fdf4 100644 --- a/src/main/java/org/jfree/chart/plot/SpiderWebPlot.java +++ b/src/main/java/org/jfree/chart/plot/SpiderWebPlot.java @@ -71,6 +71,8 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -677,7 +679,7 @@ public void setSeriesPaint(Paint paint) { * * @see #setSeriesPaint(int, Paint) */ - public Paint getSeriesPaint(int series) { + public Paint getSeriesPaint(@NonNegative int series) { // return the override, if there is one... if (this.seriesPaint != null) { @@ -710,7 +712,7 @@ public Paint getSeriesPaint(int series) { * * @see #getSeriesPaint(int) */ - public void setSeriesPaint(int series, Paint paint) { + public void setSeriesPaint(@NonNegative int series, Paint paint) { this.seriesPaintList.setPaint(series, paint); fireChangeEvent(); } @@ -770,7 +772,7 @@ public void setSeriesOutlinePaint(Paint paint) { * * @return The paint (never {@code null}). */ - public Paint getSeriesOutlinePaint(int series) { + public Paint getSeriesOutlinePaint(@NonNegative int series) { // return the override, if there is one... if (this.seriesOutlinePaint != null) { return this.seriesOutlinePaint; @@ -790,7 +792,7 @@ public Paint getSeriesOutlinePaint(int series) { * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). */ - public void setSeriesOutlinePaint(int series, Paint paint) { + public void setSeriesOutlinePaint(@NonNegative int series, Paint paint) { this.seriesOutlinePaintList.setPaint(series, paint); fireChangeEvent(); } @@ -846,7 +848,7 @@ public void setSeriesOutlineStroke(Stroke stroke) { * * @return The stroke (never {@code null}). */ - public Stroke getSeriesOutlineStroke(int series) { + public Stroke getSeriesOutlineStroke(@NonNegative int series) { // return the override, if there is one... if (this.seriesOutlineStroke != null) { @@ -869,7 +871,7 @@ public Stroke getSeriesOutlineStroke(int series) { * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). */ - public void setSeriesOutlineStroke(int series, Stroke stroke) { + public void setSeriesOutlineStroke(@NonNegative int series, Stroke stroke) { this.seriesOutlineStrokeList.setStroke(series, stroke); fireChangeEvent(); } @@ -1256,7 +1258,7 @@ protected void drawRadarPoly(Graphics2D g2, Rectangle2D plotArea, Point2D centre, PlotRenderingInfo info, - int series, int catCount, + @NonNegative int series, int catCount, double headH, double headW) { Polygon polygon = new Polygon(); @@ -1379,7 +1381,7 @@ protected void drawRadarPoly(Graphics2D g2, * * @see #getDataExtractOrder() */ - protected Number getPlotValue(int series, int cat) { + protected Number getPlotValue(@NonNegative int series, @NonNegative int cat) { Number value = null; if (this.dataExtractOrder == TableOrder.BY_ROW) { value = this.dataset.getValue(series, cat); @@ -1401,7 +1403,7 @@ else if (this.dataExtractOrder == TableOrder.BY_COLUMN) { * @param extent the extent of the arc. */ protected void drawLabel(Graphics2D g2, Rectangle2D plotArea, double value, - int cat, double startAngle, double extent) { + @NonNegative int cat, double startAngle, double extent) { FontRenderContext frc = g2.getFontRenderContext(); String label; diff --git a/src/main/java/org/jfree/chart/plot/ThermometerPlot.java b/src/main/java/org/jfree/chart/plot/ThermometerPlot.java index 5307d5507..818c691b8 100644 --- a/src/main/java/org/jfree/chart/plot/ThermometerPlot.java +++ b/src/main/java/org/jfree/chart/plot/ThermometerPlot.java @@ -93,6 +93,10 @@ package org.jfree.chart.plot; +import org.checkerframework.common.value.qual.ArrayLen; +import org.checkerframework.common.value.qual.IntVal; +import org.checkerframework.checker.index.qual.IndexFor; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -163,7 +167,7 @@ public class ThermometerPlot extends Plot implements ValueAxisPlot, public static final int UNITS_FAHRENHEIT = 1; /** A constant for unit type 'Celcius'. */ - public static final int UNITS_CELCIUS = 2; + public static final @IndexFor("UNITS") int UNITS_CELCIUS = 2; /** A constant for unit type 'Kelvin'. */ public static final int UNITS_KELVIN = 3; @@ -193,7 +197,7 @@ public class ThermometerPlot extends Plot implements ValueAxisPlot, protected static final int AXIS_GAP = 10; /** The unit strings. */ - protected static final String[] UNITS = {"", "\u00B0F", "\u00B0C", + protected static final String @ArrayLen(4) [] UNITS = {"", "\u00B0F", "\u00B0C", "\u00B0K"}; /** Index for low value in subrangeInfo matrix. */ @@ -280,7 +284,7 @@ public class ThermometerPlot extends Plot implements ValueAxisPlot, private transient Paint thermometerPaint = Color.BLACK; /** The display units */ - private int units = UNITS_CELCIUS; + private @IndexFor("UNITS") int units = UNITS_CELCIUS; /** The value label position. */ private int valueLocation = BULB; @@ -304,10 +308,10 @@ public class ThermometerPlot extends Plot implements ValueAxisPlot, private boolean showValueLines = false; /** The display sub-range. */ - private int subrange = -1; + private @IntVal({-1,0,1,2}) int subrange = -1; /** The start and end values for the subranges. */ - private double[][] subrangeInfo = { + private double @ArrayLen(3) [] @ArrayLen(4) [] subrangeInfo = { {0.0, 50.0, 0.0, 50.0}, {50.0, 75.0, 50.0, 75.0}, {75.0, 100.0, 75.0, 100.0} @@ -326,7 +330,7 @@ public class ThermometerPlot extends Plot implements ValueAxisPlot, private boolean useSubrangePaint = true; /** Paint for each range */ - private transient Paint[] subrangePaint = {Color.GREEN, Color.ORANGE, + private transient Paint @ArrayLen(3) [] subrangePaint = {Color.GREEN, Color.ORANGE, Color.RED}; /** A flag that controls whether the sub-range indicators are visible. */ @@ -1361,7 +1365,7 @@ protected static boolean isValidNumber(double d) { * * @return A boolean. */ - private boolean inSubrange(int subrange, double value) { + private boolean inSubrange(@IndexFor("this.subrangeInfo") int subrange, double value) { return (value > this.subrangeInfo[subrange][RANGE_LOW] && value <= this.subrangeInfo[subrange][RANGE_HIGH]); } @@ -1518,6 +1522,7 @@ private static boolean equal(double[][] array1, double[][] array2) { * @throws CloneNotSupportedException if the plot cannot be cloned. */ @Override + @SuppressWarnings({"index", "value"}) // clone always results in the same types public Object clone() throws CloneNotSupportedException { ThermometerPlot clone = (ThermometerPlot) super.clone(); diff --git a/src/main/java/org/jfree/chart/plot/XYPlot.java b/src/main/java/org/jfree/chart/plot/XYPlot.java index 79d7acc04..33a53f60b 100644 --- a/src/main/java/org/jfree/chart/plot/XYPlot.java +++ b/src/main/java/org/jfree/chart/plot/XYPlot.java @@ -235,6 +235,11 @@ package org.jfree.chart.plot; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.*; +import org.checkerframework.dataflow.qual.Pure; + import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -265,6 +270,7 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.TreeMap; + import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; @@ -352,22 +358,22 @@ public class XYPlot extends Plot implements ValueAxisPlot, Pannable, Zoomable, private RectangleInsets axisOffset; /** The domain axis / axes (used for the x-values). */ - private Map<Integer, ValueAxis> domainAxes; + private Map<@NonNegative Integer, ValueAxis> domainAxes; /** The domain axis locations. */ - private Map<Integer, AxisLocation> domainAxisLocations; + private Map<@NonNegative Integer, AxisLocation> domainAxisLocations; /** The range axis (used for the y-values). */ - private Map<Integer, ValueAxis> rangeAxes; + private Map<@NonNegative Integer, ValueAxis> rangeAxes; /** The range axis location. */ - private Map<Integer, AxisLocation> rangeAxisLocations; + private Map<@NonNegative Integer, AxisLocation> rangeAxisLocations; /** Storage for the datasets. */ - private Map<Integer, XYDataset> datasets; + private Map<@NonNegative Integer, XYDataset> datasets; /** Storage for the renderers. */ - private Map<Integer, XYItemRenderer> renderers; + private Map<@NonNegative Integer, XYItemRenderer> renderers; /** * Storage for the mapping between datasets/renderers and domain axes. The @@ -377,7 +383,7 @@ public class XYPlot extends Plot implements ValueAxisPlot, Pannable, Zoomable, * entry for a dataset, it is assumed to map to the primary domain axis * (index = 0). */ - private Map<Integer, List<Integer>> datasetToDomainAxesMap; + private Map<@NonNegative Integer, List<Integer>> datasetToDomainAxesMap; /** * Storage for the mapping between datasets/renderers and range axes. The @@ -387,13 +393,13 @@ public class XYPlot extends Plot implements ValueAxisPlot, Pannable, Zoomable, * entry for a dataset, it is assumed to map to the primary domain axis * (index = 0). */ - private Map<Integer, List<Integer>> datasetToRangeAxesMap; + private Map<@NonNegative Integer, List<Integer>> datasetToRangeAxesMap; /** The origin point for the quadrants (if drawn). */ private transient Point2D quadrantOrigin = new Point2D.Double(0.0, 0.0); /** The paint used for each quadrant. */ - private transient Paint[] quadrantPaint + private transient Paint @ArrayLen(4) [] quadrantPaint = new Paint[] {null, null, null, null}; /** A flag that controls whether the domain grid-lines are visible. */ @@ -527,16 +533,16 @@ public class XYPlot extends Plot implements ValueAxisPlot, Pannable, Zoomable, private boolean rangeCrosshairLockedOnData = true; /** A map of lists of foreground markers (optional) for the domain axes. */ - private Map foregroundDomainMarkers; + private Map<@NonNegative Integer, Collection> foregroundDomainMarkers; /** A map of lists of background markers (optional) for the domain axes. */ - private Map backgroundDomainMarkers; + private Map<@NonNegative Integer, Collection> backgroundDomainMarkers; /** A map of lists of foreground markers (optional) for the range axes. */ - private Map foregroundRangeMarkers; + private Map<@NonNegative Integer, Collection> foregroundRangeMarkers; /** A map of lists of background markers (optional) for the range axes. */ - private Map backgroundRangeMarkers; + private Map<@NonNegative Integer, Collection> backgroundRangeMarkers; /** * A (possibly empty) list of annotations for the plot. The list should @@ -633,18 +639,31 @@ public XYPlot(XYDataset dataset, ValueAxis domainAxis, ValueAxis rangeAxis, this.axisOffset = RectangleInsets.ZERO_INSETS; // allocate storage for datasets, axes and renderers (all optional) - this.domainAxes = new HashMap<Integer, ValueAxis>(); - this.domainAxisLocations = new HashMap<Integer, AxisLocation>(); + + HashMap domainAxes = new HashMap<@NonNegative Integer, ValueAxis>(); + this.domainAxes = domainAxes; + + HashMap domainAxisLocations = new HashMap<@NonNegative Integer, AxisLocation>(); + this.domainAxisLocations = domainAxisLocations; this.foregroundDomainMarkers = new HashMap(); this.backgroundDomainMarkers = new HashMap(); - this.rangeAxes = new HashMap<Integer, ValueAxis>(); - this.rangeAxisLocations = new HashMap<Integer, AxisLocation>(); + + HashMap rangeAxes = new HashMap<@NonNegative Integer, ValueAxis>(); + this.rangeAxes = rangeAxes; + + HashMap rangeAxisLocations = new HashMap<@NonNegative Integer, AxisLocation>(); + this.rangeAxisLocations = rangeAxisLocations; this.foregroundRangeMarkers = new HashMap(); this.backgroundRangeMarkers = new HashMap(); - this.datasets = new HashMap<Integer, XYDataset>(); - this.renderers = new HashMap<Integer, XYItemRenderer>(); + + HashMap datasets = new HashMap<@NonNegative Integer, XYDataset>(); + this.datasets = datasets; + + + HashMap renderers = new HashMap<@NonNegative Integer, XYItemRenderer>(); + this.renderers = renderers; this.datasetToDomainAxesMap = new TreeMap(); this.datasetToRangeAxesMap = new TreeMap(); @@ -737,6 +756,7 @@ public String getPlotType() { * @see #setOrientation(PlotOrientation) */ @Override + @Pure public PlotOrientation getOrientation() { return this.orientation; } @@ -806,7 +826,7 @@ public ValueAxis getDomainAxis() { * * @see #setDomainAxis(int, ValueAxis) */ - public ValueAxis getDomainAxis(int index) { + public ValueAxis getDomainAxis(@NonNegative int index) { ValueAxis result = this.domainAxes.get(index); if (result == null) { Plot parent = getParent(); @@ -841,7 +861,7 @@ public void setDomainAxis(ValueAxis axis) { * @see #getDomainAxis(int) * @see #setRangeAxis(int, ValueAxis) */ - public void setDomainAxis(int index, ValueAxis axis) { + public void setDomainAxis(@NonNegative int index, ValueAxis axis) { setDomainAxis(index, axis, true); } @@ -855,7 +875,7 @@ public void setDomainAxis(int index, ValueAxis axis) { * * @see #getDomainAxis(int) */ - public void setDomainAxis(int index, ValueAxis axis, boolean notify) { + public void setDomainAxis(@NonNegative int index, ValueAxis axis, boolean notify) { ValueAxis existing = getDomainAxis(index); if (existing != null) { existing.removeChangeListener(this); @@ -895,6 +915,7 @@ public void setDomainAxes(ValueAxis[] axes) { * * @see #setDomainAxisLocation(AxisLocation) */ + @Pure public AxisLocation getDomainAxisLocation() { return (AxisLocation) this.domainAxisLocations.get(0); } @@ -989,7 +1010,7 @@ public void configureDomainAxes() { * * @see #setDomainAxisLocation(int, AxisLocation) */ - public AxisLocation getDomainAxisLocation(int index) { + public AxisLocation getDomainAxisLocation(@NonNegative int index) { AxisLocation result = this.domainAxisLocations.get(index); if (result == null) { result = AxisLocation.getOpposite(getDomainAxisLocation()); @@ -1007,7 +1028,7 @@ public AxisLocation getDomainAxisLocation(int index) { * * @see #getDomainAxisLocation(int) */ - public void setDomainAxisLocation(int index, AxisLocation location) { + public void setDomainAxisLocation(@NonNegative int index, AxisLocation location) { // delegate... setDomainAxisLocation(index, location, true); } @@ -1026,7 +1047,7 @@ public void setDomainAxisLocation(int index, AxisLocation location) { * @see #getDomainAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation, boolean) */ - public void setDomainAxisLocation(int index, AxisLocation location, + public void setDomainAxisLocation(@NonNegative int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException( @@ -1047,7 +1068,7 @@ public void setDomainAxisLocation(int index, AxisLocation location, * * @see #getRangeAxisEdge(int) */ - public RectangleEdge getDomainAxisEdge(int index) { + public RectangleEdge getDomainAxisEdge(@NonNegative int index) { AxisLocation location = getDomainAxisLocation(index); return Plot.resolveDomainAxisLocation(location, this.orientation); } @@ -1099,6 +1120,7 @@ public void setRangeAxis(ValueAxis axis) { * * @see #setRangeAxisLocation(AxisLocation) */ + @Pure public AxisLocation getRangeAxisLocation() { return (AxisLocation) this.rangeAxisLocations.get(0); } @@ -1153,7 +1175,7 @@ public RectangleEdge getRangeAxisEdge() { * * @see #setRangeAxis(int, ValueAxis) */ - public ValueAxis getRangeAxis(int index) { + public ValueAxis getRangeAxis(@NonNegative int index) { ValueAxis result = this.rangeAxes.get(index); if (result == null) { Plot parent = getParent(); @@ -1174,7 +1196,7 @@ public ValueAxis getRangeAxis(int index) { * * @see #getRangeAxis(int) */ - public void setRangeAxis(int index, ValueAxis axis) { + public void setRangeAxis(@NonNegative int index, ValueAxis axis) { setRangeAxis(index, axis, true); } @@ -1188,7 +1210,7 @@ public void setRangeAxis(int index, ValueAxis axis) { * * @see #getRangeAxis(int) */ - public void setRangeAxis(int index, ValueAxis axis, boolean notify) { + public void setRangeAxis(@NonNegative int index, ValueAxis axis, boolean notify) { ValueAxis existing = getRangeAxis(index); if (existing != null) { existing.removeChangeListener(this); @@ -1272,7 +1294,7 @@ public void configureRangeAxes() { * * @see #setRangeAxisLocation(int, AxisLocation) */ - public AxisLocation getRangeAxisLocation(int index) { + public AxisLocation getRangeAxisLocation(@NonNegative int index) { AxisLocation result = this.rangeAxisLocations.get(index); if (result == null) { result = AxisLocation.getOpposite(getRangeAxisLocation()); @@ -1289,7 +1311,7 @@ public AxisLocation getRangeAxisLocation(int index) { * * @see #getRangeAxisLocation(int) */ - public void setRangeAxisLocation(int index, AxisLocation location) { + public void setRangeAxisLocation(@NonNegative int index, AxisLocation location) { // delegate... setRangeAxisLocation(index, location, true); } @@ -1308,7 +1330,7 @@ public void setRangeAxisLocation(int index, AxisLocation location) { * @see #getRangeAxisLocation(int) * @see #setDomainAxisLocation(int, AxisLocation, boolean) */ - public void setRangeAxisLocation(int index, AxisLocation location, + public void setRangeAxisLocation(@NonNegative int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException( @@ -1330,7 +1352,7 @@ public void setRangeAxisLocation(int index, AxisLocation location, * @see #getRangeAxisLocation(int) * @see #getOrientation() */ - public RectangleEdge getRangeAxisEdge(int index) { + public RectangleEdge getRangeAxisEdge(@NonNegative int index) { AxisLocation location = getRangeAxisLocation(index); return Plot.resolveRangeAxisLocation(location, this.orientation); } @@ -1357,7 +1379,7 @@ public XYDataset getDataset() { * * @see #setDataset(int, XYDataset) */ - public XYDataset getDataset(int index) { + public XYDataset getDataset(@NonNegative int index) { return (XYDataset) this.datasets.get(index); } @@ -1383,7 +1405,7 @@ public void setDataset(XYDataset dataset) { * * @see #getDataset(int) */ - public void setDataset(int index, XYDataset dataset) { + public void setDataset(@NonNegative int index, XYDataset dataset) { XYDataset existing = getDataset(index); if (existing != null) { existing.removeChangeListener(this); @@ -1415,8 +1437,8 @@ public int getDatasetCount() { * * @return The index or -1. */ - public int indexOf(XYDataset dataset) { - for (Map.Entry<Integer, XYDataset> entry: this.datasets.entrySet()) { + public @GTENegativeOne int indexOf(XYDataset dataset) { + for (Map.Entry<@NonNegative Integer, XYDataset> entry: this.datasets.entrySet()) { if (dataset == entry.getValue()) { return entry.getKey(); } @@ -1433,7 +1455,7 @@ public int indexOf(XYDataset dataset) { * * @see #mapDatasetToRangeAxis(int, int) */ - public void mapDatasetToDomainAxis(int index, int axisIndex) { + public void mapDatasetToDomainAxis(@NonNegative int index, int axisIndex) { List axisIndices = new java.util.ArrayList(1); axisIndices.add(new Integer(axisIndex)); mapDatasetToDomainAxes(index, axisIndices); @@ -1449,11 +1471,11 @@ public void mapDatasetToDomainAxis(int index, int axisIndex) { * * @since 1.0.12 */ - public void mapDatasetToDomainAxes(int index, List axisIndices) { + public void mapDatasetToDomainAxes(@NonNegative int index, List<Integer> axisIndices) { Args.requireNonNegative(index, "index"); checkAxisIndices(axisIndices); - Integer key = new Integer(index); - this.datasetToDomainAxesMap.put(key, new ArrayList(axisIndices)); + @NonNegative Integer key = index; + this.datasetToDomainAxesMap.put(key, new ArrayList<Integer>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } @@ -1467,7 +1489,7 @@ public void mapDatasetToDomainAxes(int index, List axisIndices) { * * @see #mapDatasetToDomainAxis(int, int) */ - public void mapDatasetToRangeAxis(int index, int axisIndex) { + public void mapDatasetToRangeAxis(@NonNegative int index, int axisIndex) { List axisIndices = new java.util.ArrayList(1); axisIndices.add(new Integer(axisIndex)); mapDatasetToRangeAxes(index, axisIndices); @@ -1483,11 +1505,11 @@ public void mapDatasetToRangeAxis(int index, int axisIndex) { * * @since 1.0.12 */ - public void mapDatasetToRangeAxes(int index, List axisIndices) { + public void mapDatasetToRangeAxes(@NonNegative int index, List<Integer> axisIndices) { Args.requireNonNegative(index, "index"); checkAxisIndices(axisIndices); - Integer key = new Integer(index); - this.datasetToRangeAxesMap.put(key, new ArrayList(axisIndices)); + @NonNegative Integer key = index; + this.datasetToRangeAxesMap.put(key, new ArrayList<Integer>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } @@ -1499,6 +1521,7 @@ public void mapDatasetToRangeAxes(int index, List axisIndices) { * * @param indices the list of indices ({@code null} permitted). */ + @Pure private void checkAxisIndices(List<Integer> indices) { // axisIndices can be: // 1. null; @@ -1526,7 +1549,7 @@ private void checkAxisIndices(List<Integer> indices) { * * @since 1.0.11 */ - public int getRendererCount() { + public @NonNegative int getRendererCount() { return this.renderers.size(); } @@ -1550,7 +1573,8 @@ public XYItemRenderer getRenderer() { * * @see #setRenderer(int, XYItemRenderer) */ - public XYItemRenderer getRenderer(int index) { + @Pure + public XYItemRenderer getRenderer(@NonNegative int index) { return (XYItemRenderer) this.renderers.get(index); } @@ -1578,7 +1602,7 @@ public void setRenderer(XYItemRenderer renderer) { * * @see #getRenderer(int) */ - public void setRenderer(int index, XYItemRenderer renderer) { + public void setRenderer(@NonNegative int index, XYItemRenderer renderer) { setRenderer(index, renderer, true); } @@ -1594,7 +1618,7 @@ public void setRenderer(int index, XYItemRenderer renderer) { * * @see #getRenderer(int) */ - public void setRenderer(int index, XYItemRenderer renderer, + public void setRenderer(@NonNegative int index, XYItemRenderer renderer, boolean notify) { XYItemRenderer existing = getRenderer(index); if (existing != null) { @@ -1687,8 +1711,8 @@ public void setSeriesRenderingOrder(SeriesRenderingOrder order) { * * @return The renderer index. */ - public int getIndexOf(XYItemRenderer renderer) { - for (Map.Entry<Integer, XYItemRenderer> entry + public @GTENegativeOne int getIndexOf(XYItemRenderer renderer) { + for (Map.Entry<@NonNegative Integer, XYItemRenderer> entry : this.renderers.entrySet()) { if (entry.getValue() == renderer) { return entry.getKey(); @@ -2358,7 +2382,7 @@ public void setQuadrantOrigin(Point2D origin) { * * @see #setQuadrantPaint(int, Paint) */ - public Paint getQuadrantPaint(int index) { + public Paint getQuadrantPaint(@NonNegative int index) { if (index < 0 || index > 3) { throw new IllegalArgumentException("The index value (" + index + ") should be in the range 0 to 3."); @@ -2375,7 +2399,7 @@ public Paint getQuadrantPaint(int index) { * * @see #getQuadrantPaint(int) */ - public void setQuadrantPaint(int index, Paint paint) { + public void setQuadrantPaint(@NonNegative int index, Paint paint) { if (index < 0 || index > 3) { throw new IllegalArgumentException("The index value (" + index + ") should be in the range 0 to 3."); @@ -2425,14 +2449,14 @@ public void addDomainMarker(Marker marker, Layer layer) { */ public void clearDomainMarkers() { if (this.backgroundDomainMarkers != null) { - Set<Integer> keys = this.backgroundDomainMarkers.keySet(); + Set<@NonNegative Integer> keys = this.backgroundDomainMarkers.keySet(); for (Integer key : keys) { clearDomainMarkers(key); } this.backgroundDomainMarkers.clear(); } if (this.foregroundDomainMarkers != null) { - Set<Integer> keys = this.foregroundDomainMarkers.keySet(); + Set<@NonNegative Integer> keys = this.foregroundDomainMarkers.keySet(); for (Integer key : keys) { clearDomainMarkers(key); } @@ -2449,7 +2473,7 @@ public void clearDomainMarkers() { * * @see #clearRangeMarkers(int) */ - public void clearDomainMarkers(int index) { + public void clearDomainMarkers(@NonNegative int index) { Integer key = new Integer(index); if (this.backgroundDomainMarkers != null) { Collection markers @@ -2493,7 +2517,7 @@ public void clearDomainMarkers(int index) { * @see #clearDomainMarkers(int) * @see #addRangeMarker(int, Marker, Layer) */ - public void addDomainMarker(int index, Marker marker, Layer layer) { + public void addDomainMarker(@NonNegative int index, Marker marker, Layer layer) { addDomainMarker(index, marker, layer, true); } @@ -2512,7 +2536,7 @@ public void addDomainMarker(int index, Marker marker, Layer layer) { * * @since 1.0.10 */ - public void addDomainMarker(int index, Marker marker, Layer layer, + public void addDomainMarker(@NonNegative int index, Marker marker, Layer layer, boolean notify) { Args.nullNotPermitted(marker, "marker"); Args.nullNotPermitted(layer, "layer"); @@ -2522,7 +2546,8 @@ public void addDomainMarker(int index, Marker marker, Layer layer, new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); - this.foregroundDomainMarkers.put(new Integer(index), markers); + @NonNegative Integer i = index; + this.foregroundDomainMarkers.put(i, markers); } markers.add(marker); } @@ -2531,7 +2556,8 @@ else if (layer == Layer.BACKGROUND) { new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); - this.backgroundDomainMarkers.put(new Integer(index), markers); + @NonNegative Integer i = index; + this.backgroundDomainMarkers.put(i, markers); } markers.add(marker); } @@ -2585,7 +2611,7 @@ public boolean removeDomainMarker(Marker marker, Layer layer) { * * @since 1.0.7 */ - public boolean removeDomainMarker(int index, Marker marker, Layer layer) { + public boolean removeDomainMarker(@NonNegative int index, Marker marker, Layer layer) { return removeDomainMarker(index, marker, layer, true); } @@ -2603,7 +2629,7 @@ public boolean removeDomainMarker(int index, Marker marker, Layer layer) { * * @since 1.0.10 */ - public boolean removeDomainMarker(int index, Marker marker, Layer layer, + public boolean removeDomainMarker(@NonNegative int index, Marker marker, Layer layer, boolean notify) { ArrayList markers; if (layer == Layer.FOREGROUND) { @@ -2663,14 +2689,16 @@ public void addRangeMarker(Marker marker, Layer layer) { */ public void clearRangeMarkers() { if (this.backgroundRangeMarkers != null) { - Set<Integer> keys = this.backgroundRangeMarkers.keySet(); + + Set<@NonNegative Integer> keys = this.backgroundRangeMarkers.keySet(); for (Integer key : keys) { clearRangeMarkers(key); } this.backgroundRangeMarkers.clear(); } if (this.foregroundRangeMarkers != null) { - Set<Integer> keys = this.foregroundRangeMarkers.keySet(); + + Set<@NonNegative Integer> keys = this.foregroundRangeMarkers.keySet(); for (Integer key : keys) { clearRangeMarkers(key); } @@ -2693,7 +2721,7 @@ public void clearRangeMarkers() { * @see #clearRangeMarkers(int) * @see #addDomainMarker(int, Marker, Layer) */ - public void addRangeMarker(int index, Marker marker, Layer layer) { + public void addRangeMarker(@NonNegative int index, Marker marker, Layer layer) { addRangeMarker(index, marker, layer, true); } @@ -2711,7 +2739,7 @@ public void addRangeMarker(int index, Marker marker, Layer layer) { * * @since 1.0.10 */ - public void addRangeMarker(int index, Marker marker, Layer layer, + public void addRangeMarker(@NonNegative int index, Marker marker, Layer layer, boolean notify) { Collection markers; if (layer == Layer.FOREGROUND) { @@ -2719,7 +2747,8 @@ public void addRangeMarker(int index, Marker marker, Layer layer, new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); - this.foregroundRangeMarkers.put(new Integer(index), markers); + @NonNegative Integer i = index; + this.foregroundRangeMarkers.put(i, markers); } markers.add(marker); } @@ -2728,7 +2757,8 @@ else if (layer == Layer.BACKGROUND) { new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); - this.backgroundRangeMarkers.put(new Integer(index), markers); + @NonNegative Integer i = index; + this.backgroundRangeMarkers.put(i, markers); } markers.add(marker); } @@ -2744,7 +2774,7 @@ else if (layer == Layer.BACKGROUND) { * * @param index the renderer index. */ - public void clearRangeMarkers(int index) { + public void clearRangeMarkers(@NonNegative int index) { Integer key = new Integer(index); if (this.backgroundRangeMarkers != null) { Collection markers @@ -2817,7 +2847,7 @@ public boolean removeRangeMarker(Marker marker, Layer layer) { * * @since 1.0.7 */ - public boolean removeRangeMarker(int index, Marker marker, Layer layer) { + public boolean removeRangeMarker(@NonNegative int index, Marker marker, Layer layer) { return removeRangeMarker(index, marker, layer, true); } @@ -2835,7 +2865,7 @@ public boolean removeRangeMarker(int index, Marker marker, Layer layer) { * * @since 1.0.10 */ - public boolean removeRangeMarker(int index, Marker marker, Layer layer, + public boolean removeRangeMarker(@NonNegative int index, Marker marker, Layer layer, boolean notify) { Args.nullNotPermitted(marker, "marker"); Args.nullNotPermitted(layer, "layer"); @@ -3028,6 +3058,7 @@ else if (this.orientation == PlotOrientation.VERTICAL) { // reserve space for the domain axes... for (ValueAxis axis: this.domainAxes.values()) { if (axis != null) { + @SuppressWarnings("index") // rewrite guaranteed index: axis is guaranteed to be a domain axis, so findDomainAxisIndex will return NN RectangleEdge edge = getDomainAxisEdge( findDomainAxisIndex(axis)); space = axis.reserveSpace(g2, this, plotArea, edge, space); @@ -3074,7 +3105,8 @@ else if (this.orientation == PlotOrientation.VERTICAL) { // reserve space for the range axes... for (ValueAxis axis: this.rangeAxes.values()) { if (axis != null) { - RectangleEdge edge = getRangeAxisEdge( + @SuppressWarnings("index") // rewrite guaranteed index: axis is guaranteed to be a range axis, so findRangeAxisIndex will return NN + RectangleEdge edge = getRangeAxisEdge( findRangeAxisIndex(axis)); space = axis.reserveSpace(g2, this, plotArea, edge, space); } @@ -3243,19 +3275,21 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, // draw the markers that are associated with a specific dataset... for (XYDataset dataset: this.datasets.values()) { - int datasetIndex = indexOf(dataset); + @SuppressWarnings("index") // rewrite guaranteed index: dataset is definitely a valid dataset, so its index will be nonnegative + @NonNegative int datasetIndex = indexOf(dataset); drawDomainMarkers(g2, dataArea, datasetIndex, Layer.BACKGROUND); } for (XYDataset dataset: this.datasets.values()) { - int datasetIndex = indexOf(dataset); + @SuppressWarnings("index") // rewrite guaranteed index: dataset is definitely a valid dataset, so its index will be nonnegative + @NonNegative int datasetIndex = indexOf(dataset); drawRangeMarkers(g2, dataArea, datasetIndex, Layer.BACKGROUND); } // now draw annotations and render data items... boolean foundData = false; DatasetRenderingOrder order = getDatasetRenderingOrder(); - List<Integer> rendererIndices = getRendererIndices(order); - List<Integer> datasetIndices = getDatasetIndices(order); + List<@NonNegative Integer> rendererIndices = getRendererIndices(order); + List<@NonNegative Integer> datasetIndices = getDatasetIndices(order); // draw background annotations for (int i : rendererIndices) { @@ -3289,6 +3323,7 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, // draw domain crosshair if required... int datasetIndex = crosshairState.getDatasetIndex(); ValueAxis xAxis = getDomainAxisForDataset(datasetIndex); + @SuppressWarnings("index") // guaranteed index: xAxis was just fetched out of the dataset, so getDomainAxisIndex will return NN RectangleEdge xAxisEdge = getDomainAxisEdge(getDomainAxisIndex(xAxis)); if (!this.domainCrosshairLockedOnData && anchor != null) { double xx; @@ -3310,6 +3345,7 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, // draw range crosshair if required... ValueAxis yAxis = getRangeAxisForDataset(datasetIndex); + @SuppressWarnings("index") // guaranteed index: yAxis was just fetched out of the dataset, so getRangeAxisIndex will return NN RectangleEdge yAxisEdge = getRangeAxisEdge(getRangeAxisIndex(yAxis)); if (!this.rangeCrosshairLockedOnData && anchor != null) { double yy; @@ -3365,9 +3401,9 @@ public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, * * @return The list of indices. */ - private List<Integer> getDatasetIndices(DatasetRenderingOrder order) { - List<Integer> result = new ArrayList<Integer>(); - for (Entry<Integer, XYDataset> entry : this.datasets.entrySet()) { + private List< @NonNegative Integer> getDatasetIndices(DatasetRenderingOrder order) { + List<@NonNegative Integer> result = new ArrayList<@NonNegative Integer>(); + for (Entry< @NonNegative Integer, XYDataset> entry : this.datasets.entrySet()) { if (entry.getValue() != null) { result.add(entry.getKey()); } @@ -3378,10 +3414,10 @@ private List<Integer> getDatasetIndices(DatasetRenderingOrder order) { } return result; } - - private List<Integer> getRendererIndices(DatasetRenderingOrder order) { - List<Integer> result = new ArrayList<Integer>(); - for (Entry<Integer, XYItemRenderer> entry : this.renderers.entrySet()) { + + private List< @NonNegative Integer> getRendererIndices(DatasetRenderingOrder order) { + List<@NonNegative Integer> result = new ArrayList<@NonNegative Integer>(); + for (Entry< @NonNegative Integer, XYItemRenderer> entry : this.renderers.entrySet()) { if (entry.getValue() != null) { result.add(entry.getKey()); } @@ -3611,7 +3647,8 @@ protected Map<Axis, AxisState> drawAxes(Graphics2D g2, Rectangle2D plotArea, // add domain axes to lists... for (ValueAxis axis : this.domainAxes.values()) { if (axis != null) { - int axisIndex = findDomainAxisIndex(axis); + @SuppressWarnings("index") // rewrite guaranteed index: axis is guaranteed to be a domain axis, so axisIndex is NN + @NonNegative int axisIndex = findDomainAxisIndex(axis); axisCollection.add(axis, getDomainAxisEdge(axisIndex)); } } @@ -3619,7 +3656,8 @@ protected Map<Axis, AxisState> drawAxes(Graphics2D g2, Rectangle2D plotArea, // add range axes to lists... for (ValueAxis axis : this.rangeAxes.values()) { if (axis != null) { - int axisIndex = findRangeAxisIndex(axis); + @SuppressWarnings("index") // rewrite guaranteed index: axis is guaranteed to be a range axis, so axisIndex is NN + @NonNegative int axisIndex = findRangeAxisIndex(axis); axisCollection.add(axis, getRangeAxisEdge(axisIndex)); } } @@ -3693,7 +3731,7 @@ protected Map<Axis, AxisState> drawAxes(Graphics2D g2, Rectangle2D plotArea, * * @return A flag that indicates whether any data was actually rendered. */ - public boolean render(Graphics2D g2, Rectangle2D dataArea, int index, + public boolean render(Graphics2D g2, Rectangle2D dataArea, @NonNegative int index, PlotRenderingInfo info, CrosshairState crosshairState) { boolean foundData = false; @@ -3729,13 +3767,15 @@ public boolean render(Graphics2D g2, Rectangle2D dataArea, int index, continue; } if (state.getProcessVisibleItemsOnly()) { - int[] itemBounds = RendererUtils.findLiveItems( + @NonNegative int[] itemBounds = RendererUtils.findLiveItems( dataset, series, xAxis.getLowerBound(), xAxis.getUpperBound()); firstItem = Math.max(itemBounds[0] - 1, 0); lastItem = Math.min(itemBounds[1] + 1, lastItem); } - state.startSeriesPass(dataset, series, firstItem, + @SuppressWarnings("index") // if 0 weren't a valid index, then this loop iteration is skipped above + @IndexFor("dataset.getSeries(series)") int firstItemTmp = firstItem; + state.startSeriesPass(dataset, series, firstItemTmp, lastItem, pass, passCount); for (int item = firstItem; item <= lastItem; item++) { renderer.drawItem(g2, state, dataArea, info, @@ -3755,14 +3795,20 @@ public boolean render(Graphics2D g2, Rectangle2D dataArea, int index, int firstItem = 0; int lastItem = dataset.getItemCount(series) - 1; if (state.getProcessVisibleItemsOnly()) { - int[] itemBounds = RendererUtils.findLiveItems( + @NonNegative int[] itemBounds = RendererUtils.findLiveItems( dataset, series, xAxis.getLowerBound(), xAxis.getUpperBound()); firstItem = Math.max(itemBounds[0] - 1, 0); lastItem = Math.min(itemBounds[1] + 1, lastItem); } - state.startSeriesPass(dataset, series, firstItem, - lastItem, pass, passCount); + @SuppressWarnings("index") // if 0 weren't a valid index, then this loop iteration is skipped above + @IndexFor("dataset.getSeries(series)") int firstItemTmp = firstItem; + + @SuppressWarnings("index") // I think this is a bug, as lastItem is checked in the parallel code above to not be -1, but that check is elided here. + @NonNegative int lastItemTmp = lastItem; + + state.startSeriesPass(dataset, series, firstItemTmp, + lastItemTmp, pass, passCount); for (int item = firstItem; item <= lastItem; item++) { renderer.drawItem(g2, state, dataArea, info, this, xAxis, yAxis, dataset, series, item, @@ -3784,15 +3830,16 @@ public boolean render(Graphics2D g2, Rectangle2D dataArea, int index, * * @return The axis. */ - public ValueAxis getDomainAxisForDataset(int index) { + public ValueAxis getDomainAxisForDataset(@NonNegative int index) { Args.requireNonNegative(index, "index"); ValueAxis valueAxis; List axisIndices = (List) this.datasetToDomainAxesMap.get( new Integer(index)); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D - Integer axisIndex = (Integer) axisIndices.get(0); - valueAxis = getDomainAxis(axisIndex.intValue()); + @SuppressWarnings("index") // first element of axisIndices is guaranteed NN + @NonNegative int axisIndex = ((Integer) axisIndices.get(0)).intValue(); + valueAxis = getDomainAxis(axisIndex); } else { valueAxis = getDomainAxis(0); @@ -3807,15 +3854,16 @@ public ValueAxis getDomainAxisForDataset(int index) { * * @return The axis. */ - public ValueAxis getRangeAxisForDataset(int index) { + public ValueAxis getRangeAxisForDataset(@NonNegative int index) { Args.requireNonNegative(index, "index"); ValueAxis valueAxis; List axisIndices = (List) this.datasetToRangeAxesMap.get( new Integer(index)); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D - Integer axisIndex = (Integer) axisIndices.get(0); - valueAxis = getRangeAxis(axisIndex.intValue()); + @SuppressWarnings("index") // first element of axisIndices is guaranteed NN + @NonNegative int axisIndex = ((Integer) axisIndices.get(0)).intValue(); + valueAxis = getRangeAxis(axisIndex); } else { valueAxis = getRangeAxis(0); @@ -3983,7 +4031,7 @@ public void drawAnnotations(Graphics2D g2, Rectangle2D dataArea, * @param layer the layer (foreground or background). */ protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea, - int index, Layer layer) { + @NonNegative int index, Layer layer) { XYItemRenderer r = getRenderer(index); if (r == null) { @@ -4016,7 +4064,7 @@ protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea, * @param layer the layer (foreground or background). */ protected void drawRangeMarkers(Graphics2D g2, Rectangle2D dataArea, - int index, Layer layer) { + @NonNegative int index, Layer layer) { XYItemRenderer r = getRenderer(index); if (r == null) { @@ -4075,7 +4123,7 @@ public Collection getRangeMarkers(Layer layer) { * * @see #getRangeMarkers(int, Layer) */ - public Collection getDomainMarkers(int index, Layer layer) { + public Collection getDomainMarkers(@NonNegative int index, Layer layer) { Collection result = null; Integer key = new Integer(index); if (layer == Layer.FOREGROUND) { @@ -4101,7 +4149,7 @@ else if (layer == Layer.BACKGROUND) { * * @see #getDomainMarkers(int, Layer) */ - public Collection getRangeMarkers(int index, Layer layer) { + public Collection getRangeMarkers(@NonNegative int index, Layer layer) { Collection result = null; Integer key = new Integer(index); if (layer == Layer.FOREGROUND) { @@ -4347,7 +4395,7 @@ private List<XYDataset> getDatasetsMappedToRangeAxis(Integer axisIndex) { * * @see #getRangeAxisIndex(ValueAxis) */ - public int getDomainAxisIndex(ValueAxis axis) { + public @GTENegativeOne int getDomainAxisIndex(ValueAxis axis) { int result = findDomainAxisIndex(axis); if (result < 0) { // try the parent plot @@ -4360,8 +4408,8 @@ public int getDomainAxisIndex(ValueAxis axis) { return result; } - private int findDomainAxisIndex(ValueAxis axis) { - for (Map.Entry<Integer, ValueAxis> entry : this.domainAxes.entrySet()) { + private @GTENegativeOne int findDomainAxisIndex(ValueAxis axis) { + for (Map.Entry<@NonNegative Integer, ValueAxis> entry : this.domainAxes.entrySet()) { if (entry.getValue() == axis) { return entry.getKey(); } @@ -4378,7 +4426,7 @@ private int findDomainAxisIndex(ValueAxis axis) { * * @see #getDomainAxisIndex(ValueAxis) */ - public int getRangeAxisIndex(ValueAxis axis) { + public @GTENegativeOne int getRangeAxisIndex(ValueAxis axis) { int result = findRangeAxisIndex(axis); if (result < 0) { // try the parent plot @@ -4391,8 +4439,8 @@ public int getRangeAxisIndex(ValueAxis axis) { return result; } - private int findRangeAxisIndex(ValueAxis axis) { - for (Map.Entry<Integer, ValueAxis> entry : this.rangeAxes.entrySet()) { + private @GTENegativeOne int findRangeAxisIndex(ValueAxis axis) { + for (Map.Entry<@NonNegative Integer, ValueAxis> entry : this.rangeAxes.entrySet()) { if (entry.getValue() == axis) { return entry.getKey(); } @@ -5228,7 +5276,7 @@ public boolean isRangeZoomable() { * * @return The series count. */ - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { int result = 0; XYDataset dataset = getDataset(); if (dataset != null) { @@ -5279,7 +5327,8 @@ public LegendItemCollection getLegendItems() { if (dataset == null) { continue; } - int datasetIndex = indexOf(dataset); + @SuppressWarnings("index") // rewrite guaranteed index: dataset is came from this object, so indexOf returns NN + @NonNegative int datasetIndex = indexOf(dataset); XYItemRenderer renderer = getRenderer(datasetIndex); if (renderer == null) { renderer = getRenderer(0); @@ -5541,13 +5590,13 @@ public Object clone() throws CloneNotSupportedException { axis.addChangeListener(clone); } } - clone.domainAxisLocations = new HashMap<Integer, AxisLocation>( + clone.domainAxisLocations = new HashMap<@NonNegative Integer, AxisLocation>( this.domainAxisLocations); - clone.rangeAxisLocations = new HashMap<Integer, AxisLocation>( + clone.rangeAxisLocations = new HashMap<@NonNegative Integer, AxisLocation>( this.rangeAxisLocations); // the datasets are not cloned, but listeners need to be added... - clone.datasets = new HashMap<Integer, XYDataset>(this.datasets); + clone.datasets = new HashMap<@NonNegative Integer, XYDataset>(this.datasets); for (XYDataset dataset : clone.datasets.values()) { if (dataset != null) { dataset.addChangeListener(clone); diff --git a/src/main/java/org/jfree/chart/plot/dial/DialPlot.java b/src/main/java/org/jfree/chart/plot/dial/DialPlot.java index 4e1950e83..f1a106b4f 100644 --- a/src/main/java/org/jfree/chart/plot/dial/DialPlot.java +++ b/src/main/java/org/jfree/chart/plot/dial/DialPlot.java @@ -46,6 +46,8 @@ package org.jfree.chart.plot.dial; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Point2D; @@ -355,7 +357,7 @@ public int getLayerIndex(DialLayer layer) { * * @param index the index. */ - public void removeLayer(int index) { + public void removeLayer(@NonNegative int index) { DialLayer layer = (DialLayer) this.layers.get(index); if (layer != null) { layer.removeChangeListener(this); @@ -370,6 +372,7 @@ public void removeLayer(int index) { * * @param layer the layer ({@code null} not permitted). */ + @SuppressWarnings("index") // This method's documentation specifically states that it is unsafe public void removeLayer(DialLayer layer) { // defer argument checking removeLayer(getLayerIndex(layer)); @@ -406,7 +409,7 @@ public int getPointerIndex(DialPointer pointer) { * * @param index the index. */ - public void removePointer(int index) { + public void removePointer(@NonNegative int index) { DialPointer pointer = (DialPointer) this.pointers.get(index); if (pointer != null) { pointer.removeChangeListener(this); @@ -421,6 +424,7 @@ public void removePointer(int index) { * * @param pointer the pointer ({@code null} not permitted). */ + @SuppressWarnings("index") // This method's documentation specifically states that it is unsafe public void removePointer(DialPointer pointer) { // defer argument checking removeLayer(getPointerIndex(pointer)); @@ -434,7 +438,7 @@ public void removePointer(DialPointer pointer) { * * @return The pointer. */ - public DialPointer getPointerForDataset(int datasetIndex) { + public DialPointer getPointerForDataset(@NonNegative int datasetIndex) { DialPointer result = null; Iterator iterator = this.pointers.iterator(); while (iterator.hasNext()) { @@ -462,7 +466,7 @@ public ValueDataset getDataset() { * * @return The dataset (possibly {@code null}). */ - public ValueDataset getDataset(int index) { + public ValueDataset getDataset(@NonNegative int index) { ValueDataset result = null; if (this.datasets.size() > index) { result = (ValueDataset) this.datasets.get(index); @@ -487,7 +491,7 @@ public void setDataset(ValueDataset dataset) { * @param index the dataset index. * @param dataset the dataset ({@code null} permitted). */ - public void setDataset(int index, ValueDataset dataset) { + public void setDataset(@NonNegative int index, ValueDataset dataset) { ValueDataset existing = (ValueDataset) this.datasets.get(index); if (existing != null) { @@ -624,7 +628,7 @@ private Rectangle2D viewToFrame(Rectangle2D view) { * * @return The data value. */ - public double getValue(int datasetIndex) { + public double getValue(@NonNegative int datasetIndex) { double result = Double.NaN; ValueDataset dataset = getDataset(datasetIndex); if (dataset != null) { @@ -643,7 +647,7 @@ public double getValue(int datasetIndex) { * @param index the scale index. * @param scale the scale ({@code null} not permitted). */ - public void addScale(int index, DialScale scale) { + public void addScale(@NonNegative int index, DialScale scale) { Args.nullNotPermitted(scale, "scale"); DialScale existing = (DialScale) this.scales.get(index); if (existing != null) { @@ -662,7 +666,7 @@ public void addScale(int index, DialScale scale) { * * @return The scale (possibly {@code null}). */ - public DialScale getScale(int index) { + public DialScale getScale(@NonNegative int index) { DialScale result = null; if (this.scales.size() > index) { result = (DialScale) this.scales.get(index); @@ -676,7 +680,7 @@ public DialScale getScale(int index) { * @param index the dataset index (zero-based). * @param scaleIndex the scale index (zero-based). */ - public void mapDatasetToScale(int index, int scaleIndex) { + public void mapDatasetToScale(@NonNegative int index, int scaleIndex) { this.datasetToScaleMap.set(index, new Integer(scaleIndex)); fireChangeEvent(); } @@ -688,9 +692,10 @@ public void mapDatasetToScale(int index, int scaleIndex) { * * @return The dial scale. */ - public DialScale getScaleForDataset(int datasetIndex) { + public DialScale getScaleForDataset(@NonNegative int datasetIndex) { DialScale result = (DialScale) this.scales.get(0); - Integer scaleIndex = (Integer) this.datasetToScaleMap.get(datasetIndex); + @SuppressWarnings("index") // all scale indices are nonnegative + @NonNegative Integer scaleIndex = (Integer) this.datasetToScaleMap.get(datasetIndex); if (scaleIndex != null) { result = getScale(scaleIndex.intValue()); } diff --git a/src/main/java/org/jfree/chart/plot/dial/DialPointer.java b/src/main/java/org/jfree/chart/plot/dial/DialPointer.java index 07dcb20a2..306958c3f 100644 --- a/src/main/java/org/jfree/chart/plot/dial/DialPointer.java +++ b/src/main/java/org/jfree/chart/plot/dial/DialPointer.java @@ -46,6 +46,8 @@ package org.jfree.chart.plot.dial; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -80,7 +82,7 @@ public abstract class DialPointer extends AbstractDialLayer /** * The dataset index for the needle. */ - int datasetIndex; + @NonNegative int datasetIndex; /** * Creates a new {@code DialPointer} instance. @@ -94,7 +96,7 @@ protected DialPointer() { * * @param datasetIndex the dataset index. */ - protected DialPointer(int datasetIndex) { + protected DialPointer(@NonNegative int datasetIndex) { this.radius = 0.9; this.datasetIndex = datasetIndex; } @@ -106,7 +108,7 @@ protected DialPointer(int datasetIndex) { * * @see #getDatasetIndex() */ - public int getDatasetIndex() { + public @NonNegative int getDatasetIndex() { return this.datasetIndex; } @@ -118,7 +120,7 @@ public int getDatasetIndex() { * * @see #getDatasetIndex() */ - public void setDatasetIndex(int index) { + public void setDatasetIndex(@NonNegative int index) { this.datasetIndex = index; notifyListeners(new DialLayerChangeEvent(this)); } @@ -235,7 +237,7 @@ public Pin() { * * @param datasetIndex the dataset index. */ - public Pin(int datasetIndex) { + public Pin(@NonNegative int datasetIndex) { super(datasetIndex); this.paint = Color.RED; this.stroke = new BasicStroke(3.0f, BasicStroke.CAP_ROUND, @@ -428,7 +430,7 @@ public Pointer() { * * @param datasetIndex the dataset index. */ - public Pointer(int datasetIndex) { + public Pointer(@NonNegative int datasetIndex) { super(datasetIndex); this.widthRadius = 0.05; this.fillPaint = Color.GRAY; diff --git a/src/main/java/org/jfree/chart/plot/dial/DialValueIndicator.java b/src/main/java/org/jfree/chart/plot/dial/DialValueIndicator.java index a2fa008ec..eeb059e99 100644 --- a/src/main/java/org/jfree/chart/plot/dial/DialValueIndicator.java +++ b/src/main/java/org/jfree/chart/plot/dial/DialValueIndicator.java @@ -44,6 +44,8 @@ package org.jfree.chart.plot.dial; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -86,7 +88,7 @@ public class DialValueIndicator extends AbstractDialLayer implements DialLayer, static final long serialVersionUID = 803094354130942585L; /** The dataset index. */ - private int datasetIndex; + private @NonNegative int datasetIndex; /** The angle that defines the anchor point. */ private double angle; @@ -148,7 +150,7 @@ public DialValueIndicator() { * * @param datasetIndex the dataset index. */ - public DialValueIndicator(int datasetIndex) { + public DialValueIndicator(@NonNegative int datasetIndex) { this.datasetIndex = datasetIndex; this.angle = -90.0; this.radius = 0.3; @@ -174,7 +176,7 @@ public DialValueIndicator(int datasetIndex) { * * @see #setDatasetIndex(int) */ - public int getDatasetIndex() { + public @NonNegative int getDatasetIndex() { return this.datasetIndex; } @@ -186,7 +188,7 @@ public int getDatasetIndex() { * * @see #getDatasetIndex() */ - public void setDatasetIndex(int index) { + public void setDatasetIndex(@NonNegative int index) { this.datasetIndex = index; notifyListeners(new DialLayerChangeEvent(this)); } diff --git a/src/main/java/org/jfree/chart/plot/dial/StandardDialRange.java b/src/main/java/org/jfree/chart/plot/dial/StandardDialRange.java index 4223d6719..766610dfd 100644 --- a/src/main/java/org/jfree/chart/plot/dial/StandardDialRange.java +++ b/src/main/java/org/jfree/chart/plot/dial/StandardDialRange.java @@ -44,6 +44,8 @@ package org.jfree.chart.plot.dial; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -73,7 +75,7 @@ public class StandardDialRange extends AbstractDialLayer implements DialLayer, static final long serialVersionUID = 345515648249364904L; /** The scale index. */ - private int scaleIndex; + private @NonNegative int scaleIndex; /** The minimum data value for the scale. */ private double lowerBound; @@ -130,7 +132,7 @@ public StandardDialRange(double lower, double upper, Paint paint) { * * @see #setScaleIndex(int) */ - public int getScaleIndex() { + public @NonNegative int getScaleIndex() { return this.scaleIndex; } @@ -142,7 +144,7 @@ public int getScaleIndex() { * * @see #getScaleIndex() */ - public void setScaleIndex(int index) { + public void setScaleIndex(@NonNegative int index) { this.scaleIndex = index; notifyListeners(new DialLayerChangeEvent(this)); } diff --git a/src/main/java/org/jfree/chart/renderer/AbstractRenderer.java b/src/main/java/org/jfree/chart/renderer/AbstractRenderer.java index 798db2063..1d6736bb9 100644 --- a/src/main/java/org/jfree/chart/renderer/AbstractRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/AbstractRenderer.java @@ -99,6 +99,8 @@ package org.jfree.chart.renderer; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -384,7 +386,7 @@ public abstract class AbstractRenderer implements Cloneable, Serializable { private boolean dataBoundsIncludesVisibleSeriesOnly = true; /** The default radius for the entity 'hotspot' */ - private int defaultEntityRadius; + private @NonNegative int defaultEntityRadius; /** Storage for registered change listeners. */ private transient EventListenerList listenerList; @@ -512,13 +514,13 @@ protected void endElementGroup(Graphics2D g2) { * * @return A boolean. */ - public boolean getItemVisible(int series, int item) { + public boolean getItemVisible(@NonNegative int series, @NonNegative int item) { return isSeriesVisible(series); } /** * Returns a boolean that indicates whether or not the specified series - * should be drawn. In fact this method should be named + * should be drawn. In fact this method should be named * lookupSeriesVisible() to be consistent with the other series * attributes and avoid confusion with the getSeriesVisible() method. * @@ -526,7 +528,7 @@ public boolean getItemVisible(int series, int item) { * * @return A boolean. */ - public boolean isSeriesVisible(int series) { + public boolean isSeriesVisible(@NonNegative int series) { boolean result = this.defaultSeriesVisible; Boolean b = this.seriesVisibleList.getBoolean(series); if (b != null) { @@ -544,7 +546,7 @@ public boolean isSeriesVisible(int series) { * * @see #setSeriesVisible(int, Boolean) */ - public Boolean getSeriesVisible(int series) { + public Boolean getSeriesVisible(@NonNegative int series) { return this.seriesVisibleList.getBoolean(series); } @@ -557,7 +559,7 @@ public Boolean getSeriesVisible(int series) { * * @see #getSeriesVisible(int) */ - public void setSeriesVisible(int series, Boolean visible) { + public void setSeriesVisible(@NonNegative int series, Boolean visible) { setSeriesVisible(series, visible, true); } @@ -572,7 +574,7 @@ public void setSeriesVisible(int series, Boolean visible) { * * @see #getSeriesVisible(int) */ - public void setSeriesVisible(int series, Boolean visible, boolean notify) { + public void setSeriesVisible(@NonNegative int series, Boolean visible, boolean notify) { this.seriesVisibleList.setBoolean(series, visible); if (notify) { // we create an event with a special flag set...the purpose of @@ -596,7 +598,7 @@ public boolean getDefaultSeriesVisible() { } /** - * Sets the default series visibility and sends a + * Sets the default series visibility and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the flag. @@ -639,7 +641,7 @@ public void setDefaultSeriesVisible(boolean visible, boolean notify) { * * @return A boolean. */ - public boolean isSeriesVisibleInLegend(int series) { + public boolean isSeriesVisibleInLegend(@NonNegative int series) { boolean result = this.defaultSeriesVisibleInLegend; Boolean b = this.seriesVisibleInLegendList.getBoolean(series); if (b != null) { @@ -660,7 +662,7 @@ public boolean isSeriesVisibleInLegend(int series) { * * @see #setSeriesVisibleInLegend(int, Boolean) */ - public Boolean getSeriesVisibleInLegend(int series) { + public Boolean getSeriesVisibleInLegend(@NonNegative int series) { return this.seriesVisibleInLegendList.getBoolean(series); } @@ -673,7 +675,7 @@ public Boolean getSeriesVisibleInLegend(int series) { * * @see #getSeriesVisibleInLegend(int) */ - public void setSeriesVisibleInLegend(int series, Boolean visible) { + public void setSeriesVisibleInLegend(@NonNegative int series, Boolean visible) { setSeriesVisibleInLegend(series, visible, true); } @@ -688,7 +690,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible) { * * @see #getSeriesVisibleInLegend(int) */ - public void setSeriesVisibleInLegend(int series, Boolean visible, + public void setSeriesVisibleInLegend(@NonNegative int series, Boolean visible, boolean notify) { this.seriesVisibleInLegendList.setBoolean(series, visible); if (notify) { @@ -729,7 +731,7 @@ public void setDefaultSeriesVisibleInLegend(boolean visible) { * * @see #getDefaultSeriesVisibleInLegend() */ - public void setDefaultSeriesVisibleInLegend(boolean visible, + public void setDefaultSeriesVisibleInLegend(boolean visible, boolean notify) { this.defaultSeriesVisibleInLegend = visible; if (notify) { @@ -752,7 +754,7 @@ public void setDefaultSeriesVisibleInLegend(boolean visible, * * @return The paint (never {@code null}). */ - public Paint getItemPaint(int row, int column) { + public Paint getItemPaint(@NonNegative int row, @NonNegative int column) { return lookupSeriesPaint(row); } @@ -765,7 +767,7 @@ public Paint getItemPaint(int row, int column) { * * @since 1.0.6 */ - public Paint lookupSeriesPaint(int series) { + public Paint lookupSeriesPaint(@NonNegative int series) { Paint seriesPaint = getSeriesPaint(series); if (seriesPaint == null && this.autoPopulateSeriesPaint) { @@ -791,7 +793,7 @@ public Paint lookupSeriesPaint(int series) { * * @see #setSeriesPaint(int, Paint) */ - public Paint getSeriesPaint(int series) { + public Paint getSeriesPaint(@NonNegative int series) { return this.paintList.getPaint(series); } @@ -804,7 +806,7 @@ public Paint getSeriesPaint(int series) { * * @see #getSeriesPaint(int) */ - public void setSeriesPaint(int series, Paint paint) { + public void setSeriesPaint(@NonNegative int series, Paint paint) { setSeriesPaint(series, paint, true); } @@ -818,7 +820,7 @@ public void setSeriesPaint(int series, Paint paint) { * * @see #getSeriesPaint(int) */ - public void setSeriesPaint(int series, Paint paint, boolean notify) { + public void setSeriesPaint(@NonNegative int series, Paint paint, boolean notify) { this.paintList.setPaint(series, paint); if (notify) { fireChangeEvent(); @@ -921,7 +923,7 @@ public void setAutoPopulateSeriesPaint(boolean auto) { * * @return The paint (never {@code null}). */ - public Paint getItemFillPaint(int row, int column) { + public Paint getItemFillPaint(@NonNegative int row, @NonNegative int column) { return lookupSeriesFillPaint(row); } @@ -934,7 +936,7 @@ public Paint getItemFillPaint(int row, int column) { * * @since 1.0.6 */ - public Paint lookupSeriesFillPaint(int series) { + public Paint lookupSeriesFillPaint(@NonNegative int series) { Paint seriesFillPaint = getSeriesFillPaint(series); if (seriesFillPaint == null && this.autoPopulateSeriesFillPaint) { @@ -960,7 +962,7 @@ public Paint lookupSeriesFillPaint(int series) { * * @see #setSeriesFillPaint(int, Paint) */ - public Paint getSeriesFillPaint(int series) { + public Paint getSeriesFillPaint(@NonNegative int series) { return this.fillPaintList.getPaint(series); } @@ -973,7 +975,7 @@ public Paint getSeriesFillPaint(int series) { * * @see #getSeriesFillPaint(int) */ - public void setSeriesFillPaint(int series, Paint paint) { + public void setSeriesFillPaint(@NonNegative int series, Paint paint) { setSeriesFillPaint(series, paint, true); } @@ -987,7 +989,7 @@ public void setSeriesFillPaint(int series, Paint paint) { * * @see #getSeriesFillPaint(int) */ - public void setSeriesFillPaint(int series, Paint paint, boolean notify) { + public void setSeriesFillPaint(@NonNegative int series, Paint paint, boolean notify) { this.fillPaintList.setPaint(series, paint); if (notify) { fireChangeEvent(); @@ -1080,7 +1082,7 @@ public void setAutoPopulateSeriesFillPaint(boolean auto) { * * @return The paint (never {@code null}). */ - public Paint getItemOutlinePaint(int row, int column) { + public Paint getItemOutlinePaint(@NonNegative int row, @NonNegative int column) { return lookupSeriesOutlinePaint(row); } @@ -1093,7 +1095,7 @@ public Paint getItemOutlinePaint(int row, int column) { * * @since 1.0.6 */ - public Paint lookupSeriesOutlinePaint(int series) { + public Paint lookupSeriesOutlinePaint(@NonNegative int series) { Paint seriesOutlinePaint = getSeriesOutlinePaint(series); if (seriesOutlinePaint == null && this.autoPopulateSeriesOutlinePaint) { @@ -1119,7 +1121,7 @@ public Paint lookupSeriesOutlinePaint(int series) { * * @see #setSeriesOutlinePaint(int, Paint) */ - public Paint getSeriesOutlinePaint(int series) { + public Paint getSeriesOutlinePaint(@NonNegative int series) { return this.outlinePaintList.getPaint(series); } @@ -1132,7 +1134,7 @@ public Paint getSeriesOutlinePaint(int series) { * * @see #getSeriesOutlinePaint(int) */ - public void setSeriesOutlinePaint(int series, Paint paint) { + public void setSeriesOutlinePaint(@NonNegative int series, Paint paint) { setSeriesOutlinePaint(series, paint, true); } @@ -1146,7 +1148,7 @@ public void setSeriesOutlinePaint(int series, Paint paint) { * * @see #getSeriesOutlinePaint(int) */ - public void setSeriesOutlinePaint(int series, Paint paint, boolean notify) { + public void setSeriesOutlinePaint(@NonNegative int series, Paint paint, boolean notify) { this.outlinePaintList.setPaint(series, paint); if (notify) { fireChangeEvent(); @@ -1237,7 +1239,7 @@ public void setAutoPopulateSeriesOutlinePaint(boolean auto) { * * @return The stroke (never {@code null}). */ - public Stroke getItemStroke(int row, int column) { + public Stroke getItemStroke(@NonNegative int row, @NonNegative int column) { return lookupSeriesStroke(row); } @@ -1250,7 +1252,7 @@ public Stroke getItemStroke(int row, int column) { * * @since 1.0.6 */ - public Stroke lookupSeriesStroke(int series) { + public Stroke lookupSeriesStroke(@NonNegative int series) { Stroke result = getSeriesStroke(series); if (result == null && this.autoPopulateSeriesStroke) { @@ -1276,7 +1278,7 @@ public Stroke lookupSeriesStroke(int series) { * * @see #setSeriesStroke(int, Stroke) */ - public Stroke getSeriesStroke(int series) { + public Stroke getSeriesStroke(@NonNegative int series) { return this.strokeList.getStroke(series); } @@ -1289,7 +1291,7 @@ public Stroke getSeriesStroke(int series) { * * @see #getSeriesStroke(int) */ - public void setSeriesStroke(int series, Stroke stroke) { + public void setSeriesStroke(@NonNegative int series, Stroke stroke) { setSeriesStroke(series, stroke, true); } @@ -1303,7 +1305,7 @@ public void setSeriesStroke(int series, Stroke stroke) { * * @see #getSeriesStroke(int) */ - public void setSeriesStroke(int series, Stroke stroke, boolean notify) { + public void setSeriesStroke(@NonNegative int series, Stroke stroke, boolean notify) { this.strokeList.setStroke(series, stroke); if (notify) { fireChangeEvent(); @@ -1407,7 +1409,7 @@ public void setAutoPopulateSeriesStroke(boolean auto) { * * @return The stroke (never {@code null}). */ - public Stroke getItemOutlineStroke(int row, int column) { + public Stroke getItemOutlineStroke(@NonNegative int row, @NonNegative int column) { return lookupSeriesOutlineStroke(row); } @@ -1420,7 +1422,7 @@ public Stroke getItemOutlineStroke(int row, int column) { * * @since 1.0.6 */ - public Stroke lookupSeriesOutlineStroke(int series) { + public Stroke lookupSeriesOutlineStroke(@NonNegative int series) { Stroke result = getSeriesOutlineStroke(series); if (result == null && this.autoPopulateSeriesOutlineStroke) { @@ -1446,7 +1448,7 @@ public Stroke lookupSeriesOutlineStroke(int series) { * * @see #setSeriesOutlineStroke(int, Stroke) */ - public Stroke getSeriesOutlineStroke(int series) { + public Stroke getSeriesOutlineStroke(@NonNegative int series) { return this.outlineStrokeList.getStroke(series); } @@ -1459,7 +1461,7 @@ public Stroke getSeriesOutlineStroke(int series) { * * @see #getSeriesOutlineStroke(int) */ - public void setSeriesOutlineStroke(int series, Stroke stroke) { + public void setSeriesOutlineStroke(@NonNegative int series, Stroke stroke) { setSeriesOutlineStroke(series, stroke, true); } @@ -1473,7 +1475,7 @@ public void setSeriesOutlineStroke(int series, Stroke stroke) { * * @see #getSeriesOutlineStroke(int) */ - public void setSeriesOutlineStroke(int series, Stroke stroke, + public void setSeriesOutlineStroke(@NonNegative int series, Stroke stroke, boolean notify) { this.outlineStrokeList.setStroke(series, stroke); if (notify) { @@ -1493,7 +1495,7 @@ public Stroke getDefaultOutlineStroke() { } /** - * Sets the default outline stroke and sends a {@link RendererChangeEvent} + * Sets the default outline stroke and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). @@ -1557,8 +1559,8 @@ public void setAutoPopulateSeriesOutlineStroke(boolean auto) { /** * Returns a shape used to represent a data item. * <p> - * The default implementation passes control to the - * {@link #lookupSeriesShape(int)} method. You can override this method if + * The default implementation passes control to the + * {@link #lookupSeriesShape(int)} method. You can override this method if * you require different behaviour. * * @param row the row (or series) index (zero-based). @@ -1566,7 +1568,7 @@ public void setAutoPopulateSeriesOutlineStroke(boolean auto) { * * @return The shape (never {@code null}). */ - public Shape getItemShape(int row, int column) { + public Shape getItemShape(@NonNegative int row, @NonNegative int column) { return lookupSeriesShape(row); } @@ -1579,7 +1581,7 @@ public Shape getItemShape(int row, int column) { * * @since 1.0.6 */ - public Shape lookupSeriesShape(int series) { + public Shape lookupSeriesShape(@NonNegative int series) { Shape result = getSeriesShape(series); if (result == null && this.autoPopulateSeriesShape) { @@ -1605,7 +1607,7 @@ public Shape lookupSeriesShape(int series) { * * @see #setSeriesShape(int, Shape) */ - public Shape getSeriesShape(int series) { + public Shape getSeriesShape(@NonNegative int series) { return this.shapeList.getShape(series); } @@ -1618,7 +1620,7 @@ public Shape getSeriesShape(int series) { * * @see #getSeriesShape(int) */ - public void setSeriesShape(int series, Shape shape) { + public void setSeriesShape(@NonNegative int series, Shape shape) { setSeriesShape(series, shape, true); } @@ -1632,7 +1634,7 @@ public void setSeriesShape(int series, Shape shape) { * * @see #getSeriesShape(int) */ - public void setSeriesShape(int series, Shape shape, boolean notify) { + public void setSeriesShape(@NonNegative int series, Shape shape, boolean notify) { this.shapeList.setShape(series, shape); if (notify) { fireChangeEvent(); @@ -1719,7 +1721,7 @@ public void setAutoPopulateSeriesShape(boolean auto) { * * @return A boolean. */ - public boolean isItemLabelVisible(int row, int column) { + public boolean isItemLabelVisible(@NonNegative int row, @NonNegative int column) { return isSeriesItemLabelsVisible(row); } @@ -1731,7 +1733,7 @@ public boolean isItemLabelVisible(int row, int column) { * * @return A boolean. */ - public boolean isSeriesItemLabelsVisible(int series) { + public boolean isSeriesItemLabelsVisible(@NonNegative int series) { Boolean b = this.itemLabelsVisibleList.getBoolean(series); if (b == null) { return this.defaultItemLabelsVisible; @@ -1746,7 +1748,7 @@ public boolean isSeriesItemLabelsVisible(int series) { * @param series the series index (zero-based). * @param visible the flag. */ - public void setSeriesItemLabelsVisible(int series, boolean visible) { + public void setSeriesItemLabelsVisible(@NonNegative int series, boolean visible) { setSeriesItemLabelsVisible(series, Boolean.valueOf(visible)); } @@ -1757,7 +1759,7 @@ public void setSeriesItemLabelsVisible(int series, boolean visible) { * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). */ - public void setSeriesItemLabelsVisible(int series, Boolean visible) { + public void setSeriesItemLabelsVisible(@NonNegative int series, Boolean visible) { setSeriesItemLabelsVisible(series, visible, true); } @@ -1770,7 +1772,7 @@ public void setSeriesItemLabelsVisible(int series, Boolean visible) { * @param notify a flag that controls whether or not listeners are * notified. */ - public void setSeriesItemLabelsVisible(int series, Boolean visible, + public void setSeriesItemLabelsVisible(@NonNegative int series, Boolean visible, boolean notify) { this.itemLabelsVisibleList.setBoolean(series, visible); if (notify) { @@ -1811,7 +1813,7 @@ public void setDefaultItemLabelsVisible(boolean visible) { * @param notify a flag that controls whether or not listeners are * notified. * - * @see #getDefaultItemLabelsVisible() + * @see #getDefaultItemLabelsVisible() */ public void setDefaultItemLabelsVisible(boolean visible, boolean notify) { this.defaultItemLabelsVisible = visible; @@ -1830,7 +1832,7 @@ public void setDefaultItemLabelsVisible(boolean visible, boolean notify) { * * @return The font (never {@code null}). */ - public Font getItemLabelFont(int row, int column) { + public Font getItemLabelFont(@NonNegative int row, @NonNegative int column) { Font result = getSeriesItemLabelFont(row); if (result == null) { result = this.defaultItemLabelFont; @@ -1847,7 +1849,7 @@ public Font getItemLabelFont(int row, int column) { * * @see #setSeriesItemLabelFont(int, Font) */ - public Font getSeriesItemLabelFont(int series) { + public Font getSeriesItemLabelFont(@NonNegative int series) { return this.itemLabelFontMap.get(series); } @@ -1860,7 +1862,7 @@ public Font getSeriesItemLabelFont(int series) { * * @see #getSeriesItemLabelFont(int) */ - public void setSeriesItemLabelFont(int series, Font font) { + public void setSeriesItemLabelFont(@NonNegative int series, Font font) { setSeriesItemLabelFont(series, font, true); } @@ -1875,7 +1877,7 @@ public void setSeriesItemLabelFont(int series, Font font) { * * @see #getSeriesItemLabelFont(int) */ - public void setSeriesItemLabelFont(int series, Font font, boolean notify) { + public void setSeriesItemLabelFont(@NonNegative int series, Font font, boolean notify) { this.itemLabelFontMap.put(series, font); if (notify) { fireChangeEvent(); @@ -1895,7 +1897,7 @@ public Font getDefaultItemLabelFont() { } /** - * Sets the default item label font and sends a {@link RendererChangeEvent} + * Sets the default item label font and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param font the font ({@code null} not permitted). @@ -1934,7 +1936,7 @@ public void setDefaultItemLabelFont(Font font, boolean notify) { * * @return The paint (never {@code null}). */ - public Paint getItemLabelPaint(int row, int column) { + public Paint getItemLabelPaint(@NonNegative int row, @NonNegative int column) { Paint result = getSeriesItemLabelPaint(row); if (result == null) { result = this.defaultItemLabelPaint; @@ -1951,7 +1953,7 @@ public Paint getItemLabelPaint(int row, int column) { * * @see #setSeriesItemLabelPaint(int, Paint) */ - public Paint getSeriesItemLabelPaint(int series) { + public Paint getSeriesItemLabelPaint(@NonNegative int series) { return this.itemLabelPaintList.getPaint(series); } @@ -1964,7 +1966,7 @@ public Paint getSeriesItemLabelPaint(int series) { * * @see #getSeriesItemLabelPaint(int) */ - public void setSeriesItemLabelPaint(int series, Paint paint) { + public void setSeriesItemLabelPaint(@NonNegative int series, Paint paint) { setSeriesItemLabelPaint(series, paint, true); } @@ -1979,7 +1981,7 @@ public void setSeriesItemLabelPaint(int series, Paint paint) { * * @see #getSeriesItemLabelPaint(int) */ - public void setSeriesItemLabelPaint(int series, Paint paint, + public void setSeriesItemLabelPaint(@NonNegative int series, Paint paint, boolean notify) { this.itemLabelPaintList.setPaint(series, paint); if (notify) { @@ -2041,7 +2043,7 @@ public void setDefaultItemLabelPaint(Paint paint, boolean notify) { * * @see #getNegativeItemLabelPosition(int, int) */ - public ItemLabelPosition getPositiveItemLabelPosition(int row, int column) { + public ItemLabelPosition getPositiveItemLabelPosition(@NonNegative int row, @NonNegative int column) { return getSeriesPositiveItemLabelPosition(row); } @@ -2054,7 +2056,7 @@ public ItemLabelPosition getPositiveItemLabelPosition(int row, int column) { * * @see #setSeriesPositiveItemLabelPosition(int, ItemLabelPosition) */ - public ItemLabelPosition getSeriesPositiveItemLabelPosition(int series) { + public ItemLabelPosition getSeriesPositiveItemLabelPosition(@NonNegative int series) { // otherwise look up the position table ItemLabelPosition position = (ItemLabelPosition) this.positiveItemLabelPositionMap.get(series); @@ -2073,7 +2075,7 @@ public ItemLabelPosition getSeriesPositiveItemLabelPosition(int series) { * * @see #getSeriesPositiveItemLabelPosition(int) */ - public void setSeriesPositiveItemLabelPosition(int series, + public void setSeriesPositiveItemLabelPosition(@NonNegative int series, ItemLabelPosition position) { setSeriesPositiveItemLabelPosition(series, position, true); } @@ -2089,7 +2091,7 @@ public void setSeriesPositiveItemLabelPosition(int series, * * @see #getSeriesPositiveItemLabelPosition(int) */ - public void setSeriesPositiveItemLabelPosition(int series, + public void setSeriesPositiveItemLabelPosition(@NonNegative int series, ItemLabelPosition position, boolean notify) { this.positiveItemLabelPositionMap.put(series, position); if (notify) { @@ -2153,7 +2155,7 @@ public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, * * @see #getPositiveItemLabelPosition(int, int) */ - public ItemLabelPosition getNegativeItemLabelPosition(int row, int column) { + public ItemLabelPosition getNegativeItemLabelPosition(@NonNegative int row, @NonNegative int column) { return getSeriesNegativeItemLabelPosition(row); } @@ -2166,9 +2168,9 @@ public ItemLabelPosition getNegativeItemLabelPosition(int row, int column) { * * @see #setSeriesNegativeItemLabelPosition(int, ItemLabelPosition) */ - public ItemLabelPosition getSeriesNegativeItemLabelPosition(int series) { + public ItemLabelPosition getSeriesNegativeItemLabelPosition(@NonNegative int series) { // otherwise look up the position list - ItemLabelPosition position + ItemLabelPosition position = this.negativeItemLabelPositionMap.get(series); if (position == null) { position = this.defaultNegativeItemLabelPosition; @@ -2185,7 +2187,7 @@ public ItemLabelPosition getSeriesNegativeItemLabelPosition(int series) { * * @see #getSeriesNegativeItemLabelPosition(int) */ - public void setSeriesNegativeItemLabelPosition(int series, + public void setSeriesNegativeItemLabelPosition(@NonNegative int series, ItemLabelPosition position) { setSeriesNegativeItemLabelPosition(series, position, true); } @@ -2201,7 +2203,7 @@ public void setSeriesNegativeItemLabelPosition(int series, * * @see #getSeriesNegativeItemLabelPosition(int) */ - public void setSeriesNegativeItemLabelPosition(int series, + public void setSeriesNegativeItemLabelPosition(@NonNegative int series, ItemLabelPosition position, boolean notify) { this.negativeItemLabelPositionMap.put(series, position); if (notify) { @@ -2283,7 +2285,7 @@ public void setItemLabelAnchorOffset(double offset) { * * @return A boolean. */ - public boolean getItemCreateEntity(int series, int item) { + public boolean getItemCreateEntity(@NonNegative int series, @NonNegative int item) { Boolean b = getSeriesCreateEntities(series); if (b != null) { return b; @@ -2302,7 +2304,7 @@ public boolean getItemCreateEntity(int series, int item) { * * @see #setSeriesCreateEntities(int, Boolean) */ - public Boolean getSeriesCreateEntities(int series) { + public Boolean getSeriesCreateEntities(@NonNegative int series) { return this.createEntitiesList.getBoolean(series); } @@ -2315,7 +2317,7 @@ public Boolean getSeriesCreateEntities(int series) { * * @see #getSeriesCreateEntities(int) */ - public void setSeriesCreateEntities(int series, Boolean create) { + public void setSeriesCreateEntities(@NonNegative int series, Boolean create) { setSeriesCreateEntities(series, create, true); } @@ -2330,7 +2332,7 @@ public void setSeriesCreateEntities(int series, Boolean create) { * * @see #getSeriesCreateEntities(int) */ - public void setSeriesCreateEntities(int series, Boolean create, + public void setSeriesCreateEntities(@NonNegative int series, Boolean create, boolean notify) { this.createEntitiesList.setBoolean(series, create); if (notify) { @@ -2388,7 +2390,7 @@ public void setDefaultCreateEntities(boolean create, boolean notify) { * * @see #setDefaultEntityRadius(int) */ - public int getDefaultEntityRadius() { + public @NonNegative int getDefaultEntityRadius() { return this.defaultEntityRadius; } @@ -2400,7 +2402,7 @@ public int getDefaultEntityRadius() { * * @see #getDefaultEntityRadius() */ - public void setDefaultEntityRadius(int radius) { + public void setDefaultEntityRadius(@NonNegative int radius) { this.defaultEntityRadius = radius; } @@ -2413,7 +2415,7 @@ public void setDefaultEntityRadius(int radius) { * * @since 1.0.11 */ - public Shape lookupLegendShape(int series) { + public Shape lookupLegendShape(@NonNegative int series) { Shape result = getLegendShape(series); if (result == null) { result = this.defaultLegendShape; @@ -2436,7 +2438,7 @@ public Shape lookupLegendShape(int series) { * * @since 1.0.11 */ - public Shape getLegendShape(int series) { + public Shape getLegendShape(@NonNegative int series) { return this.legendShapeList.getShape(series); } @@ -2449,7 +2451,7 @@ public Shape getLegendShape(int series) { * * @since 1.0.11 */ - public void setLegendShape(int series, Shape shape) { + public void setLegendShape(@NonNegative int series, Shape shape) { this.legendShapeList.setShape(series, shape); fireChangeEvent(); } @@ -2481,9 +2483,9 @@ public void setDefaultLegendShape(Shape shape) { /** * Returns the flag that controls whether or not the legend shape is * treated as a line when creating legend items. - * + * * @return A boolean. - * + * * @since 1.0.14 */ protected boolean getTreatLegendShapeAsLine() { @@ -2514,7 +2516,7 @@ protected void setTreatLegendShapeAsLine(boolean treatAsLine) { * * @since 1.0.11 */ - public Font lookupLegendTextFont(int series) { + public Font lookupLegendTextFont(@NonNegative int series) { Font result = getLegendTextFont(series); if (result == null) { result = this.defaultLegendTextFont; @@ -2534,7 +2536,7 @@ public Font lookupLegendTextFont(int series) { * * @since 1.0.11 */ - public Font getLegendTextFont(int series) { + public Font getLegendTextFont(@NonNegative int series) { return this.legendTextFontMap.get(series); } @@ -2547,7 +2549,7 @@ public Font getLegendTextFont(int series) { * * @since 1.0.11 */ - public void setLegendTextFont(int series, Font font) { + public void setLegendTextFont(@NonNegative int series, Font font) { this.legendTextFontMap.put(series, font); fireChangeEvent(); } @@ -2586,7 +2588,7 @@ public void setDefaultLegendTextFont(Font font) { * * @since 1.0.11 */ - public Paint lookupLegendTextPaint(int series) { + public Paint lookupLegendTextPaint(@NonNegative int series) { Paint result = getLegendTextPaint(series); if (result == null) { result = this.defaultLegendTextPaint; @@ -2606,7 +2608,7 @@ public Paint lookupLegendTextPaint(int series) { * * @since 1.0.11 */ - public Paint getLegendTextPaint(int series) { + public Paint getLegendTextPaint(@NonNegative int series) { return this.legendTextPaint.getPaint(series); } @@ -2619,7 +2621,7 @@ public Paint getLegendTextPaint(int series) { * * @since 1.0.11 */ - public void setLegendTextPaint(int series, Paint paint) { + public void setLegendTextPaint(@NonNegative int series, Paint paint) { this.legendTextPaint.setPaint(series, paint); fireChangeEvent(); } diff --git a/src/main/java/org/jfree/chart/renderer/DefaultPolarItemRenderer.java b/src/main/java/org/jfree/chart/renderer/DefaultPolarItemRenderer.java index e777ff71d..b35e367e1 100644 --- a/src/main/java/org/jfree/chart/renderer/DefaultPolarItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/DefaultPolarItemRenderer.java @@ -63,6 +63,10 @@ package org.jfree.chart.renderer; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics2D; @@ -367,7 +371,7 @@ public DrawingSupplier getDrawingSupplier() { * * @return A boolean. */ - public boolean isSeriesFilled(int series) { + public boolean isSeriesFilled(@NonNegative int series) { boolean result = false; Boolean b = this.seriesFilled.getBoolean(series); if (b != null) { @@ -382,7 +386,7 @@ public boolean isSeriesFilled(int series) { * @param series the series index. * @param filled the flag. */ - public void setSeriesFilled(int series, boolean filled) { + public void setSeriesFilled(@NonNegative int series, boolean filled) { this.seriesFilled.setBoolean(series, Boolean.valueOf(filled)); } @@ -455,7 +459,7 @@ public void setLegendLine(Shape line) { * used if {@code area} is {@code null}). */ protected void addEntity(EntityCollection entities, Shape area, - XYDataset dataset, int series, int item, + XYDataset dataset, @NonNegative int series, @IndexFor("#3.getSeries(#4)") int item, double entityX, double entityY) { if (!getItemCreateEntity(series, item)) { return; @@ -498,13 +502,14 @@ protected void addEntity(EntityCollection entities, Shape area, @Override public void drawSeries(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, PolarPlot plot, XYDataset dataset, - int seriesIndex) { + @NonNegative int seriesIndex) { final int numPoints = dataset.getItemCount(seriesIndex); if (numPoints == 0) { return; } GeneralPath poly = null; + @SuppressWarnings("index") // This would fail if the dataset passed to this function was not associated with this plot. Maybe a bug? ValueAxis axis = plot.getAxisForDataset(plot.indexOf(dataset)); for (int i = 0; i < numPoints; i++) { double theta = dataset.getXValue(seriesIndex, i); @@ -583,7 +588,9 @@ public void drawSeries(Graphics2D g2, Rectangle2D dataArea, // data area... if (entities != null && ShapeUtils.isPointInRect(dataArea, x, y)) { - addEntity(entities, shape, dataset, seriesIndex, i-1, x, y); + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/219: i - 1 is an index because the i increases once on each iteration of this while loop, and the loop condition is equivalent to the length of this series + @IndexFor("dataset.getSeries(seriesIndex)") int i1 = i - 1; + addEntity(entities, shape, dataset, seriesIndex, i1, x, y); } } } @@ -684,12 +691,13 @@ public void drawRadialGridLines(Graphics2D g2, PolarPlot plot, * @return The legend item. */ @Override - public LegendItem getLegendItem(int series) { + public LegendItem getLegendItem(@NonNegative int series) { LegendItem result; PolarPlot plot = getPlot(); if (plot == null) { return null; } + @SuppressWarnings("index") // guaranteed index: this renderer belongs to the plot, so plot.getIndexOf returns a non negative XYDataset dataset = plot.getDataset(plot.getIndexOf(this)); if (dataset == null) { return null; @@ -746,7 +754,7 @@ public LegendItem getLegendItem(int series) { * @since 1.0.14 */ @Override - public XYToolTipGenerator getToolTipGenerator(int series, int item) { + public XYToolTipGenerator getToolTipGenerator(@NonNegative int series, @NonNegative int item) { XYToolTipGenerator generator = (XYToolTipGenerator) this.toolTipGeneratorList.get(series); if (generator == null) { @@ -763,7 +771,7 @@ public XYToolTipGenerator getToolTipGenerator(int series, int item) { * @since 1.0.14 */ @Override - public XYToolTipGenerator getSeriesToolTipGenerator(int series) { + public XYToolTipGenerator getSeriesToolTipGenerator(@NonNegative int series) { return (XYToolTipGenerator) this.toolTipGeneratorList.get(series); } @@ -776,7 +784,7 @@ public XYToolTipGenerator getSeriesToolTipGenerator(int series) { * @since 1.0.14 */ @Override - public void setSeriesToolTipGenerator(int series, + public void setSeriesToolTipGenerator(@NonNegative int series, XYToolTipGenerator generator) { this.toolTipGeneratorList.set(series, generator); fireChangeEvent(); diff --git a/src/main/java/org/jfree/chart/renderer/LookupPaintScale.java b/src/main/java/org/jfree/chart/renderer/LookupPaintScale.java index 447484585..74d818836 100644 --- a/src/main/java/org/jfree/chart/renderer/LookupPaintScale.java +++ b/src/main/java/org/jfree/chart/renderer/LookupPaintScale.java @@ -45,6 +45,8 @@ package org.jfree.chart.renderer; +import org.checkerframework.checker.index.qual.*; + import java.awt.Color; import java.awt.Paint; import java.io.IOException; @@ -295,10 +297,11 @@ public Paint getPaint(double value) { } // for value in bounds, do the lookup... - int low = 0; - int high = this.lookupTable.size() - 1; + @NonNegative int low = 0; + @SuppressWarnings("index") // lookupTable always has at least one element + @NonNegative int high = this.lookupTable.size() - 1; while (high - low > 1) { - int current = (low + high) / 2; + @NonNegative int current = (low + high) / 2; item = (PaintItem) this.lookupTable.get(current); if (value >= item.value) { low = current; diff --git a/src/main/java/org/jfree/chart/renderer/OutlierList.java b/src/main/java/org/jfree/chart/renderer/OutlierList.java index 2ef1d57f9..78b1a7d27 100644 --- a/src/main/java/org/jfree/chart/renderer/OutlierList.java +++ b/src/main/java/org/jfree/chart/renderer/OutlierList.java @@ -44,6 +44,8 @@ package org.jfree.chart.renderer; +import org.checkerframework.checker.index.qual.*; + import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Iterator; @@ -103,7 +105,7 @@ public boolean add(Outlier outlier) { * * @return The item count. */ - public int getItemCount() { + public @NonNegative int getItemCount() { return this.outliers.size(); } diff --git a/src/main/java/org/jfree/chart/renderer/PolarItemRenderer.java b/src/main/java/org/jfree/chart/renderer/PolarItemRenderer.java index b19790e44..f45cc788d 100644 --- a/src/main/java/org/jfree/chart/renderer/PolarItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/PolarItemRenderer.java @@ -41,6 +41,8 @@ package org.jfree.chart.renderer; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.util.List; @@ -73,7 +75,7 @@ public interface PolarItemRenderer { */ public void drawSeries(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, PolarPlot plot, XYDataset dataset, - int seriesIndex); + @NonNegative int seriesIndex); /** * Draw the angular gridlines - the spokes. @@ -105,7 +107,7 @@ public void drawRadialGridLines(Graphics2D g2, PolarPlot plot, * * @return The legend item. */ - public LegendItem getLegendItem(int series); + public LegendItem getLegendItem(@NonNegative int series); /** * Returns the plot that this renderer has been assigned to. @@ -149,7 +151,7 @@ public void drawRadialGridLines(Graphics2D g2, PolarPlot plot, * * @since 1.0.14 */ - public XYToolTipGenerator getToolTipGenerator(int row, int column); + public XYToolTipGenerator getToolTipGenerator(@NonNegative int row, @NonNegative int column); /** * Returns the tool tip generator for a series. @@ -162,7 +164,7 @@ public void drawRadialGridLines(Graphics2D g2, PolarPlot plot, * * @since 1.0.14 */ - public XYToolTipGenerator getSeriesToolTipGenerator(int series); + public XYToolTipGenerator getSeriesToolTipGenerator(@NonNegative int series); /** * Sets the tool tip generator for a series and sends a @@ -175,7 +177,7 @@ public void drawRadialGridLines(Graphics2D g2, PolarPlot plot, * * @since 1.0.14 */ - public void setSeriesToolTipGenerator(int series, + public void setSeriesToolTipGenerator(@NonNegative int series, XYToolTipGenerator generator); /** diff --git a/src/main/java/org/jfree/chart/renderer/RendererUtils.java b/src/main/java/org/jfree/chart/renderer/RendererUtils.java index d362f7681..72f5203eb 100644 --- a/src/main/java/org/jfree/chart/renderer/RendererUtils.java +++ b/src/main/java/org/jfree/chart/renderer/RendererUtils.java @@ -44,6 +44,9 @@ package org.jfree.chart.renderer; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import org.jfree.chart.util.Args; import org.jfree.data.DomainOrder; import org.jfree.data.xy.XYDataset; @@ -70,7 +73,7 @@ public class RendererUtils { * * @see #findLiveItemsUpperBound(XYDataset, int, double, double) */ - public static int findLiveItemsLowerBound(XYDataset dataset, int series, + public static @NonNegative int findLiveItemsLowerBound(XYDataset dataset, @NonNegative int series, double xLow, double xHigh) { Args.nullNotPermitted(dataset, "dataset"); if (xLow >= xHigh) { @@ -83,8 +86,9 @@ public static int findLiveItemsLowerBound(XYDataset dataset, int series, if (dataset.getDomainOrder() == DomainOrder.ASCENDING) { // for data in ascending order by x-value, we are (broadly) looking // for the index of the highest x-value that is less than xLow - int low = 0; - int high = itemCount - 1; + @SuppressWarnings("index") // 0 is an index, because the item count is checked above and the function returns if there isn't at least one item + @IndexFor("dataset.getSeries(series)") int low = 0; + @IndexFor("dataset.getSeries(series)") int high = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue >= xLow) { // special case where the lowest x-value is >= xLow @@ -110,8 +114,9 @@ public static int findLiveItemsLowerBound(XYDataset dataset, int series, else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { // when the x-values are sorted in descending order, the lower // bound is found by calculating relative to the xHigh value - int low = 0; - int high = itemCount - 1; + @SuppressWarnings("index") // 0 is an index, because the item count is checked above and the function returns if there isn't at least one item + @IndexFor("dataset.getSeries(series)") int low = 0; + @IndexFor("dataset.getSeries(series)") int high = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue <= xHigh) { return low; @@ -136,9 +141,11 @@ else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { // we don't know anything about the ordering of the x-values, // but we can still skip any initial values that fall outside the // range... - int index = 0; + @SuppressWarnings("index") // 0 is an index, because the item count is checked above and the function returns if there isn't at least one item + @IndexFor("dataset.getSeries(series)") int i = 0; // skip any items that don't need including... - double x = dataset.getXValue(series, index); + double x = dataset.getXValue(series, i); + int index = i; while (index < itemCount && x < xLow) { index++; if (index < itemCount) { @@ -164,7 +171,7 @@ else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { * * @see #findLiveItemsLowerBound(XYDataset, int, double, double) */ - public static int findLiveItemsUpperBound(XYDataset dataset, int series, + public static @NonNegative int findLiveItemsUpperBound(XYDataset dataset, @NonNegative int series, double xLow, double xHigh) { Args.nullNotPermitted(dataset, "dataset"); if (xLow >= xHigh) { @@ -175,8 +182,9 @@ public static int findLiveItemsUpperBound(XYDataset dataset, int series, return 0; } if (dataset.getDomainOrder() == DomainOrder.ASCENDING) { - int low = 0; - int high = itemCount - 1; + @SuppressWarnings("index") // 0 is an index, because the item count is checked above and the function returns if there isn't at least one item + @IndexFor("dataset.getSeries(series)") int low = 0; + @IndexFor("dataset.getSeries(series)") int high = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue > xHigh) { return low; @@ -201,8 +209,9 @@ public static int findLiveItemsUpperBound(XYDataset dataset, int series, else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { // when the x-values are descending, the upper bound is found by // comparing against xLow - int low = 0; - int high = itemCount - 1; + @SuppressWarnings("index") // 0 is an index, because the item count is checked above and the function returns if there isn't at least one item + @IndexFor("dataset.getSeries(series)") int low = 0; + @IndexFor("dataset.getSeries(series)") int high = itemCount - 1; int mid = (low + high) / 2; double lowValue = dataset.getXValue(series, low); if (lowValue < xLow) { @@ -229,8 +238,8 @@ else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { // but we can still skip any trailing values that fall outside the // range... int index = itemCount - 1; - // skip any items that don't need including... - double x = dataset.getXValue(series, index); + @IndexFor("dataset.getSeries(series)") int indexTmp = index; // skip any items that don't need including... + double x = dataset.getXValue(series, indexTmp); while (index >= 0 && x > xHigh) { index--; if (index >= 0) { @@ -252,17 +261,17 @@ else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { * * @return The indices of the boundary items. */ - public static int[] findLiveItems(XYDataset dataset, int series, - double xLow, double xHigh) { + public static @NonNegative int @ArrayLen(2) [] findLiveItems(XYDataset dataset, @NonNegative int series, + double xLow, double xHigh) { // here we could probably be a little faster by searching for both // indices simultaneously, but I'll look at that later if it seems // like it matters... - int i0 = findLiveItemsLowerBound(dataset, series, xLow, xHigh); - int i1 = findLiveItemsUpperBound(dataset, series, xLow, xHigh); + @NonNegative int i0 = findLiveItemsLowerBound(dataset, series, xLow, xHigh); + @NonNegative int i1 = findLiveItemsUpperBound(dataset, series, xLow, xHigh); if (i0 > i1) { i0 = i1; } - return new int[] {i0, i1}; + return new @NonNegative int[] {i0, i1}; } } diff --git a/src/main/java/org/jfree/chart/renderer/WaferMapRenderer.java b/src/main/java/org/jfree/chart/renderer/WaferMapRenderer.java index df07663f4..997e863f9 100644 --- a/src/main/java/org/jfree/chart/renderer/WaferMapRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/WaferMapRenderer.java @@ -44,6 +44,8 @@ package org.jfree.chart.renderer; +import org.checkerframework.checker.index.qual.*; + import java.awt.Color; import java.awt.Paint; import java.awt.Shape; @@ -197,7 +199,8 @@ public Paint getChipColor(Number value) { * * @return The paint index. */ - private int getPaintIndex(Number value) { + @SuppressWarnings("index") // paint indices are always non negative + private @NonNegative int getPaintIndex(Number value) { return ((Integer) this.paintIndex.get(value)).intValue(); } @@ -301,6 +304,7 @@ public LegendItemCollection getLegendCollection() { String label = entry.getKey().toString(); String description = label; Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d); + @SuppressWarnings("index") // paint indices are always nonnegative Paint paint = lookupSeriesPaint( ((Integer) entry.getValue()).intValue()); Paint outlinePaint = Color.BLACK; @@ -324,7 +328,8 @@ public LegendItemCollection getLegendCollection() { (Integer) entry.getValue()).toString(); String description = label; Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d); - Paint paint = getSeriesPaint( + @SuppressWarnings("index") // paint indices are always nonnegative + Paint paint = getSeriesPaint( ((Integer) entry.getValue()).intValue() ); Paint outlinePaint = Color.BLACK; diff --git a/src/main/java/org/jfree/chart/renderer/category/AbstractCategoryItemRenderer.java b/src/main/java/org/jfree/chart/renderer/category/AbstractCategoryItemRenderer.java index 8e85108ff..b309b345c 100644 --- a/src/main/java/org/jfree/chart/renderer/category/AbstractCategoryItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/AbstractCategoryItemRenderer.java @@ -111,6 +111,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.*; + import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Font; @@ -213,10 +215,10 @@ public abstract class AbstractCategoryItemRenderer extends AbstractRenderer private CategorySeriesLabelGenerator legendItemURLGenerator; /** The number of rows in the dataset (temporary record). */ - private transient int rowCount; + private transient @NonNegative int rowCount; /** The number of columns in the dataset (temporary record). */ - private transient int columnCount; + private transient @NonNegative int columnCount; /** * Creates a new renderer with no tool tip generator and no URL generator. @@ -243,7 +245,7 @@ protected AbstractCategoryItemRenderer() { * @return The pass count. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 1; } @@ -290,7 +292,7 @@ public void setPlot(CategoryPlot plot) { * @return The generator (possibly {@code null}). */ @Override - public CategoryItemLabelGenerator getItemLabelGenerator(int row, + public CategoryItemLabelGenerator getItemLabelGenerator(@NonNegative int row, int column) { return getSeriesItemLabelGenerator(row); } @@ -305,7 +307,7 @@ public CategoryItemLabelGenerator getItemLabelGenerator(int row, * @see #setSeriesItemLabelGenerator(int, CategoryItemLabelGenerator) */ @Override - public CategoryItemLabelGenerator getSeriesItemLabelGenerator(int series) { + public CategoryItemLabelGenerator getSeriesItemLabelGenerator(@NonNegative int series) { // otherwise look up the generator table CategoryItemLabelGenerator generator = this.itemLabelGeneratorMap.get( @@ -326,7 +328,7 @@ public CategoryItemLabelGenerator getSeriesItemLabelGenerator(int series) { * @see #getSeriesItemLabelGenerator(int) */ @Override - public void setSeriesItemLabelGenerator(int series, + public void setSeriesItemLabelGenerator(@NonNegative int series, CategoryItemLabelGenerator generator) { setSeriesItemLabelGenerator(series, generator, true); } @@ -342,7 +344,7 @@ public void setSeriesItemLabelGenerator(int series, * @see #getSeriesItemLabelGenerator(int) */ @Override - public void setSeriesItemLabelGenerator(int series, + public void setSeriesItemLabelGenerator(@NonNegative int series, CategoryItemLabelGenerator generator, boolean notify) { this.itemLabelGeneratorMap.put(series, generator); if (notify) { @@ -409,7 +411,7 @@ public void setDefaultItemLabelGenerator( * @return The generator (possibly {@code null}). */ @Override - public CategoryToolTipGenerator getToolTipGenerator(int row, int column) { + public CategoryToolTipGenerator getToolTipGenerator(@NonNegative int row, @NonNegative int column) { CategoryToolTipGenerator result = getSeriesToolTipGenerator(row); if (result == null) { @@ -429,7 +431,7 @@ public CategoryToolTipGenerator getToolTipGenerator(int row, int column) { * @see #setSeriesToolTipGenerator(int, CategoryToolTipGenerator) */ @Override - public CategoryToolTipGenerator getSeriesToolTipGenerator(int series) { + public CategoryToolTipGenerator getSeriesToolTipGenerator(@NonNegative int series) { return this.toolTipGeneratorMap.get(series); } @@ -443,7 +445,7 @@ public CategoryToolTipGenerator getSeriesToolTipGenerator(int series) { * @see #getSeriesToolTipGenerator(int) */ @Override - public void setSeriesToolTipGenerator(int series, + public void setSeriesToolTipGenerator(@NonNegative int series, CategoryToolTipGenerator generator) { setSeriesToolTipGenerator(series, generator, true); } @@ -459,7 +461,7 @@ public void setSeriesToolTipGenerator(int series, * @see #getSeriesToolTipGenerator(int) */ @Override - public void setSeriesToolTipGenerator(int series, + public void setSeriesToolTipGenerator(@NonNegative int series, CategoryToolTipGenerator generator, boolean notify) { this.toolTipGeneratorMap.put(series, generator); if (notify) { @@ -522,7 +524,7 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, boole * @return The URL generator. */ @Override - public CategoryURLGenerator getItemURLGenerator(int row, int column) { + public CategoryURLGenerator getItemURLGenerator(@NonNegative int row, @NonNegative int column) { return getSeriesItemURLGenerator(row); } @@ -536,7 +538,7 @@ public CategoryURLGenerator getItemURLGenerator(int row, int column) { * @see #setSeriesItemURLGenerator(int, CategoryURLGenerator) */ @Override - public CategoryURLGenerator getSeriesItemURLGenerator(int series) { + public CategoryURLGenerator getSeriesItemURLGenerator(@NonNegative int series) { // otherwise look up the generator table CategoryURLGenerator generator = this.itemURLGeneratorMap.get(series); if (generator == null) { @@ -555,7 +557,7 @@ public CategoryURLGenerator getSeriesItemURLGenerator(int series) { * @see #getSeriesItemURLGenerator(int) */ @Override - public void setSeriesItemURLGenerator(int series, + public void setSeriesItemURLGenerator(@NonNegative int series, CategoryURLGenerator generator) { setSeriesItemURLGenerator(series, generator, true); } @@ -571,7 +573,7 @@ public void setSeriesItemURLGenerator(int series, * @see #getSeriesItemURLGenerator(int) */ @Override - public void setSeriesItemURLGenerator(int series, + public void setSeriesItemURLGenerator(@NonNegative int series, CategoryURLGenerator generator, boolean notify) { this.itemURLGeneratorMap.put(series, generator); if (notify) { @@ -627,7 +629,7 @@ public void setDefaultItemURLGenerator(CategoryURLGenerator generator, boolean n * * @return The row count. */ - public int getRowCount() { + public @NonNegative int getRowCount() { return this.rowCount; } @@ -637,7 +639,7 @@ public int getRowCount() { * * @return The column count. */ - public int getColumnCount() { + public @NonNegative int getColumnCount() { return this.columnCount; } @@ -674,7 +676,7 @@ protected CategoryItemRendererState createState(PlotRenderingInfo info) { */ @Override public CategoryItemRendererState initialise(Graphics2D g2, - Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, + Rectangle2D dataArea, CategoryPlot plot, @NonNegative int rendererIndex, PlotRenderingInfo info) { setPlot(plot); @@ -688,17 +690,22 @@ public CategoryItemRendererState initialise(Graphics2D g2, } CategoryItemRendererState state = createState(info); state.setElementHinting(plot.fetchElementHintingFlag()); - int[] visibleSeriesTemp = new int[this.rowCount]; + int rowCount = this.rowCount; + int[] visibleSeriesTemp = new int[rowCount]; int visibleSeriesCount = 0; - for (int row = 0; row < this.rowCount; row++) { + for (int row = 0; row < rowCount; row++) { if (isSeriesVisible(row)) { - visibleSeriesTemp[visibleSeriesCount] = row; + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/219: visibleSeriesCount is incremented at most as many times as row, which is an index + @IndexFor("visibleSeriesTemp") int visibleSeriesCountTemp = visibleSeriesCount; + visibleSeriesTemp[visibleSeriesCountTemp] = row; visibleSeriesCount++; } } - int[] visibleSeries = new int[visibleSeriesCount]; + @NonNegative int[] visibleSeries = new int[visibleSeriesCount]; + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/219: visibleSeriesCount is incremented at most as many times as row, which is an index + @IndexOrHigh({"visibleSeriesTemp", "visibleSeries"}) int visibleSeriesCountTemp = visibleSeriesCount; System.arraycopy(visibleSeriesTemp, 0, visibleSeries, 0, - visibleSeriesCount); + visibleSeriesCountTemp); state.setVisibleSeriesArray(visibleSeries); return state; } @@ -927,6 +934,7 @@ public void drawDomainMarker(Graphics2D g2, CategoryPlot plot, CategoryAxis axis, CategoryMarker marker, Rectangle2D dataArea) { Comparable category = marker.getKey(); + @SuppressWarnings("index") // this will fail if this renderer is not associated with the given plot. A bug? CategoryDataset dataset = plot.getDataset(plot.getIndexOf(this)); int columnIndex = dataset.getColumnIndex(category); if (columnIndex < 0) { @@ -1246,7 +1254,7 @@ protected Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2, * @see #getLegendItems() */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { CategoryPlot p = getPlot(); if (p == null) { @@ -1393,7 +1401,7 @@ public DrawingSupplier getDrawingSupplier() { */ protected void updateCrosshairValues(CategoryCrosshairState crosshairState, Comparable rowKey, Comparable columnKey, double value, - int datasetIndex, + @NonNegative int datasetIndex, double transX, double transY, PlotOrientation orientation) { Args.nullNotPermitted(orientation, "orientation"); @@ -1425,7 +1433,7 @@ protected void updateCrosshairValues(CategoryCrosshairState crosshairState, * label position). */ protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, - CategoryDataset dataset, int row, int column, + CategoryDataset dataset, @NonNegative int row, @NonNegative int column, double x, double y, boolean negative) { CategoryItemLabelGenerator generator = getItemLabelGenerator(row, @@ -1544,7 +1552,7 @@ public Object clone() throws CloneNotSupportedException { * * @return A domain axis. */ - protected CategoryAxis getDomainAxis(CategoryPlot plot, int index) { + protected CategoryAxis getDomainAxis(CategoryPlot plot, @NonNegative int index) { CategoryAxis result = plot.getDomainAxis(index); if (result == null) { result = plot.getDomainAxis(); @@ -1560,7 +1568,7 @@ protected CategoryAxis getDomainAxis(CategoryPlot plot, int index) { * * @return A range axis. */ - protected ValueAxis getRangeAxis(CategoryPlot plot, int index) { + protected ValueAxis getRangeAxis(CategoryPlot plot, @NonNegative int index) { ValueAxis result = plot.getRangeAxis(index); if (result == null) { result = plot.getRangeAxis(); @@ -1582,7 +1590,8 @@ public LegendItemCollection getLegendItems() { if (this.plot == null) { return result; } - int index = this.plot.getIndexOf(this); + @SuppressWarnings("index") // guaranteed index: this.plot.getIndexOf(this) is always non negative, since this.plot is the plot associated with the renderer + @NonNegative int index = this.plot.getIndexOf(this); CategoryDataset dataset = this.plot.getDataset(index); if (dataset == null) { return result; @@ -1697,7 +1706,7 @@ public void setLegendItemURLGenerator( * @param hotspot the hotspot ({@code null} not permitted). */ protected void addItemEntity(EntityCollection entities, - CategoryDataset dataset, int row, int column, Shape hotspot) { + CategoryDataset dataset, @NonNegative int row, @NonNegative int column, Shape hotspot) { Args.nullNotPermitted(hotspot, "hotspot"); if (!getItemCreateEntity(row, column)) { return; @@ -1734,7 +1743,7 @@ protected void addItemEntity(EntityCollection entities, * @since 1.0.13 */ protected void addEntity(EntityCollection entities, Shape hotspot, - CategoryDataset dataset, int row, int column, + CategoryDataset dataset, @NonNegative int row, @NonNegative int column, double entityX, double entityY) { if (!getItemCreateEntity(row, column)) { return; diff --git a/src/main/java/org/jfree/chart/renderer/category/AreaRenderer.java b/src/main/java/org/jfree/chart/renderer/category/AreaRenderer.java index b544bf798..14c5098c8 100644 --- a/src/main/java/org/jfree/chart/renderer/category/AreaRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/AreaRenderer.java @@ -74,6 +74,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -157,7 +159,7 @@ public void setEndType(AreaRendererEndType type) { * @return The legend item. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { // if there is no plot, there is no dataset to access... CategoryPlot cp = getPlot(); @@ -221,7 +223,7 @@ public LegendItem getLegendItem(int datasetIndex, int series) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { // do nothing if item is not visible or null @@ -320,7 +322,8 @@ else if (orientation == PlotOrientation.HORIZONTAL) { } // submit the current data point as a crosshair candidate - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), yy1, datasetIndex, x1, y1, orientation); diff --git a/src/main/java/org/jfree/chart/renderer/category/BarPainter.java b/src/main/java/org/jfree/chart/renderer/category/BarPainter.java index 4522152fd..e42ec405f 100644 --- a/src/main/java/org/jfree/chart/renderer/category/BarPainter.java +++ b/src/main/java/org/jfree/chart/renderer/category/BarPainter.java @@ -40,6 +40,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.RectangularShape; import org.jfree.chart.ui.RectangleEdge; @@ -72,7 +74,7 @@ public interface BarPainter { * @param base the base of the bar. */ public void paintBar(Graphics2D g2, BarRenderer renderer, - int row, int column, RectangularShape bar, RectangleEdge base); + @NonNegative int row, @NonNegative int column, RectangularShape bar, RectangleEdge base); /** * Paints the shadow for a single bar on behalf of a renderer. @@ -86,7 +88,7 @@ public void paintBar(Graphics2D g2, BarRenderer renderer, * @param pegShadow peg the shadow to the base of the bar? */ public void paintBarShadow(Graphics2D g2, BarRenderer renderer, - int row, int column, RectangularShape bar, RectangleEdge base, + @NonNegative int row, @NonNegative int column, RectangularShape bar, RectangleEdge base, boolean pegShadow); } diff --git a/src/main/java/org/jfree/chart/renderer/category/BarRenderer.java b/src/main/java/org/jfree/chart/renderer/category/BarRenderer.java index 846125d21..9c854ab9e 100644 --- a/src/main/java/org/jfree/chart/renderer/category/BarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/BarRenderer.java @@ -96,6 +96,10 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.common.value.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -743,7 +747,7 @@ public double getUpperClip() { */ @Override public CategoryItemRendererState initialise(Graphics2D g2, - Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, + Rectangle2D dataArea, CategoryPlot plot, @NonNegative int rendererIndex, PlotRenderingInfo info) { CategoryItemRendererState state = super.initialise(g2, dataArea, plot, @@ -771,7 +775,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, */ protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, - int rendererIndex, + @NonNegative int rendererIndex, CategoryItemRendererState state) { CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); @@ -827,7 +831,7 @@ else if (orientation == PlotOrientation.VERTICAL) { protected double calculateBarW0(CategoryPlot plot, PlotOrientation orientation, Rectangle2D dataArea, CategoryAxis domainAxis, CategoryItemRendererState state, - int row, int column) { + @NonNegative int row, @NonNegative int column) { // calculate bar width... double space; if (orientation == PlotOrientation.HORIZONTAL) { @@ -865,7 +869,7 @@ protected double calculateBarW0(CategoryPlot plot, * @return The coordinates for each end of the bar (or {@code null} if * the bar is not visible for the current axis range). */ - protected double[] calculateBarL0L1(double value) { + protected double @ArrayLen(2) [] calculateBarL0L1(double value) { double lclip = getLowerClip(); double uclip = getUpperClip(); double barLow = Math.min(this.base, value); @@ -917,7 +921,7 @@ public Range findRangeBounds(CategoryDataset dataset, * @return The legend item (possibly {@code null}). */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { CategoryPlot cp = getPlot(); if (cp == null) { @@ -984,8 +988,8 @@ urlText, true, shape, true, paint, isDrawBarOutline(), @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, - int column, int pass) { + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, + @NonNegative int column, int pass) { // nothing is drawn if the row index is not included in the list with // the indices of the visible rows... @@ -1080,7 +1084,8 @@ public void drawItem(Graphics2D g2, CategoryItemRendererState state, } // submit the current data point as a crosshair candidate - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), value, datasetIndex, barW0, barL0, orientation); @@ -1104,7 +1109,7 @@ public void drawItem(Graphics2D g2, CategoryItemRendererState state, * @return The width of one series. */ protected double calculateSeriesWidth(double space, CategoryAxis axis, - int categories, int series) { + int categories, @NonNegative int series) { double factor = 1.0 - getItemMargin() - axis.getLowerMargin() - axis.getUpperMargin(); if (categories > 1) { @@ -1128,8 +1133,8 @@ protected double calculateSeriesWidth(double space, CategoryAxis axis, */ protected void drawItemLabel(Graphics2D g2, CategoryDataset data, - int row, - int column, + @NonNegative int row, + @NonNegative int column, CategoryPlot plot, CategoryItemLabelGenerator generator, Rectangle2D bar, diff --git a/src/main/java/org/jfree/chart/renderer/category/BoxAndWhiskerRenderer.java b/src/main/java/org/jfree/chart/renderer/category/BoxAndWhiskerRenderer.java index 4639d665c..c45c1ffef 100644 --- a/src/main/java/org/jfree/chart/renderer/category/BoxAndWhiskerRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/BoxAndWhiskerRenderer.java @@ -91,6 +91,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; @@ -451,7 +453,7 @@ public void setWhiskerWidth(double width) { * @return The legend item (possibly {@code null}). */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { CategoryPlot cp = getPlot(); if (cp == null) { @@ -523,7 +525,7 @@ public Range findRangeBounds(CategoryDataset dataset) { */ @Override public CategoryItemRendererState initialise(Graphics2D g2, - Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, + Rectangle2D dataArea, CategoryPlot plot, @NonNegative int rendererIndex, PlotRenderingInfo info) { CategoryItemRendererState state = super.initialise(g2, dataArea, plot, @@ -584,7 +586,7 @@ else if (orientation == PlotOrientation.VERTICAL) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { // do nothing if item is not visible @@ -630,7 +632,7 @@ else if (orientation == PlotOrientation.VERTICAL) { public void drawHorizontalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, - CategoryDataset dataset, int row, int column) { + CategoryDataset dataset, @NonNegative int row, @NonNegative int column) { BoxAndWhiskerCategoryDataset bawDataset = (BoxAndWhiskerCategoryDataset) dataset; @@ -773,7 +775,7 @@ public void drawHorizontalItem(Graphics2D g2, */ public void drawVerticalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column) { + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column) { BoxAndWhiskerCategoryDataset bawDataset = (BoxAndWhiskerCategoryDataset) dataset; diff --git a/src/main/java/org/jfree/chart/renderer/category/CategoryItemRenderer.java b/src/main/java/org/jfree/chart/renderer/category/CategoryItemRenderer.java index 7c7dade56..2f67593a8 100644 --- a/src/main/java/org/jfree/chart/renderer/category/CategoryItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/CategoryItemRenderer.java @@ -97,6 +97,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; @@ -148,7 +150,7 @@ public interface CategoryItemRenderer extends LegendItemSource { * * @return The pass count. */ - public int getPassCount(); + public @NonNegative int getPassCount(); /** * Returns the plot that the renderer has been assigned to (where @@ -219,7 +221,7 @@ public interface CategoryItemRenderer extends LegendItemSource { public CategoryItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, CategoryPlot plot, - int rendererIndex, + @NonNegative int rendererIndex, PlotRenderingInfo info); /** @@ -231,7 +233,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @return A boolean. */ - public boolean getItemVisible(int series, int item); + public boolean getItemVisible(@NonNegative int series, @NonNegative int item); /** * Returns a boolean that indicates whether or not the specified series @@ -241,7 +243,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @return A boolean. */ - public boolean isSeriesVisible(int series); + public boolean isSeriesVisible(@NonNegative int series); /** * Returns the flag that controls whether a series is visible. @@ -252,7 +254,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @see #setSeriesVisible(int, Boolean) */ - public Boolean getSeriesVisible(int series); + public Boolean getSeriesVisible(@NonNegative int series); /** * Sets the flag that controls whether a series is visible and sends a @@ -263,7 +265,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @see #getSeriesVisible(int) */ - public void setSeriesVisible(int series, Boolean visible); + public void setSeriesVisible(@NonNegative int series, Boolean visible); /** * Sets the flag that controls whether a series is visible and, if @@ -276,7 +278,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @see #getSeriesVisible(int) */ - public void setSeriesVisible(int series, Boolean visible, boolean notify); + public void setSeriesVisible(@NonNegative int series, Boolean visible, boolean notify); /** * Returns the default visibility for all series. @@ -318,7 +320,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @return A boolean. */ - public boolean isSeriesVisibleInLegend(int series); + public boolean isSeriesVisibleInLegend(@NonNegative int series); /** * Returns the flag that controls whether a series is visible in the @@ -332,7 +334,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @see #setSeriesVisibleInLegend(int, Boolean) */ - public Boolean getSeriesVisibleInLegend(int series); + public Boolean getSeriesVisibleInLegend(@NonNegative int series); /** * Sets the flag that controls whether a series is visible in the legend @@ -343,7 +345,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @see #getSeriesVisibleInLegend(int) */ - public void setSeriesVisibleInLegend(int series, Boolean visible); + public void setSeriesVisibleInLegend(@NonNegative int series, Boolean visible); /** * Sets the flag that controls whether a series is visible in the legend @@ -356,7 +358,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * * @see #getSeriesVisibleInLegend(int) */ - public void setSeriesVisibleInLegend(int series, Boolean visible, + public void setSeriesVisibleInLegend(@NonNegative int series, Boolean visible, boolean notify); /** @@ -400,7 +402,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The paint (never {@code null}). */ - public Paint getItemPaint(int row, int column); + public Paint getItemPaint(@NonNegative int row, @NonNegative int column); /** * Returns the paint used to fill an item drawn by the renderer. @@ -411,7 +413,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesPaint(int, Paint) */ - public Paint getSeriesPaint(int series); + public Paint getSeriesPaint(@NonNegative int series); /** * Sets the paint used for a series and sends a {@link RendererChangeEvent} @@ -422,9 +424,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesPaint(int) */ - public void setSeriesPaint(int series, Paint paint); + public void setSeriesPaint(@NonNegative int series, Paint paint); - public void setSeriesPaint(int series, Paint paint, boolean notify); + public void setSeriesPaint(@NonNegative int series, Paint paint, boolean notify); /** * Returns the default paint. @@ -457,7 +459,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The paint (never {@code null}). */ - public Paint getItemFillPaint(int row, int column); + public Paint getItemFillPaint(@NonNegative int row, @NonNegative int column); /** * Returns the paint used to fill an item drawn by the renderer. @@ -468,7 +470,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesFillPaint(int, Paint) */ - public Paint getSeriesFillPaint(int series); + public Paint getSeriesFillPaint(@NonNegative int series); /** * Sets the paint used for a series outline and sends a @@ -479,7 +481,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesFillPaint(int) */ - public void setSeriesFillPaint(int series, Paint paint); + public void setSeriesFillPaint(@NonNegative int series, Paint paint); /** * Returns the default outline paint. @@ -510,7 +512,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The paint (never {@code null}). */ - public Paint getItemOutlinePaint(int row, int column); + public Paint getItemOutlinePaint(@NonNegative int row, @NonNegative int column); /** * Returns the paint used to outline an item drawn by the renderer. @@ -521,7 +523,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesOutlinePaint(int, Paint) */ - public Paint getSeriesOutlinePaint(int series); + public Paint getSeriesOutlinePaint(@NonNegative int series); /** * Sets the paint used for a series outline and sends a @@ -532,9 +534,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesOutlinePaint(int) */ - public void setSeriesOutlinePaint(int series, Paint paint); + public void setSeriesOutlinePaint(@NonNegative int series, Paint paint); - public void setSeriesOutlinePaint(int series, Paint paint, boolean notify); + public void setSeriesOutlinePaint(@NonNegative int series, Paint paint, boolean notify); /** * Returns the default outline paint. @@ -567,7 +569,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The stroke (never {@code null}). */ - public Stroke getItemStroke(int row, int column); + public Stroke getItemStroke(@NonNegative int row, @NonNegative int column); /** * Returns the stroke used to draw the items in a series. @@ -578,7 +580,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesStroke(int, Stroke) */ - public Stroke getSeriesStroke(int series); + public Stroke getSeriesStroke(@NonNegative int series); /** * Sets the stroke used for a series and sends a @@ -589,9 +591,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesStroke(int) */ - public void setSeriesStroke(int series, Stroke stroke); + public void setSeriesStroke(@NonNegative int series, Stroke stroke); - public void setSeriesStroke(int series, Stroke stroke, boolean notify); + public void setSeriesStroke(@NonNegative int series, Stroke stroke, boolean notify); /** * Returns the default stroke. @@ -628,7 +630,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The stroke (never {@code null}). */ - public Stroke getItemOutlineStroke(int row, int column); + public Stroke getItemOutlineStroke(@NonNegative int row, @NonNegative int column); /** * Returns the stroke used to outline the items in a series. @@ -639,7 +641,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesOutlineStroke(int, Stroke) */ - public Stroke getSeriesOutlineStroke(int series); + public Stroke getSeriesOutlineStroke(@NonNegative int series); /** * Sets the outline stroke used for a series and sends a @@ -650,9 +652,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesOutlineStroke(int) */ - public void setSeriesOutlineStroke(int series, Stroke stroke); + public void setSeriesOutlineStroke(@NonNegative int series, Stroke stroke); - public void setSeriesOutlineStroke(int series, Stroke stroke, boolean notify); + public void setSeriesOutlineStroke(@NonNegative int series, Stroke stroke, boolean notify); /** * Returns the default outline stroke. @@ -685,7 +687,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The shape (never {@code null}). */ - public Shape getItemShape(int row, int column); + public Shape getItemShape(@NonNegative int row, @NonNegative int column); /** * Returns a shape used to represent the items in a series. @@ -696,7 +698,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesShape(int, Shape) */ - public Shape getSeriesShape(int series); + public Shape getSeriesShape(@NonNegative int series); /** * Sets the shape used for a series and sends a {@link RendererChangeEvent} @@ -707,9 +709,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesShape(int) */ - public void setSeriesShape(int series, Shape shape); + public void setSeriesShape(@NonNegative int series, Shape shape); - public void setSeriesShape(int series, Shape shape, boolean notify); + public void setSeriesShape(@NonNegative int series, Shape shape, boolean notify); /** * Returns the default shape. @@ -743,7 +745,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return A boolean. */ - public boolean isItemLabelVisible(int row, int column); + public boolean isItemLabelVisible(@NonNegative int row, @NonNegative int column); /** * Returns {@code true} if the item labels for a series are visible, @@ -755,7 +757,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesItemLabelsVisible(int, Boolean) */ - public boolean isSeriesItemLabelsVisible(int series); + public boolean isSeriesItemLabelsVisible(@NonNegative int series); /** * Sets a flag that controls the visibility of the item labels for a series. @@ -765,7 +767,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #isSeriesItemLabelsVisible(int) */ - public void setSeriesItemLabelsVisible(int series, boolean visible); + public void setSeriesItemLabelsVisible(@NonNegative int series, boolean visible); /** * Sets a flag that controls the visibility of the item labels for a series. @@ -775,7 +777,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #isSeriesItemLabelsVisible(int) */ - public void setSeriesItemLabelsVisible(int series, Boolean visible); + public void setSeriesItemLabelsVisible(@NonNegative int series, Boolean visible); /** * Sets the visibility of item labels for a series and, if requested, sends @@ -788,7 +790,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #isSeriesItemLabelsVisible(int) */ - public void setSeriesItemLabelsVisible(int series, Boolean visible, + public void setSeriesItemLabelsVisible(@NonNegative int series, Boolean visible, boolean notify); /** @@ -835,7 +837,7 @@ public void setSeriesItemLabelsVisible(int series, Boolean visible, * * @return The generator (possibly {@code null}). */ - public CategoryItemLabelGenerator getItemLabelGenerator(int series, + public CategoryItemLabelGenerator getItemLabelGenerator(@NonNegative int series, int item); /** @@ -847,7 +849,7 @@ public CategoryItemLabelGenerator getItemLabelGenerator(int series, * * @see #setSeriesItemLabelGenerator(int, CategoryItemLabelGenerator) */ - public CategoryItemLabelGenerator getSeriesItemLabelGenerator(int series); + public CategoryItemLabelGenerator getSeriesItemLabelGenerator(@NonNegative int series); /** * Sets the item label generator for a series and sends a @@ -858,10 +860,10 @@ public CategoryItemLabelGenerator getItemLabelGenerator(int series, * * @see #getSeriesItemLabelGenerator(int) */ - public void setSeriesItemLabelGenerator(int series, + public void setSeriesItemLabelGenerator(@NonNegative int series, CategoryItemLabelGenerator generator); - public void setSeriesItemLabelGenerator(int series, + public void setSeriesItemLabelGenerator(@NonNegative int series, CategoryItemLabelGenerator generator, boolean notify); /** @@ -898,7 +900,7 @@ public void setDefaultItemLabelGenerator(CategoryItemLabelGenerator generator, * * @return The generator (possibly {@code null}). */ - public CategoryToolTipGenerator getToolTipGenerator(int row, int column); + public CategoryToolTipGenerator getToolTipGenerator(@NonNegative int row, @NonNegative int column); /** * Returns the tool tip generator for the specified series (a "layer 1" @@ -910,7 +912,7 @@ public void setDefaultItemLabelGenerator(CategoryItemLabelGenerator generator, * * @see #setSeriesToolTipGenerator(int, CategoryToolTipGenerator) */ - public CategoryToolTipGenerator getSeriesToolTipGenerator(int series); + public CategoryToolTipGenerator getSeriesToolTipGenerator(@NonNegative int series); /** * Sets the tool tip generator for a series and sends a @@ -922,10 +924,10 @@ public void setDefaultItemLabelGenerator(CategoryItemLabelGenerator generator, * * @see #getSeriesToolTipGenerator(int) */ - public void setSeriesToolTipGenerator(int series, + public void setSeriesToolTipGenerator(@NonNegative int series, CategoryToolTipGenerator generator); - public void setSeriesToolTipGenerator(int series, + public void setSeriesToolTipGenerator(@NonNegative int series, CategoryToolTipGenerator generator, boolean notify); /** @@ -961,7 +963,7 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @return The font (never {@code null}). */ - public Font getItemLabelFont(int row, int column); + public Font getItemLabelFont(@NonNegative int row, @NonNegative int column); /** * Returns the font for all the item labels in a series. @@ -972,7 +974,7 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @see #setSeriesItemLabelFont(int, Font) */ - public Font getSeriesItemLabelFont(int series); + public Font getSeriesItemLabelFont(@NonNegative int series); /** * Sets the item label font for a series and sends a @@ -983,9 +985,9 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @see #getSeriesItemLabelFont(int) */ - public void setSeriesItemLabelFont(int series, Font font); + public void setSeriesItemLabelFont(@NonNegative int series, Font font); - public void setSeriesItemLabelFont(int series, Font font, boolean notify); + public void setSeriesItemLabelFont(@NonNegative int series, Font font, boolean notify); /** * Returns the default item label font (this is used when no other font @@ -1019,7 +1021,7 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @return The paint (never {@code null}). */ - public Paint getItemLabelPaint(int row, int column); + public Paint getItemLabelPaint(@NonNegative int row, @NonNegative int column); /** * Returns the paint used to draw the item labels for a series. @@ -1030,7 +1032,7 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @see #setSeriesItemLabelPaint(int, Paint) */ - public Paint getSeriesItemLabelPaint(int series); + public Paint getSeriesItemLabelPaint(@NonNegative int series); /** * Sets the item label paint for a series and sends a @@ -1041,9 +1043,9 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @see #getSeriesItemLabelPaint(int) */ - public void setSeriesItemLabelPaint(int series, Paint paint); + public void setSeriesItemLabelPaint(@NonNegative int series, Paint paint); - public void setSeriesItemLabelPaint(int series, Paint paint, boolean notify); + public void setSeriesItemLabelPaint(@NonNegative int series, Paint paint, boolean notify); /** * Returns the default item label paint. @@ -1076,7 +1078,7 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @return The item label position (never {@code null}). */ - public ItemLabelPosition getPositiveItemLabelPosition(int row, int column); + public ItemLabelPosition getPositiveItemLabelPosition(@NonNegative int row, @NonNegative int column); /** * Returns the item label position for all positive values in a series. @@ -1087,7 +1089,7 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @see #setSeriesPositiveItemLabelPosition(int, ItemLabelPosition) */ - public ItemLabelPosition getSeriesPositiveItemLabelPosition(int series); + public ItemLabelPosition getSeriesPositiveItemLabelPosition(@NonNegative int series); /** * Sets the item label position for all positive values in a series and @@ -1098,7 +1100,7 @@ public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, * * @see #getSeriesPositiveItemLabelPosition(int) */ - public void setSeriesPositiveItemLabelPosition(int series, + public void setSeriesPositiveItemLabelPosition(@NonNegative int series, ItemLabelPosition position); /** @@ -1112,7 +1114,7 @@ public void setSeriesPositiveItemLabelPosition(int series, * * @see #getSeriesPositiveItemLabelPosition(int) */ - public void setSeriesPositiveItemLabelPosition(int series, + public void setSeriesPositiveItemLabelPosition(@NonNegative int series, ItemLabelPosition position, boolean notify); /** @@ -1158,7 +1160,7 @@ public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, * * @return The item label position. */ - public ItemLabelPosition getNegativeItemLabelPosition(int row, int column); + public ItemLabelPosition getNegativeItemLabelPosition(@NonNegative int row, @NonNegative int column); /** * Returns the item label position for all negative values in a series. @@ -1169,7 +1171,7 @@ public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, * * @see #setSeriesNegativeItemLabelPosition(int, ItemLabelPosition) */ - public ItemLabelPosition getSeriesNegativeItemLabelPosition(int series); + public ItemLabelPosition getSeriesNegativeItemLabelPosition(@NonNegative int series); /** * Sets the item label position for negative values in a series and sends a @@ -1180,7 +1182,7 @@ public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, * * @see #getSeriesNegativeItemLabelPosition(int) */ - public void setSeriesNegativeItemLabelPosition(int series, + public void setSeriesNegativeItemLabelPosition(@NonNegative int series, ItemLabelPosition position); /** @@ -1194,7 +1196,7 @@ public void setSeriesNegativeItemLabelPosition(int series, * * @see #getSeriesNegativeItemLabelPosition(int) */ - public void setSeriesNegativeItemLabelPosition(int series, + public void setSeriesNegativeItemLabelPosition(@NonNegative int series, ItemLabelPosition position, boolean notify); /** @@ -1230,13 +1232,13 @@ public void setDefaultNegativeItemLabelPosition(ItemLabelPosition position, // CREATE ENTITIES - public boolean getItemCreateEntity(int series, int item); + public boolean getItemCreateEntity(@NonNegative int series, @NonNegative int item); - public Boolean getSeriesCreateEntities(int series); + public Boolean getSeriesCreateEntities(@NonNegative int series); - public void setSeriesCreateEntities(int series, Boolean create); + public void setSeriesCreateEntities(@NonNegative int series, Boolean create); - public void setSeriesCreateEntities(int series, Boolean create, + public void setSeriesCreateEntities(@NonNegative int series, Boolean create, boolean notify); public boolean getDefaultCreateEntities(); @@ -1256,7 +1258,7 @@ public void setSeriesCreateEntities(int series, Boolean create, * * @return The item URL generator. */ - public CategoryURLGenerator getItemURLGenerator(int series, int item); + public CategoryURLGenerator getItemURLGenerator(@NonNegative int series, @NonNegative int item); /** * Returns the item URL generator for a series. @@ -1267,7 +1269,7 @@ public void setSeriesCreateEntities(int series, Boolean create, * * @see #setSeriesItemURLGenerator(int, CategoryURLGenerator) */ - public CategoryURLGenerator getSeriesItemURLGenerator(int series); + public CategoryURLGenerator getSeriesItemURLGenerator(@NonNegative int series); /** * Sets the item URL generator for a series. @@ -1277,10 +1279,10 @@ public void setSeriesCreateEntities(int series, Boolean create, * * @see #getSeriesItemURLGenerator(int) */ - public void setSeriesItemURLGenerator(int series, + public void setSeriesItemURLGenerator(@NonNegative int series, CategoryURLGenerator generator); - public void setSeriesItemURLGenerator(int series, + public void setSeriesItemURLGenerator(@NonNegative int series, CategoryURLGenerator generator, boolean notify); /** @@ -1314,7 +1316,7 @@ public void setSeriesItemURLGenerator(int series, * * @return The legend item (possibly {@code null}). */ - public LegendItem getLegendItem(int datasetIndex, int series); + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series); /** * Draws a background for the data area. @@ -1352,7 +1354,7 @@ public void drawOutline(Graphics2D g2, CategoryPlot plot, */ public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass); /** diff --git a/src/main/java/org/jfree/chart/renderer/category/CategoryItemRendererState.java b/src/main/java/org/jfree/chart/renderer/category/CategoryItemRendererState.java index 34373ad34..1e57ab9d3 100644 --- a/src/main/java/org/jfree/chart/renderer/category/CategoryItemRendererState.java +++ b/src/main/java/org/jfree/chart/renderer/category/CategoryItemRendererState.java @@ -45,6 +45,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.*; + import org.jfree.chart.plot.CategoryCrosshairState; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.renderer.RendererState; @@ -62,7 +64,7 @@ public class CategoryItemRendererState extends RendererState { private double seriesRunningTotal; /** The array with the indices of the visible series.*/ - private int[] visibleSeries; + private @NonNegative int[] visibleSeries; /** * State information for crosshairs in the plot (this is updated by the @@ -168,7 +170,7 @@ public void setCrosshairState(CategoryCrosshairState state) { * * @since 1.0.13 */ - public int getVisibleSeriesIndex(int rowIndex) { + public int getVisibleSeriesIndex(@NonNegative int rowIndex) { if (this.visibleSeries == null) { return rowIndex; } @@ -204,11 +206,11 @@ public int getVisibleSeriesCount() { * * @since 1.0.13 */ - public int[] getVisibleSeriesArray() { + public @NonNegative int[] getVisibleSeriesArray() { if (this.visibleSeries == null) { return null; } - int[] result = new int[this.visibleSeries.length]; + @NonNegative int[] result = new int[this.visibleSeries.length]; System.arraycopy(this.visibleSeries, 0, result, 0, this.visibleSeries.length); return result; @@ -221,7 +223,7 @@ public int[] getVisibleSeriesArray() { * * @since 1.0.13 */ - public void setVisibleSeriesArray(int[] visibleSeries) { + public void setVisibleSeriesArray(@NonNegative int[] visibleSeries) { this.visibleSeries = visibleSeries; } diff --git a/src/main/java/org/jfree/chart/renderer/category/CategoryStepRenderer.java b/src/main/java/org/jfree/chart/renderer/category/CategoryStepRenderer.java index 325e0d020..87af4469a 100644 --- a/src/main/java/org/jfree/chart/renderer/category/CategoryStepRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/CategoryStepRenderer.java @@ -51,6 +51,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -166,7 +168,7 @@ public void setStagger(boolean shouldStagger) { * @return The legend item. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { CategoryPlot p = getPlot(); if (p == null) { @@ -273,8 +275,8 @@ else if (orientation == PlotOrientation.HORIZONTAL) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, - int column, int pass) { + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, + @NonNegative int column, int pass) { // do nothing if item is not visible if (!getItemVisible(row, column)) { diff --git a/src/main/java/org/jfree/chart/renderer/category/GanttRenderer.java b/src/main/java/org/jfree/chart/renderer/category/GanttRenderer.java index d0caa30d9..914f86776 100644 --- a/src/main/java/org/jfree/chart/renderer/category/GanttRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/GanttRenderer.java @@ -55,6 +55,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; @@ -242,8 +244,8 @@ public void setEndPercent(double percent) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, - int column, int pass) { + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, + @NonNegative int column, int pass) { if (dataset instanceof GanttCategoryDataset) { GanttCategoryDataset gcd = (GanttCategoryDataset) dataset; @@ -277,8 +279,8 @@ protected void drawTasks(Graphics2D g2, CategoryAxis domainAxis, ValueAxis rangeAxis, GanttCategoryDataset dataset, - int row, - int column) { + @NonNegative int row, + @NonNegative int column) { int count = dataset.getSubIntervalCount(row, column); if (count == 0) { @@ -383,7 +385,8 @@ else if (orientation == PlotOrientation.VERTICAL) { if (subinterval == count - 1) { // submit the current data point as a crosshair candidate - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); Comparable columnKey = dataset.getColumnKey(column); Comparable rowKey = dataset.getRowKey(row); double xx = domainAxis.getCategorySeriesMiddle(columnKey, @@ -425,8 +428,8 @@ protected void drawTask(Graphics2D g2, CategoryAxis domainAxis, ValueAxis rangeAxis, GanttCategoryDataset dataset, - int row, - int column) { + @NonNegative int row, + @NonNegative int column) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); @@ -533,7 +536,8 @@ else if (plot.getOrientation() == PlotOrientation.VERTICAL) { } // submit the current data point as a crosshair candidate - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); Comparable columnKey = dataset.getColumnKey(column); Comparable rowKey = dataset.getRowKey(row); double xx = domainAxis.getCategorySeriesMiddle(columnKey, rowKey, diff --git a/src/main/java/org/jfree/chart/renderer/category/GradientBarPainter.java b/src/main/java/org/jfree/chart/renderer/category/GradientBarPainter.java index 981aa8b24..ea486f36f 100644 --- a/src/main/java/org/jfree/chart/renderer/category/GradientBarPainter.java +++ b/src/main/java/org/jfree/chart/renderer/category/GradientBarPainter.java @@ -42,6 +42,10 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; @@ -103,8 +107,8 @@ public GradientBarPainter(double g1, double g2, double g3) { * bar. */ @Override - public void paintBar(Graphics2D g2, BarRenderer renderer, int row, - int column, RectangularShape bar, RectangleEdge base) { + public void paintBar(Graphics2D g2, BarRenderer renderer, @NonNegative int row, + @NonNegative int column, RectangularShape bar, RectangleEdge base) { Paint itemPaint = renderer.getItemPaint(row, column); @@ -205,8 +209,8 @@ else if (base == RectangleEdge.LEFT || base == RectangleEdge.RIGHT) { * @param pegShadow peg the shadow to the base of the bar? */ @Override - public void paintBarShadow(Graphics2D g2, BarRenderer renderer, int row, - int column, RectangularShape bar, RectangleEdge base, + public void paintBarShadow(Graphics2D g2, BarRenderer renderer, @NonNegative int row, + @NonNegative int column, RectangularShape bar, RectangleEdge base, boolean pegShadow) { // handle a special case - if the bar colour has alpha == 0, it is @@ -289,7 +293,7 @@ else if (base == RectangleEdge.RIGHT) { * * @return An array containing four subregions. */ - private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a, + private Rectangle2D @ArrayLen(4) [] splitVerticalBar(RectangularShape bar, double a, double b, double c) { Rectangle2D[] result = new Rectangle2D[4]; double x0 = bar.getMinX(); @@ -318,7 +322,7 @@ private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a, * * @return An array containing four subregions. */ - private Rectangle2D[] splitHorizontalBar(RectangularShape bar, double a, + private Rectangle2D @ArrayLen(4) [] splitHorizontalBar(RectangularShape bar, double a, double b, double c) { Rectangle2D[] result = new Rectangle2D[4]; double y0 = bar.getMinY(); diff --git a/src/main/java/org/jfree/chart/renderer/category/GroupedStackedBarRenderer.java b/src/main/java/org/jfree/chart/renderer/category/GroupedStackedBarRenderer.java index bdb103e3d..5199b74a0 100644 --- a/src/main/java/org/jfree/chart/renderer/category/GroupedStackedBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/GroupedStackedBarRenderer.java @@ -49,6 +49,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; @@ -137,7 +139,7 @@ public Range findRangeBounds(CategoryDataset dataset) { */ @Override protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, - int rendererIndex, CategoryItemRendererState state) { + @NonNegative int rendererIndex, CategoryItemRendererState state) { // calculate the bar width CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex); @@ -196,7 +198,7 @@ else if (orientation == PlotOrientation.VERTICAL) { protected double calculateBarW0(CategoryPlot plot, PlotOrientation orientation, Rectangle2D dataArea, CategoryAxis domainAxis, CategoryItemRendererState state, - int row, int column) { + @NonNegative int row, @NonNegative int column) { // calculate bar width... double space; if (orientation == PlotOrientation.HORIZONTAL) { @@ -208,9 +210,10 @@ protected double calculateBarW0(CategoryPlot plot, double barW0 = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); int groupCount = this.seriesToGroupMap.getGroupCount(); + @SuppressWarnings("index") // this renderer is assumed to be associated with the plot passed to this function. If that isn't true, this will fail. A bug? + CategoryDataset dataset = plot.getDataset(plot.getIndexOf(this)); int groupIndex = this.seriesToGroupMap.getGroupIndex( - this.seriesToGroupMap.getGroup(plot.getDataset( - plot.getIndexOf(this)).getRowKey(row))); + this.seriesToGroupMap.getGroup(dataset.getRowKey(row))); int categoryCount = getColumnCount(); if (groupCount > 1) { double groupGap = space * getItemMargin() @@ -245,8 +248,8 @@ protected double calculateBarW0(CategoryPlot plot, @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, - int column, int pass) { + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, + @NonNegative int column, int pass) { // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); diff --git a/src/main/java/org/jfree/chart/renderer/category/IntervalBarRenderer.java b/src/main/java/org/jfree/chart/renderer/category/IntervalBarRenderer.java index 2da70168b..4e2b52768 100644 --- a/src/main/java/org/jfree/chart/renderer/category/IntervalBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/IntervalBarRenderer.java @@ -69,6 +69,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; @@ -137,7 +139,7 @@ public Range findRangeBounds(CategoryDataset dataset) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { if (dataset instanceof IntervalCategoryDataset) { @@ -166,8 +168,8 @@ public void drawItem(Graphics2D g2, CategoryItemRendererState state, */ protected void drawInterval(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, IntervalCategoryDataset dataset, int row, - int column) { + ValueAxis rangeAxis, IntervalCategoryDataset dataset, @NonNegative int row, + @NonNegative int column) { int visibleRow = state.getVisibleSeriesIndex(row); if (visibleRow < 0) { diff --git a/src/main/java/org/jfree/chart/renderer/category/LayeredBarRenderer.java b/src/main/java/org/jfree/chart/renderer/category/LayeredBarRenderer.java index 80629edc5..ff887c358 100644 --- a/src/main/java/org/jfree/chart/renderer/category/LayeredBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/LayeredBarRenderer.java @@ -54,6 +54,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; @@ -105,7 +107,7 @@ public LayeredBarRenderer() { * * @return The width for the series (1.0=100%, it is the maximum). */ - public double getSeriesBarWidth(int series) { + public double getSeriesBarWidth(@NonNegative int series) { double result = Double.NaN; Number n = (Number) this.seriesBarWidthList.get(series); if (n != null) { @@ -121,7 +123,7 @@ public double getSeriesBarWidth(int series) { * @param width the width of the series bar in percentage (1.0=100%, it is * the maximum). */ - public void setSeriesBarWidth(int series, double width) { + public void setSeriesBarWidth(@NonNegative int series, double width) { this.seriesBarWidthList.set(series, new Double(width)); } @@ -135,7 +137,7 @@ public void setSeriesBarWidth(int series, double width) { */ @Override protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, - int rendererIndex, CategoryItemRendererState state) { + @NonNegative int rendererIndex, CategoryItemRendererState state) { // calculate the bar width - this calculation differs from the // BarRenderer calculation because the bars are layered on top of one @@ -188,7 +190,7 @@ else if (orientation == PlotOrientation.VERTICAL) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset data, int row, int column, + ValueAxis rangeAxis, CategoryDataset data, @NonNegative int row, @NonNegative int column, int pass) { PlotOrientation orientation = plot.getOrientation(); @@ -218,7 +220,7 @@ public void drawItem(Graphics2D g2, CategoryItemRendererState state, protected void drawHorizontalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, - CategoryDataset dataset, int row, int column) { + CategoryDataset dataset, @NonNegative int row, @NonNegative int column) { // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); @@ -343,7 +345,7 @@ protected void drawHorizontalItem(Graphics2D g2, protected void drawVerticalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, - CategoryDataset dataset, int row, int column) { + CategoryDataset dataset, @NonNegative int row, @NonNegative int column) { // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); diff --git a/src/main/java/org/jfree/chart/renderer/category/LevelRenderer.java b/src/main/java/org/jfree/chart/renderer/category/LevelRenderer.java index 23ee9268b..b9c90e06d 100644 --- a/src/main/java/org/jfree/chart/renderer/category/LevelRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/LevelRenderer.java @@ -51,6 +51,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; @@ -180,7 +182,7 @@ public void setMaximumItemWidth(double percent) { */ @Override public CategoryItemRendererState initialise(Graphics2D g2, - Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, + Rectangle2D dataArea, CategoryPlot plot, @NonNegative int rendererIndex, PlotRenderingInfo info) { CategoryItemRendererState state = super.initialise(g2, dataArea, plot, rendererIndex, info); @@ -197,7 +199,7 @@ public CategoryItemRendererState initialise(Graphics2D g2, * @param state the renderer state. */ protected void calculateItemWidth(CategoryPlot plot, - Rectangle2D dataArea, int rendererIndex, + Rectangle2D dataArea, @NonNegative int rendererIndex, CategoryItemRendererState state) { CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); @@ -250,7 +252,7 @@ protected void calculateItemWidth(CategoryPlot plot, */ protected double calculateBarW0(CategoryPlot plot, PlotOrientation orientation, Rectangle2D dataArea, - CategoryAxis domainAxis, CategoryItemRendererState state, int row, + CategoryAxis domainAxis, CategoryItemRendererState state, @NonNegative int row, int column) { // calculate bar width... double space; @@ -298,7 +300,7 @@ protected double calculateBarW0(CategoryPlot plot, @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { // nothing is drawn if the row index is not included in the list with @@ -359,7 +361,8 @@ public void drawItem(Graphics2D g2, CategoryItemRendererState state, } // submit the current data point as a crosshair candidate - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), value, datasetIndex, barW0, barL, orientation); @@ -383,7 +386,7 @@ public void drawItem(Graphics2D g2, CategoryItemRendererState state, * @return The width of one series. */ protected double calculateSeriesWidth(double space, CategoryAxis axis, - int categories, int series) { + int categories, @NonNegative int series) { double factor = 1.0 - getItemMargin() - axis.getLowerMargin() - axis.getUpperMargin(); if (categories > 1) { diff --git a/src/main/java/org/jfree/chart/renderer/category/LineAndShapeRenderer.java b/src/main/java/org/jfree/chart/renderer/category/LineAndShapeRenderer.java index aab8a6164..69da120de 100644 --- a/src/main/java/org/jfree/chart/renderer/category/LineAndShapeRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/LineAndShapeRenderer.java @@ -94,6 +94,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -230,7 +232,7 @@ public LineAndShapeRenderer(boolean lines, boolean shapes) { * * @return A boolean. */ - public boolean getItemLineVisible(int series, int item) { + public boolean getItemLineVisible(@NonNegative int series, @NonNegative int item) { Boolean flag = getSeriesLinesVisible(series); if (flag != null) { return flag; @@ -248,7 +250,7 @@ public boolean getItemLineVisible(int series, int item) { * * @see #setSeriesLinesVisible(int, Boolean) */ - public Boolean getSeriesLinesVisible(int series) { + public Boolean getSeriesLinesVisible(@NonNegative int series) { return this.seriesLinesVisible.getBoolean(series); } @@ -261,7 +263,7 @@ public Boolean getSeriesLinesVisible(int series) { * * @see #getSeriesLinesVisible(int) */ - public void setSeriesLinesVisible(int series, Boolean flag) { + public void setSeriesLinesVisible(@NonNegative int series, Boolean flag) { this.seriesLinesVisible.setBoolean(series, flag); fireChangeEvent(); } @@ -275,7 +277,7 @@ public void setSeriesLinesVisible(int series, Boolean flag) { * * @see #getSeriesLinesVisible(int) */ - public void setSeriesLinesVisible(int series, boolean visible) { + public void setSeriesLinesVisible(@NonNegative int series, boolean visible) { setSeriesLinesVisible(series, Boolean.valueOf(visible)); } @@ -314,7 +316,7 @@ public void setDefaultLinesVisible(boolean flag) { * * @return A boolean. */ - public boolean getItemShapeVisible(int series, int item) { + public boolean getItemShapeVisible(@NonNegative int series, @NonNegative int item) { Boolean flag = getSeriesShapesVisible(series); if (flag != null) { return flag; @@ -332,7 +334,7 @@ public boolean getItemShapeVisible(int series, int item) { * * @see #setSeriesShapesVisible(int, Boolean) */ - public Boolean getSeriesShapesVisible(int series) { + public Boolean getSeriesShapesVisible(@NonNegative int series) { return this.seriesShapesVisible.getBoolean(series); } @@ -345,7 +347,7 @@ public Boolean getSeriesShapesVisible(int series) { * * @see #getSeriesShapesVisible(int) */ - public void setSeriesShapesVisible(int series, boolean visible) { + public void setSeriesShapesVisible(@NonNegative int series, boolean visible) { setSeriesShapesVisible(series, Boolean.valueOf(visible)); } @@ -358,7 +360,7 @@ public void setSeriesShapesVisible(int series, boolean visible) { * * @see #getSeriesShapesVisible(int) */ - public void setSeriesShapesVisible(int series, Boolean flag) { + public void setSeriesShapesVisible(@NonNegative int series, Boolean flag) { this.seriesShapesVisible.setBoolean(series, flag); fireChangeEvent(); } @@ -455,7 +457,7 @@ public void setUseOutlinePaint(boolean use) { * * @return A boolean. */ - public boolean getItemShapeFilled(int series, int item) { + public boolean getItemShapeFilled(@NonNegative int series, @NonNegative int item) { return getSeriesShapesFilled(series); } @@ -467,7 +469,7 @@ public boolean getItemShapeFilled(int series, int item) { * * @return A boolean. */ - public boolean getSeriesShapesFilled(int series) { + public boolean getSeriesShapesFilled(@NonNegative int series) { Boolean flag = this.seriesShapesFilled.getBoolean(series); if (flag != null) { return flag; @@ -484,7 +486,7 @@ public boolean getSeriesShapesFilled(int series) { * * @see #getSeriesShapesFilled(int) */ - public void setSeriesShapesFilled(int series, Boolean filled) { + public void setSeriesShapesFilled(@NonNegative int series, Boolean filled) { this.seriesShapesFilled.setBoolean(series, filled); fireChangeEvent(); } @@ -498,7 +500,7 @@ public void setSeriesShapesFilled(int series, Boolean filled) { * * @see #getSeriesShapesFilled(int) */ - public void setSeriesShapesFilled(int series, boolean filled) { + public void setSeriesShapesFilled(@NonNegative int series, boolean filled) { // delegate setSeriesShapesFilled(series, Boolean.valueOf(filled)); } @@ -630,7 +632,7 @@ public void setItemMargin(double margin) { * @return The legend item. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { CategoryPlot cp = getPlot(); if (cp == null) { @@ -688,7 +690,7 @@ urlText, shapeVisible, shape, getItemShapeFilled(series, 0), * @return The pass count ({@code 2} for this renderer). */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 2; } @@ -709,7 +711,7 @@ public int getPassCount() { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { // do nothing if item is not visible @@ -832,7 +834,8 @@ else if (orientation == PlotOrientation.VERTICAL) { } // submit the current data point as a crosshair candidate - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), value, datasetIndex, x1, y1, orientation); diff --git a/src/main/java/org/jfree/chart/renderer/category/MinMaxCategoryRenderer.java b/src/main/java/org/jfree/chart/renderer/category/MinMaxCategoryRenderer.java index c8b8a507d..8fe8ab1f9 100644 --- a/src/main/java/org/jfree/chart/renderer/category/MinMaxCategoryRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/MinMaxCategoryRenderer.java @@ -60,6 +60,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; @@ -323,7 +325,7 @@ public void setMinIcon(Icon icon) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { // first check the number we are plotting... diff --git a/src/main/java/org/jfree/chart/renderer/category/ScatterRenderer.java b/src/main/java/org/jfree/chart/renderer/category/ScatterRenderer.java index c2b56a35d..7f490e359 100644 --- a/src/main/java/org/jfree/chart/renderer/category/ScatterRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/ScatterRenderer.java @@ -45,6 +45,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -264,7 +266,7 @@ public void setUseOutlinePaint(boolean use) { * @param item the item index (zero-based). * @return A boolean. */ - public boolean getItemShapeFilled(int series, int item) { + public boolean getItemShapeFilled(@NonNegative int series, @NonNegative int item) { return getSeriesShapesFilled(series); } @@ -275,7 +277,7 @@ public boolean getItemShapeFilled(int series, int item) { * @param series the series index (zero-based). * @return A boolean. */ - public boolean getSeriesShapesFilled(int series) { + public boolean getSeriesShapesFilled(@NonNegative int series) { Boolean flag = this.seriesShapesFilled.getBoolean(series); if (flag != null) { return flag.booleanValue(); @@ -293,7 +295,7 @@ public boolean getSeriesShapesFilled(int series) { * @param series the series index (zero-based). * @param filled the flag. */ - public void setSeriesShapesFilled(int series, Boolean filled) { + public void setSeriesShapesFilled(@NonNegative int series, Boolean filled) { this.seriesShapesFilled.setBoolean(series, filled); fireChangeEvent(); } @@ -305,7 +307,7 @@ public void setSeriesShapesFilled(int series, Boolean filled) { * @param series the series index (zero-based). * @param filled the flag. */ - public void setSeriesShapesFilled(int series, boolean filled) { + public void setSeriesShapesFilled(@NonNegative int series, boolean filled) { this.seriesShapesFilled.setBoolean(series, Boolean.valueOf(filled)); fireChangeEvent(); } @@ -385,7 +387,7 @@ public Range findRangeBounds(CategoryDataset dataset) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { // do nothing if item is not visible @@ -462,7 +464,7 @@ else if (orientation == PlotOrientation.VERTICAL) { * @return The legend item. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { CategoryPlot cp = getPlot(); if (cp == null) { diff --git a/src/main/java/org/jfree/chart/renderer/category/StackedAreaRenderer.java b/src/main/java/org/jfree/chart/renderer/category/StackedAreaRenderer.java index e2037163a..07845967d 100644 --- a/src/main/java/org/jfree/chart/renderer/category/StackedAreaRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/StackedAreaRenderer.java @@ -68,6 +68,10 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -158,7 +162,7 @@ public void setRenderAsPercentages(boolean asPercentages) { * @return The number of passes required by the renderer. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 2; } @@ -200,7 +204,7 @@ public Range findRangeBounds(CategoryDataset dataset) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { if (!isSeriesVisible(row)) { @@ -251,7 +255,8 @@ public void drawItem(Graphics2D g2, CategoryItemRendererState state, double xx0 = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); - int itemCount = dataset.getColumnCount(); + @SuppressWarnings("index") // Possible bug. I don't see anything that implies that there is an item in the dataset, but I might I have missed it + @Positive int itemCount = dataset.getColumnCount(); double y2 = 0.0; n = dataset.getValue(row, Math.min(column + 1, itemCount - 1)); if (n != null) { @@ -412,8 +417,8 @@ else if (pass == 1) { * for all series values up to but excluding {@code series} * for {@code index}. */ - protected double[] getStackValues(CategoryDataset dataset, - int series, int index, int[] validRows) { + protected double @ArrayLen(2) [] getStackValues(CategoryDataset dataset, + int series, @NonNegative int index, @NonNegative int[] validRows) { double[] result = new double[2]; double total = 0.0; if (this.renderAsPercentages) { @@ -452,7 +457,7 @@ protected double[] getStackValues(CategoryDataset dataset, * * @return A pair of average stack values. */ - private double[] averageStackValues(double[] stack1, double[] stack2) { + private double @ArrayLen(2) [] averageStackValues(double @MinLen(2) [] stack1, double @MinLen(2) [] stack2) { double[] result = new double[2]; result[0] = (stack1[0] + stack2[0]) / 2.0; result[1] = (stack1[1] + stack2[1]) / 2.0; @@ -469,7 +474,7 @@ private double[] averageStackValues(double[] stack1, double[] stack2) { * * @return A pair of average stack values. */ - private double[] adjustedStackValues(double[] stack1, double[] stack2) { + private double @ArrayLen(2) [] adjustedStackValues(double @MinLen(2) [] stack1, double @MinLen(2) [] stack2) { double[] result = new double[2]; if (stack1[0] == 0.0 || stack2[0] == 0.0) { result[0] = 0.0; diff --git a/src/main/java/org/jfree/chart/renderer/category/StackedBarRenderer.java b/src/main/java/org/jfree/chart/renderer/category/StackedBarRenderer.java index 979795ddb..89676ba8c 100644 --- a/src/main/java/org/jfree/chart/renderer/category/StackedBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/StackedBarRenderer.java @@ -94,6 +94,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; @@ -200,7 +202,7 @@ public void setRenderAsPercentages(boolean asPercentages) { * @return The number of passes required by the renderer. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 3; } @@ -235,7 +237,7 @@ public Range findRangeBounds(CategoryDataset dataset) { */ @Override protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, - int rendererIndex, CategoryItemRendererState state) { + @NonNegative int rendererIndex, CategoryItemRendererState state) { // calculate the bar width CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex); @@ -286,8 +288,8 @@ else if (orientation == PlotOrientation.VERTICAL) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, - int column, int pass) { + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, + @NonNegative int column, int pass) { if (!isSeriesVisible(row)) { return; diff --git a/src/main/java/org/jfree/chart/renderer/category/StandardBarPainter.java b/src/main/java/org/jfree/chart/renderer/category/StandardBarPainter.java index 3ebc7cbb1..d8e101286 100644 --- a/src/main/java/org/jfree/chart/renderer/category/StandardBarPainter.java +++ b/src/main/java/org/jfree/chart/renderer/category/StandardBarPainter.java @@ -41,6 +41,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; @@ -81,8 +83,8 @@ public StandardBarPainter() { * bar. */ @Override - public void paintBar(Graphics2D g2, BarRenderer renderer, int row, - int column, RectangularShape bar, RectangleEdge base) { + public void paintBar(Graphics2D g2, BarRenderer renderer, @NonNegative int row, + @NonNegative int column, RectangularShape bar, RectangleEdge base) { Paint itemPaint = renderer.getItemPaint(row, column); GradientPaintTransformer t = renderer.getGradientPaintTransformer(); @@ -119,8 +121,8 @@ public void paintBar(Graphics2D g2, BarRenderer renderer, int row, * @param pegShadow peg the shadow to the base of the bar? */ @Override - public void paintBarShadow(Graphics2D g2, BarRenderer renderer, int row, - int column, RectangularShape bar, RectangleEdge base, + public void paintBarShadow(Graphics2D g2, BarRenderer renderer, @NonNegative int row, + @NonNegative int column, RectangularShape bar, RectangleEdge base, boolean pegShadow) { // handle a special case - if the bar colour has alpha == 0, it is diff --git a/src/main/java/org/jfree/chart/renderer/category/StatisticalBarRenderer.java b/src/main/java/org/jfree/chart/renderer/category/StatisticalBarRenderer.java index 70e9dfce8..1ffb342ba 100644 --- a/src/main/java/org/jfree/chart/renderer/category/StatisticalBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/StatisticalBarRenderer.java @@ -64,6 +64,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; @@ -219,7 +221,7 @@ public Range findRangeBounds(CategoryDataset dataset) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset data, int row, int column, + ValueAxis rangeAxis, CategoryDataset data, @NonNegative int row, @NonNegative int column, int pass) { int visibleRow = state.getVisibleSeriesIndex(row); @@ -265,9 +267,9 @@ protected void drawHorizontalItem(Graphics2D g2, CategoryAxis domainAxis, ValueAxis rangeAxis, StatisticalCategoryDataset dataset, - int visibleRow, - int row, - int column) { + @NonNegative int visibleRow, + @NonNegative int row, + @NonNegative int column) { // BAR Y double rectY = calculateBarW0(plot, PlotOrientation.HORIZONTAL, @@ -412,9 +414,9 @@ protected void drawVerticalItem(Graphics2D g2, CategoryAxis domainAxis, ValueAxis rangeAxis, StatisticalCategoryDataset dataset, - int visibleRow, - int row, - int column) { + @NonNegative int visibleRow, + @NonNegative int row, + @NonNegative int column) { // BAR X double rectX = calculateBarW0(plot, PlotOrientation.VERTICAL, dataArea, diff --git a/src/main/java/org/jfree/chart/renderer/category/StatisticalLineAndShapeRenderer.java b/src/main/java/org/jfree/chart/renderer/category/StatisticalLineAndShapeRenderer.java index 298fb1144..1510d3693 100644 --- a/src/main/java/org/jfree/chart/renderer/category/StatisticalLineAndShapeRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/StatisticalLineAndShapeRenderer.java @@ -58,6 +58,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -221,7 +223,7 @@ public Range findRangeBounds(CategoryDataset dataset) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { // do nothing if item is not visible diff --git a/src/main/java/org/jfree/chart/renderer/category/WaterfallBarRenderer.java b/src/main/java/org/jfree/chart/renderer/category/WaterfallBarRenderer.java index 5accf8a9b..76f45d1d2 100644 --- a/src/main/java/org/jfree/chart/renderer/category/WaterfallBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/category/WaterfallBarRenderer.java @@ -60,6 +60,8 @@ package org.jfree.chart.renderer.category; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; @@ -311,7 +313,7 @@ public Range findRangeBounds(CategoryDataset dataset) { @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, - ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, + ValueAxis rangeAxis, CategoryDataset dataset, @NonNegative int row, @NonNegative int column, int pass) { double previous = state.getSeriesRunningTotal(); diff --git a/src/main/java/org/jfree/chart/renderer/xy/AbstractXYItemRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/AbstractXYItemRenderer.java index a3e66fd05..d78986bcc 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/AbstractXYItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/AbstractXYItemRenderer.java @@ -129,6 +129,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Font; @@ -264,7 +269,7 @@ protected AbstractXYItemRenderer() { * @return The pass count. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 1; } @@ -324,7 +329,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, * @since 1.0.20 */ protected void beginElementGroup(Graphics2D g2, Comparable seriesKey, - int itemIndex) { + @NonNegative int itemIndex) { beginElementGroup(g2, new XYItemKey(seriesKey, itemIndex)); } @@ -342,7 +347,7 @@ protected void beginElementGroup(Graphics2D g2, Comparable seriesKey, * @return The generator (possibly {@code null}). */ @Override - public XYItemLabelGenerator getItemLabelGenerator(int series, int item) { + public XYItemLabelGenerator getItemLabelGenerator(@NonNegative int series, @NonNegative int item) { // otherwise look up the generator table XYItemLabelGenerator generator @@ -361,7 +366,7 @@ public XYItemLabelGenerator getItemLabelGenerator(int series, int item) { * @return The generator (possibly {@code null}). */ @Override - public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) { + public XYItemLabelGenerator getSeriesItemLabelGenerator(@NonNegative int series) { return this.itemLabelGeneratorMap.get(series); } @@ -373,7 +378,7 @@ public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) { * @param generator the generator ({@code null} permitted). */ @Override - public void setSeriesItemLabelGenerator(int series, + public void setSeriesItemLabelGenerator(@NonNegative int series, XYItemLabelGenerator generator) { this.itemLabelGeneratorMap.put(series, generator); fireChangeEvent(); @@ -414,7 +419,7 @@ public void setDefaultItemLabelGenerator(XYItemLabelGenerator generator) { * @return The generator (possibly {@code null}). */ @Override - public XYToolTipGenerator getToolTipGenerator(int series, int item) { + public XYToolTipGenerator getToolTipGenerator(@NonNegative int series, @NonNegative int item) { // otherwise look up the generator table XYToolTipGenerator generator @@ -433,7 +438,7 @@ public XYToolTipGenerator getToolTipGenerator(int series, int item) { * @return The generator (possibly {@code null}). */ @Override - public XYToolTipGenerator getSeriesToolTipGenerator(int series) { + public XYToolTipGenerator getSeriesToolTipGenerator(@NonNegative int series) { return this.toolTipGeneratorMap.get(series); } @@ -445,7 +450,7 @@ public XYToolTipGenerator getSeriesToolTipGenerator(int series) { * @param generator the generator ({@code null} permitted). */ @Override - public void setSeriesToolTipGenerator(int series, + public void setSeriesToolTipGenerator(@NonNegative int series, XYToolTipGenerator generator) { this.toolTipGeneratorMap.put(series, generator); fireChangeEvent(); @@ -805,7 +810,8 @@ public LegendItemCollection getLegendItems() { return new LegendItemCollection(); } LegendItemCollection result = new LegendItemCollection(); - int index = this.plot.getIndexOf(this); + @SuppressWarnings("index") // guaranteed index: this is guaranteed to be a renderer for this.plot, so indexOf will return nonnegative + @NonNegative int index = this.plot.getIndexOf(this); XYDataset dataset = this.plot.getDataset(index); if (dataset != null) { int seriesCount = dataset.getSeriesCount(); @@ -832,7 +838,7 @@ public LegendItemCollection getLegendItems() { * @return A legend item for the series. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { XYPlot xyplot = getPlot(); if (xyplot == null) { return null; @@ -1578,7 +1584,7 @@ public DrawingSupplier getDrawingSupplier() { * @since 1.0.20 */ protected void updateCrosshairValues(CrosshairState crosshairState, - double x, double y, int datasetIndex, + double x, double y, @NonNegative int datasetIndex, double transX, double transY, PlotOrientation orientation) { Args.nullNotPermitted(orientation, "orientation"); @@ -1619,7 +1625,7 @@ protected void updateCrosshairValues(CrosshairState crosshairState, * label position). */ protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, - XYDataset dataset, int series, int item, double x, double y, + XYDataset dataset, @NonNegative int series, @IndexFor("#3.getSeries(#4)") int item, double x, double y, boolean negative) { XYItemLabelGenerator generator = getItemLabelGenerator(series, item); @@ -1676,7 +1682,8 @@ else if (layer.equals(Layer.BACKGROUND)) { // should not get here throw new RuntimeException("Unknown layer."); } - int index = this.plot.getIndexOf(this); + @SuppressWarnings("index") // guaranteed index: this is a renderer of this.plot by definition, so indexOf will return nonnegative + @NonNegative int index = this.plot.getIndexOf(this); for (XYAnnotation annotation : toDraw) { annotation.draw(g2, this.plot, dataArea, domainAxis, rangeAxis, index, info); @@ -1702,7 +1709,7 @@ else if (layer.equals(Layer.BACKGROUND)) { * {@code hotspot} is {@code null}). */ protected void addEntity(EntityCollection entities, Shape hotspot, - XYDataset dataset, int series, int item, double entityX, + XYDataset dataset, @NonNegative int series, @IndexFor("#3.getSeries(#4)") int item, double entityX, double entityY) { if (!getItemCreateEntity(series, item)) { diff --git a/src/main/java/org/jfree/chart/renderer/xy/CandlestickRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/CandlestickRenderer.java index 38db9cd4b..5cd2b593b 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/CandlestickRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/CandlestickRenderer.java @@ -88,6 +88,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; @@ -646,7 +650,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { boolean horiz; PlotOrientation orientation = plot.getOrientation(); @@ -667,12 +671,14 @@ else if (orientation == PlotOrientation.VERTICAL) { } OHLCDataset highLowData = (OHLCDataset) dataset; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("highLowData.getSeries(series)") int highLowItem = item; - double x = highLowData.getXValue(series, item); - double yHigh = highLowData.getHighValue(series, item); - double yLow = highLowData.getLowValue(series, item); - double yOpen = highLowData.getOpenValue(series, item); - double yClose = highLowData.getCloseValue(series, item); + double x = highLowData.getXValue(series, highLowItem); + double yHigh = highLowData.getHighValue(series, highLowItem); + double yLow = highLowData.getLowValue(series, highLowItem); + double yOpen = highLowData.getOpenValue(series, highLowItem); + double yClose = highLowData.getCloseValue(series, highLowItem); RectangleEdge domainEdge = plot.getDomainAxisEdge(); double xx = domainAxis.valueToJava2D(x, dataArea, domainEdge); @@ -724,13 +730,15 @@ else if (orientation == PlotOrientation.VERTICAL) { break; case WIDTHMETHOD_INTERVALDATA: - IntervalXYDataset intervalXYData + IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @NonNegative @UpperBoundBottom int intervalXYItem = item; double startPos = domainAxis.valueToJava2D( - intervalXYData.getStartXValue(series, item), + intervalDataset.getStartXValue(series, intervalXYItem), dataArea, plot.getDomainAxisEdge()); double endPos = domainAxis.valueToJava2D( - intervalXYData.getEndXValue(series, item), + intervalDataset.getEndXValue(series, intervalXYItem), dataArea, plot.getDomainAxisEdge()); xxWidth = Math.abs(endPos - startPos); break; @@ -753,7 +761,7 @@ else if (orientation == PlotOrientation.VERTICAL) { g2.setStroke(s); if (this.drawVolume) { - int volume = (int) highLowData.getVolumeValue(series, item); + int volume = (int) highLowData.getVolumeValue(series, highLowItem); double volumeHeight = volume / this.maxVolume; double min, max; diff --git a/src/main/java/org/jfree/chart/renderer/xy/ClusteredXYBarRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/ClusteredXYBarRenderer.java index 578d8fd5c..3891dfcdc 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/ClusteredXYBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/ClusteredXYBarRenderer.java @@ -66,6 +66,8 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; @@ -136,7 +138,7 @@ public ClusteredXYBarRenderer(double margin, * @return {@code 2}. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 2; } @@ -225,19 +227,21 @@ protected Range findDomainBoundsWithOffset(IntervalXYDataset dataset) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("intervalDataset.getSeries(series)") int intervalXYItem = item; double y0; double y1; if (getUseYInterval()) { - y0 = intervalDataset.getStartYValue(series, item); - y1 = intervalDataset.getEndYValue(series, item); + y0 = intervalDataset.getStartYValue(series, intervalXYItem); + y1 = intervalDataset.getEndYValue(series, intervalXYItem); } else { y0 = getBase(); - y1 = intervalDataset.getYValue(series, item); + y1 = intervalDataset.getYValue(series, intervalXYItem); } if (Double.isNaN(y0) || Double.isNaN(y1)) { return; @@ -249,10 +253,10 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, plot.getRangeAxisEdge()); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); - double x0 = intervalDataset.getStartXValue(series, item); + double x0 = intervalDataset.getStartXValue(series, intervalXYItem); double xx0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation); - double x1 = intervalDataset.getEndXValue(series, item); + double x1 = intervalDataset.getEndXValue(series, intervalXYItem); double xx1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double intervalW = xx1 - xx0; // this may be negative diff --git a/src/main/java/org/jfree/chart/renderer/xy/CyclicXYItemRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/CyclicXYItemRenderer.java index 0be7fc1e8..db56b3887 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/CyclicXYItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/CyclicXYItemRenderer.java @@ -47,6 +47,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; @@ -61,6 +65,7 @@ import org.jfree.data.DomainOrder; import org.jfree.data.general.DatasetChangeListener; import org.jfree.data.general.DatasetGroup; +import org.jfree.data.general.Series; import org.jfree.data.xy.XYDataset; /** @@ -144,7 +149,7 @@ public CyclicXYItemRenderer(int type, public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { if ((!getPlotLines()) || ((!(domainAxis instanceof CyclicNumberAxis)) && (!(rangeAxis instanceof CyclicNumberAxis))) || (item <= 0)) { @@ -220,12 +225,16 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, ny[1] = ycycleBound; nx[1] = (x[1] - x[0]) * (ycycleBound - y[0]) / (y[1] - y[0]) + x[0]; + if (x.length == 3) { - nx[3] = x[2]; ny[3] = y[2]; + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/202: x's length is exactly one less than both nx and ny; x and y's lengths are the same + double dead = (nx[3] = x[2]); + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/202: x's length is exactly one less than both nx and ny; x and y's lengths are the same + double dead2 = (ny[3] = y[2]); } x = nx; y = ny; } - else if ((x.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1]) + else if ((x.length == 3) && (y.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1]) && (ycycleBound <= y[2]) || (ycycleBound >= y[2]) && (ycycleBound <= y[1]))) { double[] nx = new double[4]; @@ -240,12 +249,13 @@ else if ((x.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1]) } // If the line is not wrapping, then parent is OK - if (x.length == 2) { + if (x.length == 2 && y.length == 2) { super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass); return; } + @SuppressWarnings("index") // x and y always have the same length here OverwriteDataSet newset = new OverwriteDataSet(x, y, dataset); if (cnax != null) { @@ -264,11 +274,19 @@ else if ((x.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1]) cnay.setBoundMappedToLastCycle(y[0] <= ycycleBound); } } + + @SuppressWarnings("index") // newset is created from x and y, which are both arrays with at least two elements. So one is always a valid index to either + @LTLengthOf("newset.getSeries(series)") int one = 1; super.drawItem( g2, state, dataArea, info, plot, domainAxis, rangeAxis, - newset, series, 1, crosshairState, pass + newset, series, one, crosshairState, pass ); + if (x.length < 3 || y.length < 3) { + // dead code, necessary for typechecking. x and y have correlated lengths + return; + } + if (cnax != null) { if (xcycleBound == x[1]) { cnax.setBoundMappedToLastCycle(x[2] <= xcycleBound); @@ -285,10 +303,14 @@ else if ((x.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1]) cnay.setBoundMappedToLastCycle(y[1] <= ycycleBound); } } + + @SuppressWarnings("index") // newset is created from x and y, which are both arrays with at least two elements. But, if x and y were of length two we already returned. So, two is a valid index into newset. + @LTLengthOf("newset.getSeries(series)") int two = 2; + super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, - newset, series, 2, crosshairState, pass); + newset, series, two, crosshairState, pass); - if (x.length == 4) { + if (x.length == 4 && y.length == 4) { if (cnax != null) { if (xcycleBound == x[2]) { cnax.setBoundMappedToLastCycle(x[3] <= xcycleBound); @@ -305,8 +327,12 @@ else if ((x.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1]) cnay.setBoundMappedToLastCycle(y[2] <= ycycleBound); } } + + @SuppressWarnings("index") // newset is created from x and y, which are both arrays with exactly four elements. + @LTLengthOf("newset.getSeries(series)") int three = 3; + super.drawItem(g2, state, dataArea, info, plot, domainAxis, - rangeAxis, newset, series, 3, crosshairState, pass); + rangeAxis, newset, series, three, crosshairState, pass); } if (cnax != null) { @@ -335,7 +361,7 @@ protected static class OverwriteDataSet implements XYDataset { * @param y the y values. * @param delegateSet the dataset. */ - public OverwriteDataSet(double [] x, double[] y, + public OverwriteDataSet(double @SameLen("#2") [] x, double @SameLen("#1") [] y, XYDataset delegateSet) { this.delegateSet = delegateSet; this.x = new Double[x.length]; this.y = new Double[y.length]; @@ -363,7 +389,8 @@ public DomainOrder getDomainOrder() { * @return The item count. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // SameLen to a method with any argument https://github.com/kelloggm/checker-framework/issues/209 + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return this.x.length; } @@ -376,7 +403,8 @@ public int getItemCount(int series) { * @return The x-value. */ @Override - public Number getX(int series, int item) { + @SuppressWarnings("index") // SameLen to a method with any argument https://github.com/kelloggm/checker-framework/issues/209 + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.x[item]; } @@ -390,7 +418,7 @@ public Number getX(int series, int item) { * @return The x-value. */ @Override - public double getXValue(int series, int item) { + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number xx = getX(series, item); if (xx != null) { @@ -408,7 +436,8 @@ public double getXValue(int series, int item) { * @return The y-value. */ @Override - public Number getY(int series, int item) { + @SuppressWarnings("index") // SameLen to a method with any argument https://github.com/kelloggm/checker-framework/issues/209 + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.y[item]; } @@ -422,7 +451,7 @@ public Number getY(int series, int item) { * @return The y-value. */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number yy = getY(series, item); if (yy != null) { @@ -437,7 +466,7 @@ public double getYValue(int series, int item) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.delegateSet.getSeriesCount(); } @@ -449,7 +478,7 @@ public int getSeriesCount() { * @return The series name. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { return this.delegateSet.getSeriesKey(series); } @@ -461,7 +490,7 @@ public Comparable getSeriesKey(int series) { * @return The index. */ @Override - public int indexOf(Comparable seriesName) { + public @GTENegativeOne int indexOf(Comparable seriesName) { return this.delegateSet.indexOf(seriesName); } @@ -506,6 +535,14 @@ public void setGroup(DatasetGroup group) { // unused in parent } + /** + * A ghost method. Do not call. + */ + @Override + public Series getSeries(@NonNegative int series) { + return null; + } + } } diff --git a/src/main/java/org/jfree/chart/renderer/xy/DeviationRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/DeviationRenderer.java index 606de1b7c..cb9d837f3 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/DeviationRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/DeviationRenderer.java @@ -45,6 +45,12 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics2D; @@ -88,13 +94,13 @@ public static class State extends XYLineAndShapeRenderer.State { * A list of coordinates for the upper y-values in the current series * (after translation into Java2D space). */ - public List upperCoordinates; + public List<double @ArrayLen(2) []> upperCoordinates; /** * A list of coordinates for the lower y-values in the current series * (after translation into Java2D space). */ - public List lowerCoordinates; + public List<double @ArrayLen(2) []> lowerCoordinates; /** * Creates a new state instance. @@ -213,7 +219,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, * @return {@code 3}. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 3; } @@ -269,7 +275,7 @@ protected boolean isLinePass(int pass) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { @@ -279,11 +285,15 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, // first pass draws the shading if (pass == 0) { IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; + + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("intervalDataset.getSeries(series)") int intervalXYItem = item; + State drState = (State) state; - double x = intervalDataset.getXValue(series, item); - double yLow = intervalDataset.getStartYValue(series, item); - double yHigh = intervalDataset.getEndYValue(series, item); + double x = intervalDataset.getXValue(series, intervalXYItem); + double yLow = intervalDataset.getStartYValue(series, intervalXYItem); + double yHigh = intervalDataset.getEndYValue(series, intervalXYItem); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); @@ -314,17 +324,18 @@ else if (orientation == PlotOrientation.VERTICAL) { GeneralPath area = new GeneralPath(GeneralPath.WIND_NON_ZERO, drState.lowerCoordinates.size() + drState.upperCoordinates.size()); - double[] coords = (double[]) drState.lowerCoordinates.get(0); + double[] coords = drState.lowerCoordinates.get(0); area.moveTo((float) coords[0], (float) coords[1]); for (int i = 1; i < drState.lowerCoordinates.size(); i++) { - coords = (double[]) drState.lowerCoordinates.get(i); + coords = drState.lowerCoordinates.get(i); area.lineTo((float) coords[0], (float) coords[1]); } - int count = drState.upperCoordinates.size(); - coords = (double[]) drState.upperCoordinates.get(count - 1); + @SuppressWarnings("index") // upperCoordinates always has at least one entry, because first pass always adds one entry + @Positive int count = drState.upperCoordinates.size(); + coords = drState.upperCoordinates.get(count - 1); area.lineTo((float) coords[0], (float) coords[1]); for (int i = count - 2; i >= 0; i--) { - coords = (double[]) drState.upperCoordinates.get(i); + coords = drState.upperCoordinates.get(i); area.lineTo((float) coords[0], (float) coords[1]); } area.closePath(); diff --git a/src/main/java/org/jfree/chart/renderer/xy/GradientXYBarPainter.java b/src/main/java/org/jfree/chart/renderer/xy/GradientXYBarPainter.java index 0b79ff9a3..f5b68af0e 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/GradientXYBarPainter.java +++ b/src/main/java/org/jfree/chart/renderer/xy/GradientXYBarPainter.java @@ -41,6 +41,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; @@ -102,8 +106,8 @@ public GradientXYBarPainter(double g1, double g2, double g3) { * bar. */ @Override - public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row, - int column, RectangularShape bar, RectangleEdge base) { + public void paintBar(Graphics2D g2, XYBarRenderer renderer, @NonNegative int row, + @NonNegative int column, RectangularShape bar, RectangleEdge base) { Paint itemPaint = renderer.getItemPaint(row, column); @@ -203,8 +207,8 @@ else if (base == RectangleEdge.LEFT || base == RectangleEdge.RIGHT) { * @param pegShadow peg the shadow to the base of the bar? */ @Override - public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, int row, - int column, RectangularShape bar, RectangleEdge base, + public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, @NonNegative int row, + @NonNegative int column, RectangularShape bar, RectangleEdge base, boolean pegShadow) { // handle a special case - if the bar colour has alpha == 0, it is @@ -287,7 +291,7 @@ else if (base == RectangleEdge.RIGHT) { * * @return An array containing four subregions. */ - private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a, + private Rectangle2D @ArrayLen(4) [] splitVerticalBar(RectangularShape bar, double a, double b, double c) { Rectangle2D[] result = new Rectangle2D[4]; double x0 = bar.getMinX(); @@ -316,7 +320,7 @@ private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a, * * @return An array containing four subregions. */ - private Rectangle2D[] splitHorizontalBar(RectangularShape bar, double a, + private Rectangle2D @ArrayLen(4) [] splitHorizontalBar(RectangularShape bar, double a, double b, double c) { Rectangle2D[] result = new Rectangle2D[4]; double y0 = bar.getMinY(); diff --git a/src/main/java/org/jfree/chart/renderer/xy/HighLowRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/HighLowRenderer.java index 380eb16c4..7856c0752 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/HighLowRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/HighLowRenderer.java @@ -68,6 +68,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -320,7 +324,7 @@ public Range findRangeBounds(XYDataset dataset) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { double x = dataset.getXValue(series, item); if (!domainAxis.getRange().contains(x)) { @@ -347,8 +351,11 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, if (dataset instanceof OHLCDataset) { OHLCDataset hld = (OHLCDataset) dataset; - double yHigh = hld.getHighValue(series, item); - double yLow = hld.getLowValue(series, item); + @SuppressWarnings("index") // SameLen to a method with any argument https://github.com/kelloggm/checker-framework/issues/209 + @IndexFor("hld.getSeries(series)") int hldItem = item; + + double yHigh = hld.getHighValue(series, hldItem); + double yLow = hld.getLowValue(series, hldItem); if (!Double.isNaN(yHigh) && !Double.isNaN(yLow)) { double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, location); @@ -372,7 +379,7 @@ else if (orientation == PlotOrientation.VERTICAL) { delta = -delta; } if (getDrawOpenTicks()) { - double yOpen = hld.getOpenValue(series, item); + double yOpen = hld.getOpenValue(series, hldItem); if (!Double.isNaN(yOpen)) { double yyOpen = rangeAxis.valueToJava2D(yOpen, dataArea, location); @@ -394,7 +401,7 @@ else if (orientation == PlotOrientation.VERTICAL) { } if (getDrawCloseTicks()) { - double yClose = hld.getCloseValue(series, item); + double yClose = hld.getCloseValue(series, hldItem); if (!Double.isNaN(yClose)) { double yyClose = rangeAxis.valueToJava2D( yClose, dataArea, location); diff --git a/src/main/java/org/jfree/chart/renderer/xy/SamplingXYLineRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/SamplingXYLineRenderer.java index e43a25e6a..d7b47cce9 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/SamplingXYLineRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/SamplingXYLineRenderer.java @@ -45,6 +45,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.GeneralPath; @@ -100,7 +105,7 @@ public SamplingXYLineRenderer() { * @return The pass count. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 1; } @@ -168,8 +173,8 @@ public State(PlotRenderingInfo info) { * @param passCount the number of passes. */ @Override - public void startSeriesPass(XYDataset dataset, int series, - int firstItem, int lastItem, int pass, int passCount) { + public void startSeriesPass(XYDataset dataset, @NonNegative int series, + @IndexFor("#1.getSeries(#2)") int firstItem, @IndexFor("#1.getSeries(#2)") int lastItem, int pass, int passCount) { this.seriesPath.reset(); this.intervalPath.reset(); this.lastPointGood = false; @@ -234,7 +239,7 @@ public XYItemRendererState initialise(Graphics2D g2, public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { diff --git a/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer.java index 502868212..582a4c8d7 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer.java @@ -71,6 +71,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; @@ -344,7 +348,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, * @return 2. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 2; } @@ -396,7 +400,7 @@ public Range findRangeBounds(XYDataset dataset) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { PlotOrientation orientation = plot.getOrientation(); StackedXYAreaRendererState areaState @@ -534,7 +538,8 @@ else if (orientation == PlotOrientation.HORIZONTAL) { } } - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, ph1 + y1, datasetIndex, transX1, transY1, orientation); @@ -624,10 +629,12 @@ else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { * {@code series} for {@code index}. */ protected double getPreviousHeight(TableXYDataset dataset, - int series, int index) { + int series, @NonNegative int index) { double result = 0.0; for (int i = 0; i < series; i++) { - double value = dataset.getYValue(i, index); + @SuppressWarnings("index") // precondition of this function is that index is an index into every series up to series + @IndexFor("dataset.getSeries(i)") int iIndex = index; + double value = dataset.getYValue(i, iIndex); if (!Double.isNaN(value)) { result += value; } diff --git a/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer2.java b/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer2.java index c9da1479b..660f4a4cd 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer2.java +++ b/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer2.java @@ -57,6 +57,13 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Area; @@ -191,7 +198,7 @@ public Range findRangeBounds(XYDataset dataset) { * @return 1. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 1; } @@ -216,7 +223,7 @@ public int getPassCount() { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { // setup for collecting optional entity info... EntityCollection entities = null; @@ -237,15 +244,20 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, // get the previous point and the next point so we can calculate a // "hot spot" for the area (used by the chart entity)... - double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); - double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); + + @SuppressWarnings("index") // itemCount is at least one, so zero is an index + @IndexFor("dataset.getSeries(series)") int zero = 0; + + double x0 = dataset.getXValue(series, Math.max(item - 1, zero)); + double y0 = dataset.getYValue(series, Math.max(item - 1, zero)); if (Double.isNaN(y0)) { y0 = 0.0; } double[] stack0 = getStackValues(tdataset, series, Math.max(item - 1, 0)); - int itemCount = dataset.getItemCount(series); + @SuppressWarnings("index") // itemCount must be positive if we have an index, which we do (item) + @Positive int itemCount = dataset.getItemCount(series); double x2 = dataset.getXValue(series, Math.min(item + 1, itemCount - 1)); double y2 = dataset.getYValue(series, Math.min(item + 1, @@ -459,11 +471,13 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, * for all series values up to but excluding {@code series} * for {@code index}. */ - private double[] getStackValues(TableXYDataset dataset, - int series, int index) { + private double @MinLen(2) [] getStackValues(TableXYDataset dataset, + @NonNegative int series, @NonNegative int index) { double[] result = new double[2]; for (int i = 0; i < series; i++) { - double v = dataset.getYValue(i, index); + @SuppressWarnings("index") // index must be an index into all series < series is a precondition of this function + @IndexFor("dataset.getSeries(i)") int iIndex = index; + double v = dataset.getYValue(i, iIndex); if (!Double.isNaN(v)) { if (v >= 0.0) { result[1] += v; @@ -485,7 +499,7 @@ private double[] getStackValues(TableXYDataset dataset, * * @return A pair of average stack values. */ - private double[] averageStackValues(double[] stack1, double[] stack2) { + private double @ArrayLen(2) [] averageStackValues(double @MinLen(2) [] stack1, double @MinLen(2) [] stack2) { double[] result = new double[2]; result[0] = (stack1[0] + stack2[0]) / 2.0; result[1] = (stack1[1] + stack2[1]) / 2.0; @@ -502,7 +516,7 @@ private double[] averageStackValues(double[] stack1, double[] stack2) { * * @return A pair of average stack values. */ - private double[] adjustedStackValues(double[] stack1, double[] stack2) { + private double @ArrayLen(2) [] adjustedStackValues(double @MinLen(2) [] stack1, double @MinLen(2) [] stack2) { double[] result = new double[2]; if (stack1[0] == 0.0 || stack2[0] == 0.0) { result[0] = 0.0; diff --git a/src/main/java/org/jfree/chart/renderer/xy/StackedXYBarRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/StackedXYBarRenderer.java index 2be1d555b..850cc8790 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/StackedXYBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/StackedXYBarRenderer.java @@ -55,6 +55,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; @@ -166,7 +170,7 @@ public void setRenderAsPercentages(boolean asPercentages) { * @return {@code 2}. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 3; } @@ -237,7 +241,7 @@ public Range findRangeBounds(XYDataset dataset) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; @@ -263,7 +267,9 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, } IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; - double value = intervalDataset.getYValue(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("intervalDataset.getSeries(series)") int intervalXYItem = item; + double value = intervalDataset.getYValue(series, intervalXYItem); if (Double.isNaN(value)) { return; } @@ -286,6 +292,7 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, double negativeBase = 0.0; for (int i = 0; i < series; i++) { + @SuppressWarnings("index") // this code assumes that item is an index into every series in the dataset preceding the series, not just the series. This isn't an intrinsic property of xydatasets, so I think this may be a bug? double v = dataset.getYValue(i, item); if (!Double.isNaN(v) && isSeriesVisible(i)) { if (this.renderAsPercentages) { @@ -317,14 +324,14 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, } RectangleEdge edgeD = plot.getDomainAxisEdge(); - double startX = intervalDataset.getStartXValue(series, item); + double startX = intervalDataset.getStartXValue(series, intervalXYItem); if (Double.isNaN(startX)) { return; } double translatedStartX = domainAxis.valueToJava2D(startX, dataArea, edgeD); - double endX = intervalDataset.getEndXValue(series, item); + double endX = intervalDataset.getEndXValue(series, intervalXYItem); if (Double.isNaN(endX)) { return; } diff --git a/src/main/java/org/jfree/chart/renderer/xy/StandardXYBarPainter.java b/src/main/java/org/jfree/chart/renderer/xy/StandardXYBarPainter.java index 369515c71..18296bdbe 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/StandardXYBarPainter.java +++ b/src/main/java/org/jfree/chart/renderer/xy/StandardXYBarPainter.java @@ -40,6 +40,8 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; @@ -80,8 +82,8 @@ public StandardXYBarPainter() { * bar. */ @Override - public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row, - int column, RectangularShape bar, RectangleEdge base) { + public void paintBar(Graphics2D g2, XYBarRenderer renderer, @NonNegative int row, + @NonNegative int column, RectangularShape bar, RectangleEdge base) { Paint itemPaint = renderer.getItemPaint(row, column); GradientPaintTransformer t = renderer.getGradientPaintTransformer(); @@ -118,8 +120,8 @@ public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row, * @param pegShadow peg the shadow to the base of the bar? */ @Override - public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, int row, - int column, RectangularShape bar, RectangleEdge base, + public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, @NonNegative int row, + @NonNegative int column, RectangularShape bar, RectangleEdge base, boolean pegShadow) { // handle a special case - if the bar colour has alpha == 0, it is diff --git a/src/main/java/org/jfree/chart/renderer/xy/StandardXYItemRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/StandardXYItemRenderer.java index 79d705ab9..dda88e450 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/StandardXYItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/StandardXYItemRenderer.java @@ -111,6 +111,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Image; import java.awt.Paint; @@ -326,7 +330,7 @@ public void setBaseShapesVisible(boolean flag) { * * @see #getSeriesShapesFilled(int) */ - public boolean getItemShapeFilled(int series, int item) { + public boolean getItemShapeFilled(@NonNegative int series, @NonNegative int item) { // otherwise look up the paint table Boolean flag = this.seriesShapesFilled.getBoolean(series); @@ -346,7 +350,7 @@ public boolean getItemShapeFilled(int series, int item) { * * @return A boolean. */ - public Boolean getSeriesShapesFilled(int series) { + public Boolean getSeriesShapesFilled(@NonNegative int series) { return this.seriesShapesFilled.getBoolean(series); } @@ -359,7 +363,7 @@ public Boolean getSeriesShapesFilled(int series) { * * @see #getSeriesShapesFilled(int) */ - public void setSeriesShapesFilled(int series, Boolean flag) { + public void setSeriesShapesFilled(@NonNegative int series, Boolean flag) { this.seriesShapesFilled.setBoolean(series, flag); fireChangeEvent(); } @@ -574,7 +578,7 @@ public void setLegendLine(Shape line) { * @return A legend item for the series. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { XYPlot plot = getPlot(); if (plot == null) { return null; @@ -630,7 +634,7 @@ public static class State extends XYItemRendererState { public GeneralPath seriesPath; /** The series index. */ - private int seriesIndex; + private @GTENegativeOne int seriesIndex; /** * A flag that indicates if the last (x, y) point was 'good' @@ -672,7 +676,7 @@ public void setLastPointGood(boolean good) { * * @return The series index for the current path. */ - public int getSeriesIndex() { + public @GTENegativeOne int getSeriesIndex() { return this.seriesIndex; } @@ -681,7 +685,7 @@ public int getSeriesIndex() { * * @param index the index. */ - public void setSeriesIndex(int index) { + public void setSeriesIndex(@NonNegative int index) { this.seriesIndex = index; } } @@ -735,7 +739,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { boolean itemVisible = getItemVisible(series, item); @@ -814,8 +818,11 @@ else if (item != 0 && itemVisible) { if (getPlotDiscontinuous()) { // only draw a line if the gap between the current and // previous data point is within the threshold - int numX = dataset.getItemCount(series); - double minX = dataset.getXValue(series, 0); + @SuppressWarnings("index") // item is an index, but it's not equal to zero. This implies that there must be at least two items. + @Positive int numX = dataset.getItemCount(series); + @SuppressWarnings("index") // zero is an index, because itemCount >= 2 + @IndexFor("dataset.getSeries(series)") int zero = 0; + double minX = dataset.getXValue(series, zero); double maxX = dataset.getXValue(series, numX - 1); if (this.gapThresholdType == UnitType.ABSOLUTE) { drawLine = Math.abs(x1 - x0) <= this.gapThreshold; @@ -911,7 +918,8 @@ else if (orientation == PlotOrientation.VERTICAL) { (y1 < 0.0)); } - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); @@ -1009,7 +1017,7 @@ public Object clone() throws CloneNotSupportedException { * * @see #getPlotImages() */ - protected Image getImage(Plot plot, int series, int item, + protected Image getImage(Plot plot, @NonNegative int series, @NonNegative int item, double x, double y) { // this method must be overridden if you want to display images return null; @@ -1032,7 +1040,7 @@ protected Image getImage(Plot plot, int series, int item, * * @return The hotspot used to draw the data item. */ - protected Point getImageHotspot(Plot plot, int series, int item, + protected Point getImageHotspot(Plot plot, @NonNegative int series, @NonNegative int item, double x, double y, Image image) { int height = image.getHeight(null); diff --git a/src/main/java/org/jfree/chart/renderer/xy/VectorRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/VectorRenderer.java index 5e12db54a..a72c41360 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/VectorRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/VectorRenderer.java @@ -47,6 +47,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; @@ -112,7 +116,7 @@ public Range findDomainBounds(XYDataset dataset) { if (dataset instanceof VectorXYDataset) { VectorXYDataset vdataset = (VectorXYDataset) dataset; for (int series = 0; series < seriesCount; series++) { - int itemCount = dataset.getItemCount(series); + int itemCount = vdataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double delta = vdataset.getVectorXValue(series, item); if (delta < 0.0) { @@ -167,7 +171,7 @@ public Range findRangeBounds(XYDataset dataset) { if (dataset instanceof VectorXYDataset) { VectorXYDataset vdataset = (VectorXYDataset) dataset; for (int series = 0; series < seriesCount; series++) { - int itemCount = dataset.getItemCount(series); + int itemCount = vdataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double delta = vdataset.getVectorYValue(series, item); if (delta < 0.0) { @@ -222,15 +226,18 @@ public Range findRangeBounds(XYDataset dataset) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); double dx = 0.0; double dy = 0.0; if (dataset instanceof VectorXYDataset) { - dx = ((VectorXYDataset) dataset).getVectorXValue(series, item); - dy = ((VectorXYDataset) dataset).getVectorYValue(series, item); + VectorXYDataset vectorXYDataset = (VectorXYDataset) dataset; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("vectorXYDataset.getSeries(series)") int vectorXYItem = item; + dx = vectorXYDataset.getVectorXValue(series, vectorXYItem); + dy = vectorXYDataset.getVectorYValue(series, vectorXYItem); } double xx0 = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); diff --git a/src/main/java/org/jfree/chart/renderer/xy/WindItemRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/WindItemRenderer.java index 888662c10..6c77824d5 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/WindItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/WindItemRenderer.java @@ -57,6 +57,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; @@ -118,9 +122,11 @@ public WindItemRenderer() { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D plotArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { WindDataset windData = (WindDataset) dataset; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("windData.getSeries(series)") int windItem = item; Paint seriesPaint = getItemPaint(series, item); Stroke seriesStroke = getItemStroke(series, item); @@ -129,9 +135,9 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, // get the data point... - Number x = windData.getX(series, item); - Number windDir = windData.getWindDirection(series, item); - Number wforce = windData.getWindForce(series, item); + Number x = windData.getX(series, windItem); + Number windDir = windData.getWindDirection(series, windItem); + Number wforce = windData.getWindForce(series, windItem); double windForce = wforce.doubleValue(); double wdirt = Math.toRadians(windDir.doubleValue() * (-30.0) - 90.0); diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer.java index f20c91b7b..cd59ec3d7 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer.java @@ -88,6 +88,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.GradientPaint; import java.awt.Graphics2D; @@ -435,7 +440,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, * @return A legend item for the series. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { LegendItem result = null; XYPlot xyplot = getPlot(); if (xyplot != null) { @@ -493,7 +498,7 @@ public LegendItem getLegendItem(int datasetIndex, int series) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; @@ -513,9 +518,12 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, // get the previous point and the next point so we can calculate a // "hot spot" for the area (used by the chart entity)... - int itemCount = dataset.getItemCount(series); - double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); - double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); + @SuppressWarnings("index") // itemCount needs to be at least 1 if we have an index (which we do - item). + @Positive int itemCount = dataset.getItemCount(series); + @SuppressWarnings("index") // itemCount is at least one, so zero is an index + @IndexFor("dataset.getSeries(series)") int zero_const = 0; + double x0 = dataset.getXValue(series, Math.max(item - 1, zero_const)); + double y0 = dataset.getYValue(series, Math.max(item - 1, zero_const)); if (Double.isNaN(y0)) { y0 = 0.0; } @@ -645,7 +653,8 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, } } - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer2.java b/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer2.java index 9e360d3e6..8c01863a3 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer2.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer2.java @@ -86,6 +86,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -229,7 +234,7 @@ public void setLegendArea(Shape area) { * @return A legend item for the series. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { LegendItem result = null; XYPlot xyplot = getPlot(); if (xyplot != null) { @@ -287,7 +292,7 @@ public LegendItem getLegendItem(int datasetIndex, int series) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; @@ -304,10 +309,13 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, double transY1 = rangeAxis.valueToJava2D(y1, dataArea, plot.getRangeAxisEdge()); + @SuppressWarnings("index") // itemCount is at least one, so zero is an index + @IndexFor("dataset.getSeries(series)") int zero = 0; + // get the previous point and the next point so we can calculate a // "hot spot" for the area (used by the chart entity)... - double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); - double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); + double x0 = dataset.getXValue(series, Math.max(item - 1, zero)); + double y0 = dataset.getYValue(series, Math.max(item - 1, zero)); if (Double.isNaN(y0)) { y0 = 0.0; } @@ -316,7 +324,8 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, double transY0 = rangeAxis.valueToJava2D(y0, dataArea, plot.getRangeAxisEdge()); - int itemCount = dataset.getItemCount(series); + @SuppressWarnings("index") // itemCount must be positive, since we have an index (item) + @Positive int itemCount = dataset.getItemCount(series); double x2 = dataset.getXValue(series, Math.min(item + 1, itemCount - 1)); double y2 = dataset.getYValue(series, Math.min(item + 1, @@ -368,7 +377,9 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, g2.setPaint(lookupSeriesOutlinePaint(series)); g2.draw(hotspot); } - int datasetIndex = plot.indexOf(dataset); + + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYBarPainter.java b/src/main/java/org/jfree/chart/renderer/xy/XYBarPainter.java index def957e46..b9add7bb0 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYBarPainter.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYBarPainter.java @@ -40,6 +40,8 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.geom.RectangularShape; import org.jfree.chart.ui.RectangleEdge; @@ -72,7 +74,7 @@ public interface XYBarPainter { * @param base the base of the bar. */ public void paintBar(Graphics2D g2, XYBarRenderer renderer, - int row, int column, RectangularShape bar, RectangleEdge base); + @NonNegative int row, @NonNegative int column, RectangularShape bar, RectangleEdge base); /** * Paints the shadow for a single bar on behalf of a renderer. @@ -86,7 +88,7 @@ public void paintBar(Graphics2D g2, XYBarRenderer renderer, * @param pegShadow peg the shadow to the base of the bar? */ public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, - int row, int column, RectangularShape bar, RectangleEdge base, + @NonNegative int row, @NonNegative int column, RectangularShape bar, RectangleEdge base, boolean pegShadow); } diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYBarRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYBarRenderer.java index b184ed70d..762a8c043 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYBarRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYBarRenderer.java @@ -106,6 +106,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; @@ -713,6 +717,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset dataset, PlotRenderingInfo info) { XYBarRendererState state = new XYBarRendererState(info); + @SuppressWarnings("index") // dataset is assumed to be associated with plot, but isn't checked. A bug? ValueAxis rangeAxis = plot.getRangeAxisForDataset(plot.indexOf( dataset)); state.setG2Base(rangeAxis.valueToJava2D(this.base, dataArea, @@ -731,7 +736,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, * @return A legend item for the series. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { XYPlot xyplot = getPlot(); if (xyplot == null) { return null; @@ -803,21 +808,24 @@ public LegendItem getLegendItem(int datasetIndex, int series) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; } IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("intervalDataset.getSeries(series)") int intervalItem = item; + double value0; double value1; if (this.useYInterval) { - value0 = intervalDataset.getStartYValue(series, item); - value1 = intervalDataset.getEndYValue(series, item); + value0 = intervalDataset.getStartYValue(series, intervalItem); + value1 = intervalDataset.getEndYValue(series, intervalItem); } else { value0 = this.base; - value1 = intervalDataset.getYValue(series, item); + value1 = intervalDataset.getYValue(series, intervalItem); } if (Double.isNaN(value0) || Double.isNaN(value1)) { return; @@ -839,11 +847,11 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, double bottom = Math.min(translatedValue0, translatedValue1); double top = Math.max(translatedValue0, translatedValue1); - double startX = intervalDataset.getStartXValue(series, item); + double startX = intervalDataset.getStartXValue(series, intervalItem); if (Double.isNaN(startX)) { return; } - double endX = intervalDataset.getEndXValue(series, item); + double endX = intervalDataset.getEndXValue(series, intervalItem); if (Double.isNaN(endX)) { return; } @@ -859,7 +867,7 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, // is there an alignment adjustment to be made? if (this.barAlignmentFactor >= 0.0 && this.barAlignmentFactor <= 1.0) { - double x = intervalDataset.getXValue(series, item); + double x = intervalDataset.getXValue(series, intervalItem); double interval = endX - startX; startX = x - interval * this.barAlignmentFactor; endX = startX + interval; @@ -939,7 +947,8 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, double transX1 = domainAxis.valueToJava2D(x1, dataArea, location); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, plot.getRangeAxisEdge()); - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // plot and dataset must be associated with each other. This is assumed but not checked. A bug? + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, plot.getOrientation()); @@ -967,7 +976,7 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, * @param negative a flag indicating a negative value. */ protected void drawItemLabel(Graphics2D g2, XYDataset dataset, - int series, int item, XYPlot plot, XYItemLabelGenerator generator, + @NonNegative int series, @IndexFor("#2.getSeries(#3)") int item, XYPlot plot, XYItemLabelGenerator generator, Rectangle2D bar, boolean negative) { if (generator == null) { diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYBlockRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYBlockRenderer.java index 2a72a137f..c48c6900c 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYBlockRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYBlockRenderer.java @@ -2,7 +2,7 @@ * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * - * (C) Copyright 2000-2017, by Object Refinery Limited and Contributors. + * (C) Copyright 2000-2018, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * @@ -27,27 +27,17 @@ * -------------------- * XYBlockRenderer.java * -------------------- - * (C) Copyright 2006-2017, by Object Refinery Limited. + * (C) Copyright 2006-2018, by Object Refinery Limited. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -; * - * Changes - * ------- - * 05-Jul-2006 : Version 1 (DG); - * 02-Feb-2007 : Added getPaintScale() method (DG); - * 09-Mar-2007 : Fixed cloning (DG); - * 03-Aug-2007 : Fix for bug 1766646 (DG); - * 07-Apr-2008 : Added entity collection code (DG); - * 22-Apr-2008 : Implemented PublicCloneable (DG); - * 03-Jul-2013 : Use ParamChecks (DG); - * 20-Feb-2017 : Add update for crosshairs (DG); - * */ package org.jfree.chart.renderer.xy; -import java.awt.BasicStroke; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; @@ -109,6 +99,15 @@ public class XYBlockRenderer extends AbstractXYItemRenderer /** The paint scale. */ private PaintScale paintScale; + + /** A flag that controls whether outlines are drawn for blocks. */ + private boolean drawOutlines; + + /** + * A flag that controls whether the outline paint is used for drawing block + * outlines. + */ + private boolean useOutlinePaint; /** * Creates a new {@code XYBlockRenderer} instance with default @@ -117,6 +116,8 @@ public class XYBlockRenderer extends AbstractXYItemRenderer public XYBlockRenderer() { updateOffsets(); this.paintScale = new LookupPaintScale(); + this.drawOutlines = true; + this.useOutlinePaint = false; // use item paint by default } /** @@ -234,6 +235,59 @@ public void setPaintScale(PaintScale scale) { fireChangeEvent(); } + /** + * Returns {@code true} if outlines should be drawn for blocks, and + * {@code false} otherwise. The default value is {@code true}. + * + * @return A boolean. + * + * @see #setDrawOutlines(boolean) + */ + public boolean getDrawOutlines() { + return this.drawOutlines; + } + + /** + * Sets the flag that controls whether outlines are drawn for + * blocks, and sends a {@link RendererChangeEvent} to all registered + * listeners. + * + * @param flag the flag. + * + * @see #getDrawOutlines() + */ + public void setDrawOutlines(boolean flag) { + this.drawOutlines = flag; + fireChangeEvent(); + } + + /** + * Returns {@code true} if the renderer should use the outline paint + * setting to draw block outlines, and {@code false} if it should just + * use the regular item paint. + * + * @return A boolean. + * + * @see #setUseOutlinePaint(boolean) + */ + public boolean getUseOutlinePaint() { + return this.useOutlinePaint; + } + + /** + * Sets the flag that controls whether the outline paint is used to draw + * block outlines, and sends a {@link RendererChangeEvent} to all + * registered listeners. + * + * @param flag the flag. + * + * @see #getUseOutlinePaint() + */ + public void setUseOutlinePaint(boolean flag) { + this.useOutlinePaint = flag; + fireChangeEvent(); + } + /** * Updates the offsets to take into account the block width, height and * anchor. @@ -349,13 +403,16 @@ public Range findRangeBounds(XYDataset dataset) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); double z = 0.0; if (dataset instanceof XYZDataset) { - z = ((XYZDataset) dataset).getZValue(series, item); + XYZDataset xyzDataset = ((XYZDataset) dataset); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("xyzDataset.getSeries(series)") int xyzItem = item; + z = xyzDataset.getZValue(series, xyzItem); } Paint p = this.paintScale.getPaint(z); @@ -381,15 +438,21 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, } g2.setPaint(p); g2.fill(block); - g2.setStroke(new BasicStroke(1.0f)); - g2.draw(block); + if (getDrawOutlines()) { + if (getUseOutlinePaint()) { + g2.setPaint(getItemOutlinePaint(series, item)); + } + g2.setStroke(lookupSeriesOutlineStroke(series)); + g2.draw(block); + } if (isItemLabelVisible(series, item)) { drawItemLabel(g2, orientation, dataset, series, item, block.getCenterX(), block.getCenterY(), y < 0.0); } - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); double transX = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); double transY = rangeAxis.valueToJava2D(y, dataArea, @@ -440,6 +503,12 @@ public boolean equals(Object obj) { if (!this.paintScale.equals(that.paintScale)) { return false; } + if (this.drawOutlines != that.drawOutlines) { + return false; + } + if (this.useOutlinePaint != that.useOutlinePaint) { + return false; + } return super.equals(obj); } diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYBoxAndWhiskerRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYBoxAndWhiskerRenderer.java index 5750d4a1c..ea4ab6377 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYBoxAndWhiskerRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYBoxAndWhiskerRenderer.java @@ -78,6 +78,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; @@ -303,7 +308,7 @@ public Range findRangeBounds(XYDataset dataset) { * * @since 1.0.10 */ - protected Paint lookupBoxPaint(int series, int item) { + protected Paint lookupBoxPaint(@NonNegative int series, @NonNegative int item) { Paint p = getBoxPaint(); if (p != null) { return p; @@ -338,7 +343,7 @@ protected Paint lookupBoxPaint(int series, int item) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { PlotOrientation orientation = plot.getOrientation(); @@ -373,8 +378,8 @@ else if (orientation == PlotOrientation.VERTICAL) { */ public void drawHorizontalItem(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, - ValueAxis rangeAxis, XYDataset dataset, int series, - int item, CrosshairState crosshairState, int pass) { + ValueAxis rangeAxis, XYDataset dataset, @NonNegative int series, + @IndexFor("#7.getSeries(#8)") int item, CrosshairState crosshairState, int pass) { // setup for collecting optional entity info... EntityCollection entities = null; @@ -385,13 +390,16 @@ public void drawHorizontalItem(Graphics2D g2, Rectangle2D dataArea, BoxAndWhiskerXYDataset boxAndWhiskerData = (BoxAndWhiskerXYDataset) dataset; - Number x = boxAndWhiskerData.getX(series, item); - Number yMax = boxAndWhiskerData.getMaxRegularValue(series, item); - Number yMin = boxAndWhiskerData.getMinRegularValue(series, item); - Number yMedian = boxAndWhiskerData.getMedianValue(series, item); - Number yAverage = boxAndWhiskerData.getMeanValue(series, item); - Number yQ1Median = boxAndWhiskerData.getQ1Value(series, item); - Number yQ3Median = boxAndWhiskerData.getQ3Value(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("boxAndWhiskerData.getSeries(series)") int boxAndWhiskerItem = item; + + Number x = boxAndWhiskerData.getX(series, boxAndWhiskerItem); + Number yMax = boxAndWhiskerData.getMaxRegularValue(series, boxAndWhiskerItem); + Number yMin = boxAndWhiskerData.getMinRegularValue(series, boxAndWhiskerItem); + Number yMedian = boxAndWhiskerData.getMedianValue(series, boxAndWhiskerItem); + Number yAverage = boxAndWhiskerData.getMeanValue(series, boxAndWhiskerItem); + Number yQ1Median = boxAndWhiskerData.getQ1Value(series, boxAndWhiskerItem); + Number yQ3Median = boxAndWhiskerData.getQ3Value(series, boxAndWhiskerItem); double xx = domainAxis.valueToJava2D(x.doubleValue(), dataArea, plot.getDomainAxisEdge()); @@ -513,8 +521,8 @@ else if (exactBoxWidth > maxBoxWidth) { */ public void drawVerticalItem(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, - ValueAxis rangeAxis, XYDataset dataset, int series, - int item, CrosshairState crosshairState, int pass) { + ValueAxis rangeAxis, XYDataset dataset, @NonNegative int series, + @IndexFor("#7.getSeries(#8)") int item, CrosshairState crosshairState, int pass) { // setup for collecting optional entity info... EntityCollection entities = null; @@ -525,14 +533,17 @@ public void drawVerticalItem(Graphics2D g2, Rectangle2D dataArea, BoxAndWhiskerXYDataset boxAndWhiskerData = (BoxAndWhiskerXYDataset) dataset; - Number x = boxAndWhiskerData.getX(series, item); - Number yMax = boxAndWhiskerData.getMaxRegularValue(series, item); - Number yMin = boxAndWhiskerData.getMinRegularValue(series, item); - Number yMedian = boxAndWhiskerData.getMedianValue(series, item); - Number yAverage = boxAndWhiskerData.getMeanValue(series, item); - Number yQ1Median = boxAndWhiskerData.getQ1Value(series, item); - Number yQ3Median = boxAndWhiskerData.getQ3Value(series, item); - List yOutliers = boxAndWhiskerData.getOutliers(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("boxAndWhiskerData.getSeries(series)") int boxAndWhiskerItem = item; + + Number x = boxAndWhiskerData.getX(series, boxAndWhiskerItem); + Number yMax = boxAndWhiskerData.getMaxRegularValue(series, boxAndWhiskerItem); + Number yMin = boxAndWhiskerData.getMinRegularValue(series, boxAndWhiskerItem); + Number yMedian = boxAndWhiskerData.getMedianValue(series, boxAndWhiskerItem); + Number yAverage = boxAndWhiskerData.getMeanValue(series, boxAndWhiskerItem); + Number yQ1Median = boxAndWhiskerData.getQ1Value(series, boxAndWhiskerItem); + Number yQ3Median = boxAndWhiskerData.getQ3Value(series, boxAndWhiskerItem); + List yOutliers = boxAndWhiskerData.getOutliers(series, boxAndWhiskerItem); // yOutliers can be null, but we'd prefer it to be an empty list in // that case... if (yOutliers == null) { @@ -644,21 +655,21 @@ else if (exactBoxWidth > maxBoxWidth) { for (int i = 0; i < yOutliers.size(); i++) { double outlier = ((Number) yOutliers.get(i)).doubleValue(); if (outlier > boxAndWhiskerData.getMaxOutlier(series, - item).doubleValue()) { + boxAndWhiskerItem).doubleValue()) { outlierListCollection.setHighFarOut(true); } else if (outlier < boxAndWhiskerData.getMinOutlier(series, - item).doubleValue()) { + boxAndWhiskerItem).doubleValue()) { outlierListCollection.setLowFarOut(true); } else if (outlier > boxAndWhiskerData.getMaxRegularValue(series, - item).doubleValue()) { + boxAndWhiskerItem).doubleValue()) { yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, location); outliers.add(new Outlier(xx, yyOutlier, oRadius)); } else if (outlier < boxAndWhiskerData.getMinRegularValue(series, - item).doubleValue()) { + boxAndWhiskerItem).doubleValue()) { yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, location); outliers.add(new Outlier(xx, yyOutlier, oRadius)); diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYBubbleRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYBubbleRenderer.java index 238079a8c..a34581c37 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYBubbleRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYBubbleRenderer.java @@ -64,6 +64,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -175,7 +180,7 @@ public int getScaleType() { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { // return straight away if the item is not visible if (!getItemVisible(series, item)) { @@ -190,7 +195,10 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, double z = Double.NaN; if (dataset instanceof XYZDataset) { XYZDataset xyzData = (XYZDataset) dataset; - z = xyzData.getZValue(series, item); + + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("xyzData.getSeries(series)") int xyzItem = item; + z = xyzData.getZValue(series, xyzItem); } if (!Double.isNaN(z)) { RectangleEdge domainAxisLocation = plot.getDomainAxisEdge(); @@ -269,7 +277,8 @@ else if (orientation == PlotOrientation.HORIZONTAL) { } } - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x, y, datasetIndex, transX, transY, orientation); } @@ -286,7 +295,7 @@ else if (orientation == PlotOrientation.HORIZONTAL) { * @return A legend item for the series. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { LegendItem result = null; XYPlot plot = getPlot(); if (plot == null) { diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYDifferenceRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYDifferenceRenderer.java index 5a181152d..dc22dfbf2 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYDifferenceRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYDifferenceRenderer.java @@ -83,6 +83,9 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; @@ -343,7 +346,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, * @return The number of passes required by the renderer. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 2; } @@ -369,7 +372,7 @@ public int getPassCount() { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { if (pass == 0) { drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis, @@ -405,8 +408,8 @@ protected void drawItemPass0(Graphics2D x_graphics, ValueAxis x_domainAxis, ValueAxis x_rangeAxis, XYDataset x_dataset, - int x_series, - int x_item, + @NonNegative int x_series, + @IndexFor("#7.getSeries(#8)") int x_item, CrosshairState x_crosshairState) { if (!((0 == x_series) && (0 == x_item))) { @@ -434,8 +437,10 @@ protected void drawItemPass0(Graphics2D x_graphics, LinkedList l_polygonYs = new LinkedList(); // state - int l_minuendItem = 0; - int l_minuendItemCount = x_dataset.getItemCount(0); + @SuppressWarnings("index") // one of the guarantees of a non-degenerate series is that it has at least one item + @IndexFor("x_dataset.getSeries(0)") int l_minuendItem = 0; + @SuppressWarnings("index") // the call to isEitherSeriesDegenerate above guarantees this is true + @Positive int l_minuendItemCount = x_dataset.getItemCount(0); Double l_minuendCurX = null; Double l_minuendNextX = null; Double l_minuendCurY = null; @@ -443,7 +448,8 @@ protected void drawItemPass0(Graphics2D x_graphics, double l_minuendMaxY = Double.NEGATIVE_INFINITY; double l_minuendMinY = Double.POSITIVE_INFINITY; - int l_subtrahendItem = 0; + @SuppressWarnings("index") // one of the guarantees of a non-degenerate series is that it has at least one item + @IndexFor("x_dataset.getSeries(1)") int l_subtrahendItem = 0; int l_subtrahendItemCount = 0; // actual value set below Double l_subtrahendCurX = null; Double l_subtrahendNextX = null; @@ -454,9 +460,11 @@ protected void drawItemPass0(Graphics2D x_graphics, // if a subtrahend is not specified, assume it is zero if (b_impliedZeroSubtrahend) { - l_subtrahendItem = 0; + //l_subtrahendItem = 0; l_subtrahendItemCount = 2; - l_subtrahendCurX = new Double(x_dataset.getXValue(0, 0)); + @SuppressWarnings("index") // itemCount is positive for series zero, so zero is an index into that series + @IndexFor("x_dataset.getSeries(0)") int zero = 0; + l_subtrahendCurX = new Double(x_dataset.getXValue(0, zero)); l_subtrahendNextX = new Double(x_dataset.getXValue(0, (l_minuendItemCount - 1))); l_subtrahendCurY = new Double(0.0); @@ -495,8 +503,10 @@ protected void drawItemPass0(Graphics2D x_graphics, // get the x and y coordinates l_x1 = x_dataset.getXValue(0, l_minuendItem); l_y1 = x_dataset.getYValue(0, l_minuendItem); - l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); - l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); + @SuppressWarnings("index") // non-degenerate series always have both x and y coordinates + @IndexFor("x_dataset.getSeries(0)") int l_minuendItemPlusOne = l_minuendItem + 1; + l_x2 = x_dataset.getXValue(0, l_minuendItemPlusOne); + l_y2 = x_dataset.getYValue(0, l_minuendItemPlusOne); l_minuendCurX = new Double(l_x1); l_minuendCurY = new Double(l_y1); @@ -512,8 +522,10 @@ protected void drawItemPass0(Graphics2D x_graphics, else { l_x3 = x_dataset.getXValue(1, l_subtrahendItem); l_y3 = x_dataset.getYValue(1, l_subtrahendItem); - l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); - l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); + @SuppressWarnings("index") // non-degenerate series always have both x and y coordinates + @IndexFor("x_dataset.getSeries(1)") int l_subtrahendItemPlusOne = l_subtrahendItem + 1; + l_x4 = x_dataset.getXValue(1, l_subtrahendItemPlusOne); + l_y4 = x_dataset.getYValue(1, l_subtrahendItemPlusOne); l_subtrahendCurX = new Double(l_x3); l_subtrahendCurY = new Double(l_y3); @@ -522,15 +534,17 @@ protected void drawItemPass0(Graphics2D x_graphics, } if (l_x2 <= l_x3) { - // minuend needs to be fast forwarded - l_minuendItem++; + @SuppressWarnings("index") // minuend needs to be fast forwarded + @IndexFor("x_dataset.getSeries(0)") int l_minuendItemTmp = l_minuendItem + 1; + l_minuendItem = l_minuendItemTmp; b_minuendFastForward = true; continue; } if (l_x4 <= l_x1) { - // subtrahend needs to be fast forwarded - l_subtrahendItem++; + @SuppressWarnings("index") // subtrahend needs to be fast forwarded + @IndexFor("x_dataset.getSeries(1)") int l_subtrahendItemTmp = l_subtrahendItem + 1; + l_subtrahendItem = l_subtrahendItemTmp; b_subtrahendFastForward = true; continue; } @@ -582,8 +596,11 @@ protected void drawItemPass0(Graphics2D x_graphics, l_minuendMaxY = Math.max(l_minuendMaxY, l_y1); l_minuendMinY = Math.min(l_minuendMinY, l_y1); - l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); - l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); + @SuppressWarnings("index") // non-degenerate series always have both x and y coordinates + @IndexFor("x_dataset.getSeries(0)") int l_minuendItemPlusOne = l_minuendItem + 1; + + l_x2 = x_dataset.getXValue(0, l_minuendItemPlusOne); + l_y2 = x_dataset.getYValue(0, l_minuendItemPlusOne); l_minuendNextX = new Double(l_x2); l_minuendNextY = new Double(l_y2); } @@ -604,8 +621,11 @@ protected void drawItemPass0(Graphics2D x_graphics, l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_y3); l_subtrahendMinY = Math.min(l_subtrahendMinY, l_y3); - l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); - l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); + @SuppressWarnings("index") // non-degenerate series always have both x and y coordinates + @IndexFor("x_dataset.getSeries(1)") int l_subtrahendItemPlusOne = l_subtrahendItem + 1; + + l_x4 = x_dataset.getXValue(1, l_subtrahendItemPlusOne); + l_y4 = x_dataset.getYValue(1, l_subtrahendItemPlusOne); l_subtrahendNextX = new Double(l_x4); l_subtrahendNextY = new Double(l_y4); } @@ -763,18 +783,20 @@ protected void drawItemPass0(Graphics2D x_graphics, l_polygonYs.add(l_intersectY); } - // advance the minuend if needed if (l_x2 <= l_x4) { - l_minuendItem++; + @SuppressWarnings("index") // advance the minuend if needed + @IndexFor("x_dataset.getSeries(0)") int l_minuendItemTmp = l_minuendItem + 1; + l_minuendItem = l_minuendItemTmp; b_minuendAdvanced = true; } else { b_minuendAdvanced = false; } - // advance the subtrahend if needed if (l_x4 <= l_x2) { - l_subtrahendItem++; + @SuppressWarnings("index") // advance the subtrahend if needed + @IndexFor("x_dataset.getSeries(1)") int l_subtrahendItemTmp = l_subtrahendItem + 1; + l_subtrahendItem = l_subtrahendItemTmp; b_subtrahendAdvanced = true; } else { @@ -863,8 +885,8 @@ protected void drawItemPass1(Graphics2D x_graphics, ValueAxis x_domainAxis, ValueAxis x_rangeAxis, XYDataset x_dataset, - int x_series, - int x_item, + @NonNegative int x_series, + @IndexFor("#7.getSeries(#8)") int x_item, CrosshairState x_crosshairState) { Shape l_entityArea = null; @@ -936,7 +958,8 @@ protected void drawItemPass1(Graphics2D x_graphics, x_item, l_x1, l_y1, (l_y1 < 0.0)); } - int datasetIndex = x_plot.indexOf(x_dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = x_plot.indexOf(x_dataset); updateCrosshairValues(x_crosshairState, l_x0, l_y0, datasetIndex, l_x1, l_y1, l_orientation); @@ -994,12 +1017,16 @@ private boolean isEitherSeriesDegenerate(XYDataset x_dataset, */ private boolean areSeriesDisjoint(XYDataset x_dataset) { - int l_minuendItemCount = x_dataset.getItemCount(0); - double l_minuendFirst = x_dataset.getXValue(0, 0); + @SuppressWarnings("index") // This method can only called on XYDataset_s with at least one item. See drawItemPass0 + @Positive int l_minuendItemCount = x_dataset.getItemCount(0); + @SuppressWarnings("index") // This method can only called on XYDataset_s with at least one item. See drawItemPass0 + @IndexFor({"x_dataset.getSeries(0)", "x_dataset.getSeries(1)"}) int zero = 0; + double l_minuendFirst = x_dataset.getXValue(0, zero); double l_minuendLast = x_dataset.getXValue(0, l_minuendItemCount - 1); int l_subtrahendItemCount = x_dataset.getItemCount(1); - double l_subtrahendFirst = x_dataset.getXValue(1, 0); + double l_subtrahendFirst = x_dataset.getXValue(1, zero); + @SuppressWarnings("index") // This method can only called on XYDataset_s with at least one item. See drawItemPass0 double l_subtrahendLast = x_dataset.getXValue(1, l_subtrahendItemCount - 1); @@ -1023,6 +1050,7 @@ private boolean areSeriesDisjoint(XYDataset x_dataset) { * @param x_yValues a linked list of the y values (expects values to be * of type Double). */ + @SuppressWarnings({"Duplicates", "UnnecessaryUnboxing"}) private void createPolygon (Graphics2D x_graphics, Rectangle2D x_dataArea, XYPlot x_plot, @@ -1036,8 +1064,10 @@ private void createPolygon (Graphics2D x_graphics, RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); - Object[] l_xValues = x_xValues.toArray(); - Object[] l_yValues = x_yValues.toArray(); + @SuppressWarnings({"index", "value"}) // documentation bug: x_xValues should have at least 1 element + Object @MinLen(1) [] l_xValues = x_xValues.toArray(); + @SuppressWarnings({"index", "value"}) // documentation bug: x_yValues should have at least 1 element, and the two linked lists need to be coordinated + Object @MinLen(1) @SameLen("l_xValues") [] l_yValues = x_yValues.toArray(); GeneralPath l_path = new GeneralPath(); @@ -1115,7 +1145,7 @@ private void createPolygon (Graphics2D x_graphics, * @return A legend item for the series. */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { LegendItem result = null; XYPlot p = getPlot(); if (p != null) { diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYDotRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYDotRenderer.java index 10520c940..ebc46afb7 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYDotRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYDotRenderer.java @@ -55,6 +55,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -228,7 +233,7 @@ public void setLegendShape(Shape shape) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { @@ -259,7 +264,8 @@ else if (orientation == PlotOrientation.VERTICAL) { this.dotHeight); } - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x, y, datasetIndex, transX, transY, orientation); } @@ -275,7 +281,7 @@ else if (orientation == PlotOrientation.VERTICAL) { * @return A legend item for the series (possibly {@code null}). */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { // if the renderer isn't assigned to a plot, then we don't have a // dataset... diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYErrorRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYErrorRenderer.java index 8ecaec9ff..2b603deba 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYErrorRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYErrorRenderer.java @@ -43,6 +43,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; @@ -304,17 +308,21 @@ public Range findRangeBounds(XYDataset dataset) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { if (pass == 0 && dataset instanceof IntervalXYDataset && getItemVisible(series, item)) { IntervalXYDataset ixyd = (IntervalXYDataset) dataset; + + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("ixyd.getSeries(series)") int ixydItem = item; + PlotOrientation orientation = plot.getOrientation(); if (this.drawXError) { // draw the error bar for the x-interval - double x0 = ixyd.getStartXValue(series, item); - double x1 = ixyd.getEndXValue(series, item); - double y = ixyd.getYValue(series, item); + double x0 = ixyd.getStartXValue(series, ixydItem); + double x1 = ixyd.getEndXValue(series, ixydItem); + double y = ixyd.getYValue(series, ixydItem); RectangleEdge edge = plot.getDomainAxisEdge(); double xx0 = domainAxis.valueToJava2D(x0, dataArea, edge); double xx1 = domainAxis.valueToJava2D(x1, dataArea, edge); @@ -352,9 +360,9 @@ && getItemVisible(series, item)) { } if (this.drawYError) { // draw the error bar for the y-interval - double y0 = ixyd.getStartYValue(series, item); - double y1 = ixyd.getEndYValue(series, item); - double x = ixyd.getXValue(series, item); + double y0 = ixyd.getStartYValue(series, ixydItem); + double y1 = ixyd.getEndYValue(series, ixydItem); + double x = ixyd.getXValue(series, ixydItem); RectangleEdge edge = plot.getRangeAxisEdge(); double yy0 = rangeAxis.valueToJava2D(y0, dataArea, edge); double yy1 = rangeAxis.valueToJava2D(y1, dataArea, edge); diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYItemRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYItemRenderer.java index c359d4c1e..e45920dc5 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYItemRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYItemRenderer.java @@ -86,6 +86,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; @@ -141,7 +145,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @return The pass count. */ - public int getPassCount(); + public @NonNegative int getPassCount(); /** * Returns the lower and upper bounds (range) of the x-values in the @@ -197,7 +201,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @return A boolean. */ - public boolean getItemVisible(int series, int item); + public boolean getItemVisible(@NonNegative int series, @NonNegative int item); /** * Returns a boolean that indicates whether or not the specified series @@ -207,7 +211,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @return A boolean. */ - public boolean isSeriesVisible(int series); + public boolean isSeriesVisible(@NonNegative int series); /** * Returns the flag that controls whether a series is visible. @@ -218,7 +222,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @see #setSeriesVisible(int, Boolean) */ - public Boolean getSeriesVisible(int series); + public Boolean getSeriesVisible(@NonNegative int series); /** * Sets the flag that controls whether a series is visible and sends a @@ -229,7 +233,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @see #getSeriesVisible(int) */ - public void setSeriesVisible(int series, Boolean visible); + public void setSeriesVisible(@NonNegative int series, Boolean visible); /** * Sets the flag that controls whether a series is visible and, if @@ -242,7 +246,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @see #getSeriesVisible(int) */ - public void setSeriesVisible(int series, Boolean visible, boolean notify); + public void setSeriesVisible(@NonNegative int series, Boolean visible, boolean notify); /** * Returns the default visibility for all series. @@ -284,7 +288,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @return A boolean. */ - public boolean isSeriesVisibleInLegend(int series); + public boolean isSeriesVisibleInLegend(@NonNegative int series); /** * Returns the flag that controls whether a series is visible in the @@ -298,7 +302,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @see #setSeriesVisibleInLegend(int, Boolean) */ - public Boolean getSeriesVisibleInLegend(int series); + public Boolean getSeriesVisibleInLegend(@NonNegative int series); /** * Sets the flag that controls whether a series is visible in the legend @@ -309,7 +313,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @see #getSeriesVisibleInLegend(int) */ - public void setSeriesVisibleInLegend(int series, Boolean visible); + public void setSeriesVisibleInLegend(@NonNegative int series, Boolean visible); /** * Sets the flag that controls whether a series is visible in the legend @@ -322,7 +326,7 @@ public interface XYItemRenderer extends LegendItemSource { * * @see #getSeriesVisibleInLegend(int) */ - public void setSeriesVisibleInLegend(int series, Boolean visible, + public void setSeriesVisibleInLegend(@NonNegative int series, Boolean visible, boolean notify); /** @@ -366,7 +370,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The paint (never {@code null}). */ - public Paint getItemPaint(int row, int column); + public Paint getItemPaint(@NonNegative int row, @NonNegative int column); /** * Returns the paint used to color an item drawn by the renderer. @@ -377,7 +381,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesPaint(int, Paint) */ - public Paint getSeriesPaint(int series); + public Paint getSeriesPaint(@NonNegative int series); /** * Sets the paint used for a series and sends a {@link RendererChangeEvent} @@ -388,9 +392,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesPaint(int) */ - public void setSeriesPaint(int series, Paint paint); + public void setSeriesPaint(@NonNegative int series, Paint paint); - public void setSeriesPaint(int series, Paint paint, boolean notify); + public void setSeriesPaint(@NonNegative int series, Paint paint, boolean notify); /** * Returns the default paint. @@ -423,7 +427,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The paint (never {@code null}). */ - public Paint getItemFillPaint(int row, int column); + public Paint getItemFillPaint(@NonNegative int row, @NonNegative int column); /** * Returns the paint used to fill an item drawn by the renderer. @@ -432,7 +436,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The paint (possibly {@code null}). */ - public Paint getSeriesFillPaint(int series); + public Paint getSeriesFillPaint(@NonNegative int series); /** * Sets the paint used for a series and sends a @@ -441,9 +445,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). */ - public void setSeriesFillPaint(int series, Paint paint); + public void setSeriesFillPaint(@NonNegative int series, Paint paint); - public void setSeriesFillPaint(int series, Paint paint, boolean notify); + public void setSeriesFillPaint(@NonNegative int series, Paint paint, boolean notify); /** * Returns the default paint. @@ -472,7 +476,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The paint (never {@code null}). */ - public Paint getItemOutlinePaint(int row, int column); + public Paint getItemOutlinePaint(@NonNegative int row, @NonNegative int column); /** * Returns the paint used to outline an item drawn by the renderer. @@ -483,7 +487,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesOutlinePaint(int, Paint) */ - public Paint getSeriesOutlinePaint(int series); + public Paint getSeriesOutlinePaint(@NonNegative int series); /** * Sets the paint used for a series outline and sends a @@ -494,9 +498,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesOutlinePaint(int) */ - public void setSeriesOutlinePaint(int series, Paint paint); + public void setSeriesOutlinePaint(@NonNegative int series, Paint paint); - public void setSeriesOutlinePaint(int series, Paint paint, boolean notify); + public void setSeriesOutlinePaint(@NonNegative int series, Paint paint, boolean notify); /** * Returns the default outline paint. @@ -529,7 +533,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The stroke (never {@code null}). */ - public Stroke getItemStroke(int row, int column); + public Stroke getItemStroke(@NonNegative int row, @NonNegative int column); /** * Returns the stroke used to draw the items in a series. @@ -540,7 +544,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesStroke(int, Stroke) */ - public Stroke getSeriesStroke(int series); + public Stroke getSeriesStroke(@NonNegative int series); /** * Sets the stroke used for a series and sends a @@ -551,9 +555,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesStroke(int) */ - public void setSeriesStroke(int series, Stroke stroke); + public void setSeriesStroke(@NonNegative int series, Stroke stroke); - public void setSeriesStroke(int series, Stroke stroke, boolean notify); + public void setSeriesStroke(@NonNegative int series, Stroke stroke, boolean notify); /** * Returns the default stroke. @@ -588,7 +592,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The stroke (never {@code null}). */ - public Stroke getItemOutlineStroke(int row, int column); + public Stroke getItemOutlineStroke(@NonNegative int row, @NonNegative int column); /** * Returns the stroke used to outline the items in a series. @@ -599,7 +603,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesOutlineStroke(int, Stroke) */ - public Stroke getSeriesOutlineStroke(int series); + public Stroke getSeriesOutlineStroke(@NonNegative int series); /** * Sets the outline stroke used for a series and sends a @@ -610,9 +614,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesOutlineStroke(int) */ - public void setSeriesOutlineStroke(int series, Stroke stroke); + public void setSeriesOutlineStroke(@NonNegative int series, Stroke stroke); - public void setSeriesOutlineStroke(int series, Stroke stroke, boolean notify); + public void setSeriesOutlineStroke(@NonNegative int series, Stroke stroke, boolean notify); /** * Returns the default outline stroke. @@ -645,7 +649,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The shape (never {@code null}). */ - public Shape getItemShape(int row, int column); + public Shape getItemShape(@NonNegative int row, @NonNegative int column); /** * Returns a shape used to represent the items in a series. @@ -656,7 +660,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesShape(int, Shape) */ - public Shape getSeriesShape(int series); + public Shape getSeriesShape(@NonNegative int series); /** * Sets the shape used for a series and sends a {@link RendererChangeEvent} @@ -667,9 +671,9 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesShape(int) */ - public void setSeriesShape(int series, Shape shape); + public void setSeriesShape(@NonNegative int series, Shape shape); - public void setSeriesShape(int series, Shape shape, boolean notify); + public void setSeriesShape(@NonNegative int series, Shape shape, boolean notify); /** * Returns the default shape. @@ -703,7 +707,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The legend item (possibly {@code null}). */ - public LegendItem getLegendItem(int datasetIndex, int series); + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series); //// LEGEND ITEM LABEL GENERATOR ////////////////////////////////////////// @@ -736,7 +740,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @return The generator (possibly {@code null}). */ - public XYToolTipGenerator getToolTipGenerator(int row, int column); + public XYToolTipGenerator getToolTipGenerator(@NonNegative int row, @NonNegative int column); /** * Returns the tool tip generator for a series. @@ -747,7 +751,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #setSeriesToolTipGenerator(int, XYToolTipGenerator) */ - public XYToolTipGenerator getSeriesToolTipGenerator(int series); + public XYToolTipGenerator getSeriesToolTipGenerator(@NonNegative int series); /** * Sets the tool tip generator for a series and sends a @@ -758,7 +762,7 @@ public void setSeriesVisibleInLegend(int series, Boolean visible, * * @see #getSeriesToolTipGenerator(int) */ - public void setSeriesToolTipGenerator(int series, + public void setSeriesToolTipGenerator(@NonNegative int series, XYToolTipGenerator generator); /** @@ -807,7 +811,7 @@ public void setSeriesToolTipGenerator(int series, * * @return A boolean. */ - public boolean isItemLabelVisible(int row, int column); + public boolean isItemLabelVisible(@NonNegative int row, @NonNegative int column); /** * Returns {@code true} if the item labels for a series are visible, @@ -817,7 +821,7 @@ public void setSeriesToolTipGenerator(int series, * * @return A boolean. */ - public boolean isSeriesItemLabelsVisible(int series); + public boolean isSeriesItemLabelsVisible(@NonNegative int series); /** * Sets a flag that controls the visibility of the item labels for a @@ -829,7 +833,7 @@ public void setSeriesToolTipGenerator(int series, * * @see #isSeriesItemLabelsVisible(int) */ - public void setSeriesItemLabelsVisible(int series, boolean visible); + public void setSeriesItemLabelsVisible(@NonNegative int series, boolean visible); /** * Sets a flag that controls the visibility of the item labels for a series. @@ -839,7 +843,7 @@ public void setSeriesToolTipGenerator(int series, * * @see #isSeriesItemLabelsVisible(int) */ - public void setSeriesItemLabelsVisible(int series, Boolean visible); + public void setSeriesItemLabelsVisible(@NonNegative int series, Boolean visible); /** * Sets the visibility of item labels for a series and, if requested, @@ -852,7 +856,7 @@ public void setSeriesToolTipGenerator(int series, * * @see #isSeriesItemLabelsVisible(int) */ - public void setSeriesItemLabelsVisible(int series, Boolean visible, + public void setSeriesItemLabelsVisible(@NonNegative int series, Boolean visible, boolean notify); /** @@ -896,7 +900,7 @@ public void setSeriesItemLabelsVisible(int series, Boolean visible, * * @return The generator (possibly {@code null}). */ - public XYItemLabelGenerator getItemLabelGenerator(int row, int column); + public XYItemLabelGenerator getItemLabelGenerator(@NonNegative int row, @NonNegative int column); /** * Returns the item label generator for a series. @@ -907,7 +911,7 @@ public void setSeriesItemLabelsVisible(int series, Boolean visible, * * @see #setSeriesItemLabelGenerator(int, XYItemLabelGenerator) */ - public XYItemLabelGenerator getSeriesItemLabelGenerator(int series); + public XYItemLabelGenerator getSeriesItemLabelGenerator(@NonNegative int series); /** * Sets the item label generator for a series and sends a @@ -918,7 +922,7 @@ public void setSeriesItemLabelsVisible(int series, Boolean visible, * * @see #getSeriesItemLabelGenerator(int) */ - public void setSeriesItemLabelGenerator(int series, + public void setSeriesItemLabelGenerator(@NonNegative int series, XYItemLabelGenerator generator); /** @@ -950,7 +954,7 @@ public void setSeriesItemLabelGenerator(int series, * * @return The font (never {@code null}). */ - public Font getItemLabelFont(int row, int column); + public Font getItemLabelFont(@NonNegative int row, @NonNegative int column); /** * Returns the font for all the item labels in a series. @@ -959,7 +963,7 @@ public void setSeriesItemLabelGenerator(int series, * * @return The font (possibly {@code null}). */ - public Font getSeriesItemLabelFont(int series); + public Font getSeriesItemLabelFont(@NonNegative int series); /** * Sets the item label font for a series and sends a @@ -970,7 +974,7 @@ public void setSeriesItemLabelGenerator(int series, * * @see #getSeriesItemLabelFont(int) */ - public void setSeriesItemLabelFont(int series, Font font); + public void setSeriesItemLabelFont(@NonNegative int series, Font font); /** * Returns the default item label font (this is used when no other font @@ -1002,7 +1006,7 @@ public void setSeriesItemLabelGenerator(int series, * * @return The paint (never {@code null}). */ - public Paint getItemLabelPaint(int row, int column); + public Paint getItemLabelPaint(@NonNegative int row, @NonNegative int column); /** * Returns the paint used to draw the item labels for a series. @@ -1013,7 +1017,7 @@ public void setSeriesItemLabelGenerator(int series, * * @see #setSeriesItemLabelPaint(int, Paint) */ - public Paint getSeriesItemLabelPaint(int series); + public Paint getSeriesItemLabelPaint(@NonNegative int series); /** * Sets the item label paint for a series and sends a @@ -1024,7 +1028,7 @@ public void setSeriesItemLabelGenerator(int series, * * @see #getSeriesItemLabelPaint(int) */ - public void setSeriesItemLabelPaint(int series, Paint paint); + public void setSeriesItemLabelPaint(@NonNegative int series, Paint paint); /** * Returns the default item label paint. @@ -1051,7 +1055,7 @@ public void setSeriesItemLabelGenerator(int series, * * @return The item label position (never {@code null}). */ - public ItemLabelPosition getPositiveItemLabelPosition(int row, int column); + public ItemLabelPosition getPositiveItemLabelPosition(@NonNegative int row, @NonNegative int column); /** * Returns the item label position for all positive values in a series. @@ -1060,7 +1064,7 @@ public void setSeriesItemLabelGenerator(int series, * * @return The item label position (never {@code null}). */ - public ItemLabelPosition getSeriesPositiveItemLabelPosition(int series); + public ItemLabelPosition getSeriesPositiveItemLabelPosition(@NonNegative int series); /** * Sets the item label position for all positive values in a series and @@ -1069,7 +1073,7 @@ public void setSeriesItemLabelGenerator(int series, * @param series the series index (zero-based). * @param position the position ({@code null} permitted). */ - public void setSeriesPositiveItemLabelPosition(int series, + public void setSeriesPositiveItemLabelPosition(@NonNegative int series, ItemLabelPosition position); /** @@ -1081,7 +1085,7 @@ public void setSeriesPositiveItemLabelPosition(int series, * @param position the position ({@code null} permitted). * @param notify notify registered listeners? */ - public void setSeriesPositiveItemLabelPosition(int series, + public void setSeriesPositiveItemLabelPosition(@NonNegative int series, ItemLabelPosition position, boolean notify); /** @@ -1121,7 +1125,7 @@ public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, * * @return The item label position (never {@code null}). */ - public ItemLabelPosition getNegativeItemLabelPosition(int row, int column); + public ItemLabelPosition getNegativeItemLabelPosition(@NonNegative int row, @NonNegative int column); /** * Returns the item label position for all negative values in a series. @@ -1130,7 +1134,7 @@ public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, * * @return The item label position (never {@code null}). */ - public ItemLabelPosition getSeriesNegativeItemLabelPosition(int series); + public ItemLabelPosition getSeriesNegativeItemLabelPosition(@NonNegative int series); /** * Sets the item label position for negative values in a series and sends a @@ -1139,7 +1143,7 @@ public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, * @param series the series index (zero-based). * @param position the position ({@code null} permitted). */ - public void setSeriesNegativeItemLabelPosition(int series, + public void setSeriesNegativeItemLabelPosition(@NonNegative int series, ItemLabelPosition position); /** @@ -1151,7 +1155,7 @@ public void setSeriesNegativeItemLabelPosition(int series, * @param position the position ({@code null} permitted). * @param notify notify registered listeners? */ - public void setSeriesNegativeItemLabelPosition(int series, + public void setSeriesNegativeItemLabelPosition(@NonNegative int series, ItemLabelPosition position, boolean notify); /** @@ -1182,13 +1186,13 @@ public void setDefaultNegativeItemLabelPosition(ItemLabelPosition position, // CREATE ENTITIES - public boolean getItemCreateEntity(int series, int item); + public boolean getItemCreateEntity(@NonNegative int series, @NonNegative int item); - public Boolean getSeriesCreateEntities(int series); + public Boolean getSeriesCreateEntities(@NonNegative int series); - public void setSeriesCreateEntities(int series, Boolean create); + public void setSeriesCreateEntities(@NonNegative int series, Boolean create); - public void setSeriesCreateEntities(int series, Boolean create, + public void setSeriesCreateEntities(@NonNegative int series, Boolean create, boolean notify); public boolean getDefaultCreateEntities(); @@ -1293,7 +1297,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass); + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass); /** * Fills a band between two values on the axis. This can be used to color diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYItemRendererState.java b/src/main/java/org/jfree/chart/renderer/xy/XYItemRendererState.java index d226037e4..04c2b5f48 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYItemRendererState.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYItemRendererState.java @@ -48,6 +48,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.geom.Line2D; import org.jfree.chart.plot.PlotRenderingInfo; @@ -65,14 +70,14 @@ public class XYItemRendererState extends RendererState { * * @since 1.0.11 */ - private int firstItemIndex; + private @NonNegative int firstItemIndex; /** * The last item in the current series that will be displayed. * * @since 1.0.11 */ - private int lastItemIndex; + private @NonNegative int lastItemIndex; /** * A line object that the renderer can reuse to save instantiating a lot @@ -167,8 +172,8 @@ public int getLastItemIndex() { * * @since 1.0.11 */ - public void startSeriesPass(XYDataset dataset, int series, int firstItem, - int lastItem, int pass, int passCount) { + public void startSeriesPass(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int firstItem, + @IndexFor("#1.getSeries(#2)") int lastItem, int pass, int passCount) { this.firstItemIndex = firstItem; this.lastItemIndex = lastItem; } @@ -190,7 +195,7 @@ public void startSeriesPass(XYDataset dataset, int series, int firstItem, * * @since 1.0.11 */ - public void endSeriesPass(XYDataset dataset, int series, int firstItem, + public void endSeriesPass(XYDataset dataset, @NonNegative int series, int firstItem, int lastItem, int pass, int passCount) { // do nothing...this is just a hook for subclasses } diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYLineAndShapeRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYLineAndShapeRenderer.java index 1e1949ea8..c2b793435 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYLineAndShapeRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYLineAndShapeRenderer.java @@ -73,6 +73,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; @@ -239,7 +244,7 @@ public void setDrawSeriesLineAsPath(boolean flag) { * @return The pass count. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 2; } @@ -254,7 +259,7 @@ public int getPassCount() { * * @return A boolean. */ - public boolean getItemLineVisible(int series, int item) { + public boolean getItemLineVisible(@NonNegative int series, @NonNegative int item) { Boolean flag = getSeriesLinesVisible(series); if (flag != null) { return flag; @@ -272,7 +277,7 @@ public boolean getItemLineVisible(int series, int item) { * * @see #setSeriesLinesVisible(int, Boolean) */ - public Boolean getSeriesLinesVisible(int series) { + public Boolean getSeriesLinesVisible(@NonNegative int series) { return this.seriesLinesVisible.getBoolean(series); } @@ -285,7 +290,7 @@ public Boolean getSeriesLinesVisible(int series) { * * @see #getSeriesLinesVisible(int) */ - public void setSeriesLinesVisible(int series, Boolean flag) { + public void setSeriesLinesVisible(@NonNegative int series, Boolean flag) { this.seriesLinesVisible.setBoolean(series, flag); fireChangeEvent(); } @@ -299,7 +304,7 @@ public void setSeriesLinesVisible(int series, Boolean flag) { * * @see #getSeriesLinesVisible(int) */ - public void setSeriesLinesVisible(int series, boolean visible) { + public void setSeriesLinesVisible(@NonNegative int series, boolean visible) { setSeriesLinesVisible(series, Boolean.valueOf(visible)); } @@ -367,7 +372,7 @@ public void setLegendLine(Shape line) { * * @return A boolean. */ - public boolean getItemShapeVisible(int series, int item) { + public boolean getItemShapeVisible(@NonNegative int series, @NonNegative int item) { Boolean flag = getSeriesShapesVisible(series); if (flag != null) { return flag; @@ -385,7 +390,7 @@ public boolean getItemShapeVisible(int series, int item) { * * @see #setSeriesShapesVisible(int, Boolean) */ - public Boolean getSeriesShapesVisible(int series) { + public Boolean getSeriesShapesVisible(@NonNegative int series) { return this.seriesShapesVisible.getBoolean(series); } @@ -398,7 +403,7 @@ public Boolean getSeriesShapesVisible(int series) { * * @see #getSeriesShapesVisible(int) */ - public void setSeriesShapesVisible(int series, boolean visible) { + public void setSeriesShapesVisible(@NonNegative int series, boolean visible) { setSeriesShapesVisible(series, Boolean.valueOf(visible)); } @@ -411,7 +416,7 @@ public void setSeriesShapesVisible(int series, boolean visible) { * * @see #getSeriesShapesVisible(int) */ - public void setSeriesShapesVisible(int series, Boolean flag) { + public void setSeriesShapesVisible(@NonNegative int series, Boolean flag) { this.seriesShapesVisible.setBoolean(series, flag); fireChangeEvent(); } @@ -455,7 +460,7 @@ public void setDefaultShapesVisible(boolean flag) { * * @return A boolean. */ - public boolean getItemShapeFilled(int series, int item) { + public boolean getItemShapeFilled(@NonNegative int series, @NonNegative int item) { Boolean flag = getSeriesShapesFilled(series); if (flag != null) { return flag; @@ -474,7 +479,7 @@ public boolean getItemShapeFilled(int series, int item) { * * @see #setSeriesShapesFilled(int, Boolean) */ - public Boolean getSeriesShapesFilled(int series) { + public Boolean getSeriesShapesFilled(@NonNegative int series) { return this.seriesShapesFilled.getBoolean(series); } @@ -487,7 +492,7 @@ public Boolean getSeriesShapesFilled(int series) { * * @see #getSeriesShapesFilled(int) */ - public void setSeriesShapesFilled(int series, boolean flag) { + public void setSeriesShapesFilled(@NonNegative int series, boolean flag) { setSeriesShapesFilled(series, Boolean.valueOf(flag)); } @@ -500,7 +505,7 @@ public void setSeriesShapesFilled(int series, boolean flag) { * * @see #getSeriesShapesFilled(int) */ - public void setSeriesShapesFilled(int series, Boolean flag) { + public void setSeriesShapesFilled(@NonNegative int series, Boolean flag) { this.seriesShapesFilled.setBoolean(series, flag); fireChangeEvent(); } @@ -678,8 +683,8 @@ public void setLastPointGood(boolean good) { * @param passCount the number of passes. */ @Override - public void startSeriesPass(XYDataset dataset, int series, - int firstItem, int lastItem, int pass, int passCount) { + public void startSeriesPass(XYDataset dataset, @NonNegative int series, + @IndexFor("#1.getSeries(#2)") int firstItem, @IndexFor("#1.getSeries(#2)") int lastItem, int pass, int passCount) { this.seriesPath.reset(); this.lastPointGood = false; super.startSeriesPass(dataset, series, firstItem, lastItem, pass, @@ -732,7 +737,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { @@ -811,8 +816,8 @@ protected void drawPrimaryLine(XYItemRendererState state, XYPlot plot, XYDataset dataset, int pass, - int series, - int item, + @NonNegative int series, + @IndexFor("#4.getSeries(#6)") int item, ValueAxis domainAxis, ValueAxis rangeAxis, Rectangle2D dataArea) { @@ -871,8 +876,8 @@ else if (orientation == PlotOrientation.VERTICAL) { * @param item the item index. * @param shape the shape. */ - protected void drawFirstPassShape(Graphics2D g2, int pass, int series, - int item, Shape shape) { + protected void drawFirstPassShape(Graphics2D g2, int pass, @NonNegative int series, + @NonNegative int item, Shape shape) { g2.setStroke(getItemStroke(series, item)); g2.setPaint(getItemPaint(series, item)); g2.draw(shape); @@ -899,7 +904,7 @@ protected void drawFirstPassShape(Graphics2D g2, int pass, int series, */ protected void drawPrimaryLineAsPath(XYItemRendererState state, Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, - int series, int item, ValueAxis domainAxis, ValueAxis rangeAxis, + @NonNegative int series, @IndexFor("#4.getSeries(#6)") int item, ValueAxis domainAxis, ValueAxis rangeAxis, Rectangle2D dataArea) { RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); @@ -958,7 +963,7 @@ protected void drawPrimaryLineAsPath(XYItemRendererState state, * @param entities the entity collection. */ protected void drawSecondaryPass(Graphics2D g2, XYPlot plot, - XYDataset dataset, int pass, int series, int item, + XYDataset dataset, int pass, @NonNegative int series, @IndexFor("#3.getSeries(#5)") int item, ValueAxis domainAxis, Rectangle2D dataArea, ValueAxis rangeAxis, CrosshairState crosshairState, EntityCollection entities) { @@ -1024,7 +1029,8 @@ else if (orientation == PlotOrientation.VERTICAL) { (y1 < 0.0)); } - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); @@ -1045,7 +1051,7 @@ else if (orientation == PlotOrientation.VERTICAL) { * @return A legend item for the series (possibly {@code null}). */ @Override - public LegendItem getLegendItem(int datasetIndex, int series) { + public LegendItem getLegendItem(@NonNegative int datasetIndex, @NonNegative int series) { XYPlot plot = getPlot(); if (plot == null) { return null; diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYShapeRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYShapeRenderer.java index 941aaa603..c3d423bb4 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYShapeRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYShapeRenderer.java @@ -47,6 +47,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -403,7 +408,7 @@ public Range findZBounds(XYZDataset dataset) { * @return {@code 2}. */ @Override - public int getPassCount() { + public @NonNegative int getPassCount() { return 2; } @@ -427,7 +432,7 @@ public int getPassCount() { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { Shape hotspot; EntityCollection entities = null; @@ -489,8 +494,8 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, g2.draw(shape); } } - - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot See XYDotRenderer#drawItem - this is the same problem + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x, y, datasetIndex, transX, transY, orientation); @@ -510,9 +515,10 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, * * @return The paint. */ - protected Paint getPaint(XYDataset dataset, int series, int item) { + protected Paint getPaint(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { Paint p; if (dataset instanceof XYZDataset) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 double z = ((XYZDataset) dataset).getZValue(series, item); p = this.paintScale.getPaint(z); } else { diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java index 2d6ede226..cf0f42b4d 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java @@ -49,6 +49,9 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; @@ -299,7 +302,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, @Override protected void drawPrimaryLineAsPath(XYItemRendererState state, Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, - int series, int item, ValueAxis xAxis, ValueAxis yAxis, + @NonNegative int series, @IndexFor("#4.getSeries(#6)") int item, ValueAxis xAxis, ValueAxis yAxis, Rectangle2D dataArea) { XYSplineState s = (XYSplineState) state; @@ -322,7 +325,8 @@ protected void drawPrimaryLineAsPath(XYItemRendererState state, } if (item == dataset.getItemCount(series) - 1) { // construct path - if (s.points.size() > 1) { + int spointsSize = s.points.size(); + if (spointsSize > 1) { Point2D origin; if (this.fillType == FillType.TO_ZERO) { float xz = (float) xAxis.valueToJava2D(0, dataArea, @@ -373,7 +377,8 @@ protected void drawPrimaryLineAsPath(XYItemRendererState state, s.seriesPath.lineTo(cp1.getX(), cp1.getY()); } else { // construct spline - int np = s.points.size(); // number of points + @SuppressWarnings({"index", "value"}) // array-list interop: s.points is minlen 3 at this point + @IntRange(from = 3) int np = s.points.size(); // number of points float[] d = new float[np]; // Newton form coefficients float[] x = new float[np]; // x-coordinates of nodes float y, oldy; @@ -429,10 +434,10 @@ protected void drawPrimaryLineAsPath(XYItemRendererState state, if (this.fillType != FillType.NONE) { if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { s.fillArea.lineTo(origin.getX(), s.points.get( - s.points.size() - 1).getY()); + spointsSize - 1).getY()); } else { s.fillArea.lineTo(s.points.get( - s.points.size() - 1).getX(), origin.getY()); + spointsSize - 1).getX(), origin.getY()); } s.fillArea.closePath(); } @@ -460,7 +465,7 @@ protected void drawPrimaryLineAsPath(XYItemRendererState state, } private void solveTridiag(float[] sub, float[] diag, float[] sup, - float[] b, int n) { + float[] b, @IndexFor({"#1", "#2", "#3", "#4"}) int n) { /* solve linear system with tridiagonal n by n matrix a using Gaussian elimination *without* pivoting where a(i,i-1) = sub[i] for 2<=i<=n diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYStepAreaRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYStepAreaRenderer.java index ca47c4e52..a81fbde2e 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYStepAreaRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYStepAreaRenderer.java @@ -58,6 +58,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Polygon; @@ -394,7 +398,7 @@ public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { PlotOrientation orientation = plot.getOrientation(); @@ -557,7 +561,8 @@ else if (orientation == PlotOrientation.HORIZONTAL) { // do we need to update the crosshair values? if (!Double.isNaN(y1)) { - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); } diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYStepRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYStepRenderer.java index 55a7dd0e3..352e1567a 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYStepRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYStepRenderer.java @@ -73,6 +73,10 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; @@ -200,7 +204,7 @@ public void setStepPoint(double stepPoint) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { @@ -273,7 +277,8 @@ else if (orientation == PlotOrientation.VERTICAL) { } // submit this data item as a candidate for the crosshair point - int datasetIndex = plot.indexOf(dataset); + @SuppressWarnings("index") // documentation bug: dataset is assumed to be associated with plot + @NonNegative int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); diff --git a/src/main/java/org/jfree/chart/renderer/xy/YIntervalRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/YIntervalRenderer.java index d8efeac33..cd584e738 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/YIntervalRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/YIntervalRenderer.java @@ -50,6 +50,11 @@ package org.jfree.chart.renderer.xy; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; @@ -177,7 +182,7 @@ public Range findRangeBounds(XYDataset dataset) { public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, - int series, int item, CrosshairState crosshairState, int pass) { + @NonNegative int series, @IndexFor("#8.getSeries(#9)") int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { @@ -191,10 +196,12 @@ public void drawItem(Graphics2D g2, XYItemRendererState state, } IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + @IndexFor("intervalDataset.getSeries(series)") int intervalItem = item; - double x = intervalDataset.getXValue(series, item); - double yLow = intervalDataset.getStartYValue(series, item); - double yHigh = intervalDataset.getEndYValue(series, item); + double x = intervalDataset.getXValue(series, intervalItem); + double yLow = intervalDataset.getStartYValue(series, intervalItem); + double yHigh = intervalDataset.getEndYValue(series, intervalItem); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); @@ -261,8 +268,8 @@ else if (orientation == PlotOrientation.VERTICAL) { * @param y the y coordinate (in Java2D space). */ private void drawAdditionalItemLabel(Graphics2D g2, - PlotOrientation orientation, XYDataset dataset, int series, - int item, double x, double y) { + PlotOrientation orientation, XYDataset dataset, @NonNegative int series, + @IndexFor("#3.getSeries(#4)") int item, double x, double y) { if (this.additionalItemLabelGenerator == null) { return; diff --git a/src/main/java/org/jfree/chart/text/G2TextMeasurer.java b/src/main/java/org/jfree/chart/text/G2TextMeasurer.java index 7e4c78a52..29392330e 100644 --- a/src/main/java/org/jfree/chart/text/G2TextMeasurer.java +++ b/src/main/java/org/jfree/chart/text/G2TextMeasurer.java @@ -28,6 +28,9 @@ package org.jfree.chart.text; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; @@ -59,7 +62,7 @@ public G2TextMeasurer(Graphics2D g2) { * @return The string width. */ @Override - public float getStringWidth(String text, int start, int end) { + public float getStringWidth(String text, @IndexOrHigh("#1") int start, @IndexOrHigh("#1") int end) { FontMetrics fm = this.g2.getFontMetrics(); Rectangle2D bounds = TextUtils.getTextBounds(text.substring(start, end), this.g2, fm); diff --git a/src/main/java/org/jfree/chart/text/TextBlock.java b/src/main/java/org/jfree/chart/text/TextBlock.java index f912a94b6..a8138820b 100644 --- a/src/main/java/org/jfree/chart/text/TextBlock.java +++ b/src/main/java/org/jfree/chart/text/TextBlock.java @@ -28,6 +28,8 @@ package org.jfree.chart.text; +import org.checkerframework.common.value.qual.ArrayLen; + import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; @@ -239,7 +241,7 @@ else if (this.lineAlignment == HorizontalAlignment.RIGHT) { * * @return The offsets (float[0] = x offset, float[1] = y offset). */ - private float[] calculateOffsets(TextBlockAnchor anchor, double width, + private float @ArrayLen(2) [] calculateOffsets(TextBlockAnchor anchor, double width, double height) { float[] result = new float[2]; float xAdj = 0.0f; diff --git a/src/main/java/org/jfree/chart/text/TextMeasurer.java b/src/main/java/org/jfree/chart/text/TextMeasurer.java index 2ed5211e3..109370bc3 100644 --- a/src/main/java/org/jfree/chart/text/TextMeasurer.java +++ b/src/main/java/org/jfree/chart/text/TextMeasurer.java @@ -28,6 +28,8 @@ package org.jfree.chart.text; +import org.checkerframework.checker.index.qual.*; + /** * An object that can measure text. */ @@ -43,7 +45,7 @@ public interface TextMeasurer { * * @return The width of the string in Java2D units. */ - public float getStringWidth(String text, int start, int end); + public float getStringWidth(String text, @IndexOrHigh("#1") int start, @IndexOrHigh("#1") int end); } diff --git a/src/main/java/org/jfree/chart/text/TextUtils.java b/src/main/java/org/jfree/chart/text/TextUtils.java index eaaf0e905..8135aa5d1 100644 --- a/src/main/java/org/jfree/chart/text/TextUtils.java +++ b/src/main/java/org/jfree/chart/text/TextUtils.java @@ -28,6 +28,11 @@ package org.jfree.chart.text; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; @@ -161,7 +166,7 @@ public static TextBlock createTextBlock(String text, Font font, TextBlock result = new TextBlock(); BreakIterator iterator = BreakIterator.getLineInstance(); iterator.setText(text); - int current = 0; + @NonNegative int current = 0; int lines = 0; int length = text.length(); while (current < length && lines < maxLines) { @@ -173,7 +178,11 @@ public static TextBlock createTextBlock(String text, Font font, } else if (next == current) { next++; // we must take one more character or we'll loop forever } - result.addLine(text.substring(current, next), font, paint); + + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/158: next is either < current < text.length, or next = current + 1 <= text.length + @IndexOrHigh("text") int next1 = next; + + result.addLine(text.substring(current, next1), font, paint); lines++; current = next; while (current < text.length()&& text.charAt(current) == '\n') { @@ -210,7 +219,7 @@ public static TextBlock createTextBlock(String text, Font font, * * @return The index of the next line break. */ - private static int nextLineBreak(String text, int start, float width, + private static @GTENegativeOne int nextLineBreak(String text, @IndexOrHigh("#1") int start, float width, BreakIterator iterator, TextMeasurer measurer) { // this method is (loosely) based on code in JFreeReport's @@ -224,14 +233,19 @@ private static int nextLineBreak(String text, int start, float width, newline = Integer.MAX_VALUE; } while (((end = iterator.following(current)) != BreakIterator.DONE)) { - x += measurer.getStringWidth(text, current, end); + @SuppressWarnings("index") // an assumption of this method is that the BreakIterator is associated with text + float dx = measurer.getStringWidth(text, current, end); + x += dx; if (x > width) { if (firstWord) { - while (measurer.getStringWidth(text, start, end) > width) { + while (dx > width) { end--; if (end <= start) { return end; } + @SuppressWarnings("index") // an assumption of this method is that the BreakIterator is associated with text + float dxNew = measurer.getStringWidth(text, start, end); + dx = dxNew; } return end; } @@ -359,7 +373,7 @@ public static Rectangle2D drawAlignedString(String text, Graphics2D g2, * * @return The offsets. */ - private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, + private static float @ArrayLen(3) [] deriveTextBoundsAnchorOffsets(Graphics2D g2, String text, TextAnchor anchor, Rectangle2D textBounds) { float[] result = new float[3]; @@ -580,7 +594,7 @@ public static Shape calculateRotatedStringBounds(String text, Graphics2D g2, * * @return The offsets. */ - private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, + private static float @ArrayLen(2) [] deriveTextBoundsAnchorOffsets(Graphics2D g2, String text, TextAnchor anchor) { float[] result = new float[2]; @@ -635,7 +649,7 @@ else if (anchor.isBottom()) { * * @return The offsets. */ - private static float[] deriveRotationAnchorOffsets(Graphics2D g2, + private static float @ArrayLen(2) [] deriveRotationAnchorOffsets(Graphics2D g2, String text, TextAnchor anchor) { float[] result = new float[2]; diff --git a/src/main/java/org/jfree/chart/title/LegendItemBlockContainer.java b/src/main/java/org/jfree/chart/title/LegendItemBlockContainer.java index 12c95f537..b188956c4 100644 --- a/src/main/java/org/jfree/chart/title/LegendItemBlockContainer.java +++ b/src/main/java/org/jfree/chart/title/LegendItemBlockContainer.java @@ -42,6 +42,8 @@ package org.jfree.chart.title; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Rectangle2D; @@ -81,7 +83,7 @@ public class LegendItemBlockContainer extends BlockContainer { private int datasetIndex; /** The series index. */ - private int series; + private @NonNegative int series; /** The tool tip text (can be {@code null}). */ private String toolTipText; @@ -132,7 +134,7 @@ public Comparable getSeriesKey() { * * @return The series index. */ - public int getSeriesIndex() { + public @NonNegative int getSeriesIndex() { return this.series; } diff --git a/src/main/java/org/jfree/chart/ui/LCBLayout.java b/src/main/java/org/jfree/chart/ui/LCBLayout.java index 9871beb3d..bd1febfa2 100644 --- a/src/main/java/org/jfree/chart/ui/LCBLayout.java +++ b/src/main/java/org/jfree/chart/ui/LCBLayout.java @@ -28,6 +28,11 @@ package org.jfree.chart.ui; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.awt.Component; import java.awt.Container; import java.awt.Dimension; @@ -47,7 +52,7 @@ public class LCBLayout implements LayoutManager, Serializable { private static final int COLUMNS = 3; /** Tracks the column widths. */ - private int[] colWidth; + private int @ArrayLen(3) [] colWidth; /** Tracks the row heights. */ private int[] rowHeight; @@ -66,7 +71,7 @@ public class LCBLayout implements LayoutManager, Serializable { * * @param maxrows the maximum number of rows. */ - public LCBLayout(int maxrows) { + public LCBLayout(@NonNegative int maxrows) { this.labelGap = 10; this.buttonGap = 6; this.vGap = 2; @@ -87,7 +92,8 @@ public Dimension preferredLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); - int nrows = ncomponents / COLUMNS; + @SuppressWarnings("index") // documentation bug: (parent's components / COLUMNS) must be less than the max rows for this LCBLayout, or this will crash, and that should be documented, or this will crash. I believe this is by design, but it's not good code. + @IndexOrHigh("this.rowHeight") int nrows = ncomponents / COLUMNS; for (int c = 0; c < COLUMNS; c++) { for (int r = 0; r < nrows; r++) { Component component = parent.getComponent(r * COLUMNS + c); @@ -128,7 +134,8 @@ public Dimension minimumLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); - int nrows = ncomponents / COLUMNS; + @SuppressWarnings("index") // documentation bug: (parent's components / COLUMNS) must be less than the max rows for this LCBLayout, or this will crash, and that should be documented, or this will crash. I believe this is by design, but it's not good code. + @IndexOrHigh("this.rowHeight") int nrows = ncomponents / COLUMNS; for (int c = 0; c < COLUMNS; c++) { for (int r = 0; r < nrows; r++) { Component component = parent.getComponent(r * COLUMNS + c); @@ -167,7 +174,8 @@ public void layoutContainer(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); - int nrows = ncomponents / COLUMNS; + @SuppressWarnings("index") // documentation bug: (parent's components / COLUMNS) must be less than the max rows for this LCBLayout, or this will crash, and that should be documented, or this will crash. I believe this is by design, but it's not good code. + @IndexOrHigh("this.rowHeight") int nrows = ncomponents / COLUMNS; for (int c = 0; c < COLUMNS; c++) { for (int r = 0; r < nrows; r++) { Component component = parent.getComponent(r * COLUMNS + c); diff --git a/src/main/java/org/jfree/chart/ui/NumberCellRenderer.java b/src/main/java/org/jfree/chart/ui/NumberCellRenderer.java index a909fe2c9..192615250 100644 --- a/src/main/java/org/jfree/chart/ui/NumberCellRenderer.java +++ b/src/main/java/org/jfree/chart/ui/NumberCellRenderer.java @@ -28,6 +28,8 @@ package org.jfree.chart.ui; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Component; import java.text.NumberFormat; import javax.swing.JTable; @@ -63,9 +65,9 @@ public NumberCellRenderer() { * @return the component that can render the contents of the cell. */ @Override - public Component getTableCellRendererComponent(JTable table, + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { + boolean hasFocus, @NonNegative int row, @NonNegative int column) { setFont(null); NumberFormat nf = NumberFormat.getNumberInstance(); diff --git a/src/main/java/org/jfree/chart/urls/CategoryURLGenerator.java b/src/main/java/org/jfree/chart/urls/CategoryURLGenerator.java index e6cc16bf2..c798da87a 100644 --- a/src/main/java/org/jfree/chart/urls/CategoryURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/CategoryURLGenerator.java @@ -52,6 +52,8 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.category.CategoryDataset; /** @@ -72,7 +74,7 @@ public interface CategoryURLGenerator { * * @return A string containing the URL. */ - public String generateURL(CategoryDataset dataset, int series, - int category); + public String generateURL(CategoryDataset dataset, @NonNegative int series, + @NonNegative int category); } diff --git a/src/main/java/org/jfree/chart/urls/CustomCategoryURLGenerator.java b/src/main/java/org/jfree/chart/urls/CustomCategoryURLGenerator.java index 740036215..7f062d844 100644 --- a/src/main/java/org/jfree/chart/urls/CustomCategoryURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/CustomCategoryURLGenerator.java @@ -41,6 +41,8 @@ */ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -69,7 +71,7 @@ public CustomCategoryURLGenerator() { * * @return The list count. */ - public int getListCount() { + public @NonNegative int getListCount() { return this.urlSeries.size(); } @@ -80,7 +82,7 @@ public int getListCount() { * * @return The URL count. */ - public int getURLCount(int list) { + public int getURLCount(@NonNegative int list) { int result = 0; List urls = (List) this.urlSeries.get(list); if (urls != null) { @@ -97,7 +99,7 @@ public int getURLCount(int list) { * * @return The URL (possibly {@code null}). */ - public String getURL(int series, int item) { + public String getURL(@NonNegative int series, @NonNegative int item) { String result = null; if (series < getListCount()) { List urls = (List) this.urlSeries.get(series); @@ -120,7 +122,7 @@ public String getURL(int series, int item) { * @return A string containing the URL (possibly {@code null}). */ @Override - public String generateURL(CategoryDataset dataset, int series, int item) { + public String generateURL(CategoryDataset dataset, @NonNegative int series, @NonNegative int item) { return getURL(series, item); } diff --git a/src/main/java/org/jfree/chart/urls/CustomPieURLGenerator.java b/src/main/java/org/jfree/chart/urls/CustomPieURLGenerator.java index a670b5134..829cdd560 100644 --- a/src/main/java/org/jfree/chart/urls/CustomPieURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/CustomPieURLGenerator.java @@ -42,6 +42,11 @@ package org.jfree.chart.urls; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; @@ -87,7 +92,7 @@ public CustomPieURLGenerator() { */ @Override public String generateURL(PieDataset dataset, Comparable key, - int pieIndex) { + @NonNegative int pieIndex) { return getURL(key, pieIndex); } @@ -98,7 +103,7 @@ public String generateURL(PieDataset dataset, Comparable key, * * @see #addURLs(Map) */ - public int getListCount() { + public @NonNegative int getListCount() { return this.urls.size(); } @@ -112,7 +117,7 @@ public int getListCount() { * * @see #getListCount() */ - public int getURLCount(int list) { + public int getURLCount(@NonNegative int list) { int result = 0; Map urlMap = (Map) this.urls.get(list); if (urlMap != null) { @@ -129,7 +134,7 @@ public int getURLCount(int list) { * * @return The URL. */ - public String getURL(Comparable key, int mapIndex) { + public String getURL(Comparable key, @NonNegative int mapIndex) { String result = null; if (mapIndex < getListCount()) { Map urlMap = (Map) this.urls.get(mapIndex); diff --git a/src/main/java/org/jfree/chart/urls/CustomXYURLGenerator.java b/src/main/java/org/jfree/chart/urls/CustomXYURLGenerator.java index e348c9894..02670e88f 100644 --- a/src/main/java/org/jfree/chart/urls/CustomXYURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/CustomXYURLGenerator.java @@ -47,6 +47,8 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -78,7 +80,7 @@ public CustomXYURLGenerator() { * * @return The list count. */ - public int getListCount() { + public @NonNegative int getListCount() { return this.urlSeries.size(); } @@ -89,7 +91,7 @@ public int getListCount() { * * @return The URL count. */ - public int getURLCount(int list) { + public int getURLCount(@NonNegative int list) { int result = 0; List urls = (List) this.urlSeries.get(list); if (urls != null) { @@ -106,7 +108,7 @@ public int getURLCount(int list) { * * @return The URL (possibly {@code null}). */ - public String getURL(int series, int item) { + public String getURL(@NonNegative int series, @NonNegative int item) { String result = null; if (series < getListCount()) { List urls = (List) this.urlSeries.get(series); @@ -129,7 +131,7 @@ public String getURL(int series, int item) { * @return A string containing the URL (possibly {@code null}). */ @Override - public String generateURL(XYDataset dataset, int series, int item) { + public String generateURL(XYDataset dataset, @NonNegative int series, @NonNegative int item) { return getURL(series, item); } diff --git a/src/main/java/org/jfree/chart/urls/PieURLGenerator.java b/src/main/java/org/jfree/chart/urls/PieURLGenerator.java index 067ffe467..bfdb077ff 100644 --- a/src/main/java/org/jfree/chart/urls/PieURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/PieURLGenerator.java @@ -47,6 +47,8 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.*; + import org.jfree.data.general.PieDataset; /** @@ -75,6 +77,6 @@ public interface PieURLGenerator { * * @return A string containing the URL. */ - public String generateURL(PieDataset dataset, Comparable key, int pieIndex); + public String generateURL(PieDataset dataset, Comparable key, @NonNegative int pieIndex); } diff --git a/src/main/java/org/jfree/chart/urls/StandardCategoryURLGenerator.java b/src/main/java/org/jfree/chart/urls/StandardCategoryURLGenerator.java index eae090b38..d1d626d66 100644 --- a/src/main/java/org/jfree/chart/urls/StandardCategoryURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/StandardCategoryURLGenerator.java @@ -55,6 +55,8 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -132,8 +134,8 @@ public StandardCategoryURLGenerator(String prefix, * @return The generated URL. */ @Override - public String generateURL(CategoryDataset dataset, int series, - int category) { + public String generateURL(CategoryDataset dataset, @NonNegative int series, + @NonNegative int category) { String url = this.prefix; Comparable seriesKey = dataset.getRowKey(series); Comparable categoryKey = dataset.getColumnKey(category); diff --git a/src/main/java/org/jfree/chart/urls/StandardXYURLGenerator.java b/src/main/java/org/jfree/chart/urls/StandardXYURLGenerator.java index a87b904b7..7f0b705e5 100644 --- a/src/main/java/org/jfree/chart/urls/StandardXYURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/StandardXYURLGenerator.java @@ -49,6 +49,8 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; @@ -129,7 +131,7 @@ public StandardXYURLGenerator(String prefix, String seriesParameterName, * @return The generated URL. */ @Override - public String generateURL(XYDataset dataset, int series, int item) { + public String generateURL(XYDataset dataset, @NonNegative int series, @NonNegative int item) { // TODO: URLEncode? String url = this.prefix; boolean firstParameter = url.indexOf("?") == -1; diff --git a/src/main/java/org/jfree/chart/urls/StandardXYZURLGenerator.java b/src/main/java/org/jfree/chart/urls/StandardXYZURLGenerator.java index 0e60c9a9a..de3271c40 100644 --- a/src/main/java/org/jfree/chart/urls/StandardXYZURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/StandardXYZURLGenerator.java @@ -40,6 +40,8 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.xy.XYZDataset; /** @@ -58,7 +60,7 @@ public class StandardXYZURLGenerator extends StandardXYURLGenerator * @return A string containing the generated URL. */ @Override - public String generateURL(XYZDataset dataset, int series, int item) { + public String generateURL(XYZDataset dataset, @NonNegative int series, @NonNegative int item) { return super.generateURL(dataset, series, item); } diff --git a/src/main/java/org/jfree/chart/urls/TimeSeriesURLGenerator.java b/src/main/java/org/jfree/chart/urls/TimeSeriesURLGenerator.java index b94769e3b..ba3cbf83c 100644 --- a/src/main/java/org/jfree/chart/urls/TimeSeriesURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/TimeSeriesURLGenerator.java @@ -51,6 +51,10 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -165,7 +169,7 @@ public String getItemParameterName() { * @return The generated URL. */ @Override - public String generateURL(XYDataset dataset, int series, int item) { + public String generateURL(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item) { String result = this.prefix; boolean firstParameter = !result.contains("?"); Comparable seriesKey = dataset.getSeriesKey(series); diff --git a/src/main/java/org/jfree/chart/urls/XYURLGenerator.java b/src/main/java/org/jfree/chart/urls/XYURLGenerator.java index 90e468629..5a8ab59da 100644 --- a/src/main/java/org/jfree/chart/urls/XYURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/XYURLGenerator.java @@ -45,6 +45,10 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.xy.XYDataset; /** @@ -66,6 +70,6 @@ public interface XYURLGenerator { * @return A string containing the generated URL (possibly * {@code null}). */ - public String generateURL(XYDataset dataset, int series, int item); + public String generateURL(XYDataset dataset, @NonNegative int series, @IndexFor("#1.getSeries(#2)") int item); } diff --git a/src/main/java/org/jfree/chart/urls/XYZURLGenerator.java b/src/main/java/org/jfree/chart/urls/XYZURLGenerator.java index f63e6dc0b..1cc2edd8e 100644 --- a/src/main/java/org/jfree/chart/urls/XYZURLGenerator.java +++ b/src/main/java/org/jfree/chart/urls/XYZURLGenerator.java @@ -41,6 +41,8 @@ package org.jfree.chart.urls; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.xy.XYZDataset; /** @@ -61,6 +63,6 @@ public interface XYZURLGenerator extends XYURLGenerator { * * @return A string containing the generated URL. */ - public String generateURL(XYZDataset dataset, int series, int item); + public String generateURL(XYZDataset dataset, @NonNegative int series, @NonNegative int item); } diff --git a/src/main/java/org/jfree/chart/util/AbstractObjectList.java b/src/main/java/org/jfree/chart/util/AbstractObjectList.java index 89c25e7c1..8ce3fdc0d 100644 --- a/src/main/java/org/jfree/chart/util/AbstractObjectList.java +++ b/src/main/java/org/jfree/chart/util/AbstractObjectList.java @@ -28,6 +28,13 @@ package org.jfree.chart.util; +import org.checkerframework.common.value.qual.IntVal; +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.index.qual.GTENegativeOne; +import org.checkerframework.checker.index.qual.LengthOf; +import org.checkerframework.checker.index.qual.LTLengthOf; +import org.checkerframework.checker.index.qual.IndexFor; + import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -43,16 +50,16 @@ public class AbstractObjectList implements Cloneable, Serializable { private static final long serialVersionUID = 7789833772597351595L; /** The default initial capacity of the list. */ - public static final int DEFAULT_INITIAL_CAPACITY = 8; + public static final @NonNegative int DEFAULT_INITIAL_CAPACITY = 8; /** Storage for the objects. */ private transient Object[] objects; /** The current list size. */ - private int size = 0; + private @LengthOf("this.objects") int size = 0; /** The default increment. */ - private int increment = DEFAULT_INITIAL_CAPACITY; + private @NonNegative int increment = DEFAULT_INITIAL_CAPACITY; /** * Creates a new list with the default initial capacity. @@ -66,7 +73,7 @@ protected AbstractObjectList() { * * @param initialCapacity the initial capacity. */ - protected AbstractObjectList(int initialCapacity) { + protected AbstractObjectList(@NonNegative int initialCapacity) { this (initialCapacity, initialCapacity); } @@ -76,7 +83,7 @@ protected AbstractObjectList(int initialCapacity) { * @param initialCapacity the initial capacity. * @param increment the increment. */ - protected AbstractObjectList(int initialCapacity, int increment) { + protected AbstractObjectList(@NonNegative int initialCapacity, @NonNegative int increment) { this.objects = new Object[initialCapacity]; this.increment = increment; } @@ -89,7 +96,7 @@ protected AbstractObjectList(int initialCapacity, int increment) { * * @return The object or {@code null}. */ - protected Object get(int index) { + protected Object get(@NonNegative int index) { Object result = null; if (index >= 0 && index < this.size) { result = this.objects[index]; @@ -103,15 +110,20 @@ protected Object get(int index) { * @param index the object index. * @param object the object ({@code null} permitted). */ - protected void set(int index, Object object) { + protected void set(@NonNegative int index, Object object) { if (index < 0) { throw new IllegalArgumentException("Requires index >= 0."); } if (index >= this.objects.length) { Object[] enlarged = new Object[index + this.increment]; - System.arraycopy(this.objects, 0, enlarged, 0, this.objects.length); + @SuppressWarnings("index") // this.objects.length <= index <= enlarged.length + @LTLengthOf(value={"this.objects", "enlarged"}, offset={"-1", "-1"}) int objectsLen = this.objects.length; + System.arraycopy(this.objects, 0, enlarged, 0, objectsLen); this.objects = enlarged; } + @SuppressWarnings("index") // if index wasn't large enough before, this.objects was enlarged by the code above + @IndexFor("this.objects") int newIndex = index; + index = newIndex; this.objects[index] = object; this.size = Math.max(this.size, index + 1); } @@ -141,7 +153,7 @@ public int size() { * * @return The index or -1. */ - protected int indexOf(Object object) { + protected @GTENegativeOne int indexOf(Object object) { for (int index = 0; index < this.size; index++) { if (this.objects[index] == object) { return (index); @@ -254,7 +266,8 @@ private void readObject(ObjectInputStream stream) this.objects = new Object[this.size]; final int count = stream.readInt(); for (int i = 0; i < count; i++) { - final int index = stream.readInt(); + @SuppressWarnings("index") // stream only contains GTEN1 ints? or is this a bug? + final @GTENegativeOne int index = stream.readInt(); if (index != -1) { set(index, stream.readObject()); } diff --git a/src/main/java/org/jfree/chart/util/AttrStringUtils.java b/src/main/java/org/jfree/chart/util/AttrStringUtils.java index 699db6c70..9ee555693 100644 --- a/src/main/java/org/jfree/chart/util/AttrStringUtils.java +++ b/src/main/java/org/jfree/chart/util/AttrStringUtils.java @@ -41,6 +41,9 @@ package org.jfree.chart.util; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.awt.Graphics2D; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; @@ -174,7 +177,7 @@ public static void drawRotatedString(AttributedString text, Graphics2D g2, y + textAdj[1] + rotateAdj[1]); } - private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, + private static float @ArrayLen(3) [] deriveTextBoundsAnchorOffsets(Graphics2D g2, AttributedString text, TextAnchor anchor, Rectangle2D textBounds) { TextLayout layout = new TextLayout(text.getIterator(), g2.getFontRenderContext()); @@ -231,7 +234,7 @@ else if (isBottom(anchor)) { * * @return The offsets. */ - private static float[] deriveRotationAnchorOffsets(Graphics2D g2, + private static float @ArrayLen(2) [] deriveRotationAnchorOffsets(Graphics2D g2, AttributedString text, TextAnchor anchor) { float[] result = new float[2]; diff --git a/src/main/java/org/jfree/chart/util/BooleanList.java b/src/main/java/org/jfree/chart/util/BooleanList.java index 4983236fe..4c4d1cc00 100644 --- a/src/main/java/org/jfree/chart/util/BooleanList.java +++ b/src/main/java/org/jfree/chart/util/BooleanList.java @@ -28,6 +28,8 @@ package org.jfree.chart.util; +import org.checkerframework.checker.index.qual.NonNegative; + /** * A list of {@code Boolean} objects. */ @@ -49,7 +51,7 @@ public BooleanList() { * * @return a {@link Boolean} from the list. */ - public Boolean getBoolean(int index) { + public Boolean getBoolean(@NonNegative int index) { return (Boolean) get(index); } @@ -60,7 +62,7 @@ public Boolean getBoolean(int index) { * @param index the index (zero-based). * @param b the boolean. */ - public void setBoolean(int index, Boolean b) { + public void setBoolean(@NonNegative int index, Boolean b) { set(index, b); } diff --git a/src/main/java/org/jfree/chart/util/DefaultShadowGenerator.java b/src/main/java/org/jfree/chart/util/DefaultShadowGenerator.java index 23a4495a7..0c7759d0c 100644 --- a/src/main/java/org/jfree/chart/util/DefaultShadowGenerator.java +++ b/src/main/java/org/jfree/chart/util/DefaultShadowGenerator.java @@ -42,6 +42,9 @@ package org.jfree.chart.util; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; @@ -62,7 +65,7 @@ public class DefaultShadowGenerator implements ShadowGenerator, Serializable { private static final long serialVersionUID = 2732993885591386064L; /** The shadow size. */ - private int shadowSize; + private @NonNegative int shadowSize; /** The shadow color. */ private Color shadowColor; @@ -92,7 +95,7 @@ public DefaultShadowGenerator() { * @param distance the shadow offset distance. * @param angle the shadow offset angle (in radians). */ - public DefaultShadowGenerator(int size, Color color, float opacity, + public DefaultShadowGenerator(@NonNegative int size, Color color, float opacity, int distance, double angle) { Args.nullNotPermitted(color, "color"); this.shadowSize = size; @@ -223,7 +226,8 @@ protected void applyShadow(BufferedImage image) { for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) { aSum = 0; historyIdx = 0; - for (int x = 0; x < this.shadowSize; x++, bufferOffset++) { + for (int x = 0; x < aHistory.length; x++, bufferOffset++) { + @SuppressWarnings("index") // dataBuffer has an internal structure: it's divided into dstHeight sections of dstWidth. We iterate through dstHeight with y, and bufferOffset moves through the width int a = dataBuffer[bufferOffset] >>> 24; aHistory[x] = a; aSum += a; @@ -233,14 +237,20 @@ protected void applyShadow(BufferedImage image) { for (int x = xStart; x < xStop; x++, bufferOffset++) { int a = (int) (aSum * sumDivider); - dataBuffer[bufferOffset] = a << 24 | shadowRgb; + @SuppressWarnings("index") // dataBuffer has an internal structure: it's divided into dstHeight sections of dstWidth. We iterate through dstHeight with y, and bufferOffset moves through the width + int dead = (dataBuffer[bufferOffset] = a << 24 | shadowRgb); + + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/158: historyIdx is always less than this.shadowSize, which is the length of aHistory + @IndexFor("aHistory") int historyIdx1 = historyIdx; // substract the oldest pixel from the sum - aSum -= aHistory[historyIdx]; + aSum -= aHistory[historyIdx1]; // get the lastest pixel - a = dataBuffer[bufferOffset + right] >>> 24; - aHistory[historyIdx] = a; + @SuppressWarnings("index") // dataBuffer has an internal structure: it's divided into dstHeight sections of dstWidth. We iterate through dstHeight with y, and bufferOffset moves through the width + int aTmp = dataBuffer[bufferOffset + right] >>> 24; + a = aTmp; + aHistory[historyIdx1] = a; aSum += a; if (++historyIdx >= this.shadowSize) { @@ -253,9 +263,10 @@ protected void applyShadow(BufferedImage image) { for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) { aSum = 0; historyIdx = 0; - for (int y = 0; y < this.shadowSize; y++, + for (int y = 0; y < aHistory.length; y++, bufferOffset += dstWidth) { - int a = dataBuffer[bufferOffset] >>> 24; + @SuppressWarnings("index") // dataBuffer has an internal structure: it's divided into dstHeight sections of dstWidth. We iterate through dstWidth with x, and bufferOffset moves through the height + int a = dataBuffer[bufferOffset] >>> 24; aHistory[y] = a; aSum += a; } @@ -264,14 +275,20 @@ protected void applyShadow(BufferedImage image) { for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) { int a = (int) (aSum * sumDivider); - dataBuffer[bufferOffset] = a << 24 | shadowRgb; + @SuppressWarnings("index") // dataBuffer has an internal structure: it's divided into dstHeight sections of dstWidth. We iterate through dstWidth with x, and bufferOffset moves through the height + int dead = (dataBuffer[bufferOffset] = a << 24 | shadowRgb); + + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/158: historyIdx is always less than this.shadowSize, which is the length of aHistory + @IndexFor("aHistory") int historyIdx1 = historyIdx; // substract the oldest pixel from the sum - aSum -= aHistory[historyIdx]; + aSum -= aHistory[historyIdx1]; // get the lastest pixel - a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24; - aHistory[historyIdx] = a; + @SuppressWarnings("index") // dataBuffer has an internal structure: it's divided into dstHeight sections of dstWidth. We iterate through dstWidth with x, and bufferOffset moves through the height + int aTmp = dataBuffer[bufferOffset + lastPixelOffset] >>> 24; + a = aTmp; + aHistory[historyIdx1] = a; aSum += a; if (++historyIdx >= this.shadowSize) { diff --git a/src/main/java/org/jfree/chart/util/ObjectList.java b/src/main/java/org/jfree/chart/util/ObjectList.java index ec115580d..38813b47a 100644 --- a/src/main/java/org/jfree/chart/util/ObjectList.java +++ b/src/main/java/org/jfree/chart/util/ObjectList.java @@ -28,6 +28,10 @@ package org.jfree.chart.util; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + /** * A list of objects that can grow as required. * <p> @@ -46,7 +50,7 @@ public ObjectList() { * * @param initialCapacity the initial capacity. */ - public ObjectList(int initialCapacity) { + public ObjectList(@NonNegative int initialCapacity) { super(initialCapacity); } @@ -64,7 +68,7 @@ public ObjectList(int initialCapacity) { * @return The object or {@code null}. */ @Override - public Object get(int index) { + public Object get(@NonNegative int index) { return super.get(index); } @@ -75,7 +79,7 @@ public Object get(int index) { * @param object the object ({@code null} permitted). */ @Override - public void set(int index, Object object) { + public void set(@NonNegative int index, Object object) { super.set(index, object); } @@ -87,7 +91,7 @@ public void set(int index, Object object) { * @return The index or -1. */ @Override - public int indexOf(Object object) { + public @GTENegativeOne int indexOf(Object object) { return super.indexOf(object); } diff --git a/src/main/java/org/jfree/chart/util/PaintAlpha.java b/src/main/java/org/jfree/chart/util/PaintAlpha.java index 2348aa118..56497a7e3 100644 --- a/src/main/java/org/jfree/chart/util/PaintAlpha.java +++ b/src/main/java/org/jfree/chart/util/PaintAlpha.java @@ -43,6 +43,9 @@ package org.jfree.chart.util; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.awt.Color; import java.awt.GradientPaint; import java.awt.LinearGradientPaint; @@ -250,7 +253,10 @@ private static TexturePaint darkerTexturePaint(TexturePaint paint) { final int wid = ras.getWidth(); - /**/ int[] pix = new int[wid * img.getSampleModel().getNumBands()]; + @SuppressWarnings("index") // wid is NN, as is img.getSampleModel().getNumBands(). Both require JDK annotations on classes in java.awt.image + @NonNegative int pixLength = wid * img.getSampleModel().getNumBands(); + + /**/ int[] pix = new int[pixLength]; /* (pix-buffer is large enough for all pixels of one row) */ /** @@ -281,7 +287,7 @@ private static TexturePaint darkerTexturePaint(TexturePaint paint) { pix = ras.getPixels(miX, y, wid, 1, pix); for (int p = 0; p < pix.length; p++) { - nco = img.getColorModel().getComponents(pix[p], nco, 0); + nco = img.getColorModel().getComponents(pix[p], nco, 0); nco[0] *= FACTOR; // Red nco[1] *= FACTOR; // Green nco[2] *= FACTOR; // Blue. Now map computed colour to @@ -312,11 +318,16 @@ private static TexturePaint darkerTexturePaint(TexturePaint paint) { pix = ras.getPixels(miX, y, wid, 1, pix); - for (int p = 0; p < pix.length;) { - pix[p] = (int)(pix[p++] * FACTOR); // Red - pix[p] = (int)(pix[p++] * FACTOR); // Green - pix[p] = (int)(pix[p++] * FACTOR); // Blue - /* Ignore alpha-channel -> */p++; + for (int p = 0; p < pix.length; p++) { + switch(p % 4) { + case 0: // Red + case 1: // Green + case 2: // Blue + pix[p] = (int)(pix[p] * FACTOR); + break; + case 3: + /* Ignore alpha-channel -> */continue; + } } /**/ ras.setPixels(miX, y, wid, 1, pix); } diff --git a/src/main/java/org/jfree/chart/util/PaintList.java b/src/main/java/org/jfree/chart/util/PaintList.java index f3f02994c..51a710244 100644 --- a/src/main/java/org/jfree/chart/util/PaintList.java +++ b/src/main/java/org/jfree/chart/util/PaintList.java @@ -28,6 +28,8 @@ package org.jfree.chart.util; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Paint; import java.io.IOException; import java.io.ObjectInputStream; @@ -54,7 +56,7 @@ public PaintList() { * * @return The object. */ - public Paint getPaint(int index) { + public Paint getPaint(@NonNegative int index) { return (Paint) get(index); } @@ -64,7 +66,7 @@ public Paint getPaint(int index) { * @param index the index (zero-based). * @param paint the {@link Paint}. */ - public void setPaint(int index, Paint paint) { + public void setPaint(@NonNegative int index, Paint paint) { set(index, paint); } @@ -139,6 +141,7 @@ private void writeObject(ObjectOutputStream stream) throws IOException { * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ + @SuppressWarnings("index") // stream must have been constructed from an instance of this class private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); diff --git a/src/main/java/org/jfree/chart/util/ShapeList.java b/src/main/java/org/jfree/chart/util/ShapeList.java index 60cb8c9fa..6939d5017 100644 --- a/src/main/java/org/jfree/chart/util/ShapeList.java +++ b/src/main/java/org/jfree/chart/util/ShapeList.java @@ -28,6 +28,8 @@ package org.jfree.chart.util; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Shape; import java.io.IOException; import java.io.ObjectInputStream; @@ -52,7 +54,7 @@ public ShapeList() { * * @return The object. */ - public Shape getShape(int index) { + public Shape getShape(@NonNegative int index) { return (Shape) get(index); } @@ -63,7 +65,7 @@ public Shape getShape(int index) { * @param index the index (zero-based). * @param shape the {@link Shape}. */ - public void setShape(int index, Shape shape) { + public void setShape(@NonNegative int index, Shape shape) { set(index, shape); } @@ -148,6 +150,7 @@ private void writeObject(ObjectOutputStream stream) throws IOException { * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ + @SuppressWarnings("index") // readObject assumes that the object read is actually an object of this class private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); diff --git a/src/main/java/org/jfree/chart/util/StringUtils.java b/src/main/java/org/jfree/chart/util/StringUtils.java index 9299ed405..2f37a13c2 100644 --- a/src/main/java/org/jfree/chart/util/StringUtils.java +++ b/src/main/java/org/jfree/chart/util/StringUtils.java @@ -28,6 +28,8 @@ package org.jfree.chart.util; +import org.checkerframework.checker.index.qual.*; + /** * String utilities. */ @@ -63,10 +65,11 @@ public static boolean startsWithIgnoreCase(String base, String start) { * @return true, if the string ends with the given ending text. */ public static boolean endsWithIgnoreCase(String base, String end) { - if (base.length() < end.length()) { + int baseLen = base.length(); + if (baseLen < end.length()) { return false; } - return base.regionMatches(true, base.length() - end.length(), end, 0, end.length()); + return base.regionMatches(true, baseLen - end.length(), end, 0, end.length()); } /** diff --git a/src/main/java/org/jfree/chart/util/StrokeList.java b/src/main/java/org/jfree/chart/util/StrokeList.java index 72cf3208f..9ea014f98 100644 --- a/src/main/java/org/jfree/chart/util/StrokeList.java +++ b/src/main/java/org/jfree/chart/util/StrokeList.java @@ -28,6 +28,8 @@ package org.jfree.chart.util; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Stroke; import java.io.IOException; import java.io.ObjectInputStream; @@ -52,7 +54,7 @@ public StrokeList() { * * @return The object. */ - public Stroke getStroke(int index) { + public Stroke getStroke(@NonNegative int index) { return (Stroke) get(index); } @@ -62,7 +64,7 @@ public Stroke getStroke(int index) { * @param index the index (zero-based). * @param stroke the {@link Stroke}. */ - public void setStroke(int index, Stroke stroke) { + public void setStroke(@NonNegative int index, Stroke stroke) { set(index, stroke); } @@ -145,6 +147,7 @@ private void writeObject(ObjectOutputStream stream) throws IOException { * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ + @SuppressWarnings("index") // stream must have been constructed from an instance of this class private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); diff --git a/src/main/java/org/jfree/data/ComparableObjectSeries.java b/src/main/java/org/jfree/data/ComparableObjectSeries.java index 34f844ced..d70daedad 100644 --- a/src/main/java/org/jfree/data/ComparableObjectSeries.java +++ b/src/main/java/org/jfree/data/ComparableObjectSeries.java @@ -42,6 +42,9 @@ package org.jfree.data; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Collections; import java.util.List; @@ -64,7 +67,7 @@ public class ComparableObjectSeries extends Series protected List data; /** The maximum number of items for the series. */ - private int maximumItemCount = Integer.MAX_VALUE; + private @NonNegative int maximumItemCount = Integer.MAX_VALUE; /** A flag that controls whether the items are automatically sorted. */ private boolean autoSort; @@ -128,7 +131,7 @@ public boolean getAllowDuplicateXValues() { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.data.size(); } @@ -139,7 +142,7 @@ public int getItemCount() { * @return The maximum item count. * @see #setMaximumItemCount(int) */ - public int getMaximumItemCount() { + public @NonNegative int getMaximumItemCount() { return this.maximumItemCount; } @@ -157,7 +160,7 @@ public int getMaximumItemCount() { * * @param maximum the maximum number of items for the series. */ - public void setMaximumItemCount(int maximum) { + public void setMaximumItemCount(@NonNegative int maximum) { this.maximumItemCount = maximum; boolean dataRemoved = false; while (this.data.size() > maximum) { @@ -218,7 +221,9 @@ protected void add(ComparableObjectItem item, boolean notify) { if (this.autoSort) { int index = Collections.binarySearch(this.data, item); if (index < 0) { - this.data.add(-index - 1, item); + @SuppressWarnings("index") // binary search on list, which SearchIndex does not handle because lists are not fixed-size data structure + @NonNegative int reverseIndex = -index - 1; + this.data.add(reverseIndex, item); } else { if (this.allowDuplicateXValues) { @@ -314,7 +319,7 @@ protected void update(Comparable x, Object y) { * @param index the item (zero based index). * @param y the new value ({@code null} permitted). */ - protected void updateByIndex(int index, Object y) { + protected void updateByIndex(@NonNegative int index, Object y) { ComparableObjectItem item = getDataItem(index); item.setObject(y); fireSeriesChanged(); @@ -327,7 +332,7 @@ protected void updateByIndex(int index, Object y) { * * @return The data item with the specified index. */ - protected ComparableObjectItem getDataItem(int index) { + protected ComparableObjectItem getDataItem(@NonNegative int index) { return (ComparableObjectItem) this.data.get(index); } @@ -338,7 +343,7 @@ protected ComparableObjectItem getDataItem(int index) { * @param start the start index (zero-based). * @param end the end index (zero-based). */ - protected void delete(int start, int end) { + protected void delete(@NonNegative int start, @NonNegative int end) { for (int i = start; i <= end; i++) { this.data.remove(start); } @@ -365,7 +370,7 @@ public void clear() { * * @return The item removed. */ - protected ComparableObjectItem remove(int index) { + protected ComparableObjectItem remove(@NonNegative int index) { ComparableObjectItem result = (ComparableObjectItem) this.data.remove( index); fireSeriesChanged(); @@ -380,6 +385,7 @@ protected ComparableObjectItem remove(int index) { * @return The item removed. */ + @SuppressWarnings("index") // guaranteed index: method precondition is that this element is in the list, so indexOf will return NN public ComparableObjectItem remove(Comparable x) { return remove(indexOf(x)); } diff --git a/src/main/java/org/jfree/data/DataUtils.java b/src/main/java/org/jfree/data/DataUtils.java index 2361b4ade..6008139d5 100644 --- a/src/main/java/org/jfree/data/DataUtils.java +++ b/src/main/java/org/jfree/data/DataUtils.java @@ -48,6 +48,10 @@ package org.jfree.data; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Arrays; import org.jfree.chart.util.Args; import org.jfree.data.general.DatasetUtils; @@ -98,7 +102,7 @@ public static boolean equal(double[][] a, double[][] b) { * * @since 1.0.13 */ - public static double[][] clone(double[][] source) { + public static double @SameLen("#1") [][] clone(double[][] source) { Args.nullNotPermitted(source, "source"); double[][] clone = new double[source.length][]; for (int i = 0; i < source.length; i++) { @@ -120,7 +124,7 @@ public static double[][] clone(double[][] source) { * * @return The total of the values in the specified column. */ - public static double calculateColumnTotal(Values2D data, int column) { + public static double calculateColumnTotal(Values2D data, @NonNegative int column) { Args.nullNotPermitted(data, "data"); double total = 0.0; int rowCount = data.getRowCount(); @@ -145,8 +149,8 @@ public static double calculateColumnTotal(Values2D data, int column) { * * @since 1.0.13 */ - public static double calculateColumnTotal(Values2D data, int column, - int[] validRows) { + public static double calculateColumnTotal(Values2D data, @NonNegative int column, + @NonNegative int[] validRows) { Args.nullNotPermitted(data, "data"); double total = 0.0; int rowCount = data.getRowCount(); @@ -171,7 +175,7 @@ public static double calculateColumnTotal(Values2D data, int column, * * @return The total of the values in the specified row. */ - public static double calculateRowTotal(Values2D data, int row) { + public static double calculateRowTotal(Values2D data, @NonNegative int row) { Args.nullNotPermitted(data, "data"); double total = 0.0; int columnCount = data.getColumnCount(); @@ -196,8 +200,8 @@ public static double calculateRowTotal(Values2D data, int row) { * * @since 1.0.13 */ - public static double calculateRowTotal(Values2D data, int row, - int[] validCols) { + public static double calculateRowTotal(Values2D data, @NonNegative int row, + @NonNegative int[] validCols) { Args.nullNotPermitted(data, "data"); double total = 0.0; int colCount = data.getColumnCount(); @@ -238,10 +242,11 @@ public static Number[] createNumberArray(double[] data) { * * @return An array of {@code double}. */ - public static Number[][] createNumberArray2D(double[][] data) { + @SuppressWarnings("value") // the result array is manifestly the same size as the parameter + public static Number @SameLen("#1") @PolyValue [][] createNumberArray2D(double @PolyValue [][] data) { Args.nullNotPermitted(data, "data"); int l1 = data.length; - Number[][] result = new Number[l1][]; + Number[][] result = new Number[data.length][]; for (int i = 0; i < l1; i++) { result[i] = createNumberArray(data[i]); } diff --git a/src/main/java/org/jfree/data/DefaultKeyedValues.java b/src/main/java/org/jfree/data/DefaultKeyedValues.java index 47915a2b8..1e72d36dd 100644 --- a/src/main/java/org/jfree/data/DefaultKeyedValues.java +++ b/src/main/java/org/jfree/data/DefaultKeyedValues.java @@ -60,6 +60,9 @@ package org.jfree.data; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; @@ -107,7 +110,7 @@ public DefaultKeyedValues() { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.indexMap.size(); } @@ -121,7 +124,7 @@ public int getItemCount() { * @throws IndexOutOfBoundsException if {@code item} is out of bounds. */ @Override - public Number getValue(int item) { + public Number getValue(@NonNegative int item) { return (Number) this.values.get(item); } @@ -135,7 +138,7 @@ public Number getValue(int item) { * @throws IndexOutOfBoundsException if {@code item} is out of bounds. */ @Override - public Comparable getKey(int index) { + public Comparable getKey(@NonNegative int index) { return (Comparable) this.keys.get(index); } @@ -150,13 +153,15 @@ public Comparable getKey(int index) { * {@code null}. */ @Override - public int getIndex(Comparable key) { + public @GTENegativeOne int getIndex(Comparable key) { Args.nullNotPermitted(key, "key"); final Integer i = (Integer) this.indexMap.get(key); if (i == null) { return -1; // key not found } - return i.intValue(); + @SuppressWarnings("index") // the map only contains indices, so if the value is in the map, then result is non-negative + @NonNegative int result = i.intValue(); + return result; } /** @@ -254,7 +259,7 @@ public void setValue(Comparable key, Number value) { * * @since 1.0.6 */ - public void insertValue(int position, Comparable key, double value) { + public void insertValue(@NonNegative int position, Comparable key, double value) { insertValue(position, key, new Double(value)); } @@ -269,7 +274,7 @@ public void insertValue(int position, Comparable key, double value) { * * @since 1.0.6 */ - public void insertValue(int position, Comparable key, Number value) { + public void insertValue(@NonNegative int position, Comparable key, Number value) { if (position < 0 || position > getItemCount()) { throw new IllegalArgumentException("'position' out of bounds."); } @@ -312,7 +317,7 @@ private void rebuildIndex () { * @throws IndexOutOfBoundsException if {@code index} is not within * the specified range. */ - public void removeValue(int index) { + public void removeValue(@NonNegative int index) { this.keys.remove(index); this.values.remove(index); rebuildIndex(); diff --git a/src/main/java/org/jfree/data/DefaultKeyedValues2D.java b/src/main/java/org/jfree/data/DefaultKeyedValues2D.java index 16cd05775..afa822720 100644 --- a/src/main/java/org/jfree/data/DefaultKeyedValues2D.java +++ b/src/main/java/org/jfree/data/DefaultKeyedValues2D.java @@ -58,6 +58,10 @@ package org.jfree.data; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Collections; import java.util.Iterator; @@ -116,7 +120,7 @@ public DefaultKeyedValues2D(boolean sortRowKeys) { * @see #getColumnCount() */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.rowKeys.size(); } @@ -128,7 +132,8 @@ public int getRowCount() { * @see #getRowCount() */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { return this.columnKeys.size(); } @@ -143,7 +148,8 @@ public int getColumnCount() { * @see #getValue(Comparable, Comparable) */ @Override - public Number getValue(int row, int column) { + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column) { Number result = null; DefaultKeyedValues rowData = (DefaultKeyedValues) this.rows.get(row); if (rowData != null) { @@ -169,7 +175,7 @@ public Number getValue(int row, int column) { * @see #getColumnKey(int) */ @Override - public Comparable getRowKey(int row) { + public Comparable getRowKey(@NonNegative int row) { return (Comparable) this.rowKeys.get(row); } @@ -184,7 +190,8 @@ public Comparable getRowKey(int row) { * @see #getColumnIndex(Comparable) */ @Override - public int getRowIndex(Comparable key) { + @SuppressWarnings("index") // I think this is a bug. Binary search can return a search index, inconsistent with docs on this method + public @GTENegativeOne int getRowIndex(Comparable key) { Args.nullNotPermitted(key, "key"); if (this.sortRowKeys) { return Collections.binarySearch(this.rowKeys, key); @@ -218,7 +225,7 @@ public List getRowKeys() { * @see #getRowKey(int) */ @Override - public Comparable getColumnKey(int column) { + public Comparable getColumnKey(@NonNegative int column) { return (Comparable) this.columnKeys.get(column); } @@ -233,7 +240,7 @@ public Comparable getColumnKey(int column) { * @see #getRowIndex(Comparable) */ @Override - public int getColumnIndex(Comparable key) { + public @GTENegativeOne int getColumnIndex(Comparable key) { Args.nullNotPermitted(key, "key"); return this.columnKeys.indexOf(key); } @@ -360,7 +367,8 @@ public void removeValue(Comparable rowKey, Comparable columnKey) { // 1. check whether the row is now empty. boolean allNull = true; - int rowIndex = getRowIndex(rowKey); + @SuppressWarnings("index") // array-list interop: this method assumes that the rowKey is valid, which can't be checked b/c it's a list + @NonNegative int rowIndex = getRowIndex(rowKey); DefaultKeyedValues row = (DefaultKeyedValues) this.rows.get(rowIndex); for (int item = 0, itemCount = row.getItemCount(); item < itemCount; @@ -411,7 +419,7 @@ public void removeValue(Comparable rowKey, Comparable columnKey) { * @see #removeRow(Comparable) * @see #removeColumn(int) */ - public void removeRow(int rowIndex) { + public void removeRow(@NonNegative int rowIndex) { this.rowKeys.remove(rowIndex); this.rows.remove(rowIndex); } @@ -446,7 +454,7 @@ public void removeRow(Comparable rowKey) { * @see #removeColumn(Comparable) * @see #removeRow(int) */ - public void removeColumn(int columnIndex) { + public void removeColumn(@NonNegative int columnIndex) { Comparable columnKey = getColumnKey(columnIndex); removeColumn(columnKey); } diff --git a/src/main/java/org/jfree/data/KeyToGroupMap.java b/src/main/java/org/jfree/data/KeyToGroupMap.java index 60fed49e0..964dc8ae6 100644 --- a/src/main/java/org/jfree/data/KeyToGroupMap.java +++ b/src/main/java/org/jfree/data/KeyToGroupMap.java @@ -45,6 +45,9 @@ package org.jfree.data; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -99,7 +102,8 @@ public KeyToGroupMap(Comparable defaultGroup) { * * @return The number of groups in the map. */ - public int getGroupCount() { + @Pure + public @Positive int getGroupCount() { return this.groups.size() + 1; } @@ -131,7 +135,7 @@ public List getGroups() { * @return The group index (or -1 if the group is not represented within * the map). */ - public int getGroupIndex(Comparable group) { + public @GTENegativeOne int getGroupIndex(Comparable group) { int result = this.groups.indexOf(group); if (result < 0) { if (this.defaultGroup.equals(group)) { @@ -202,7 +206,7 @@ public void mapKeyToGroup(Comparable key, Comparable group) { * * @return The key count. */ - public int getKeyCount(Comparable group) { + public @NonNegative int getKeyCount(Comparable group) { Args.nullNotPermitted(group, "group"); int result = 0; Iterator iterator = this.keyToGroupMap.values().iterator(); diff --git a/src/main/java/org/jfree/data/KeyedObjects.java b/src/main/java/org/jfree/data/KeyedObjects.java index 4a6a3149e..eb702dd2f 100644 --- a/src/main/java/org/jfree/data/KeyedObjects.java +++ b/src/main/java/org/jfree/data/KeyedObjects.java @@ -44,6 +44,9 @@ package org.jfree.data; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Iterator; import java.util.List; @@ -73,7 +76,7 @@ public KeyedObjects() { * * @return The item count. */ - public int getItemCount() { + public @NonNegative int getItemCount() { return this.data.size(); } @@ -86,7 +89,7 @@ public int getItemCount() { * * @throws IndexOutOfBoundsException if {@code item} is out of bounds. */ - public Object getObject(int item) { + public Object getObject(@NonNegative int item) { Object result = null; KeyedObject kobj = (KeyedObject) this.data.get(item); if (kobj != null) { @@ -106,7 +109,7 @@ public Object getObject(int item) { * * @see #getIndex(Comparable) */ - public Comparable getKey(int index) { + public Comparable getKey(@NonNegative int index) { Comparable result = null; KeyedObject item = (KeyedObject) this.data.get(index); if (item != null) { @@ -124,7 +127,7 @@ public Comparable getKey(int index) { * * @see #getKey(int) */ - public int getIndex(Comparable key) { + public @GTENegativeOne int getIndex(Comparable key) { Args.nullNotPermitted(key, "key"); int i = 0; Iterator iterator = this.data.iterator(); @@ -244,7 +247,7 @@ public void insertValue(int position, Comparable key, Object value) { * * @see #removeValue(Comparable) */ - public void removeValue(int index) { + public void removeValue(@NonNegative int index) { this.data.remove(index); } diff --git a/src/main/java/org/jfree/data/KeyedObjects2D.java b/src/main/java/org/jfree/data/KeyedObjects2D.java index c02ab6210..d2943eb16 100644 --- a/src/main/java/org/jfree/data/KeyedObjects2D.java +++ b/src/main/java/org/jfree/data/KeyedObjects2D.java @@ -45,6 +45,9 @@ package org.jfree.data; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Collections; import java.util.Iterator; @@ -85,7 +88,7 @@ public KeyedObjects2D() { * * @see #getColumnCount() */ - public int getRowCount() { + public @NonNegative int getRowCount() { return this.rowKeys.size(); } @@ -96,7 +99,7 @@ public int getRowCount() { * * @see #getRowCount() */ - public int getColumnCount() { + public @NonNegative int getColumnCount() { return this.columnKeys.size(); } @@ -110,7 +113,7 @@ public int getColumnCount() { * * @see #getObject(Comparable, Comparable) */ - public Object getObject(int row, int column) { + public Object getObject(@NonNegative int row, @NonNegative int column) { Object result = null; KeyedObjects rowData = (KeyedObjects) this.rows.get(row); if (rowData != null) { @@ -134,7 +137,7 @@ public Object getObject(int row, int column) { * * @see #getRowIndex(Comparable) */ - public Comparable getRowKey(int row) { + public Comparable getRowKey(@NonNegative int row) { return (Comparable) this.rowKeys.get(row); } @@ -148,7 +151,7 @@ public Comparable getRowKey(int row) { * * @see #getRowKey(int) */ - public int getRowIndex(Comparable key) { + public @GTENegativeOne int getRowIndex(Comparable key) { Args.nullNotPermitted(key, "key"); return this.rowKeys.indexOf(key); } @@ -173,7 +176,7 @@ public List getRowKeys() { * * @see #getColumnIndex(Comparable) */ - public Comparable getColumnKey(int column) { + public Comparable getColumnKey(@NonNegative int column) { return (Comparable) this.columnKeys.get(column); } @@ -187,7 +190,7 @@ public Comparable getColumnKey(int column) { * * @see #getColumnKey(int) */ - public int getColumnIndex(Comparable key) { + public @GTENegativeOne int getColumnIndex(Comparable key) { Args.nullNotPermitted(key, "key"); return this.columnKeys.indexOf(key); } @@ -352,7 +355,7 @@ public void removeObject(Comparable rowKey, Comparable columnKey) { * * @see #removeColumn(int) */ - public void removeRow(int rowIndex) { + public void removeRow(@NonNegative int rowIndex) { this.rowKeys.remove(rowIndex); this.rows.remove(rowIndex); } @@ -382,7 +385,7 @@ public void removeRow(Comparable rowKey) { * * @see #removeRow(int) */ - public void removeColumn(int columnIndex) { + public void removeColumn(@NonNegative int columnIndex) { Comparable columnKey = getColumnKey(columnIndex); removeColumn(columnKey); } diff --git a/src/main/java/org/jfree/data/KeyedValues.java b/src/main/java/org/jfree/data/KeyedValues.java index ee4f6a2f5..53462e9c0 100644 --- a/src/main/java/org/jfree/data/KeyedValues.java +++ b/src/main/java/org/jfree/data/KeyedValues.java @@ -44,6 +44,9 @@ package org.jfree.data; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.List; /** @@ -68,7 +71,7 @@ public interface KeyedValues extends Values { * @throws IndexOutOfBoundsException if {@code index} is not in the * specified range. */ - public Comparable getKey(int index); + public Comparable getKey(@NonNegative int index); /** * Returns the index for a given key. @@ -79,7 +82,7 @@ public interface KeyedValues extends Values { * * @throws IllegalArgumentException if {@code key} is {@code null}. */ - public int getIndex(Comparable key); + public @GTENegativeOne int getIndex(Comparable key); /** * Returns the keys for the values in the collection. Note that you can diff --git a/src/main/java/org/jfree/data/KeyedValues2D.java b/src/main/java/org/jfree/data/KeyedValues2D.java index af15e5c38..ba0a7efe2 100644 --- a/src/main/java/org/jfree/data/KeyedValues2D.java +++ b/src/main/java/org/jfree/data/KeyedValues2D.java @@ -41,6 +41,9 @@ package org.jfree.data; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.List; /** @@ -58,7 +61,7 @@ public interface KeyedValues2D extends Values2D { * * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ - public Comparable getRowKey(int row); + public Comparable getRowKey(@NonNegative int row); /** * Returns the row index for a given key. @@ -67,7 +70,7 @@ public interface KeyedValues2D extends Values2D { * * @return The row index, or {@code -1} if the key is unrecognised. */ - public int getRowIndex(Comparable key); + public @GTENegativeOne int getRowIndex(Comparable key); /** * Returns the row keys. @@ -85,7 +88,7 @@ public interface KeyedValues2D extends Values2D { * * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ - public Comparable getColumnKey(int column); + public Comparable getColumnKey(@NonNegative int column); /** * Returns the column index for a given key. @@ -94,7 +97,7 @@ public interface KeyedValues2D extends Values2D { * * @return The column index, or {@code -1} if the key is unrecognised. */ - public int getColumnIndex(Comparable key); + public @GTENegativeOne int getColumnIndex(Comparable key); /** * Returns the column keys. diff --git a/src/main/java/org/jfree/data/Values.java b/src/main/java/org/jfree/data/Values.java index f26892f8d..02a69d421 100644 --- a/src/main/java/org/jfree/data/Values.java +++ b/src/main/java/org/jfree/data/Values.java @@ -43,6 +43,8 @@ package org.jfree.data; +import org.checkerframework.checker.index.qual.NonNegative; + /** * An interface through which (single-dimension) data values can be accessed. */ @@ -53,7 +55,7 @@ public interface Values { * * @return The item count (possibly zero). */ - public int getItemCount(); + public @NonNegative int getItemCount(); /** * Returns the value with the specified index. @@ -66,6 +68,6 @@ public interface Values { * @throws IndexOutOfBoundsException if {@code index} is not in the * specified range. */ - public Number getValue(int index); + public Number getValue(@NonNegative int index); } diff --git a/src/main/java/org/jfree/data/Values2D.java b/src/main/java/org/jfree/data/Values2D.java index 0fd3df209..bbf04f959 100644 --- a/src/main/java/org/jfree/data/Values2D.java +++ b/src/main/java/org/jfree/data/Values2D.java @@ -40,6 +40,9 @@ package org.jfree.data; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.NonNegative; + /** * A general purpose interface that can be used to access a table of values. */ @@ -50,14 +53,15 @@ public interface Values2D { * * @return The row count. */ - public int getRowCount(); + public @NonNegative int getRowCount(); /** * Returns the number of columns in the table. * * @return The column count. */ - public int getColumnCount(); + @Pure + public @NonNegative int getColumnCount(); /** * Returns a value from the table. @@ -70,6 +74,7 @@ public interface Values2D { * @throws IndexOutOfBoundsException if the {@code row} * or {@code column} is out of bounds. */ - public Number getValue(int row, int column); + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column); } diff --git a/src/main/java/org/jfree/data/category/CategoryToPieDataset.java b/src/main/java/org/jfree/data/category/CategoryToPieDataset.java index 26cb792d9..b4baf4e9c 100644 --- a/src/main/java/org/jfree/data/category/CategoryToPieDataset.java +++ b/src/main/java/org/jfree/data/category/CategoryToPieDataset.java @@ -49,6 +49,9 @@ package org.jfree.data.category; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Collections; import java.util.List; import org.jfree.chart.util.Args; @@ -76,7 +79,7 @@ public class CategoryToPieDataset extends AbstractDataset private TableOrder extract; /** The row or column index. */ - private int index; + private @NonNegative int index; /** * An adaptor class that converts any {@link CategoryDataset} into a @@ -91,7 +94,7 @@ public class CategoryToPieDataset extends AbstractDataset * @param index the row or column index. */ public CategoryToPieDataset(CategoryDataset source, TableOrder extract, - int index) { + @NonNegative int index) { Args.nullNotPermitted(extract, "extract"); this.source = source; if (this.source != null) { @@ -131,7 +134,7 @@ public TableOrder getExtractType() { * * @since 1.0.2 */ - public int getExtractIndex() { + public @NonNegative int getExtractIndex() { return this.index; } @@ -142,7 +145,7 @@ public int getExtractIndex() { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { int result = 0; if (this.source != null) { if (this.extract == TableOrder.BY_ROW) { @@ -166,7 +169,7 @@ else if (this.extract == TableOrder.BY_COLUMN) { * range {@code 0} to {@code getItemCount() -1}. */ @Override - public Number getValue(int item) { + public Number getValue(@NonNegative int item) { Number result = null; if (item < 0 || item >= getItemCount()) { // this will include the case where the underlying dataset is null @@ -194,7 +197,7 @@ else if (this.extract == TableOrder.BY_COLUMN) { * specified range. */ @Override - public Comparable getKey(int index) { + public Comparable getKey(@NonNegative int index) { Comparable result = null; if (index < 0 || index >= getItemCount()) { // this includes the case where the underlying dataset is null @@ -218,7 +221,7 @@ else if (this.extract == TableOrder.BY_COLUMN) { * @return The index for the key, or {@code -1}. */ @Override - public int getIndex(Comparable key) { + public @GTENegativeOne int getIndex(Comparable key) { int result = -1; if (this.source != null) { if (this.extract == TableOrder.BY_ROW) { diff --git a/src/main/java/org/jfree/data/category/DefaultCategoryDataset.java b/src/main/java/org/jfree/data/category/DefaultCategoryDataset.java index 9c2b7bfc5..b5b363c71 100644 --- a/src/main/java/org/jfree/data/category/DefaultCategoryDataset.java +++ b/src/main/java/org/jfree/data/category/DefaultCategoryDataset.java @@ -48,6 +48,10 @@ package org.jfree.data.category; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.List; import org.jfree.chart.util.PublicCloneable; @@ -84,7 +88,7 @@ public DefaultCategoryDataset() { * @see #getColumnCount() */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.data.getRowCount(); } @@ -96,7 +100,8 @@ public int getRowCount() { * @see #getRowCount() */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { return this.data.getColumnCount(); } @@ -112,7 +117,8 @@ public int getColumnCount() { * @see #removeValue(Comparable, Comparable) */ @Override - public Number getValue(int row, int column) { + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column) { return this.data.getValue(row, column); } @@ -128,7 +134,7 @@ public Number getValue(int row, int column) { * @see #getColumnKey(int) */ @Override - public Comparable getRowKey(int row) { + public Comparable getRowKey(@NonNegative int row) { return this.data.getRowKey(row); } @@ -142,7 +148,7 @@ public Comparable getRowKey(int row) { * @see #getRowKey(int) */ @Override - public int getRowIndex(Comparable key) { + public @GTENegativeOne int getRowIndex(Comparable key) { // defer null argument check return this.data.getRowIndex(key); } @@ -169,7 +175,7 @@ public List getRowKeys() { * @see #getColumnIndex(Comparable) */ @Override - public Comparable getColumnKey(int column) { + public Comparable getColumnKey(@NonNegative int column) { return this.data.getColumnKey(column); } @@ -183,7 +189,7 @@ public Comparable getColumnKey(int column) { * @see #getColumnKey(int) */ @Override - public int getColumnIndex(Comparable key) { + public @GTENegativeOne int getColumnIndex(Comparable key) { // defer null argument check return this.data.getColumnIndex(key); } @@ -321,7 +327,7 @@ public void removeValue(Comparable rowKey, Comparable columnKey) { * * @see #removeColumn(int) */ - public void removeRow(int rowIndex) { + public void removeRow(@NonNegative int rowIndex) { this.data.removeRow(rowIndex); fireDatasetChanged(); } @@ -347,7 +353,7 @@ public void removeRow(Comparable rowKey) { * * @see #removeRow(int) */ - public void removeColumn(int columnIndex) { + public void removeColumn(@NonNegative int columnIndex) { this.data.removeColumn(columnIndex); fireDatasetChanged(); } diff --git a/src/main/java/org/jfree/data/category/DefaultIntervalCategoryDataset.java b/src/main/java/org/jfree/data/category/DefaultIntervalCategoryDataset.java index 458ce1e47..5cd3672ed 100644 --- a/src/main/java/org/jfree/data/category/DefaultIntervalCategoryDataset.java +++ b/src/main/java/org/jfree/data/category/DefaultIntervalCategoryDataset.java @@ -48,6 +48,10 @@ package org.jfree.data.category; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -56,6 +60,7 @@ import org.jfree.chart.util.Args; import org.jfree.chart.util.ResourceBundleWrapper; +import org.jfree.data.ComparableObjectSeries; import org.jfree.data.DataUtils; import org.jfree.data.UnknownKeyException; import org.jfree.data.general.AbstractSeriesDataset; @@ -91,7 +96,8 @@ public class DefaultIntervalCategoryDataset extends AbstractSeriesDataset * @param ends the ending values for the intervals ({@code null} not * permitted). */ - public DefaultIntervalCategoryDataset(double[][] starts, double[][] ends) { + @SuppressWarnings("index") // results of DataUtils.createNumberArray should have the same length as their inputs, but SameLens don't propagate correctly :( + public DefaultIntervalCategoryDataset(double @SameLen({"#1", "#2"}) [][] starts, double @SameLen({"#1", "#2"}) [][] ends) { this(DataUtils.createNumberArray2D(starts), DataUtils.createNumberArray2D(ends)); } @@ -107,7 +113,7 @@ public DefaultIntervalCategoryDataset(double[][] starts, double[][] ends) { * @param starts the start values data. * @param ends the end values data. */ - public DefaultIntervalCategoryDataset(Number[][] starts, Number[][] ends) { + public DefaultIntervalCategoryDataset(Number @SameLen({"#1", "#2"}) [][] starts, Number @SameLen({"#1", "#2"}) [][] ends) { this(null, null, starts, ends); } @@ -124,8 +130,8 @@ public DefaultIntervalCategoryDataset(Number[][] starts, Number[][] ends) { * @param ends the end values data, indexed as data[series][category]. */ public DefaultIntervalCategoryDataset(String[] seriesNames, - Number[][] starts, - Number[][] ends) { + Number @SameLen({"#2", "#3"}) [][] starts, + Number @SameLen({"#2", "#3"}) [][] ends) { this(seriesNames, null, starts, ends); @@ -145,8 +151,8 @@ public DefaultIntervalCategoryDataset(String[] seriesNames, */ public DefaultIntervalCategoryDataset(Comparable[] seriesKeys, Comparable[] categoryKeys, - Number[][] starts, - Number[][] ends) { + Number @SameLen("#4") [][] starts, + Number @SameLen("#3") [][] ends) { this.startData = starts; this.endData = ends; @@ -167,6 +173,14 @@ public DefaultIntervalCategoryDataset(Comparable[] seriesKeys, } if (seriesCount > 0) { + @SuppressWarnings({"index", "value"}) // seriesCount is the length of starts, so checking it against zero implies minlen(1) + Number @MinLen(1) [][] starts1 = starts; + starts = starts1; + + @SuppressWarnings({"index", "value"}) // seriesCount is the length of starts, so checking it against zero implies minlen(1) + Number @MinLen(1) [][] ends1 = ends; + ends = ends1; + // set up the series names... if (seriesKeys != null) { @@ -225,7 +239,7 @@ public DefaultIntervalCategoryDataset(Comparable[] seriesKeys, * @see #getCategoryCount() */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { int result = 0; if (this.startData != null) { result = this.startData.length; @@ -243,7 +257,7 @@ public int getSeriesCount() { * @see #getRowIndex(Comparable) * @see #getSeriesKey(int) */ - public int getSeriesIndex(Comparable seriesKey) { + public @GTENegativeOne int getSeriesIndex(Comparable seriesKey) { int result = -1; for (int i = 0; i < this.seriesKeys.length; i++) { if (seriesKey.equals(this.seriesKeys[i])) { @@ -264,11 +278,13 @@ public int getSeriesIndex(Comparable seriesKey) { * @see #getSeriesIndex(Comparable) */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { if ((series >= getSeriesCount()) || (series < 0)) { throw new IllegalArgumentException("No such series : " + series); } - return this.seriesKeys[series]; + @SuppressWarnings("index") // array-list interop: getSeriesCount() should be annotated as LengthOf, but because most implementations of this interface implement the series with a list it isn't + Comparable result = this.seriesKeys[series]; + return result; } /** @@ -297,11 +313,13 @@ public void setSeriesKeys(Comparable[] seriesKeys) { * * @see #getColumnCount() */ - public int getCategoryCount() { + public @NonNegative int getCategoryCount() { int result = 0; if (this.startData != null) { if (getSeriesCount() > 0) { - result = this.startData[0].length; + @SuppressWarnings("index") // array-list interop: getSeriesCount is the length of startData, but can't be annotated that way b/c implementation detail + int newResult = this.startData[0].length; + result = newResult; } } return result; @@ -394,7 +412,8 @@ public Number getValue(Comparable series, Comparable category) { * @see #getEndValue(int, int) */ @Override - public Number getValue(int series, int category) { + @Pure + public Number getValue(@NonNegative int series, @NonNegative int category) { return getEndValue(series, category); } @@ -434,7 +453,7 @@ public Number getStartValue(Comparable series, Comparable category) { * @see #getStartValue(Comparable, Comparable) */ @Override - public Number getStartValue(int series, int category) { + public Number getStartValue(@NonNegative int series, @NonNegative int category) { // check arguments... if ((series < 0) || (series >= getSeriesCount())) { @@ -450,8 +469,9 @@ public Number getStartValue(int series, int category) { } // fetch the value... - return this.startData[series][category]; - + @SuppressWarnings("index") // array-list interop: that these are indices into these arrays is an implementation detail. Most implementations of this interface are backed by a list. + Number result = this.startData[series][category]; + return result; } /** @@ -488,7 +508,7 @@ public Number getEndValue(Comparable series, Comparable category) { * @see #getEndValue(Comparable, Comparable) */ @Override - public Number getEndValue(int series, int category) { + public Number getEndValue(@NonNegative int series, @NonNegative int category) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.getValue(): " @@ -500,8 +520,9 @@ public Number getEndValue(int series, int category) { "DefaultIntervalCategoryDataset.getValue(): " + "category index out of range."); } - - return this.endData[series][category]; + @SuppressWarnings("index") // array-list interop: that these are indices into these arrays is an implementation detail. Most implementations of this interface are backed by a list. + Number result = this.endData[series][category]; + return result; } /** @@ -514,7 +535,7 @@ public Number getEndValue(int series, int category) { * * @see #setEndValue(int, Comparable, Number) */ - public void setStartValue(int series, Comparable category, Number value) { + public void setStartValue(@NonNegative int series, Comparable category, Number value) { // does the series exist? if ((series < 0) || (series > getSeriesCount() - 1)) { @@ -532,7 +553,8 @@ public void setStartValue(int series, Comparable category, Number value) { } // update the data... - this.startData[series][categoryIndex] = value; + @SuppressWarnings("index") // array-list interop: that these are indices into these arrays is an implementation detail. Most implementations of this interface are backed by a list. + Number tmp = (this.startData[series][categoryIndex] = value); fireDatasetChanged(); } @@ -547,7 +569,7 @@ public void setStartValue(int series, Comparable category, Number value) { * * @see #setStartValue(int, Comparable, Number) */ - public void setEndValue(int series, Comparable category, Number value) { + public void setEndValue(@NonNegative int series, Comparable category, Number value) { // does the series exist? if ((series < 0) || (series > getSeriesCount() - 1)) { @@ -565,7 +587,8 @@ public void setEndValue(int series, Comparable category, Number value) { } // update the data... - this.endData[series][categoryIndex] = value; + @SuppressWarnings("index") // array-list interop: that these are indices into these arrays is an implementation detail. Most implementations of this interface are backed by a list. + Number tmp = (this.endData[series][categoryIndex] = value); fireDatasetChanged(); } @@ -579,7 +602,7 @@ public void setEndValue(int series, Comparable category, Number value) { * * @see #getColumnIndex(Comparable) */ - public int getCategoryIndex(Comparable category) { + public @GTENegativeOne int getCategoryIndex(Comparable category) { int result = -1; for (int i = 0; i < this.categoryKeys.length; i++) { if (category.equals(this.categoryKeys[i])) { @@ -599,7 +622,7 @@ public int getCategoryIndex(Comparable category) { * * @return An array of <i>prefixN</i> with N = { 1 .. count}. */ - private Comparable[] generateKeys(int count, String prefix) { + private Comparable[] generateKeys(@NonNegative int count, String prefix) { Comparable[] result = new Comparable[count]; String name; for (int i = 0; i < count; i++) { @@ -619,7 +642,8 @@ private Comparable[] generateKeys(int count, String prefix) { * @see #getRowKey(int) */ @Override - public Comparable getColumnKey(int column) { + @SuppressWarnings("index") // array-list interop: because this underlying array isn't exposed by the interface this method is inherited from, there is no way to write an upperbound annotation on column + public Comparable getColumnKey(@NonNegative int column) { return this.categoryKeys[column]; } @@ -633,7 +657,7 @@ public Comparable getColumnKey(int column) { * @see #getCategoryIndex(Comparable) */ @Override - public int getColumnIndex(Comparable columnKey) { + public @GTENegativeOne int getColumnIndex(Comparable columnKey) { Args.nullNotPermitted(columnKey, "columnKey"); return getCategoryIndex(columnKey); } @@ -648,7 +672,7 @@ public int getColumnIndex(Comparable columnKey) { * @see #getSeriesIndex(Comparable) */ @Override - public int getRowIndex(Comparable rowKey) { + public @GTENegativeOne int getRowIndex(Comparable rowKey) { return getSeriesIndex(rowKey); } @@ -682,7 +706,8 @@ public List getRowKeys() { * @see #getColumnKey(int) */ @Override - public Comparable getRowKey(int row) { + @SuppressWarnings("index") // array-list interop: because this underlying array isn't exposed by the interface this method is inherited from, there is no way to write an upperbound annotation on column + public Comparable getRowKey(@NonNegative int row) { if ((row >= getRowCount()) || (row < 0)) { throw new IllegalArgumentException( "The 'row' argument is out of bounds."); @@ -700,7 +725,8 @@ public Comparable getRowKey(int row) { * @see #getRowCount() */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { return this.categoryKeys.length; } @@ -713,7 +739,7 @@ public int getColumnCount() { * @see #getColumnCount() */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.seriesKeys.length; } diff --git a/src/main/java/org/jfree/data/category/IntervalCategoryDataset.java b/src/main/java/org/jfree/data/category/IntervalCategoryDataset.java index 1dc829105..2d313e603 100644 --- a/src/main/java/org/jfree/data/category/IntervalCategoryDataset.java +++ b/src/main/java/org/jfree/data/category/IntervalCategoryDataset.java @@ -46,6 +46,8 @@ package org.jfree.data.category; +import org.checkerframework.checker.index.qual.NonNegative; + /** * A category dataset that defines a value range for each series/category * combination. @@ -62,7 +64,7 @@ public interface IntervalCategoryDataset extends CategoryDataset { * * @see #getEndValue(int, int) */ - public Number getStartValue(int series, int category); + public Number getStartValue(@NonNegative int series, @NonNegative int category); /** * Returns the start value for the interval for a given series and category. @@ -86,7 +88,7 @@ public interface IntervalCategoryDataset extends CategoryDataset { * * @see #getStartValue(int, int) */ - public Number getEndValue(int series, int category); + public Number getEndValue(@NonNegative int series, @NonNegative int category); /** * Returns the end value for the interval for a given series and category. diff --git a/src/main/java/org/jfree/data/category/SlidingCategoryDataset.java b/src/main/java/org/jfree/data/category/SlidingCategoryDataset.java index 6575a4394..4eea10c53 100644 --- a/src/main/java/org/jfree/data/category/SlidingCategoryDataset.java +++ b/src/main/java/org/jfree/data/category/SlidingCategoryDataset.java @@ -41,6 +41,10 @@ package org.jfree.data.category; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Collections; import java.util.List; import org.jfree.chart.util.PublicCloneable; @@ -64,10 +68,10 @@ public class SlidingCategoryDataset extends AbstractDataset private CategoryDataset underlying; /** The index of the first category to present. */ - private int firstCategoryIndex; + private @NonNegative int firstCategoryIndex; /** The maximum number of categories to present. */ - private int maximumCategoryCount; + private @NonNegative int maximumCategoryCount; /** * Creates a new instance. @@ -78,8 +82,8 @@ public class SlidingCategoryDataset extends AbstractDataset * underlying dataset. * @param maxColumns the maximumColumnCount. */ - public SlidingCategoryDataset(CategoryDataset underlying, int firstColumn, - int maxColumns) { + public SlidingCategoryDataset(CategoryDataset underlying, @NonNegative int firstColumn, + @NonNegative int maxColumns) { this.underlying = underlying; this.firstCategoryIndex = firstColumn; this.maximumCategoryCount = maxColumns; @@ -101,7 +105,7 @@ public CategoryDataset getUnderlyingDataset() { * * @see #setFirstCategoryIndex(int) */ - public int getFirstCategoryIndex() { + public @NonNegative int getFirstCategoryIndex() { return this.firstCategoryIndex; } @@ -114,7 +118,7 @@ public int getFirstCategoryIndex() { * * @see #getFirstCategoryIndex() */ - public void setFirstCategoryIndex(int first) { + public void setFirstCategoryIndex(@NonNegative int first) { if (first < 0 || first >= this.underlying.getColumnCount()) { throw new IllegalArgumentException("Invalid index."); } @@ -129,7 +133,7 @@ public void setFirstCategoryIndex(int first) { * * @see #setMaximumCategoryCount(int) */ - public int getMaximumCategoryCount() { + public @NonNegative int getMaximumCategoryCount() { return this.maximumCategoryCount; } @@ -141,7 +145,7 @@ public int getMaximumCategoryCount() { * * @see #getMaximumCategoryCount() */ - public void setMaximumCategoryCount(int max) { + public void setMaximumCategoryCount(@NonNegative int max) { if (max < 0) { throw new IllegalArgumentException("Requires 'max' >= 0."); } @@ -154,7 +158,8 @@ public void setMaximumCategoryCount(int max) { * * @return The index. */ - private int lastCategoryIndex() { + @Pure + private @GTENegativeOne int lastCategoryIndex() { if (this.maximumCategoryCount == 0) { return -1; } @@ -170,7 +175,7 @@ private int lastCategoryIndex() { * @return The column index, or -1 if the key is not recognised. */ @Override - public int getColumnIndex(Comparable key) { + public @GTENegativeOne int getColumnIndex(Comparable key) { int index = this.underlying.getColumnIndex(key); if (index >= this.firstCategoryIndex && index <= lastCategoryIndex()) { return index - this.firstCategoryIndex; @@ -188,7 +193,7 @@ public int getColumnIndex(Comparable key) { * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ @Override - public Comparable getColumnKey(int column) { + public Comparable getColumnKey(@NonNegative int column) { return this.underlying.getColumnKey(column + this.firstCategoryIndex); } @@ -217,7 +222,7 @@ public List getColumnKeys() { * @return The row index, or {@code -1} if the key is unrecognised. */ @Override - public int getRowIndex(Comparable key) { + public @GTENegativeOne int getRowIndex(Comparable key) { return this.underlying.getRowIndex(key); } @@ -231,7 +236,7 @@ public int getRowIndex(Comparable key) { * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ @Override - public Comparable getRowKey(int row) { + public Comparable getRowKey(@NonNegative int row) { return this.underlying.getRowKey(row); } @@ -276,7 +281,8 @@ else if (r == -1) { * @return The column count. */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { int last = lastCategoryIndex(); if (last == -1) { return 0; @@ -292,7 +298,7 @@ public int getColumnCount() { * @return The row count. */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.underlying.getRowCount(); } @@ -305,7 +311,8 @@ public int getRowCount() { * @return The value (possibly {@code null}). */ @Override - public Number getValue(int row, int column) { + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column) { return this.underlying.getValue(row, column + this.firstCategoryIndex); } diff --git a/src/main/java/org/jfree/data/gantt/GanttCategoryDataset.java b/src/main/java/org/jfree/data/gantt/GanttCategoryDataset.java index 300d73f2f..610c21213 100644 --- a/src/main/java/org/jfree/data/gantt/GanttCategoryDataset.java +++ b/src/main/java/org/jfree/data/gantt/GanttCategoryDataset.java @@ -42,6 +42,8 @@ package org.jfree.data.gantt; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.category.IntervalCategoryDataset; /** @@ -60,7 +62,7 @@ public interface GanttCategoryDataset extends IntervalCategoryDataset { * * @see #getPercentComplete(Comparable, Comparable) */ - public Number getPercentComplete(int row, int column); + public Number getPercentComplete(@NonNegative int row, @NonNegative int column); /** * Returns the percent complete for a given item. @@ -84,7 +86,7 @@ public interface GanttCategoryDataset extends IntervalCategoryDataset { * * @see #getSubIntervalCount(Comparable, Comparable) */ - public int getSubIntervalCount(int row, int column); + public @NonNegative int getSubIntervalCount(@NonNegative int row, @NonNegative int column); /** * Returns the number of sub-intervals for a given item. @@ -96,7 +98,7 @@ public interface GanttCategoryDataset extends IntervalCategoryDataset { * * @see #getSubIntervalCount(int, int) */ - public int getSubIntervalCount(Comparable rowKey, Comparable columnKey); + public @NonNegative int getSubIntervalCount(Comparable rowKey, Comparable columnKey); /** * Returns the start value of a sub-interval for a given item. @@ -109,7 +111,7 @@ public interface GanttCategoryDataset extends IntervalCategoryDataset { * * @see #getEndValue(int, int, int) */ - public Number getStartValue(int row, int column, int subinterval); + public Number getStartValue(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval); /** * Returns the start value of a sub-interval for a given item. @@ -123,7 +125,7 @@ public interface GanttCategoryDataset extends IntervalCategoryDataset { * @see #getEndValue(Comparable, Comparable, int) */ public Number getStartValue(Comparable rowKey, Comparable columnKey, - int subinterval); + @NonNegative int subinterval); /** * Returns the end value of a sub-interval for a given item. @@ -136,7 +138,7 @@ public Number getStartValue(Comparable rowKey, Comparable columnKey, * * @see #getStartValue(int, int, int) */ - public Number getEndValue(int row, int column, int subinterval); + public Number getEndValue(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval); /** * Returns the end value of a sub-interval for a given item. @@ -150,7 +152,7 @@ public Number getStartValue(Comparable rowKey, Comparable columnKey, * @see #getStartValue(Comparable, Comparable, int) */ public Number getEndValue(Comparable rowKey, Comparable columnKey, - int subinterval); + @NonNegative int subinterval); /** * Returns the percentage complete value of a sub-interval for a given item. @@ -163,7 +165,7 @@ public Number getEndValue(Comparable rowKey, Comparable columnKey, * * @see #getPercentComplete(Comparable, Comparable, int) */ - public Number getPercentComplete(int row, int column, int subinterval); + public Number getPercentComplete(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval); /** * Returns the percentage complete value of a sub-interval for a given item. @@ -177,6 +179,6 @@ public Number getEndValue(Comparable rowKey, Comparable columnKey, * @see #getPercentComplete(int, int, int) */ public Number getPercentComplete(Comparable rowKey, Comparable columnKey, - int subinterval); + @NonNegative int subinterval); } diff --git a/src/main/java/org/jfree/data/gantt/SlidingGanttCategoryDataset.java b/src/main/java/org/jfree/data/gantt/SlidingGanttCategoryDataset.java index a7cbe817f..87471baef 100644 --- a/src/main/java/org/jfree/data/gantt/SlidingGanttCategoryDataset.java +++ b/src/main/java/org/jfree/data/gantt/SlidingGanttCategoryDataset.java @@ -40,6 +40,11 @@ package org.jfree.data.gantt; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Collections; import java.util.List; import org.jfree.chart.util.PublicCloneable; @@ -63,10 +68,10 @@ public class SlidingGanttCategoryDataset extends AbstractDataset private GanttCategoryDataset underlying; /** The index of the first category to present. */ - private int firstCategoryIndex; + private @NonNegative int firstCategoryIndex; /** The maximum number of categories to present. */ - private int maximumCategoryCount; + private @NonNegative int maximumCategoryCount; /** * Creates a new instance. @@ -78,7 +83,7 @@ public class SlidingGanttCategoryDataset extends AbstractDataset * @param maxColumns the maximumColumnCount. */ public SlidingGanttCategoryDataset(GanttCategoryDataset underlying, - int firstColumn, int maxColumns) { + @NonNegative int firstColumn, @NonNegative int maxColumns) { this.underlying = underlying; this.firstCategoryIndex = firstColumn; this.maximumCategoryCount = maxColumns; @@ -100,7 +105,7 @@ public GanttCategoryDataset getUnderlyingDataset() { * * @see #setFirstCategoryIndex(int) */ - public int getFirstCategoryIndex() { + public @NonNegative int getFirstCategoryIndex() { return this.firstCategoryIndex; } @@ -113,7 +118,7 @@ public int getFirstCategoryIndex() { * * @see #getFirstCategoryIndex() */ - public void setFirstCategoryIndex(int first) { + public void setFirstCategoryIndex(@NonNegative int first) { if (first < 0 || first >= this.underlying.getColumnCount()) { throw new IllegalArgumentException("Invalid index."); } @@ -128,7 +133,7 @@ public void setFirstCategoryIndex(int first) { * * @see #setMaximumCategoryCount(int) */ - public int getMaximumCategoryCount() { + public @NonNegative int getMaximumCategoryCount() { return this.maximumCategoryCount; } @@ -140,7 +145,7 @@ public int getMaximumCategoryCount() { * * @see #getMaximumCategoryCount() */ - public void setMaximumCategoryCount(int max) { + public void setMaximumCategoryCount(@NonNegative int max) { if (max < 0) { throw new IllegalArgumentException("Requires 'max' >= 0."); } @@ -153,7 +158,7 @@ public void setMaximumCategoryCount(int max) { * * @return The index. */ - private int lastCategoryIndex() { + private @GTENegativeOne int lastCategoryIndex() { if (this.maximumCategoryCount == 0) { return -1; } @@ -169,10 +174,11 @@ private int lastCategoryIndex() { * @return The column index, or -1 if the key is not recognised. */ @Override - public int getColumnIndex(Comparable key) { + public @GTENegativeOne int getColumnIndex(Comparable key) { int index = this.underlying.getColumnIndex(key); - if (index >= this.firstCategoryIndex && index <= lastCategoryIndex()) { - return index - this.firstCategoryIndex; + int firstCategoryIndex = this.firstCategoryIndex; + if (index >= firstCategoryIndex && index <= lastCategoryIndex()) { + return index - firstCategoryIndex; } return -1; // we didn't find the key } @@ -187,7 +193,7 @@ public int getColumnIndex(Comparable key) { * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ @Override - public Comparable getColumnKey(int column) { + public Comparable getColumnKey(@NonNegative int column) { return this.underlying.getColumnKey(column + this.firstCategoryIndex); } @@ -216,7 +222,7 @@ public List getColumnKeys() { * @return The row index, or {@code -1} if the key is unrecognised. */ @Override - public int getRowIndex(Comparable key) { + public @GTENegativeOne int getRowIndex(Comparable key) { return this.underlying.getRowIndex(key); } @@ -230,7 +236,7 @@ public int getRowIndex(Comparable key) { * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ @Override - public Comparable getRowKey(int row) { + public Comparable getRowKey(@NonNegative int row) { return this.underlying.getRowKey(row); } @@ -275,7 +281,8 @@ else if (r == -1) { * @return The column count. */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { int last = lastCategoryIndex(); if (last == -1) { return 0; @@ -291,7 +298,7 @@ public int getColumnCount() { * @return The row count. */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.underlying.getRowCount(); } @@ -304,7 +311,8 @@ public int getRowCount() { * @return The value (possibly {@code null}). */ @Override - public Number getValue(int row, int column) { + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column) { return this.underlying.getValue(row, column + this.firstCategoryIndex); } @@ -345,7 +353,7 @@ else if (r == -1) { */ @Override public Number getPercentComplete(Comparable rowKey, Comparable columnKey, - int subinterval) { + @NonNegative int subinterval) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c == -1) { @@ -373,7 +381,7 @@ else if (r == -1) { */ @Override public Number getEndValue(Comparable rowKey, Comparable columnKey, - int subinterval) { + @NonNegative int subinterval) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c == -1) { @@ -400,7 +408,7 @@ else if (r == -1) { * @see #getStartValue(int, int, int) */ @Override - public Number getEndValue(int row, int column, int subinterval) { + public Number getEndValue(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval) { return this.underlying.getEndValue(row, column + this.firstCategoryIndex, subinterval); } @@ -414,7 +422,7 @@ public Number getEndValue(int row, int column, int subinterval) { * @return The percent complete. */ @Override - public Number getPercentComplete(int series, int category) { + public Number getPercentComplete(@NonNegative int series, @NonNegative int category) { return this.underlying.getPercentComplete(series, category + this.firstCategoryIndex); } @@ -431,7 +439,7 @@ public Number getPercentComplete(int series, int category) { * @see #getPercentComplete(Comparable, Comparable, int) */ @Override - public Number getPercentComplete(int row, int column, int subinterval) { + public Number getPercentComplete(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval) { return this.underlying.getPercentComplete(row, column + this.firstCategoryIndex, subinterval); } @@ -449,7 +457,7 @@ public Number getPercentComplete(int row, int column, int subinterval) { */ @Override public Number getStartValue(Comparable rowKey, Comparable columnKey, - int subinterval) { + @NonNegative int subinterval) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c == -1) { @@ -476,7 +484,7 @@ else if (r == -1) { * @see #getEndValue(int, int, int) */ @Override - public Number getStartValue(int row, int column, int subinterval) { + public Number getStartValue(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval) { return this.underlying.getStartValue(row, column + this.firstCategoryIndex, subinterval); } @@ -492,7 +500,7 @@ public Number getStartValue(int row, int column, int subinterval) { * @see #getSubIntervalCount(int, int) */ @Override - public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { + public @NonNegative int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c == -1) { @@ -517,7 +525,7 @@ public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { * @see #getSubIntervalCount(Comparable, Comparable) */ @Override - public int getSubIntervalCount(int row, int column) { + public @NonNegative int getSubIntervalCount(@NonNegative int row, @NonNegative int column) { return this.underlying.getSubIntervalCount(row, column + this.firstCategoryIndex); } @@ -558,7 +566,7 @@ public Number getStartValue(Comparable rowKey, Comparable columnKey) { * @see #getEndValue(int, int) */ @Override - public Number getStartValue(int row, int column) { + public Number getStartValue(@NonNegative int row, @NonNegative int column) { return this.underlying.getStartValue(row, column + this.firstCategoryIndex); } @@ -596,7 +604,7 @@ public Number getEndValue(Comparable rowKey, Comparable columnKey) { * @return The end value (possibly {@code null}). */ @Override - public Number getEndValue(int series, int category) { + public Number getEndValue(@NonNegative int series, @NonNegative int category) { return this.underlying.getEndValue(series, category + this.firstCategoryIndex); } diff --git a/src/main/java/org/jfree/data/gantt/Task.java b/src/main/java/org/jfree/data/gantt/Task.java index 9b2338de3..0d3145ce8 100644 --- a/src/main/java/org/jfree/data/gantt/Task.java +++ b/src/main/java/org/jfree/data/gantt/Task.java @@ -44,6 +44,8 @@ package org.jfree.data.gantt; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Date; import java.util.List; @@ -190,7 +192,7 @@ public void removeSubtask(Task subtask) { * * @return The sub-task count. */ - public int getSubtaskCount() { + public @NonNegative int getSubtaskCount() { return this.subtasks.size(); } @@ -201,7 +203,7 @@ public int getSubtaskCount() { * * @return The sub-task. */ - public Task getSubtask(int index) { + public Task getSubtask(@NonNegative int index) { return (Task) this.subtasks.get(index); } diff --git a/src/main/java/org/jfree/data/gantt/TaskSeries.java b/src/main/java/org/jfree/data/gantt/TaskSeries.java index 852266b64..5e30da466 100644 --- a/src/main/java/org/jfree/data/gantt/TaskSeries.java +++ b/src/main/java/org/jfree/data/gantt/TaskSeries.java @@ -46,6 +46,8 @@ package org.jfree.data.gantt; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Collections; import java.util.List; import org.jfree.chart.util.ObjectUtils; @@ -115,7 +117,7 @@ public void removeAll() { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.tasks.size(); } @@ -126,7 +128,7 @@ public int getItemCount() { * * @return The task. */ - public Task get(int index) { + public Task get(@NonNegative int index) { return (Task) this.tasks.get(index); } diff --git a/src/main/java/org/jfree/data/gantt/TaskSeriesCollection.java b/src/main/java/org/jfree/data/gantt/TaskSeriesCollection.java index 7894844a5..7379e846d 100644 --- a/src/main/java/org/jfree/data/gantt/TaskSeriesCollection.java +++ b/src/main/java/org/jfree/data/gantt/TaskSeriesCollection.java @@ -51,6 +51,11 @@ package org.jfree.data.gantt; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Iterator; import java.util.List; @@ -120,7 +125,7 @@ public TaskSeries getSeries(Comparable key) { * * @since 1.0.1 */ - public TaskSeries getSeries(int series) { + public TaskSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -133,7 +138,7 @@ public TaskSeries getSeries(int series) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return getRowCount(); } @@ -145,7 +150,7 @@ public int getSeriesCount() { * @return The name of a series. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { TaskSeries ts = (TaskSeries) this.data.get(series); return ts.getKey(); } @@ -156,7 +161,7 @@ public Comparable getSeriesKey(int series) { * @return The series count. */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.data.size(); } @@ -176,7 +181,8 @@ public List getRowKeys() { * @return The column count. */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { return this.keys.size(); } @@ -198,7 +204,7 @@ public List getColumnKeys() { * @return The column key. */ @Override - public Comparable getColumnKey(int index) { + public Comparable getColumnKey(@NonNegative int index) { return (Comparable) this.keys.get(index); } @@ -210,7 +216,7 @@ public Comparable getColumnKey(int index) { * @return The column index. */ @Override - public int getColumnIndex(Comparable columnKey) { + public @GTENegativeOne int getColumnIndex(Comparable columnKey) { Args.nullNotPermitted(columnKey, "columnKey"); return this.keys.indexOf(columnKey); } @@ -223,7 +229,7 @@ public int getColumnIndex(Comparable columnKey) { * @return The index. */ @Override - public int getRowIndex(Comparable rowKey) { + public @GTENegativeOne int getRowIndex(Comparable rowKey) { int result = -1; int count = this.data.size(); for (int i = 0; i < count; i++) { @@ -244,7 +250,7 @@ public int getRowIndex(Comparable rowKey) { * @return The key. */ @Override - public Comparable getRowKey(int index) { + public Comparable getRowKey(@NonNegative int index) { TaskSeries series = (TaskSeries) this.data.get(index); return series.getKey(); } @@ -297,7 +303,7 @@ public void remove(TaskSeries series) { * * @param series the series (zero based index). */ - public void remove(int series) { + public void remove(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException( "TaskSeriesCollection.remove(): index outside valid range."); @@ -354,7 +360,8 @@ public Number getValue(Comparable rowKey, Comparable columnKey) { * @return The start value. */ @Override - public Number getValue(int row, int column) { + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column) { return getStartValue(row, column); } @@ -370,7 +377,8 @@ public Number getValue(int row, int column) { @Override public Number getStartValue(Comparable rowKey, Comparable columnKey) { Number result = null; - int row = getRowIndex(rowKey); + @SuppressWarnings("index") // rowKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { @@ -391,7 +399,7 @@ public Number getStartValue(Comparable rowKey, Comparable columnKey) { * @return The start value. */ @Override - public Number getStartValue(int row, int column) { + public Number getStartValue(@NonNegative int row, @NonNegative int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getStartValue(rowKey, columnKey); @@ -409,7 +417,8 @@ public Number getStartValue(int row, int column) { @Override public Number getEndValue(Comparable rowKey, Comparable columnKey) { Number result = null; - int row = getRowIndex(rowKey); + @SuppressWarnings("index") // rowKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { @@ -430,7 +439,7 @@ public Number getEndValue(Comparable rowKey, Comparable columnKey) { * @return The end value. */ @Override - public Number getEndValue(int row, int column) { + public Number getEndValue(@NonNegative int row, @NonNegative int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getEndValue(rowKey, columnKey); @@ -445,7 +454,7 @@ public Number getEndValue(int row, int column) { * @return The percent complete (possibly {@code null}). */ @Override - public Number getPercentComplete(int row, int column) { + public Number getPercentComplete(@NonNegative int row, @NonNegative int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getPercentComplete(rowKey, columnKey); @@ -462,7 +471,8 @@ public Number getPercentComplete(int row, int column) { @Override public Number getPercentComplete(Comparable rowKey, Comparable columnKey) { Number result = null; - int row = getRowIndex(rowKey); + @SuppressWarnings("index") // rowKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { @@ -480,7 +490,7 @@ public Number getPercentComplete(Comparable rowKey, Comparable columnKey) { * @return The sub-interval count. */ @Override - public int getSubIntervalCount(int row, int column) { + public @NonNegative int getSubIntervalCount(@NonNegative int row, @NonNegative int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getSubIntervalCount(rowKey, columnKey); @@ -495,9 +505,10 @@ public int getSubIntervalCount(int row, int column) { * @return The sub-interval count. */ @Override - public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { + public @NonNegative int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { int result = 0; - int row = getRowIndex(rowKey); + @SuppressWarnings("index") // rowKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { @@ -516,7 +527,7 @@ public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { * @return The start value (possibly {@code null}). */ @Override - public Number getStartValue(int row, int column, int subinterval) { + public Number getStartValue(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getStartValue(rowKey, columnKey, subinterval); @@ -533,9 +544,10 @@ public Number getStartValue(int row, int column, int subinterval) { */ @Override public Number getStartValue(Comparable rowKey, Comparable columnKey, - int subinterval) { + @NonNegative int subinterval) { Number result = null; - int row = getRowIndex(rowKey); + @SuppressWarnings("index") // rowKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { @@ -558,7 +570,7 @@ public Number getStartValue(Comparable rowKey, Comparable columnKey, * @return The end value (possibly {@code null}). */ @Override - public Number getEndValue(int row, int column, int subinterval) { + public Number getEndValue(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getEndValue(rowKey, columnKey, subinterval); @@ -575,9 +587,10 @@ public Number getEndValue(int row, int column, int subinterval) { */ @Override public Number getEndValue(Comparable rowKey, Comparable columnKey, - int subinterval) { + @NonNegative int subinterval) { Number result = null; - int row = getRowIndex(rowKey); + @SuppressWarnings("index") // rowKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { @@ -600,7 +613,7 @@ public Number getEndValue(Comparable rowKey, Comparable columnKey, * @return The percent complete value (possibly {@code null}). */ @Override - public Number getPercentComplete(int row, int column, int subinterval) { + public Number getPercentComplete(@NonNegative int row, @NonNegative int column, @NonNegative int subinterval) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getPercentComplete(rowKey, columnKey, subinterval); @@ -617,9 +630,10 @@ public Number getPercentComplete(int row, int column, int subinterval) { */ @Override public Number getPercentComplete(Comparable rowKey, Comparable columnKey, - int subinterval) { + @NonNegative int subinterval) { Number result = null; - int row = getRowIndex(rowKey); + @SuppressWarnings("index") // rowKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { diff --git a/src/main/java/org/jfree/data/gantt/XYTaskDataset.java b/src/main/java/org/jfree/data/gantt/XYTaskDataset.java index 3f4a27894..8ca740b08 100644 --- a/src/main/java/org/jfree/data/gantt/XYTaskDataset.java +++ b/src/main/java/org/jfree/data/gantt/XYTaskDataset.java @@ -41,6 +41,10 @@ package org.jfree.data.gantt; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Date; import org.jfree.chart.axis.SymbolAxis; @@ -157,7 +161,7 @@ public void setTransposed(boolean transposed) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.underlying.getSeriesCount(); } @@ -169,7 +173,7 @@ public int getSeriesCount() { * @return The name of a series. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { return this.underlying.getSeriesKey(series); } @@ -181,7 +185,8 @@ public Comparable getSeriesKey(int series) { * @return The item count. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: The underlying representation here is a mutable collection, but this annotation is needed to match the interface + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return this.underlying.getSeries(series).getItemCount(); } @@ -194,7 +199,7 @@ public int getItemCount(int series) { * @return The value. */ @Override - public double getXValue(int series, int item) { + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { if (!this.transposed) { return getSeriesValue(series); } @@ -214,7 +219,7 @@ public double getXValue(int series, int item) { * @return The start date/time. */ @Override - public double getStartXValue(int series, int item) { + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { if (!this.transposed) { return getSeriesStartValue(series); } @@ -234,7 +239,7 @@ public double getStartXValue(int series, int item) { * @return The end date/time. */ @Override - public double getEndXValue(int series, int item) { + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { if (!this.transposed) { return getSeriesEndValue(series); } @@ -252,7 +257,7 @@ public double getEndXValue(int series, int item) { * @return The x-value (in milliseconds). */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getXValue(series, item)); } @@ -267,7 +272,7 @@ public Number getX(int series, int item) { * @return The start date/time. */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getStartXValue(series, item)); } @@ -282,7 +287,7 @@ public Number getStartX(int series, int item) { * @return The end date/time. */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getEndXValue(series, item)); } @@ -295,7 +300,7 @@ public Number getEndX(int series, int item) { * @return The value. */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { if (!this.transposed) { return getItemValue(series, item); } @@ -314,7 +319,7 @@ public double getYValue(int series, int item) { * @return The y-interval start. */ @Override - public double getStartYValue(int series, int item) { + public double getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { if (!this.transposed) { return getItemStartValue(series, item); } @@ -333,7 +338,7 @@ public double getStartYValue(int series, int item) { * @return The y-interval end. */ @Override - public double getEndYValue(int series, int item) { + public double getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { if (!this.transposed) { return getItemEndValue(series, item); } @@ -353,7 +358,7 @@ public double getEndYValue(int series, int item) { * @return The y-value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getYValue(series, item)); } @@ -367,7 +372,7 @@ public Number getY(int series, int item) { * @return The y-interval start. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getStartYValue(series, item)); } @@ -381,23 +386,23 @@ public Number getStartY(int series, int item) { * @return The y-interval end. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getEndYValue(series, item)); } - private double getSeriesValue(int series) { + private double getSeriesValue(@NonNegative int series) { return series; } - private double getSeriesStartValue(int series) { + private double getSeriesStartValue(@NonNegative int series) { return series - this.seriesWidth / 2.0; } - private double getSeriesEndValue(int series) { + private double getSeriesEndValue(@NonNegative int series) { return series + this.seriesWidth / 2.0; } - private double getItemValue(int series, int item) { + private double getItemValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TaskSeries s = this.underlying.getSeries(series); Task t = s.get(item); TimePeriod duration = t.getDuration(); @@ -406,7 +411,7 @@ private double getItemValue(int series, int item) { return (start.getTime() + end.getTime()) / 2.0; } - private double getItemStartValue(int series, int item) { + private double getItemStartValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TaskSeries s = this.underlying.getSeries(series); Task t = s.get(item); TimePeriod duration = t.getDuration(); @@ -414,7 +419,7 @@ private double getItemStartValue(int series, int item) { return start.getTime(); } - private double getItemEndValue(int series, int item) { + private double getItemEndValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TaskSeries s = this.underlying.getSeries(series); Task t = s.get(item); TimePeriod duration = t.getDuration(); diff --git a/src/main/java/org/jfree/data/general/AbstractSeriesDataset.java b/src/main/java/org/jfree/data/general/AbstractSeriesDataset.java index 060ee959d..b3cb30355 100644 --- a/src/main/java/org/jfree/data/general/AbstractSeriesDataset.java +++ b/src/main/java/org/jfree/data/general/AbstractSeriesDataset.java @@ -44,6 +44,10 @@ package org.jfree.data.general; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; /** @@ -69,7 +73,7 @@ protected AbstractSeriesDataset() { * @return The series count. */ @Override - public abstract int getSeriesCount(); + public abstract @NonNegative int getSeriesCount(); /** * Returns the key for a series. @@ -84,7 +88,7 @@ protected AbstractSeriesDataset() { * @return The series key. */ @Override - public abstract Comparable getSeriesKey(int series); + public abstract Comparable getSeriesKey(@NonNegative int series); /** * Returns the index of the named series, or -1. @@ -94,7 +98,7 @@ protected AbstractSeriesDataset() { * @return The index. */ @Override - public int indexOf(Comparable seriesKey) { + public @GTENegativeOne int indexOf(Comparable seriesKey) { int seriesCount = getSeriesCount(); for (int s = 0; s < seriesCount; s++) { if (getSeriesKey(s).equals(seriesKey)) { @@ -114,4 +118,12 @@ public void seriesChanged(SeriesChangeEvent event) { fireDatasetChanged(); } + /** + * This is a ghost method that should never be called. It is used only in annotations + * to represent the conceptual "series" that the dataset provides. + */ + @Override + public Series getSeries(@NonNegative int series) throws Exception{ + throw new Exception("unimplemented"); + } } diff --git a/src/main/java/org/jfree/data/general/DatasetUtils.java b/src/main/java/org/jfree/data/general/DatasetUtils.java index 57fedbdb6..85b3d1fec 100644 --- a/src/main/java/org/jfree/data/general/DatasetUtils.java +++ b/src/main/java/org/jfree/data/general/DatasetUtils.java @@ -129,6 +129,12 @@ package org.jfree.data.general; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -213,7 +219,8 @@ public static double calculatePieDatasetTotal(PieDataset dataset) { */ public static PieDataset createPieDatasetForRow(CategoryDataset dataset, Comparable rowKey) { - int row = dataset.getRowIndex(rowKey); + @SuppressWarnings("index") // rowKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int row = dataset.getRowIndex(rowKey); return createPieDatasetForRow(dataset, row); } @@ -227,7 +234,7 @@ public static PieDataset createPieDatasetForRow(CategoryDataset dataset, * @return A pie dataset. */ public static PieDataset createPieDatasetForRow(CategoryDataset dataset, - int row) { + @NonNegative int row) { DefaultPieDataset result = new DefaultPieDataset(); int columnCount = dataset.getColumnCount(); for (int current = 0; current < columnCount; current++) { @@ -248,7 +255,8 @@ public static PieDataset createPieDatasetForRow(CategoryDataset dataset, */ public static PieDataset createPieDatasetForColumn(CategoryDataset dataset, Comparable columnKey) { - int column = dataset.getColumnIndex(columnKey); + @SuppressWarnings("index") // columnKey is being assumed to be a valid key. It should probably be checked - this is a bug + @NonNegative int column = dataset.getColumnIndex(columnKey); return createPieDatasetForColumn(dataset, column); } @@ -262,7 +270,7 @@ public static PieDataset createPieDatasetForColumn(CategoryDataset dataset, * @return A pie dataset. */ public static PieDataset createPieDatasetForColumn(CategoryDataset dataset, - int column) { + @NonNegative int column) { DefaultPieDataset result = new DefaultPieDataset(); int rowCount = dataset.getRowCount(); for (int i = 0; i < rowCount; i++) { @@ -422,8 +430,8 @@ public static CategoryDataset createCategoryDataset(String rowKeyPrefix, * * @return The dataset. */ - public static CategoryDataset createCategoryDataset(Comparable[] rowKeys, - Comparable[] columnKeys, double[][] data) { + public static CategoryDataset createCategoryDataset(Comparable @SameLen("#3") [] rowKeys, + Comparable[] columnKeys, double @SameLen("#1") [] @SameLen("#2") [] data) { Args.nullNotPermitted(rowKeys, "rowKeys"); Args.nullNotPermitted(columnKeys, "columnKeys"); @@ -498,7 +506,7 @@ public static CategoryDataset createCategoryDataset(Comparable rowKey, * @return A dataset. */ public static XYDataset sampleFunction2D(Function2D f, double start, - double end, int samples, Comparable seriesKey) { + double end, @Positive int samples, Comparable seriesKey) { // defer argument checking XYSeries series = sampleFunction2DToSeries(f, start, end, samples, @@ -523,7 +531,7 @@ public static XYDataset sampleFunction2D(Function2D f, double start, * @since 1.0.13 */ public static XYSeries sampleFunction2DToSeries(Function2D f, - double start, double end, int samples, Comparable seriesKey) { + double start, double end, @Positive int samples, Comparable seriesKey) { Args.nullNotPermitted(f, "f"); Args.nullNotPermitted(seriesKey, "seriesKey"); @@ -733,7 +741,7 @@ public static Range iterateDomainBounds(XYDataset dataset, if (includeInterval && dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; for (int series = 0; series < seriesCount; series++) { - int itemCount = dataset.getItemCount(series); + int itemCount = intervalXYData.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value = intervalXYData.getXValue(series, item); lvalue = intervalXYData.getStartXValue(series, item); @@ -1020,7 +1028,8 @@ public static Range iterateToFindRangeBounds(CategoryDataset dataset, Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.getRowIndex(seriesKey); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.getRowIndex(seriesKey); int itemCount = dataset.getColumnCount(); for (int item = 0; item < itemCount; item++) { Number lvalue = bx.getMinRegularValue(series, item); @@ -1049,7 +1058,8 @@ else if (includeInterval Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.getRowIndex(seriesKey); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.getRowIndex(seriesKey); for (int column = 0; column < columnCount; column++) { lvalue = icd.getStartValue(series, column); uvalue = icd.getEndValue(series, column); @@ -1071,7 +1081,8 @@ else if (includeInterval Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.getRowIndex(seriesKey); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.getRowIndex(seriesKey); for (int column = 0; column < columnCount; column++) { List values = mvcd.getValues(series, column); Iterator valueIterator = values.iterator(); @@ -1097,7 +1108,8 @@ else if (includeInterval Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.getRowIndex(seriesKey); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.getRowIndex(seriesKey); for (int column = 0; column < columnCount; column++) { Number meanN = scd.getMeanValue(series, column); if (meanN != null) { @@ -1123,7 +1135,8 @@ else if (includeInterval Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.getRowIndex(seriesKey); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.getRowIndex(seriesKey); for (int column = 0; column < columnCount; column++) { Number value = dataset.getValue(series, column); if (value != null) { @@ -1182,7 +1195,7 @@ public static Range iterateRangeBounds(XYDataset dataset, // handle special case of IntervalXYDataset IntervalXYDataset ixyd = (IntervalXYDataset) dataset; for (int series = 0; series < seriesCount; series++) { - int itemCount = dataset.getItemCount(series); + int itemCount = ixyd.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value = ixyd.getYValue(series, item); double lvalue = ixyd.getStartYValue(series, item); @@ -1206,7 +1219,7 @@ else if (includeInterval && dataset instanceof OHLCDataset) { // handle special case of OHLCDataset OHLCDataset ohlc = (OHLCDataset) dataset; for (int series = 0; series < seriesCount; series++) { - int itemCount = dataset.getItemCount(series); + int itemCount = ohlc.getItemCount(series); for (int item = 0; item < itemCount; item++) { double lvalue = ohlc.getLowValue(series, item); double uvalue = ohlc.getHighValue(series, item); @@ -1370,8 +1383,9 @@ public static Range iterateToFindDomainBounds(XYDataset dataset, Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.indexOf(seriesKey); - int itemCount = dataset.getItemCount(series); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.indexOf(seriesKey); + int itemCount = ixyd.getItemCount(series); for (int item = 0; item < itemCount; item++) { double xvalue = ixyd.getXValue(series, item); double lvalue = ixyd.getStartXValue(series, item); @@ -1393,7 +1407,8 @@ public static Range iterateToFindDomainBounds(XYDataset dataset, Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.indexOf(seriesKey); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = dataset.getXValue(series, item); @@ -1446,8 +1461,9 @@ public static Range iterateToFindRangeBounds(XYDataset dataset, Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.indexOf(seriesKey); - int itemCount = dataset.getItemCount(series); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.indexOf(seriesKey); + int itemCount = ohlc.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = ohlc.getXValue(series, item); if (xRange.contains(x)) { @@ -1469,8 +1485,9 @@ else if (includeInterval && dataset instanceof BoxAndWhiskerXYDataset) { Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.indexOf(seriesKey); - int itemCount = dataset.getItemCount(series); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.indexOf(seriesKey); + int itemCount = bx.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = bx.getXValue(series, item); if (xRange.contains(x)) { @@ -1492,8 +1509,9 @@ else if (includeInterval && dataset instanceof IntervalXYDataset) { Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.indexOf(seriesKey); - int itemCount = dataset.getItemCount(series); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.indexOf(seriesKey); + int itemCount = ixyd.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = ixyd.getXValue(series, item); if (xRange.contains(x)) { @@ -1518,7 +1536,8 @@ else if (includeInterval && dataset instanceof IntervalXYDataset) { Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.indexOf(seriesKey); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = dataset.getXValue(series, item); @@ -1566,7 +1585,8 @@ public static Range iterateToFindZBounds(XYZDataset dataset, Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); - int series = dataset.indexOf(seriesKey); + @SuppressWarnings("index") // guaranteed index: guaranteed to be an index since getRowIndex called on a key known to be in the dataset + @NonNegative int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = dataset.getXValue(series, item); @@ -1619,7 +1639,9 @@ public static Number findMinimumDomainValue(XYDataset dataset) { if (dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; - value = intervalXYData.getStartXValue(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + double valueTmp = intervalXYData.getStartXValue(series, item); + value = valueTmp; } else { value = dataset.getXValue(series, item); @@ -1674,7 +1696,9 @@ public static Number findMaximumDomainValue(XYDataset dataset) { if (dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; - value = intervalXYData.getEndXValue(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + double valueTmp = intervalXYData.getEndXValue(series, item); + value = valueTmp; } else { value = dataset.getXValue(series, item); @@ -1726,7 +1750,8 @@ public static Number findMinimumRangeValue(CategoryDataset dataset) { if (dataset instanceof IntervalCategoryDataset) { IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset; - value = icd.getStartValue(series, item); + Number valueTmp = icd.getStartValue(series, item); + value = valueTmp; } else { value = dataset.getValue(series, item); @@ -1780,11 +1805,15 @@ public static Number findMinimumRangeValue(XYDataset dataset) { if (dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; - value = intervalXYData.getStartYValue(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + double valueTmp = intervalXYData.getStartYValue(series, item); + value = valueTmp; } else if (dataset instanceof OHLCDataset) { OHLCDataset highLowData = (OHLCDataset) dataset; - value = highLowData.getLowValue(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + double valueTmp = highLowData.getLowValue(series, item); + value = valueTmp; } else { value = dataset.getYValue(series, item); @@ -1893,11 +1922,15 @@ public static Number findMaximumRangeValue(XYDataset dataset) { if (dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; - value = intervalXYData.getEndYValue(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + double valueTmp = intervalXYData.getEndYValue(series, item); + value = valueTmp; } else if (dataset instanceof OHLCDataset) { OHLCDataset highLowData = (OHLCDataset) dataset; - value = highLowData.getHighValue(series, item); + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + double valueTmp = highLowData.getHighValue(series, item); + value = valueTmp; } else { value = dataset.getYValue(series, item); @@ -1990,8 +2023,9 @@ public static Range findStackedRangeBounds(CategoryDataset dataset, Range result = null; // create an array holding the group indices for each series... - int[] groupIndex = new int[dataset.getRowCount()]; - for (int i = 0; i < dataset.getRowCount(); i++) { + int rowCount = dataset.getRowCount(); + int[] groupIndex = new int[rowCount]; + for (int i = 0; i < rowCount; i++) { groupIndex[i] = map.getGroupIndex(map.getGroup( dataset.getRowKey(i))); } @@ -2005,19 +2039,20 @@ public static Range findStackedRangeBounds(CategoryDataset dataset, for (int item = 0; item < categoryCount; item++) { double[] positive = new double[groupCount]; double[] negative = new double[groupCount]; - int seriesCount = dataset.getRowCount(); - for (int series = 0; series < seriesCount; series++) { + for (int series = 0; series < rowCount; series++) { Number number = dataset.getValue(series, item); if (number != null) { hasValidData = true; double value = number.doubleValue(); + @SuppressWarnings("index") // group indices are all <= group count by definition + @IndexFor({"positive", "negative", "maximum", "minimum"}) int groupIndexTmp = groupIndex[series]; if (value > 0.0) { - positive[groupIndex[series]] - = positive[groupIndex[series]] + value; + positive[groupIndexTmp] + = positive[groupIndexTmp] + value; } if (value < 0.0) { - negative[groupIndex[series]] - = negative[groupIndex[series]] + value; + negative[groupIndexTmp] + = negative[groupIndexTmp] + value; // '+', remember value is negative } } @@ -2142,6 +2177,7 @@ public static Range findStackedRangeBounds(TableXYDataset dataset, double negative = base; int seriesCount = dataset.getSeriesCount(); for (int seriesNo = 0; seriesNo < seriesCount; seriesNo++) { + @SuppressWarnings("index") // all the series of a TableXYDataset have the same length double y = dataset.getYValue(seriesNo, itemNo); if (!Double.isNaN(y)) { if (y > 0.0) { @@ -2178,10 +2214,11 @@ public static Range findStackedRangeBounds(TableXYDataset dataset, * * @since 1.0.5 */ - public static double calculateStackTotal(TableXYDataset dataset, int item) { + public static double calculateStackTotal(TableXYDataset dataset, @NonNegative int item) { double total = 0.0; int seriesCount = dataset.getSeriesCount(); for (int s = 0; s < seriesCount; s++) { + @SuppressWarnings("index") // This assumes that all series in the dataset have the same number of items double value = dataset.getYValue(s, item); if (!Double.isNaN(value)) { total = total + value; @@ -2243,19 +2280,25 @@ public static Range findCumulativeRangeBounds(CategoryDataset dataset) { * * @since 1.0.16 */ - public static double findYValue(XYDataset dataset, int series, double x) { + public static double findYValue(XYDataset dataset, @NonNegative int series, double x) { // delegate null check on dataset - int[] indices = findItemIndicesForX(dataset, series, x); + @IndexOrLow("dataset.getSeries(series)") int[] indices = findItemIndicesForX(dataset, series, x); if (indices[0] == -1) { return Double.NaN; } - if (indices[0] == indices[1]) { - return dataset.getYValue(series, indices[0]); + + @IndexFor("dataset.getSeries(series)") int indices0 = indices[0]; + + @SuppressWarnings("index") // guaranteed index: the check above is sufficient to establish that no entries in indices are negative, since the values are correlated. + @IndexFor("dataset.getSeries(series)") int indices1 = indices[1]; + + if (indices0 == indices1) { + return dataset.getYValue(series, indices0); } - double x0 = dataset.getXValue(series, indices[0]); - double x1 = dataset.getXValue(series, indices[1]); - double y0 = dataset.getYValue(series, indices[0]); - double y1 = dataset.getYValue(series, indices[1]); + double x0 = dataset.getXValue(series, indices0); + double x1 = dataset.getXValue(series, indices1); + double y0 = dataset.getYValue(series, indices0); + double y1 = dataset.getYValue(series, indices1); return y0 + (y1 - y0) * (x - x0) / (x1 - x0); } @@ -2280,7 +2323,7 @@ public static double findYValue(XYDataset dataset, int series, double x) { * * @see #findYValue(org.jfree.data.xy.XYDataset, int, double) */ - public static int[] findItemIndicesForX(XYDataset dataset, int series, + public static @IndexOrLow("#1.getSeries(#2)") int @ArrayLen(2) [] findItemIndicesForX(XYDataset dataset, @NonNegative int series, double x) { Args.nullNotPermitted(dataset, "dataset"); int itemCount = dataset.getItemCount(series); @@ -2288,15 +2331,19 @@ public static int[] findItemIndicesForX(XYDataset dataset, int series, return new int[] {-1, -1}; } if (itemCount == 1) { - if (x == dataset.getXValue(series, 0)) { - return new int[] {0, 0}; + @SuppressWarnings("index") // 0 is a valid index, because itemCount > 1 + @IndexFor("dataset.getSeries(series)") int zero = 0; + double xValue = dataset.getXValue(series, zero); + if (x == xValue) { + return new int[] {zero, zero}; } else { return new int[] {-1, -1}; } } if (dataset.getDomainOrder() == DomainOrder.ASCENDING) { - int low = 0; - int high = itemCount - 1; + @SuppressWarnings("index") // 0 is a valid index, because itemCount > 1 + @IndexFor("dataset.getSeries(series)") int low = 0; + @IndexFor("dataset.getSeries(series)") int high = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue > x) { return new int[] {-1, -1}; @@ -2328,8 +2375,9 @@ public static int[] findItemIndicesForX(XYDataset dataset, int series, return new int[] {low, high}; } else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { - int high = 0; - int low = itemCount - 1; + @SuppressWarnings("index") // 0 is a valid index, because itemCount > 1 + @IndexFor("dataset.getSeries(series)") int high = 0; + @IndexFor("dataset.getSeries(series)") int low = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue > x) { return new int[] {-1, -1}; @@ -2358,9 +2406,11 @@ else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { // we don't know anything about the ordering of the x-values, // so we iterate until we find the first crossing of x (if any) // we know there are at least 2 items in the series at this point - double prev = dataset.getXValue(series, 0); + @SuppressWarnings("index") // 0 is a valid index, because itemCount > 1 + @IndexFor("dataset.getSeries(series)") int zero = 0; + double prev = dataset.getXValue(series, zero); if (x == prev) { - return new int[] {0, 0}; // exact match on first item + return new int[] {zero, zero}; // exact match on first item } for (int i = 1; i < itemCount; i++) { double next = dataset.getXValue(series, i); diff --git a/src/main/java/org/jfree/data/general/DefaultHeatMapDataset.java b/src/main/java/org/jfree/data/general/DefaultHeatMapDataset.java index cd09f2938..77977c870 100644 --- a/src/main/java/org/jfree/data/general/DefaultHeatMapDataset.java +++ b/src/main/java/org/jfree/data/general/DefaultHeatMapDataset.java @@ -40,6 +40,9 @@ package org.jfree.data.general; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DataUtils; @@ -53,10 +56,10 @@ public class DefaultHeatMapDataset extends AbstractDataset implements HeatMapDataset, Cloneable, PublicCloneable, Serializable { /** The number of samples in this dataset for the x-dimension. */ - private int xSamples; + private @Positive @LTEqLengthOf("this.getData()") int xSamples; /** The number of samples in this dataset for the y-dimension. */ - private int ySamples; + private @Positive @LTEqLengthOf("this.getData()[0]") int ySamples; /** The minimum x-value in the dataset. */ private double minX; @@ -71,7 +74,7 @@ public class DefaultHeatMapDataset extends AbstractDataset private double maxY; /** Storage for the z-values. */ - private double[][] zValues; + private double @MinLen(1) @SameLen("this.getData()") [] @MinLen(1) @SameLen("this.getData()[0]") [] zValues; /** * Creates a new dataset where all the z-values are initially 0. This is @@ -84,7 +87,7 @@ public class DefaultHeatMapDataset extends AbstractDataset * @param minY the minimum y-value in the dataset. * @param maxY the maximum y-value in the dataset. */ - public DefaultHeatMapDataset(int xSamples, int ySamples, double minX, + public DefaultHeatMapDataset(@Positive int xSamples, @Positive int ySamples, double minX, double maxX, double minY, double maxY) { if (xSamples < 1) { @@ -106,15 +109,23 @@ public DefaultHeatMapDataset(int xSamples, int ySamples, double minX, throw new IllegalArgumentException("'maxY' cannot be INF or NaN."); } - this.xSamples = xSamples; - this.ySamples = ySamples; + @SuppressWarnings("index") // establishing invariant of a ghost method + @Positive @LTEqLengthOf("this.getData()") int xSamplesTmp = xSamples; + this.xSamples = xSamplesTmp; + @SuppressWarnings("index") // establishing invariant of a ghost method + @Positive @LTEqLengthOf("this.getData()[0]") int ySamplesTmp = ySamples; + this.ySamples = ySamplesTmp; this.minX = minX; this.maxX = maxX; this.minY = minY; this.maxY = maxY; - this.zValues = new double[xSamples][]; + @SuppressWarnings({"index", "value"}) // establishing invariant of a ghost method: since ySamples is positive and will be used to create each inner array, the MinLen is true + double @SameLen("this.getData()") [] @SameLen("this.getData()[0]") @MinLen(1) [] zValues = new double[xSamples][]; + this.zValues = zValues; for (int x = 0; x < xSamples; x++) { - this.zValues[x] = new double[ySamples]; + @SuppressWarnings("index") // establishing invariant of a ghost method + double @SameLen("this.getData()[0]") [] zValuesX = new double[ySamples]; + this.zValues[x] = zValuesX; } } @@ -126,7 +137,7 @@ public DefaultHeatMapDataset(int xSamples, int ySamples, double minX, * @return The number of x-values (always > 0). */ @Override - public int getXSampleCount() { + public @Positive @LTEqLengthOf("this.getData()") int getXSampleCount() { return this.xSamples; } @@ -138,7 +149,7 @@ public int getXSampleCount() { * @return The number of y-values (always > 0). */ @Override - public int getYSampleCount() { + public @Positive @LTEqLengthOf("this.getData()[0]") int getYSampleCount() { return this.ySamples; } @@ -198,7 +209,7 @@ public double getMaximumYValue() { * @return The x-value. */ @Override - public double getXValue(int xIndex) { + public double getXValue(@NonNegative int xIndex) { double x = this.minX + (this.maxX - this.minX) * (xIndex / (double) this.xSamples); return x; @@ -212,7 +223,7 @@ public double getXValue(int xIndex) { * @return The y-value. */ @Override - public double getYValue(int yIndex) { + public double getYValue(@NonNegative int yIndex) { double y = this.minY + (this.maxY - this.minY) * (yIndex / (double) this.ySamples); return y; @@ -228,7 +239,7 @@ public double getYValue(int yIndex) { * @return The z-value. */ @Override - public double getZValue(int xIndex, int yIndex) { + public double getZValue(@IndexFor("this.getData()") int xIndex, @IndexFor("this.getData()[0]") int yIndex) { return this.zValues[xIndex][yIndex]; } @@ -244,10 +255,18 @@ public double getZValue(int xIndex, int yIndex) { * @return The z-value. */ @Override - public Number getZ(int xIndex, int yIndex) { + public Number getZ(@IndexFor("this.getData()") int xIndex, @IndexFor("this.getData()[0]") int yIndex) { return new Double(getZValue(xIndex, yIndex)); } + /** + * This is a ghost method. Implementations should return null. + */ + @Override + public double @MinLen(1) [] @MinLen(1) [] getData() { + return null; + } + /** * Updates a z-value in the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. @@ -256,7 +275,7 @@ public Number getZ(int xIndex, int yIndex) { * @param yIndex the y-index. * @param z the new z-value. */ - public void setZValue(int xIndex, int yIndex, double z) { + public void setZValue(@IndexFor("this.getData()") int xIndex, @IndexFor("this.getData()[0]") int yIndex, double z) { setZValue(xIndex, yIndex, z, true); } @@ -269,7 +288,7 @@ public void setZValue(int xIndex, int yIndex, double z) { * @param z the new z-value. * @param notify notify listeners? */ - public void setZValue(int xIndex, int yIndex, double z, boolean notify) { + public void setZValue(@IndexFor("this.getData()") int xIndex, @IndexFor("this.getData()[0]") int yIndex, double z, boolean notify) { this.zValues[xIndex][yIndex] = z; if (notify) { fireDatasetChanged(); @@ -326,6 +345,7 @@ public boolean equals(Object obj) { * cloning. */ @Override + @SuppressWarnings({"index", "value"}) // clone's internal state and this object's internal state are the same. Not inferred by SameLen Checker. public Object clone() throws CloneNotSupportedException { DefaultHeatMapDataset clone = (DefaultHeatMapDataset) super.clone(); clone.zValues = DataUtils.clone(this.zValues); diff --git a/src/main/java/org/jfree/data/general/DefaultPieDataset.java b/src/main/java/org/jfree/data/general/DefaultPieDataset.java index 7078722ce..5bda8893e 100644 --- a/src/main/java/org/jfree/data/general/DefaultPieDataset.java +++ b/src/main/java/org/jfree/data/general/DefaultPieDataset.java @@ -57,6 +57,9 @@ package org.jfree.data.general; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Collections; import java.util.List; @@ -107,7 +110,7 @@ public DefaultPieDataset(KeyedValues data) { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.data.getItemCount(); } @@ -134,7 +137,7 @@ public List getKeys() { * specified range. */ @Override - public Comparable getKey(int item) { + public Comparable getKey(@NonNegative int item) { return this.data.getKey(item); } @@ -149,7 +152,7 @@ public Comparable getKey(int item) { * {@code null}. */ @Override - public int getIndex(Comparable key) { + public @GTENegativeOne int getIndex(Comparable key) { return this.data.getIndex(key); } @@ -161,7 +164,7 @@ public int getIndex(Comparable key) { * @return The value (possibly {@code null}). */ @Override - public Number getValue(int item) { + public Number getValue(@NonNegative int item) { Number result = null; if (getItemCount() > item) { result = this.data.getValue(item); @@ -226,7 +229,7 @@ public void setValue(Comparable key, double value) { * * @since 1.0.6 */ - public void insertValue(int position, Comparable key, double value) { + public void insertValue(@NonNegative int position, Comparable key, double value) { insertValue(position, key, new Double(value)); } @@ -243,7 +246,7 @@ public void insertValue(int position, Comparable key, double value) { * * @since 1.0.6 */ - public void insertValue(int position, Comparable key, Number value) { + public void insertValue(@NonNegative int position, Comparable key, Number value) { this.data.insertValue(position, key, value); fireDatasetChanged(); } diff --git a/src/main/java/org/jfree/data/general/HeatMapDataset.java b/src/main/java/org/jfree/data/general/HeatMapDataset.java index db7f8e979..4f4458789 100644 --- a/src/main/java/org/jfree/data/general/HeatMapDataset.java +++ b/src/main/java/org/jfree/data/general/HeatMapDataset.java @@ -40,6 +40,10 @@ package org.jfree.data.general; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; + /** * A dataset that represents a rectangular grid of (x, y, z) values. The x * and y values appear at regular intervals in the dataset, while the z-values @@ -56,7 +60,7 @@ public interface HeatMapDataset { * * @return The number of x-values (always > 0). */ - public int getXSampleCount(); + public @Positive @LTEqLengthOf("this.getData()") int getXSampleCount(); /** * Returns the number of y values (or samples) for the dataset. The @@ -65,7 +69,7 @@ public interface HeatMapDataset { * * @return The number of y-values (always > 0). */ - public int getYSampleCount(); + public @Positive @LTEqLengthOf("this.getData()[0]") int getYSampleCount(); /** * Returns the lowest x-value represented in this dataset. A requirement @@ -110,7 +114,7 @@ public interface HeatMapDataset { * * @return The x-value. */ - public double getXValue(int xIndex); + public double getXValue(@NonNegative int xIndex); /** * A convenience method that returns the y-value for the given index. @@ -119,7 +123,7 @@ public interface HeatMapDataset { * * @return The y-value. */ - public double getYValue(int yIndex); + public double getYValue(@NonNegative int yIndex); /** * Returns the z-value at the specified sample position in the dataset. @@ -130,7 +134,7 @@ public interface HeatMapDataset { * * @return The z-value. */ - public double getZValue(int xIndex, int yIndex); + public double getZValue(@IndexFor("this.getData()") int xIndex, @IndexFor("this.getData()[0]") int yIndex); /** * Returns the z-value at the specified sample position in the dataset. @@ -149,6 +153,10 @@ public interface HeatMapDataset { * * @return The z-value (possibly {@code null}). */ - public Number getZ(int xIndex, int yIndex); + public Number getZ(@IndexFor("this.getData()") int xIndex, @IndexFor("this.getData()[0]") int yIndex); + /** + * This is a ghost method. Implementations should return null. + */ + public double @MinLen(1) [] @MinLen(1) [] getData(); } diff --git a/src/main/java/org/jfree/data/general/HeatMapUtils.java b/src/main/java/org/jfree/data/general/HeatMapUtils.java index ec43459f4..4e0c072ec 100644 --- a/src/main/java/org/jfree/data/general/HeatMapUtils.java +++ b/src/main/java/org/jfree/data/general/HeatMapUtils.java @@ -41,6 +41,9 @@ package org.jfree.data.general; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.awt.Graphics2D; import java.awt.Paint; import java.awt.image.BufferedImage; @@ -68,7 +71,7 @@ public abstract class HeatMapUtils { * @return The dataset. */ public static XYDataset extractRowFromHeatMapDataset(HeatMapDataset dataset, - int row, Comparable seriesName) { + @IndexFor("#1.getData()[0]") int row, Comparable seriesName) { XYSeries series = new XYSeries(seriesName); int cols = dataset.getXSampleCount(); for (int c = 0; c < cols; c++) { @@ -89,7 +92,7 @@ public static XYDataset extractRowFromHeatMapDataset(HeatMapDataset dataset, * @return The dataset. */ public static XYDataset extractColumnFromHeatMapDataset( - HeatMapDataset dataset, int column, Comparable seriesName) { + HeatMapDataset dataset, @IndexFor("#1.getData()") int column, Comparable seriesName) { XYSeries series = new XYSeries(seriesName); int rows = dataset.getYSampleCount(); for (int r = 0; r < rows; r++) { diff --git a/src/main/java/org/jfree/data/general/Series.java b/src/main/java/org/jfree/data/general/Series.java index 7c755dd0d..914050727 100644 --- a/src/main/java/org/jfree/data/general/Series.java +++ b/src/main/java/org/jfree/data/general/Series.java @@ -56,6 +56,8 @@ package org.jfree.data.general; +import org.checkerframework.checker.index.qual.*; + import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.beans.PropertyVetoException; @@ -235,7 +237,7 @@ public boolean isEmpty() { * * @return The number of data items in the series. */ - public abstract int getItemCount(); + public abstract @NonNegative int getItemCount(); /** * Returns a clone of the series. diff --git a/src/main/java/org/jfree/data/general/SeriesDataset.java b/src/main/java/org/jfree/data/general/SeriesDataset.java index aa04441a7..acc52a69b 100644 --- a/src/main/java/org/jfree/data/general/SeriesDataset.java +++ b/src/main/java/org/jfree/data/general/SeriesDataset.java @@ -43,6 +43,10 @@ package org.jfree.data.general; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.category.CategoryDataset; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.IntervalXYZDataset; @@ -65,7 +69,7 @@ public interface SeriesDataset extends Dataset { * * @return The series count. */ - public int getSeriesCount(); + public @NonNegative int getSeriesCount(); /** * Returns the key for a series. @@ -75,7 +79,7 @@ public interface SeriesDataset extends Dataset { * * @return The key for the series. */ - public Comparable getSeriesKey(int series); + public Comparable getSeriesKey(@NonNegative int series); /** * Returns the index of the series with the specified key, or -1 if there @@ -85,6 +89,16 @@ public interface SeriesDataset extends Dataset { * * @return The index, or -1. */ - public int indexOf(Comparable seriesKey); + public @GTENegativeOne int indexOf(Comparable seriesKey); + + /** + * This is a ghost method for use in annotations. It is never called at runtime, and should never be invoked. + * Implementing classes should have this method throw an exception indicating that it is unimplemented. + * I'd rather use a default method here, but JFreeChart only compiles using Java 7. + * + * @param series a series index + * @return a representation of the series + */ + Series getSeries(@NonNegative int series) throws Exception; } diff --git a/src/main/java/org/jfree/data/general/WaferMapDataset.java b/src/main/java/org/jfree/data/general/WaferMapDataset.java index c836b8cbe..2df257fdb 100644 --- a/src/main/java/org/jfree/data/general/WaferMapDataset.java +++ b/src/main/java/org/jfree/data/general/WaferMapDataset.java @@ -43,6 +43,8 @@ package org.jfree.data.general; +import org.checkerframework.checker.index.qual.*; + import java.util.Set; import java.util.TreeSet; @@ -61,10 +63,10 @@ public class WaferMapDataset extends AbstractDataset { private DefaultKeyedValues2D data; /** wafer x dimension */ - private int maxChipX; + private @NonNegative int maxChipX; /** wafer y dimension */ - private int maxChipY; + private @NonNegative int maxChipY; /** space to draw between chips */ private double chipSpace; @@ -84,7 +86,7 @@ public class WaferMapDataset extends AbstractDataset { * @param maxChipX the wafer x-dimension. * @param maxChipY the wafer y-dimension. */ - public WaferMapDataset(int maxChipX, int maxChipY) { + public WaferMapDataset(@NonNegative int maxChipX, @NonNegative int maxChipY) { this(maxChipX, maxChipY, null); } @@ -95,7 +97,7 @@ public WaferMapDataset(int maxChipX, int maxChipY) { * @param maxChipY the wafer y-dimension. * @param chipSpace the space between chips. */ - public WaferMapDataset(int maxChipX, int maxChipY, Number chipSpace) { + public WaferMapDataset(@NonNegative int maxChipX, @NonNegative int maxChipY, Number chipSpace) { this.maxValue = new Double(Double.NEGATIVE_INFINITY); this.minValue = new Double(Double.POSITIVE_INFINITY); @@ -130,7 +132,7 @@ public void addValue(Number value, Comparable chipx, Comparable chipy) { * @param x the x-index. * @param y the y-index. */ - public void addValue(int v, int x, int y) { + public void addValue(int v, @NonNegative int x, @NonNegative int y) { setValue(new Double(v), new Integer(x), new Integer(y)); } @@ -156,7 +158,7 @@ public void setValue(Number value, Comparable chipx, Comparable chipy) { * * @return The number of unique values. */ - public int getUniqueValueCount() { + public @NonNegative int getUniqueValueCount() { return getUniqueValues().size(); } @@ -262,7 +264,7 @@ public Number getMinValue() { * * @return The number of chips in the x-dimension. */ - public int getMaxChipX() { + public @NonNegative int getMaxChipX() { return this.maxChipX; } @@ -271,7 +273,7 @@ public int getMaxChipX() { * * @param maxChipX the number of chips in the x-dimension. */ - public void setMaxChipX(int maxChipX) { + public void setMaxChipX(@NonNegative int maxChipX) { this.maxChipX = maxChipX; } @@ -280,7 +282,7 @@ public void setMaxChipX(int maxChipX) { * * @return The number of chips. */ - public int getMaxChipY() { + public @NonNegative int getMaxChipY() { return this.maxChipY; } @@ -289,7 +291,7 @@ public int getMaxChipY() { * * @param maxChipY the number of chips. */ - public void setMaxChipY(int maxChipY) { + public void setMaxChipY(@NonNegative int maxChipY) { this.maxChipY = maxChipY; } diff --git a/src/main/java/org/jfree/data/io/CSV.java b/src/main/java/org/jfree/data/io/CSV.java index 8dfad2694..4f5a7eafc 100644 --- a/src/main/java/org/jfree/data/io/CSV.java +++ b/src/main/java/org/jfree/data/io/CSV.java @@ -40,6 +40,9 @@ package org.jfree.data.io; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; @@ -127,6 +130,7 @@ private List extractColumnKeys(String line) { if (line.charAt(i) == this.fieldDelimiter) { if (fieldIndex > 0) { // first field is ignored, since // column 0 is for row keys + @SuppressWarnings("index") // MinLen type refinement in for loop https://github.com/kelloggm/checker-framework/issues/138 String key = line.substring(start, i); keys.add(removeStringDelimiters(key)); } @@ -134,6 +138,7 @@ private List extractColumnKeys(String line) { fieldIndex++; } } + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/219: the for loop above cannot increment start more than line.length times, so start is IOH of line String key = line.substring(start, line.length()); keys.add(removeStringDelimiters(key)); return keys; @@ -151,11 +156,11 @@ private void extractRowKeyAndData(String line, List columnKeys) { Comparable rowKey = null; int fieldIndex = 0; - int start = 0; + @IndexOrHigh("line") int start = 0; for (int i = 0; i < line.length(); i++) { if (line.charAt(i) == this.fieldDelimiter) { if (fieldIndex == 0) { // first field contains the row key - String key = line.substring(start, i); + String key = line.substring(start, i); rowKey = removeStringDelimiters(key); } else { // remaining fields contain values @@ -167,10 +172,16 @@ private void extractRowKeyAndData(String line, (Comparable) columnKeys.get(fieldIndex - 1) ); } - start = i + 1; + @IndexOrHigh("line") int skipIndex = i + 1; + start = skipIndex; fieldIndex++; } } + + @SuppressWarnings("index") // CSV format guarantees at least one field + @Positive int fieldIndexTmp = fieldIndex; + fieldIndex = fieldIndexTmp; + Double value = Double.valueOf( removeStringDelimiters(line.substring(start, line.length())) ); @@ -187,6 +198,7 @@ private void extractRowKeyAndData(String line, * * @return The key without delimiters. */ + @SuppressWarnings({"index", "value"}) // I manually audited this. It relies on several properties of the passed String that I can't express: that removing whitespace leaves a MinLen(1) string, that if the first character of the trimmed string is a delimiter, there is another character, that if the last character is a delimiter, it isn't the only character private String removeStringDelimiters(String key) { String k = key.trim(); if (k.charAt(0) == this.textDelimiter) { diff --git a/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java b/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java index 514ac0433..059e3b95e 100644 --- a/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java +++ b/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java @@ -72,6 +72,8 @@ package org.jfree.data.jdbc; +import org.checkerframework.checker.index.qual.*; + import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; @@ -242,7 +244,8 @@ public void executeQuery(Connection con, String query) resultSet = statement.executeQuery(query); ResultSetMetaData metaData = resultSet.getMetaData(); - int numberOfColumns = metaData.getColumnCount(); + @SuppressWarnings("index") // ResultSetMetaData#getColumnCount needs an annotation + @Positive int numberOfColumns = metaData.getColumnCount(); int numberOfValidColumns = 0; int [] columnTypes = new int[numberOfColumns]; for (int column = 0; column < numberOfColumns; column++) { @@ -286,12 +289,14 @@ public void executeQuery(Connection con, String query) /// First column is X data this.columnNames = new String[numberOfValidColumns - 1]; /// Get the column names and cache them. - int currentColumn = 0; + @IndexFor("this.columnNames") int currentColumn = 0; for (int column = 1; column < numberOfColumns; column++) { if (columnTypes[column] != Types.NULL) { this.columnNames[currentColumn] = metaData.getColumnLabel(column + 1); - ++currentColumn; + @SuppressWarnings("index") // this update only happens when the column is valid + @IndexFor("this.columnNames") int currentColumnTmp = currentColumn + 1; + currentColumn = currentColumnTmp; } } @@ -423,7 +428,7 @@ public void executeQuery(Connection con, String query) * @see XYDataset */ @Override - public Number getX(int seriesIndex, int itemIndex) { + public Number getX(@NonNegative int seriesIndex, @IndexFor("this.getSeries(#1)") int itemIndex) { ArrayList row = (ArrayList) this.rows.get(itemIndex); return (Number) row.get(0); } @@ -439,7 +444,7 @@ public Number getX(int seriesIndex, int itemIndex) { * @see XYDataset */ @Override - public Number getY(int seriesIndex, int itemIndex) { + public Number getY(@NonNegative int seriesIndex, @IndexFor("this.getSeries(#1)") int itemIndex) { ArrayList row = (ArrayList) this.rows.get(itemIndex); return (Number) row.get(seriesIndex + 1); } @@ -454,7 +459,8 @@ public Number getY(int seriesIndex, int itemIndex) { * @see XYDataset */ @Override - public int getItemCount(int seriesIndex) { + @SuppressWarnings("index") // array-list interop: getItemCount needs to have this type for the other implementations + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int seriesIndex) { return this.rows.size(); } @@ -465,7 +471,7 @@ public int getItemCount(int seriesIndex) { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return getItemCount(0); } @@ -478,7 +484,7 @@ public int getItemCount() { * @see Dataset */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.columnNames.length; } @@ -493,7 +499,7 @@ public int getSeriesCount() { * @see Dataset */ @Override - public Comparable getSeriesKey(int seriesIndex) { + public Comparable getSeriesKey(@NonNegative int seriesIndex) { if ((seriesIndex < this.columnNames.length) && (this.columnNames[seriesIndex] != null)) { diff --git a/src/main/java/org/jfree/data/json/impl/JSONObject.java b/src/main/java/org/jfree/data/json/impl/JSONObject.java index 58eae99a1..0209fed3b 100644 --- a/src/main/java/org/jfree/data/json/impl/JSONObject.java +++ b/src/main/java/org/jfree/data/json/impl/JSONObject.java @@ -23,7 +23,6 @@ package org.jfree.data.json.impl; - import java.io.IOException; import java.io.Writer; import java.util.HashMap; diff --git a/src/main/java/org/jfree/data/json/impl/JSONStreamAware.java b/src/main/java/org/jfree/data/json/impl/JSONStreamAware.java index e036f832e..1135c0100 100644 --- a/src/main/java/org/jfree/data/json/impl/JSONStreamAware.java +++ b/src/main/java/org/jfree/data/json/impl/JSONStreamAware.java @@ -23,7 +23,6 @@ package org.jfree.data.json.impl; - import java.io.IOException; import java.io.Writer; diff --git a/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCalculator.java b/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCalculator.java index 17caaada3..bbec3c871 100644 --- a/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCalculator.java +++ b/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCalculator.java @@ -47,6 +47,9 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -186,14 +189,18 @@ public static double calculateQ1(List values) { if (count > 0) { if (count % 2 == 1) { if (count > 1) { - result = Statistics.calculateMedian(values, 0, count / 2); + int count1 = count / 2; + result = Statistics.calculateMedian(values, 0, count1); } else { result = Statistics.calculateMedian(values, 0, 0); } } else { - result = Statistics.calculateMedian(values, 0, count / 2 - 1); + @SuppressWarnings({"index","value"}) // since count is a nonnegative even number, this expression is >= 0 + @IntRange(from=0) int count1 = count / 2 - 1; + count1 = count1; + result = Statistics.calculateMedian(values, 0, count1); } } @@ -218,16 +225,21 @@ public static double calculateQ3(List values) { if (count > 0) { if (count % 2 == 1) { if (count > 1) { - result = Statistics.calculateMedian(values, count / 2, - count - 1); + int countDiv2 = count / 2; + int countMinus1 = count - 1; + result = Statistics.calculateMedian(values, countDiv2, + countMinus1); } else { result = Statistics.calculateMedian(values, 0, 0); } } else { - result = Statistics.calculateMedian(values, count / 2, - count - 1); + int countDiv2 = count / 2; + int countMinus1 = count - 1; + + result = Statistics.calculateMedian(values, countDiv2, + countMinus1); } } return result; diff --git a/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCategoryDataset.java b/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCategoryDataset.java index 66e0bd14f..17d22704b 100644 --- a/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCategoryDataset.java +++ b/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCategoryDataset.java @@ -45,6 +45,8 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.List; import org.jfree.data.category.CategoryDataset; @@ -63,7 +65,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return The mean value. */ - public Number getMeanValue(int row, int column); + public Number getMeanValue(@NonNegative int row, @NonNegative int column); /** * Returns the average value for an item. @@ -83,7 +85,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return The median value. */ - public Number getMedianValue(int row, int column); + public Number getMedianValue(@NonNegative int row, @NonNegative int column); /** * Returns the median value for an item. @@ -103,7 +105,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return The q1median value. */ - public Number getQ1Value(int row, int column); + public Number getQ1Value(@NonNegative int row, @NonNegative int column); /** * Returns the q1median value for an item. @@ -123,7 +125,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return The q3median value. */ - public Number getQ3Value(int row, int column); + public Number getQ3Value(@NonNegative int row, @NonNegative int column); /** * Returns the q3median value for an item. @@ -143,7 +145,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return The minimum regular value. */ - public Number getMinRegularValue(int row, int column); + public Number getMinRegularValue(@NonNegative int row, @NonNegative int column); /** * Returns the minimum regular (non-outlier) value for an item. @@ -163,7 +165,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return The maximum regular value. */ - public Number getMaxRegularValue(int row, int column); + public Number getMaxRegularValue(@NonNegative int row, @NonNegative int column); /** * Returns the maximum regular (non-outlier) value for an item. @@ -183,7 +185,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return The minimum outlier. */ - public Number getMinOutlier(int row, int column); + public Number getMinOutlier(@NonNegative int row, @NonNegative int column); /** * Returns the minimum outlier (non-farout) for an item. @@ -203,7 +205,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return The maximum outlier. */ - public Number getMaxOutlier(int row, int column); + public Number getMaxOutlier(@NonNegative int row, @NonNegative int column); /** * Returns the maximum outlier (non-farout) for an item. @@ -224,7 +226,7 @@ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { * * @return A list of outliers for an item. */ - public List getOutliers(int row, int column); + public List getOutliers(@NonNegative int row, @NonNegative int column); /** * Returns a list of outlier values for an item. The list may be empty, diff --git a/src/main/java/org/jfree/data/statistics/BoxAndWhiskerXYDataset.java b/src/main/java/org/jfree/data/statistics/BoxAndWhiskerXYDataset.java index fa3dc3097..e61c818c6 100644 --- a/src/main/java/org/jfree/data/statistics/BoxAndWhiskerXYDataset.java +++ b/src/main/java/org/jfree/data/statistics/BoxAndWhiskerXYDataset.java @@ -52,6 +52,10 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.List; import org.jfree.data.xy.XYDataset; @@ -73,7 +77,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * * @return The mean for the specified series and item. */ - public Number getMeanValue(int series, int item); + public Number getMeanValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the median-value for the specified series and item. @@ -83,7 +87,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * * @return The median-value for the specified series and item. */ - public Number getMedianValue(int series, int item); + public Number getMedianValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the Q1 median-value for the specified series and item. @@ -93,7 +97,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * * @return The Q1 median-value for the specified series and item. */ - public Number getQ1Value(int series, int item); + public Number getQ1Value(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the Q3 median-value for the specified series and item. @@ -103,7 +107,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * * @return The Q3 median-value for the specified series and item. */ - public Number getQ3Value(int series, int item); + public Number getQ3Value(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the min-value for the specified series and item. @@ -113,7 +117,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * * @return The min-value for the specified series and item. */ - public Number getMinRegularValue(int series, int item); + public Number getMinRegularValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the max-value for the specified series and item. @@ -123,7 +127,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * * @return The max-value for the specified series and item. */ - public Number getMaxRegularValue(int series, int item); + public Number getMaxRegularValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the minimum value which is not a farout. @@ -132,7 +136,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * * @return A {@code Number} representing the maximum non-farout value. */ - public Number getMinOutlier(int series, int item); + public Number getMinOutlier(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the maximum value which is not a farout, ie Q3 + (interquartile @@ -143,7 +147,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * * @return A {@code Number} representing the maximum non-farout value. */ - public Number getMaxOutlier(int series, int item); + public Number getMaxOutlier(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns a list of outliers for the specified series and item. @@ -154,7 +158,7 @@ public interface BoxAndWhiskerXYDataset extends XYDataset { * @return The list of outliers for the specified series and item * (possibly {@code null}). */ - public List getOutliers(int series, int item); + public List getOutliers(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the value used as the outlier coefficient. The outlier diff --git a/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerCategoryDataset.java b/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerCategoryDataset.java index 086a37709..0dcd7864a 100644 --- a/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerCategoryDataset.java +++ b/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerCategoryDataset.java @@ -56,6 +56,9 @@ package org.jfree.data.statistics; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; + import java.util.List; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PublicCloneable; @@ -80,23 +83,23 @@ public class DefaultBoxAndWhiskerCategoryDataset extends AbstractDataset private double minimumRangeValue; /** The row index for the cell that the minimum range value comes from. */ - private int minimumRangeValueRow; + private @GTENegativeOne int minimumRangeValueRow; /** * The column index for the cell that the minimum range value comes from. */ - private int minimumRangeValueColumn; + private @GTENegativeOne int minimumRangeValueColumn; /** The maximum range value. */ private double maximumRangeValue; /** The row index for the cell that the maximum range value comes from. */ - private int maximumRangeValueRow; + private @GTENegativeOne int maximumRangeValueRow; /** * The column index for the cell that the maximum range value comes from. */ - private int maximumRangeValueColumn; + private @GTENegativeOne int maximumRangeValueColumn; /** * Creates a new dataset. @@ -227,7 +230,7 @@ public void remove(Comparable rowKey, Comparable columnKey) { * * @since 1.0.7 */ - public void removeRow(int rowIndex) { + public void removeRow(@NonNegative int rowIndex) { this.data.removeRow(rowIndex); updateBounds(); fireDatasetChanged(); @@ -259,7 +262,7 @@ public void removeRow(Comparable rowKey) { * * @since 1.0.7 */ - public void removeColumn(int columnIndex) { + public void removeColumn(@NonNegative int columnIndex) { this.data.removeColumn(columnIndex); updateBounds(); fireDatasetChanged(); @@ -301,7 +304,7 @@ public void clear() { * * @return The item. */ - public BoxAndWhiskerItem getItem(int row, int column) { + public BoxAndWhiskerItem getItem(@NonNegative int row, @NonNegative int column) { return (BoxAndWhiskerItem) this.data.getObject(row, column); } @@ -317,7 +320,8 @@ public BoxAndWhiskerItem getItem(int row, int column) { * @see #getValue(Comparable, Comparable) */ @Override - public Number getValue(int row, int column) { + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column) { return getMedianValue(row, column); } @@ -348,7 +352,7 @@ public Number getValue(Comparable rowKey, Comparable columnKey) { * @see #getItem(int, int) */ @Override - public Number getMeanValue(int row, int column) { + public Number getMeanValue(@NonNegative int row, @NonNegative int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, @@ -392,7 +396,7 @@ public Number getMeanValue(Comparable rowKey, Comparable columnKey) { * @see #getItem(int, int) */ @Override - public Number getMedianValue(int row, int column) { + public Number getMedianValue(@NonNegative int row, @NonNegative int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, column); @@ -434,7 +438,7 @@ public Number getMedianValue(Comparable rowKey, Comparable columnKey) { * @see #getItem(int, int) */ @Override - public Number getQ1Value(int row, int column) { + public Number getQ1Value(@NonNegative int row, @NonNegative int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); @@ -476,7 +480,7 @@ public Number getQ1Value(Comparable rowKey, Comparable columnKey) { * @see #getItem(int, int) */ @Override - public Number getQ3Value(int row, int column) { + public Number getQ3Value(@NonNegative int row, @NonNegative int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); @@ -517,7 +521,7 @@ public Number getQ3Value(Comparable rowKey, Comparable columnKey) { * @see #getColumnKey(int) */ @Override - public int getColumnIndex(Comparable key) { + public @GTENegativeOne int getColumnIndex(Comparable key) { return this.data.getColumnIndex(key); } @@ -531,7 +535,7 @@ public int getColumnIndex(Comparable key) { * @see #getColumnIndex(Comparable) */ @Override - public Comparable getColumnKey(int column) { + public Comparable getColumnKey(@NonNegative int column) { return this.data.getColumnKey(column); } @@ -557,7 +561,7 @@ public List getColumnKeys() { * @see #getRowKey(int) */ @Override - public int getRowIndex(Comparable key) { + public @GTENegativeOne int getRowIndex(Comparable key) { // defer null argument check return this.data.getRowIndex(key); } @@ -572,7 +576,7 @@ public int getRowIndex(Comparable key) { * @see #getRowIndex(Comparable) */ @Override - public Comparable getRowKey(int row) { + public Comparable getRowKey(@NonNegative int row) { return this.data.getRowKey(row); } @@ -596,7 +600,7 @@ public List getRowKeys() { * @see #getColumnCount() */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.data.getRowCount(); } @@ -608,7 +612,8 @@ public int getRowCount() { * @see #getRowCount() */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { return this.data.getColumnCount(); } @@ -666,7 +671,7 @@ public Range getRangeBounds(boolean includeInterval) { * @see #getItem(int, int) */ @Override - public Number getMinRegularValue(int row, int column) { + public Number getMinRegularValue(@NonNegative int row, @NonNegative int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); @@ -708,7 +713,7 @@ public Number getMinRegularValue(Comparable rowKey, Comparable columnKey) { * @see #getItem(int, int) */ @Override - public Number getMaxRegularValue(int row, int column) { + public Number getMaxRegularValue(@NonNegative int row, @NonNegative int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); @@ -750,7 +755,7 @@ public Number getMaxRegularValue(Comparable rowKey, Comparable columnKey) { * @see #getItem(int, int) */ @Override - public Number getMinOutlier(int row, int column) { + public Number getMinOutlier(@NonNegative int row, @NonNegative int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); @@ -792,7 +797,7 @@ public Number getMinOutlier(Comparable rowKey, Comparable columnKey) { * @see #getItem(int, int) */ @Override - public Number getMaxOutlier(int row, int column) { + public Number getMaxOutlier(@NonNegative int row, @NonNegative int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); @@ -834,7 +839,7 @@ public Number getMaxOutlier(Comparable rowKey, Comparable columnKey) { * @see #getItem(int, int) */ @Override - public List getOutliers(int row, int column) { + public List getOutliers(@NonNegative int row, @NonNegative int column) { List result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); diff --git a/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerXYDataset.java b/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerXYDataset.java index a4fbbf6c3..51732f5a9 100644 --- a/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerXYDataset.java +++ b/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerXYDataset.java @@ -60,6 +60,10 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -200,7 +204,7 @@ public void setFaroutCoefficient(double faroutCoefficient) { * @return The number of series. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return 1; } @@ -212,7 +216,7 @@ public int getSeriesCount() { * @return The number of items in the specified series. */ @Override - public int getItemCount(int series) { + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return this.dates.size(); } @@ -270,7 +274,7 @@ public Comparable getSeriesKey(int i) { * * @return The item. */ - public BoxAndWhiskerItem getItem(int series, int item) { + public BoxAndWhiskerItem getItem(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return (BoxAndWhiskerItem) this.items.get(item); } @@ -286,7 +290,7 @@ public BoxAndWhiskerItem getItem(int series, int item) { * @return The x-value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Long(((Date) this.dates.get(item)).getTime()); } @@ -300,7 +304,7 @@ public Number getX(int series, int item) { * * @return The x-value as a Date. */ - public Date getXDate(int series, int item) { + public Date getXDate(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return (Date) this.dates.get(item); } @@ -316,7 +320,7 @@ public Date getXDate(int series, int item) { * @return The y-value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getMeanValue(series, item); } @@ -329,7 +333,7 @@ public Number getY(int series, int item) { * @return The mean for the specified series and item. */ @Override - public Number getMeanValue(int series, int item) { + public Number getMeanValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { @@ -347,7 +351,7 @@ public Number getMeanValue(int series, int item) { * @return The median-value for the specified series and item. */ @Override - public Number getMedianValue(int series, int item) { + public Number getMedianValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { @@ -365,7 +369,7 @@ public Number getMedianValue(int series, int item) { * @return The Q1 median-value for the specified series and item. */ @Override - public Number getQ1Value(int series, int item) { + public Number getQ1Value(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { @@ -383,7 +387,7 @@ public Number getQ1Value(int series, int item) { * @return The Q3 median-value for the specified series and item. */ @Override - public Number getQ3Value(int series, int item) { + public Number getQ3Value(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { @@ -401,7 +405,7 @@ public Number getQ3Value(int series, int item) { * @return The min-value for the specified series and item. */ @Override - public Number getMinRegularValue(int series, int item) { + public Number getMinRegularValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { @@ -419,7 +423,7 @@ public Number getMinRegularValue(int series, int item) { * @return The max-value for the specified series and item. */ @Override - public Number getMaxRegularValue(int series, int item) { + public Number getMaxRegularValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { @@ -436,7 +440,7 @@ public Number getMaxRegularValue(int series, int item) { * @return A {@code Number} representing the maximum non-farout value. */ @Override - public Number getMinOutlier(int series, int item) { + public Number getMinOutlier(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { @@ -455,7 +459,7 @@ public Number getMinOutlier(int series, int item) { * @return A {@code Number} representing the maximum non-farout value. */ @Override - public Number getMaxOutlier(int series, int item) { + public Number getMaxOutlier(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { @@ -474,7 +478,7 @@ public Number getMaxOutlier(int series, int item) { * (possibly {@code null}). */ @Override - public List getOutliers(int series, int item) { + public List getOutliers(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { List result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { diff --git a/src/main/java/org/jfree/data/statistics/DefaultMultiValueCategoryDataset.java b/src/main/java/org/jfree/data/statistics/DefaultMultiValueCategoryDataset.java index a1a85dcfe..799319008 100644 --- a/src/main/java/org/jfree/data/statistics/DefaultMultiValueCategoryDataset.java +++ b/src/main/java/org/jfree/data/statistics/DefaultMultiValueCategoryDataset.java @@ -42,6 +42,9 @@ package org.jfree.data.statistics; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; + import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -163,7 +166,7 @@ else if (minval < this.minimumRangeValue.doubleValue()) { * @return The list of values. */ @Override - public List getValues(int row, int column) { + public List getValues(@NonNegative int row, @NonNegative int column) { List values = (List) this.data.getObject(row, column); if (values != null) { return Collections.unmodifiableList(values); @@ -226,7 +229,8 @@ public Number getValue(Comparable row, Comparable column) { * @return The average value. */ @Override - public Number getValue(int row, int column) { + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column) { List l = (List) this.data.getObject(row, column); double average = 0.0d; int count = 0; @@ -254,7 +258,7 @@ public Number getValue(int row, int column) { * @return The column index. */ @Override - public int getColumnIndex(Comparable key) { + public @GTENegativeOne int getColumnIndex(Comparable key) { return this.data.getColumnIndex(key); } @@ -266,7 +270,7 @@ public int getColumnIndex(Comparable key) { * @return The column key. */ @Override - public Comparable getColumnKey(int column) { + public Comparable getColumnKey(@NonNegative int column) { return this.data.getColumnKey(column); } @@ -288,7 +292,7 @@ public List getColumnKeys() { * @return The row index. */ @Override - public int getRowIndex(Comparable key) { + public @GTENegativeOne int getRowIndex(Comparable key) { return this.data.getRowIndex(key); } @@ -300,7 +304,7 @@ public int getRowIndex(Comparable key) { * @return The row key. */ @Override - public Comparable getRowKey(int row) { + public Comparable getRowKey(@NonNegative int row) { return this.data.getRowKey(row); } @@ -320,7 +324,7 @@ public List getRowKeys() { * @return The row count. */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.data.getRowCount(); } @@ -330,7 +334,8 @@ public int getRowCount() { * @return The column count. */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { return this.data.getColumnCount(); } diff --git a/src/main/java/org/jfree/data/statistics/DefaultStatisticalCategoryDataset.java b/src/main/java/org/jfree/data/statistics/DefaultStatisticalCategoryDataset.java index 4775338b1..966586871 100644 --- a/src/main/java/org/jfree/data/statistics/DefaultStatisticalCategoryDataset.java +++ b/src/main/java/org/jfree/data/statistics/DefaultStatisticalCategoryDataset.java @@ -57,6 +57,10 @@ package org.jfree.data.statistics; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.List; import org.jfree.chart.util.PublicCloneable; @@ -152,7 +156,7 @@ public DefaultStatisticalCategoryDataset() { * @return The mean value (possibly {@code null}). */ @Override - public Number getMeanValue(int row, int column) { + public Number getMeanValue(@NonNegative int row, @NonNegative int column) { Number result = null; MeanAndStandardDeviation masd = (MeanAndStandardDeviation) this.data.getObject(row, column); @@ -172,7 +176,8 @@ public Number getMeanValue(int row, int column) { * @return The value (possibly {@code null}). */ @Override - public Number getValue(int row, int column) { + @Pure + public Number getValue(@NonNegative int row, @NonNegative int column) { return getMeanValue(row, column); } @@ -218,7 +223,7 @@ public Number getMeanValue(Comparable rowKey, Comparable columnKey) { * @return The standard deviation (possibly {@code null}). */ @Override - public Number getStdDevValue(int row, int column) { + public Number getStdDevValue(@NonNegative int row, @NonNegative int column) { Number result = null; MeanAndStandardDeviation masd = (MeanAndStandardDeviation) this.data.getObject(row, column); @@ -255,7 +260,7 @@ public Number getStdDevValue(Comparable rowKey, Comparable columnKey) { * @return The column index. */ @Override - public int getColumnIndex(Comparable key) { + public @GTENegativeOne int getColumnIndex(Comparable key) { // defer null argument check return this.data.getColumnIndex(key); } @@ -268,7 +273,7 @@ public int getColumnIndex(Comparable key) { * @return The column key. */ @Override - public Comparable getColumnKey(int column) { + public Comparable getColumnKey(@NonNegative int column) { return this.data.getColumnKey(column); } @@ -290,7 +295,7 @@ public List getColumnKeys() { * @return The row index. */ @Override - public int getRowIndex(Comparable key) { + public @GTENegativeOne int getRowIndex(Comparable key) { // defer null argument check return this.data.getRowIndex(key); } @@ -303,7 +308,7 @@ public int getRowIndex(Comparable key) { * @return The row key. */ @Override - public Comparable getRowKey(int row) { + public Comparable getRowKey(@NonNegative int row) { return this.data.getRowKey(row); } @@ -325,7 +330,7 @@ public List getRowKeys() { * @see #getColumnCount() */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.data.getRowCount(); } @@ -337,7 +342,8 @@ public int getRowCount() { * @see #getRowCount() */ @Override - public int getColumnCount() { + @Pure + public @NonNegative int getColumnCount() { return this.data.getColumnCount(); } @@ -478,7 +484,7 @@ public void remove(Comparable rowKey, Comparable columnKey) { * * @since 1.0.7 */ - public void removeRow(int rowIndex) { + public void removeRow(@NonNegative int rowIndex) { this.data.removeRow(rowIndex); updateBounds(); fireDatasetChanged(); @@ -510,7 +516,7 @@ public void removeRow(Comparable rowKey) { * * @since 1.0.7 */ - public void removeColumn(int columnIndex) { + public void removeColumn(@NonNegative int columnIndex) { this.data.removeColumn(columnIndex); updateBounds(); fireDatasetChanged(); diff --git a/src/main/java/org/jfree/data/statistics/HistogramBin.java b/src/main/java/org/jfree/data/statistics/HistogramBin.java index 6b6ec7804..29f659ff1 100644 --- a/src/main/java/org/jfree/data/statistics/HistogramBin.java +++ b/src/main/java/org/jfree/data/statistics/HistogramBin.java @@ -44,6 +44,8 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; /** @@ -55,7 +57,7 @@ public class HistogramBin implements Cloneable, Serializable { private static final long serialVersionUID = 7614685080015589931L; /** The number of items in the bin. */ - private int count; + private @NonNegative int count; /** The start boundary. */ private double startBoundary; @@ -84,7 +86,7 @@ public HistogramBin(double startBoundary, double endBoundary) { * * @return The item count. */ - public int getCount() { + public @NonNegative int getCount() { return this.count; } diff --git a/src/main/java/org/jfree/data/statistics/HistogramDataset.java b/src/main/java/org/jfree/data/statistics/HistogramDataset.java index 12d386421..47088a517 100644 --- a/src/main/java/org/jfree/data/statistics/HistogramDataset.java +++ b/src/main/java/org/jfree/data/statistics/HistogramDataset.java @@ -62,6 +62,12 @@ package org.jfree.data.statistics; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; @@ -131,7 +137,7 @@ public void setType(HistogramType type) { * @param values the values ({@code null} not permitted). * @param bins the number of bins (must be at least 1). */ - public void addSeries(Comparable key, double[] values, int bins) { + public void addSeries(Comparable key, double @MinLen(1) [] values, @Positive int bins) { // defer argument checking... double minimum = getMinimum(values); double maximum = getMaximum(values); @@ -150,7 +156,7 @@ public void addSeries(Comparable key, double[] values, int bins) { * @param minimum the lower bound of the bin range. * @param maximum the upper bound of the bin range. */ - public void addSeries(Comparable key, double[] values, int bins, + public void addSeries(Comparable key, double[] values, @Positive int bins, double minimum, double maximum) { Args.nullNotPermitted(key, "key"); @@ -195,6 +201,7 @@ public void addSeries(Comparable key, double[] values, int bins, binIndex = bins - 1; } } + @SuppressWarnings("index") // see the comment about the known bug above HistogramBin bin = (HistogramBin) binList.get(binIndex); bin.incrementCount(); } @@ -216,7 +223,7 @@ public void addSeries(Comparable key, double[] values, int bins, * * @return The minimum value. */ - private double getMinimum(double[] values) { + private double getMinimum(double @MinLen(1) [] values) { if (values == null || values.length < 1) { throw new IllegalArgumentException( "Null or zero length 'values' argument."); @@ -238,7 +245,7 @@ private double getMinimum(double[] values) { * * @return The maximum value. */ - private double getMaximum(double[] values) { + private double getMaximum(double @MinLen(1) [] values) { if (values == null || values.length < 1) { throw new IllegalArgumentException( "Null or zero length 'values' argument."); @@ -263,7 +270,7 @@ private double getMaximum(double[] values) { * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ - List getBins(int series) { + List getBins(@NonNegative int series) { Map map = (Map) this.list.get(series); return (List) map.get("bins"); } @@ -275,7 +282,7 @@ List getBins(int series) { * * @return The total. */ - private int getTotal(int series) { + private int getTotal(@NonNegative int series) { Map map = (Map) this.list.get(series); return ((Integer) map.get("values.length")).intValue(); } @@ -287,7 +294,7 @@ private int getTotal(int series) { * * @return The bin width. */ - private double getBinWidth(int series) { + private double getBinWidth(@NonNegative int series) { Map map = (Map) this.list.get(series); return ((Double) map.get("bin width")).doubleValue(); } @@ -298,7 +305,7 @@ private double getBinWidth(int series) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.list.size(); } @@ -314,7 +321,7 @@ public int getSeriesCount() { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { Map map = (Map) this.list.get(series); return (Comparable) map.get("key"); } @@ -331,7 +338,7 @@ public Comparable getSeriesKey(int series) { * specified range. */ @Override - public int getItemCount(int series) { + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return getBins(series).size(); } @@ -351,7 +358,7 @@ public int getItemCount(int series) { * specified range. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { List bins = getBins(series); HistogramBin bin = (HistogramBin) bins.get(item); double x = (bin.getStartBoundary() + bin.getEndBoundary()) / 2.; @@ -372,7 +379,7 @@ public Number getX(int series, int item) { * specified range. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { List bins = getBins(series); HistogramBin bin = (HistogramBin) bins.get(item); double total = getTotal(series); @@ -405,7 +412,7 @@ else if (this.type == HistogramType.SCALE_AREA_TO_1) { * specified range. */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { List bins = getBins(series); HistogramBin bin = (HistogramBin) bins.get(item); return new Double(bin.getStartBoundary()); @@ -424,7 +431,7 @@ public Number getStartX(int series, int item) { * specified range. */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { List bins = getBins(series); HistogramBin bin = (HistogramBin) bins.get(item); return new Double(bin.getEndBoundary()); @@ -445,7 +452,7 @@ public Number getEndX(int series, int item) { * specified range. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -464,7 +471,7 @@ public Number getStartY(int series, int item) { * specified range. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } diff --git a/src/main/java/org/jfree/data/statistics/MultiValueCategoryDataset.java b/src/main/java/org/jfree/data/statistics/MultiValueCategoryDataset.java index a5041baa1..5e82b0b1a 100644 --- a/src/main/java/org/jfree/data/statistics/MultiValueCategoryDataset.java +++ b/src/main/java/org/jfree/data/statistics/MultiValueCategoryDataset.java @@ -40,6 +40,8 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.List; import org.jfree.data.category.CategoryDataset; @@ -60,7 +62,7 @@ public interface MultiValueCategoryDataset extends CategoryDataset { * * @return The list of values. */ - public List getValues(int row, int column); + public List getValues(@NonNegative int row, @NonNegative int column); /** * Returns a list (possibly empty) of the values for the specified item. diff --git a/src/main/java/org/jfree/data/statistics/Regression.java b/src/main/java/org/jfree/data/statistics/Regression.java index 83b126b35..8ac936860 100644 --- a/src/main/java/org/jfree/data/statistics/Regression.java +++ b/src/main/java/org/jfree/data/statistics/Regression.java @@ -46,6 +46,12 @@ package org.jfree.data.statistics; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; @@ -63,7 +69,7 @@ public abstract class Regression { * * @return The parameters. */ - public static double[] getOLSRegression(double[][] data) { + public static double @ArrayLen(2) [] getOLSRegression(double @MinLen(2) [] @MinLen(2) [] data) { int n = data.length; if (n < 2) { @@ -107,7 +113,7 @@ public static double[] getOLSRegression(double[][] data) { * * @return The parameters. */ - public static double[] getOLSRegression(XYDataset data, int series) { + public static double @ArrayLen(2) [] getOLSRegression(XYDataset data, @NonNegative int series) { int n = data.getItemCount(series); if (n < 2) { @@ -150,7 +156,7 @@ public static double[] getOLSRegression(XYDataset data, int series) { * * @return The parameters. */ - public static double[] getPowerRegression(double[][] data) { + public static double @ArrayLen(2) [] getPowerRegression(double @MinLen(2) [] @MinLen(2) [] data) { int n = data.length; if (n < 2) { @@ -194,7 +200,7 @@ public static double[] getPowerRegression(double[][] data) { * * @return The parameters. */ - public static double[] getPowerRegression(XYDataset data, int series) { + public static double @ArrayLen(2) [] getPowerRegression(XYDataset data, @NonNegative int series) { int n = data.getItemCount(series); if (n < 2) { @@ -247,31 +253,34 @@ public static double[] getPowerRegression(XYDataset data, int series) { * * @since 1.0.14 */ - public static double[] getPolynomialRegression(XYDataset dataset, - int series, int order) { + public static double[] getPolynomialRegression(XYDataset dataset, + @NonNegative int series, @Positive int order) { Args.nullNotPermitted(dataset, "dataset"); int itemCount = dataset.getItemCount(series); if (itemCount < order + 1) { throw new IllegalArgumentException("Not enough data."); } - int validItems = 0; double[][] data = new double[2][itemCount]; + @SuppressWarnings("index") // validItems will only be used as a index if there is at least one item + @LTLengthOf(value={"data[0]","data[1]"}, offset={"0","0"}) @NonNegative int validItems = 0; for(int item = 0; item < itemCount; item++){ double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); if (!Double.isNaN(x) && !Double.isNaN(y)){ data[0][validItems] = x; data[1][validItems] = y; - validItems++; + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/219: validItems is incremented at most as often as item, which is a valid index + @LTLengthOf(value={"data[0]","data[1]"}, offset={"0","0"}) @NonNegative int validItemsTmp = validItems + 1; + validItems = validItemsTmp; } } if (validItems < order + 1) { throw new IllegalArgumentException("Not enough data."); } - int equations = order + 1; - int coefficients = order + 2; + @Positive int equations = order + 1; + @Positive int coefficients = order + 2; double[] result = new double[equations + 1]; - double[][] matrix = new double[equations][coefficients]; + double[] @MinLen(1) [] matrix = new double[equations][coefficients]; double sumX = 0.0; double sumY = 0.0; @@ -279,26 +288,34 @@ public static double[] getPolynomialRegression(XYDataset dataset, sumX += data[0][item]; sumY += data[1][item]; for(int eq = 0; eq < equations; eq++){ - for(int coe = 0; coe < coefficients - 1; coe++){ + for(int coe = 0; coe < matrix[eq].length - 1; coe++){ matrix[eq][coe] += Math.pow(data[0][item],eq + coe); } - matrix[eq][coefficients - 1] += data[1][item] + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/202: coefficients is positive, and matrix's subarrays are all exactly `coefficients` long + @IndexFor("matrix[eq]") int coe1 = coefficients - 1; + matrix[eq][coe1] += data[1][item] * Math.pow(data[0][item],eq); } } double[][] subMatrix = calculateSubMatrix(matrix); for (int eq = 1; eq < equations; eq++) { matrix[eq][0] = 0; - for (int coe = 1; coe < coefficients; coe++) { - matrix[eq][coe] = subMatrix[eq - 1][coe - 1]; + for (int coe = 1; coe < matrix[eq].length; coe++) { + @SuppressWarnings("index") // subMatrix is a reduced version of matrix, with rows and columns shifted so that subtracting one is always safe + double subMatrixEntry = subMatrix[eq - 1][coe - 1]; + matrix[eq][coe] = subMatrixEntry; } } for (int eq = equations - 1; eq > -1; eq--) { - double value = matrix[eq][coefficients - 1]; - for (int coe = eq; coe < coefficients -1; coe++) { - value -= matrix[eq][coe] * result[coe]; + double value = matrix[eq][matrix[eq].length - 1]; + for (int coe = eq; coe < matrix[eq].length -1; coe++) { + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/202: coe is LTOM for matrix[eq], which means that it is LTL for result[eq], which is one smaller in each dimension + double resultCoefficient = result[coe]; + value -= matrix[eq][coe] * resultCoefficient; } - result[eq] = value / matrix[eq][eq]; + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/158 equations < coeffecients (equations = order + 1, coeffecients = order + 2) + double diag = matrix[eq][eq]; + result[eq] = value / diag; } double meanY = sumY / validItems; double yObsSquare = 0.0; @@ -326,30 +343,41 @@ public static double[] getPolynomialRegression(XYDataset dataset, * * @return The new matrix. */ - private static double[][] calculateSubMatrix(double[][] matrix){ - int equations = matrix.length; - int coefficients = matrix[0].length; + private static double[][] calculateSubMatrix(double @MinLen(1) [] @MinLen(1) [] matrix){ + @Positive int equations = matrix.length; + @Positive int coefficients = matrix[0].length; double[][] result = new double[equations - 1][coefficients - 1]; for (int eq = 1; eq < equations; eq++) { double factor = matrix[0][0] / matrix[eq][0]; - for (int coe = 1; coe < coefficients; coe++) { - result[eq - 1][coe -1] = matrix[0][coe] - matrix[eq][coe] + // I added the second condition here - matrix is rectangular, and this is the easiest way to guarantee this typechecks + for (int coe = 1; coe < matrix[eq].length && coe < matrix[0].length; coe++) { + int resultEq = eq - 1; + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/202: result is one smaller in both dimensions than matrix, and coe is an index for matrix. Also note the use of a temporary here: the Index Checker's java expression parser chokes on "result[eq - 1]" + @IndexFor("result[resultEq]") int resultCoe = coe -1; + result[resultEq][resultCoe] = matrix[0][coe] - matrix[eq][coe] * factor; } } if (equations == 1) { return result; } + @SuppressWarnings({"value", "index"}) // https://github.com/kelloggm/checker-framework/issues/158: equations != 1 -> equations >= 2 -> result is minlen(1) + double @MinLen(1) [] @MinLen(1) [] result1 = result; + // check for zero pivot element - if (result[0][0] == 0) { + if (result1[0][0] == 0) { boolean found = false; - for (int i = 0; i < result.length; i ++) { - if (result[i][0] != 0) { + for (int i = 0; i < result1.length; i ++) { + if (result1[i][0] != 0) { found = true; - double[] temp = result[0]; - System.arraycopy(result[i], 0, result[0], 0, - result[i].length); - System.arraycopy(temp, 0, result[i], 0, temp.length); + double[] temp = result1[0]; + @SuppressWarnings("index") // result1 is a rectangular array + @IndexOrHigh({"result1[i]", "result1[0]"}) int result1ILength = result1[i].length; + System.arraycopy(result1[i], 0, result1[0], 0, + result1ILength); + @SuppressWarnings("index") // result1 is a rectangular array + @IndexOrHigh({"result1[i]", "temp", "result1[0]"}) int tempLen = temp.length; + System.arraycopy(temp, 0, result1[i], 0, tempLen); break; } } @@ -358,14 +386,16 @@ private static double[][] calculateSubMatrix(double[][] matrix){ return new double[equations - 1][coefficients - 1]; } } - double[][] subMatrix = calculateSubMatrix(result); + double[][] subMatrix = calculateSubMatrix(result1); for (int eq = 1; eq < equations - 1; eq++) { - result[eq][0] = 0; - for (int coe = 1; coe < coefficients - 1; coe++) { - result[eq][coe] = subMatrix[eq - 1][coe - 1]; + result1[eq][0] = 0; + for (int coe = 1; coe < result1[eq].length; coe++) { + @SuppressWarnings("index") // subMatrix is always one smaller than result1 + double submatrixresult1 = subMatrix[eq - 1][coe - 1]; + result1[eq][coe] = submatrixresult1; } } - return result; + return result1; } } diff --git a/src/main/java/org/jfree/data/statistics/SimpleHistogramBin.java b/src/main/java/org/jfree/data/statistics/SimpleHistogramBin.java index 3026b6d7a..ac347ff14 100644 --- a/src/main/java/org/jfree/data/statistics/SimpleHistogramBin.java +++ b/src/main/java/org/jfree/data/statistics/SimpleHistogramBin.java @@ -40,6 +40,8 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import org.jfree.chart.util.PublicCloneable; @@ -71,7 +73,7 @@ public class SimpleHistogramBin implements Comparable, private boolean includeUpperBound; /** The item count. */ - private int itemCount; + private @NonNegative int itemCount; /** * Creates a new bin. @@ -127,7 +129,7 @@ public double getUpperBound() { * * @return The item count. */ - public int getItemCount() { + public @NonNegative int getItemCount() { return this.itemCount; } @@ -136,7 +138,7 @@ public int getItemCount() { * * @param count the item count. */ - public void setItemCount(int count) { + public void setItemCount(@NonNegative int count) { this.itemCount = count; } diff --git a/src/main/java/org/jfree/data/statistics/SimpleHistogramDataset.java b/src/main/java/org/jfree/data/statistics/SimpleHistogramDataset.java index be6b95832..ccfed587b 100644 --- a/src/main/java/org/jfree/data/statistics/SimpleHistogramDataset.java +++ b/src/main/java/org/jfree/data/statistics/SimpleHistogramDataset.java @@ -43,6 +43,10 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -126,7 +130,7 @@ public void setAdjustForBinSize(boolean adjust) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return 1; } @@ -139,7 +143,7 @@ public int getSeriesCount() { * @return The key for the series. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { return this.key; } @@ -162,7 +166,7 @@ public DomainOrder getDomainOrder() { * @return The item count. */ @Override - public int getItemCount(int series) { + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return this.bins.size(); } @@ -282,7 +286,7 @@ public void removeAllBins() { * @return The x-value (never {@code null}). */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getXValue(series, item)); } @@ -295,7 +299,7 @@ public Number getX(int series, int item) { * @return The x-value. */ @Override - public double getXValue(int series, int item) { + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); return (bin.getLowerBound() + bin.getUpperBound()) / 2.0; } @@ -309,7 +313,7 @@ public double getXValue(int series, int item) { * @return The y-value (possibly {@code null}). */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getYValue(series, item)); } @@ -324,7 +328,7 @@ public Number getY(int series, int item) { * @see #getAdjustForBinSize() */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); if (this.adjustForBinSize) { return bin.getItemCount() @@ -344,7 +348,7 @@ public double getYValue(int series, int item) { * @return The value. */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getStartXValue(series, item)); } @@ -358,7 +362,7 @@ public Number getStartX(int series, int item) { * @return The start x-value. */ @Override - public double getStartXValue(int series, int item) { + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); return bin.getLowerBound(); } @@ -372,7 +376,7 @@ public double getStartXValue(int series, int item) { * @return The value. */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getEndXValue(series, item)); } @@ -386,7 +390,7 @@ public Number getEndX(int series, int item) { * @return The end x-value. */ @Override - public double getEndXValue(int series, int item) { + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); return bin.getUpperBound(); } @@ -400,7 +404,7 @@ public double getEndXValue(int series, int item) { * @return The value. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -414,7 +418,7 @@ public Number getStartY(int series, int item) { * @return The start y-value. */ @Override - public double getStartYValue(int series, int item) { + public double getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getYValue(series, item); } @@ -427,7 +431,7 @@ public double getStartYValue(int series, int item) { * @return The value. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -441,7 +445,7 @@ public Number getEndY(int series, int item) { * @return The end y-value. */ @Override - public double getEndYValue(int series, int item) { + public double getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getYValue(series, item); } diff --git a/src/main/java/org/jfree/data/statistics/StatisticalCategoryDataset.java b/src/main/java/org/jfree/data/statistics/StatisticalCategoryDataset.java index 7c9cb4921..c61fa4f5c 100644 --- a/src/main/java/org/jfree/data/statistics/StatisticalCategoryDataset.java +++ b/src/main/java/org/jfree/data/statistics/StatisticalCategoryDataset.java @@ -45,6 +45,8 @@ package org.jfree.data.statistics; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.category.CategoryDataset; /** @@ -61,7 +63,7 @@ public interface StatisticalCategoryDataset extends CategoryDataset { * * @return The mean value (possibly {@code null}). */ - public Number getMeanValue(int row, int column); + public Number getMeanValue(@NonNegative int row, @NonNegative int column); /** * Returns the mean value for an item. @@ -81,7 +83,7 @@ public interface StatisticalCategoryDataset extends CategoryDataset { * * @return The standard deviation (possibly {@code null}). */ - public Number getStdDevValue(int row, int column); + public Number getStdDevValue(@NonNegative int row, @NonNegative int column); /** * Returns the standard deviation value for an item. diff --git a/src/main/java/org/jfree/data/statistics/Statistics.java b/src/main/java/org/jfree/data/statistics/Statistics.java index 8a4eb34b1..5a6fdf517 100644 --- a/src/main/java/org/jfree/data/statistics/Statistics.java +++ b/src/main/java/org/jfree/data/statistics/Statistics.java @@ -49,6 +49,10 @@ package org.jfree.data.statistics; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -220,6 +224,7 @@ public static double calculateMedian(List values, boolean copyAndSort) { } } else { + @SuppressWarnings("index") // count is a positive even integer -> count >= 2 -> count / 2 - 1 is NN Number value1 = (Number) values.get(count / 2 - 1); Number value2 = (Number) values.get(count / 2); result = (value1.doubleValue() + value2.doubleValue()) @@ -240,7 +245,7 @@ public static double calculateMedian(List values, boolean copyAndSort) { * * @return The median. */ - public static double calculateMedian(List values, int start, int end) { + public static double calculateMedian(List values, @NonNegative @LessThan("#3 + 1") int start, @NonNegative int end) { return calculateMedian(values, start, end, true); } @@ -257,7 +262,7 @@ public static double calculateMedian(List values, int start, int end) { * * @return The median. */ - public static double calculateMedian(List values, int start, int end, + public static double calculateMedian(List values, @NonNegative @LessThan("#3 + 1") int start, @NonNegative int end, boolean copyAndSort) { double result = Double.NaN; @@ -284,6 +289,7 @@ public static double calculateMedian(List values, int start, int end, } } else { + @SuppressWarnings("index") // count is a positive even integer -> count >= 2 -> count / 2 - 1 is NN Number value1 = (Number) values.get(start + count / 2 - 1); Number value2 = (Number) values.get(start + count / 2); result @@ -303,7 +309,7 @@ public static double calculateMedian(List values, int start, int end, * * @return The standard deviation of a set of numbers. */ - public static double getStdDev(Number[] data) { + public static double getStdDev(Number @MinLen(1) [] data) { Args.nullNotPermitted(data, "data"); if (data.length == 0) { throw new IllegalArgumentException("Zero length 'data' array."); @@ -327,7 +333,7 @@ public static double getStdDev(Number[] data) { * * @return A double array with the intercept in [0] and the slope in [1]. */ - public static double[] getLinearFit(Number[] xData, Number[] yData) { + public static double @ArrayLen(2) [] getLinearFit(Number @SameLen("#2") [] xData, Number @SameLen("#1") [] yData) { Args.nullNotPermitted(xData, "xData"); Args.nullNotPermitted(yData, "yData"); @@ -354,7 +360,7 @@ public static double[] getLinearFit(Number[] xData, Number[] yData) { * * @return The slope. */ - public static double getSlope(Number[] xData, Number[] yData) { + public static double getSlope(Number @SameLen("#2") [] xData, Number @SameLen("#1") [] yData) { Args.nullNotPermitted(xData, "xData"); Args.nullNotPermitted(yData, "yData"); if (xData.length != yData.length) { @@ -376,7 +382,7 @@ public static double getSlope(Number[] xData, Number[] yData) { sx = sx + xData[counter].doubleValue(); sxx = sxx + Math.pow(xData[counter].doubleValue(), 2); sxy = sxy + yData[counter].doubleValue() - * xData[counter].doubleValue(); + * xData[counter].doubleValue(); sy = sy + yData[counter].doubleValue(); } return (sxy - (sx * sy) / counter) / (sxx - (sx * sx) / counter); @@ -396,7 +402,7 @@ public static double getSlope(Number[] xData, Number[] yData) { * * @return The correlation. */ - public static double getCorrelation(Number[] data1, Number[] data2) { + public static double getCorrelation(Number @SameLen("#2") [] data1, Number @SameLen("#1") [] data2) { Args.nullNotPermitted(data1, "data1"); Args.nullNotPermitted(data2, "data2"); if (data1.length != data2.length) { @@ -439,8 +445,8 @@ public static double getCorrelation(Number[] data1, Number[] data2) { * @return A double[][] the length of the data set in the first dimension, * with two doubles for x and y in the second dimension */ - public static double[][] getMovingAverage(Number[] xData, Number[] yData, - int period) { + public static double[] @ArrayLen(2) [] getMovingAverage(Number @SameLen("#2") [] xData, Number @SameLen("#1") [] yData, + @IndexFor("#1") int period) { // check arguments... if (xData.length != yData.length) { @@ -452,13 +458,18 @@ public static double[][] getMovingAverage(Number[] xData, Number[] yData, "Period can't be longer than dataset."); } - double[][] result = new double[xData.length - period][2]; + @NonNegative int resultLen = xData.length - period; + double[] @ArrayLen(2) [] result = new double[resultLen][2]; for (int i = 0; i < result.length; i++) { - result[i][0] = xData[i + period].doubleValue(); + @SuppressWarnings("index") // result's length is exactly xData.length - period, so adding period to an index for result is always an index for xData + @IndexFor("xData") int iPeriod = i + period; + result[i][0] = xData[iPeriod].doubleValue(); // holds the moving average sum double sum = 0.0; for (int j = 0; j < period; j++) { - sum += yData[i + j].doubleValue(); + @SuppressWarnings("index") // result's length is exactly yData.length - period, so adding a nonnegative value less than period to an index for result is always an index for yData + @IndexFor("yData") int ij = i + j; + sum += yData[ij].doubleValue(); } sum = sum / period; result[i][1] = sum; diff --git a/src/main/java/org/jfree/data/time/Day.java b/src/main/java/org/jfree/data/time/Day.java index fd31a2361..77e1a5f42 100644 --- a/src/main/java/org/jfree/data/time/Day.java +++ b/src/main/java/org/jfree/data/time/Day.java @@ -65,6 +65,9 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import java.text.DateFormat; import java.text.ParseException; @@ -126,7 +129,7 @@ public Day() { * @param month the month (1 to 12). * @param year the year (1900 <= year <= 9999). */ - public Day(int day, int month, int year) { + public Day(@IntRange(from = 1, to = 31) int day, @IntRange(from = 1, to = 12) int month, @IntRange(from = 1900, to = 9999) int year) { this.serialDate = SerialDate.createInstance(day, month, year); peg(Calendar.getInstance()); } @@ -162,6 +165,7 @@ public Day(Date time) { * @param zone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Day(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); @@ -171,7 +175,8 @@ public Day(Date time, TimeZone zone, Locale locale) { int d = calendar.get(Calendar.DAY_OF_MONTH); int m = calendar.get(Calendar.MONTH) + 1; int y = calendar.get(Calendar.YEAR); - this.serialDate = SerialDate.createInstance(d, m, y); + SerialDate tmpDate = SerialDate.createInstance(d, m, y); + this.serialDate = tmpDate; peg(calendar); } @@ -193,7 +198,7 @@ public SerialDate getSerialDate() { * * @return The year. */ - public int getYear() { + public @IntRange(from=1900, to=9999) int getYear() { return this.serialDate.getYYYY(); } @@ -202,7 +207,7 @@ public int getYear() { * * @return The month. */ - public int getMonth() { + public @IntRange(from = 1, to = 12) int getMonth() { return this.serialDate.getMonth(); } @@ -211,7 +216,7 @@ public int getMonth() { * * @return The day of the month. */ - public int getDayOfMonth() { + public @IntRange(from = 1, to = 31) int getDayOfMonth() { return this.serialDate.getDayOfMonth(); } diff --git a/src/main/java/org/jfree/data/time/DynamicTimeSeriesCollection.java b/src/main/java/org/jfree/data/time/DynamicTimeSeriesCollection.java index 288c573f2..3d759cd05 100644 --- a/src/main/java/org/jfree/data/time/DynamicTimeSeriesCollection.java +++ b/src/main/java/org/jfree/data/time/DynamicTimeSeriesCollection.java @@ -62,6 +62,12 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Calendar; import java.util.TimeZone; @@ -104,22 +110,22 @@ public class DynamicTimeSeriesCollection extends AbstractIntervalXYDataset public static final int END = 2; /** The maximum number of items for each series (can be overridden). */ - private int maximumItemCount = 2000; // an arbitrary safe default value + private @NonNegative int maximumItemCount = 2000; // an arbitrary safe default value /** The history count. */ - protected int historyCount; + protected @LTEqLengthOf("this.pointsInTime") @Positive int historyCount; /** Storage for the series keys. */ - private Comparable[] seriesKeys; + private Comparable @SameLen("this.valueHistory") [] seriesKeys; /** The time period class - barely used, and could be removed (DG). */ private Class timePeriodClass = Minute.class; // default value; /** Storage for the x-values. */ - protected RegularTimePeriod[] pointsInTime; + protected RegularTimePeriod @MinLen(1) [] pointsInTime; /** The number of series. */ - private int seriesCount; + private @NonNegative int seriesCount; /** * A wrapper for a fixed array of float values. @@ -127,7 +133,7 @@ public class DynamicTimeSeriesCollection extends AbstractIntervalXYDataset protected class ValueSequence { /** Storage for the float values. */ - float[] dataPoints; + float @SameLen("this") [] dataPoints; /** * Default constructor: @@ -141,8 +147,10 @@ public ValueSequence() { * * @param length the length. */ - public ValueSequence(int length) { - this.dataPoints = new float[length]; + public ValueSequence(@NonNegative int length) { + @SuppressWarnings("index") // SameLen on custom collections requires a suppressed warning to establish representation invariant https://github.com/kelloggm/checker-framework/issues/213 + float @SameLen("this") [] dataPointsTmp = new float[length]; + this.dataPoints = dataPointsTmp; for (int i = 0; i < length; i++) { this.dataPoints[i] = 0.0f; } @@ -154,7 +162,7 @@ public ValueSequence(int length) { * @param index the index. * @param value the value. */ - public void enterData(int index, float value) { + public void enterData(@IndexFor("this") int index, float value) { this.dataPoints[index] = value; } @@ -165,13 +173,13 @@ public void enterData(int index, float value) { * * @return The value. */ - public float getData(int index) { + public float getData(@IndexFor("this") int index) { return this.dataPoints[index]; } } /** An array for storing the objects that represent each series. */ - protected ValueSequence[] valueHistory; + protected @SameLen("this.pointsInTime") ValueSequence @SameLen("this.seriesKeys") [] valueHistory; /** A working calendar (to recycle) */ protected Calendar workingCalendar; @@ -190,10 +198,10 @@ public float getData(int index) { private boolean domainIsPointsInTime; /** index for mapping: points to the oldest valid time and data. */ - private int oldestAt; // as a class variable, initializes == 0 + private @IndexFor("this.pointsInTime") int oldestAt; // as a class variable, initializes == 0 /** Index of the newest data item. */ - private int newestAt; + private @IndexFor("this.pointsInTime") int newestAt; // cached values used for interface DomainInfo: @@ -228,9 +236,11 @@ public float getData(int index) { * @param nSeries the number of series to be accommodated. * @param nMoments the number of TimePeriods to be spanned. */ - public DynamicTimeSeriesCollection(int nSeries, int nMoments) { + public DynamicTimeSeriesCollection(@NonNegative int nSeries, @Positive int nMoments) { this(nSeries, nMoments, new Millisecond(), TimeZone.getDefault()); - this.newestAt = nMoments - 1; + @SuppressWarnings("index") // nMoments is used later to populate the field this annotation refers to + @IndexFor("this.pointsInTime") int newestAtTmp = nMoments - 1; + this.newestAt = newestAtTmp; } /** @@ -240,10 +250,12 @@ public DynamicTimeSeriesCollection(int nSeries, int nMoments) { * @param nMoments the number of TimePeriods to be spanned * @param zone the timezone. */ - public DynamicTimeSeriesCollection(int nSeries, int nMoments, + public DynamicTimeSeriesCollection(@NonNegative int nSeries, @Positive int nMoments, TimeZone zone) { this(nSeries, nMoments, new Millisecond(), zone); - this.newestAt = nMoments - 1; + @SuppressWarnings("index") // nMoments is used later to populate the field this annotation refers to + @IndexFor("this.pointsInTime") int newestAtTmp = nMoments - 1; + this.newestAt = newestAtTmp; } /** @@ -253,7 +265,7 @@ public DynamicTimeSeriesCollection(int nSeries, int nMoments, * @param nMoments the number of items per series. * @param timeSample a time period sample. */ - public DynamicTimeSeriesCollection(int nSeries, int nMoments, + public DynamicTimeSeriesCollection(@NonNegative int nSeries, @Positive int nMoments, RegularTimePeriod timeSample) { this(nSeries, nMoments, timeSample, TimeZone.getDefault()); } @@ -266,7 +278,8 @@ public DynamicTimeSeriesCollection(int nSeries, int nMoments, * @param timeSample a time period sample. * @param zone the time zone. */ - public DynamicTimeSeriesCollection(int nSeries, int nMoments, + @SuppressWarnings("index") // this constructor establishes the repr. invariants + public DynamicTimeSeriesCollection(@NonNegative int nSeries, @Positive int nMoments, RegularTimePeriod timeSample, TimeZone zone) { // the first initialization must precede creation of the ValueSet array: @@ -277,7 +290,6 @@ public DynamicTimeSeriesCollection(int nSeries, int nMoments, for (int i = 0; i < nSeries; i++) { this.seriesKeys[i] = ""; } - this.newestAt = nMoments - 1; this.valueHistory = new ValueSequence[nSeries]; this.timePeriodClass = timeSample.getClass(); @@ -291,6 +303,9 @@ public DynamicTimeSeriesCollection(int nSeries, int nMoments, } else if (this.timePeriodClass == Hour.class) { this.pointsInTime = new Hour[nMoments]; } + + this.newestAt = nMoments - 1; + /// .. etc.... this.workingCalendar = Calendar.getInstance(zone); this.position = START; @@ -317,6 +332,7 @@ public synchronized long setTimeBase(RegularTimePeriod start) { } long oldestL = this.pointsInTime[0].getFirstMillisecond( this.workingCalendar); + @SuppressWarnings("index") // This seems like a bug to me. There's nothing in the docs for this class that suggests that pointsInTime must have more than 1 element long nextL = this.pointsInTime[1].getFirstMillisecond( this.workingCalendar); this.deltaTime = nextL - oldestL; @@ -373,9 +389,8 @@ public void setPosition(int position) { * Use this as-is during setup only, or add the synchronized keyword around * the copy loop. */ - public void addSeries(float[] values, int seriesNumber, + public void addSeries(float @LengthOf("this.historyCount")[] values, @NonNegative int seriesNumber, Comparable seriesKey) { - invalidateRangeInfo(); int i; if (values == null) { @@ -387,17 +402,20 @@ public void addSeries(float[] values, int seriesNumber, + "cannot add more series than specified in c'tor"); } if (this.valueHistory[seriesNumber] == null) { - this.valueHistory[seriesNumber] - = new ValueSequence(this.historyCount); + @SuppressWarnings("index") // this.historyCount is the length of this.pointsInTime + @SameLen("this.pointsInTime") ValueSequence valueSequence = new ValueSequence(this.historyCount); + this.valueHistory[seriesNumber] = valueSequence; this.seriesCount++; } // But if that series array already exists, just overwrite its contents + @SuppressWarnings("index") // https://github.com/kelloggm/checker-framework/issues/158: this.valueHistory[seriesNumber]'s length is <= this.historyCount + @IndexFor({"this.valueHistory[seriesNumber]","values"}) int historyCount = this.historyCount; // Avoid IndexOutOfBoundsException: int srcLength = values.length; - int copyLength = this.historyCount; + int copyLength = historyCount; boolean fillNeeded = false; - if (srcLength < this.historyCount) { + if (srcLength < historyCount) { fillNeeded = true; copyLength = srcLength; } @@ -407,7 +425,7 @@ public void addSeries(float[] values, int seriesNumber, this.valueHistory[seriesNumber].enterData(i, values[i]); } if (fillNeeded) { - for (i = copyLength; i < this.historyCount; i++) { + for (i = copyLength; i < historyCount; i++) { this.valueHistory[seriesNumber].enterData(i, 0.0f); } } @@ -424,7 +442,7 @@ public void addSeries(float[] values, int seriesNumber, * @param seriesNumber the series. * @param key the new key. */ - public void setSeriesKey(int seriesNumber, Comparable key) { + public void setSeriesKey(@IndexFor("this.seriesKeys") int seriesNumber, Comparable key) { this.seriesKeys[seriesNumber] = key; } @@ -435,7 +453,7 @@ public void setSeriesKey(int seriesNumber, Comparable key) { * @param index ??. * @param value the value. */ - public void addValue(int seriesNumber, int index, float value) { + public void addValue(@NonNegative int seriesNumber, @IndexFor("this.getSeries(#1)") int index, float value) { invalidateRangeInfo(); if (seriesNumber >= this.valueHistory.length) { throw new IllegalArgumentException( @@ -444,14 +462,17 @@ public void addValue(int seriesNumber, int index, float value) { ); } if (this.valueHistory[seriesNumber] == null) { - this.valueHistory[seriesNumber] - = new ValueSequence(this.historyCount); + @SuppressWarnings("index") // this.historyCount is the length of this.pointsInTime + @SameLen("this.pointsInTime") ValueSequence valueSequence = new ValueSequence(this.historyCount); + this.valueHistory[seriesNumber] = valueSequence; this.seriesCount++; } // But if that series array already exists, just overwrite its contents //synchronized(this) //{ - this.valueHistory[seriesNumber].enterData(index, value); + @SuppressWarnings("index") // String array bug https://github.com/kelloggm/checker-framework/issues/205 + @LTLengthOf("this.valueHistory[seriesNumber]") int index0 = index; + this.valueHistory[seriesNumber].enterData(index0, value); //} fireSeriesChanged(); } @@ -462,7 +483,8 @@ public void addValue(int seriesNumber, int index, float value) { * @return The series count. */ @Override - public int getSeriesCount() { + @SuppressWarnings("index") + public @NonNegative @LTEqLengthOf("this.valueHistory") int getSeriesCount() { return this.seriesCount; } @@ -476,7 +498,8 @@ public int getSeriesCount() { * @return The item count. */ @Override - public int getItemCount(int series) { // all arrays equal length, + @SuppressWarnings("index") // SameLen to a method with any argument https://github.com/kelloggm/checker-framework/issues/209: I'd like to write SameLen on this.pointsInTime + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { // all arrays equal length, // so ignore argument: return this.historyCount; } @@ -487,10 +510,12 @@ public int getItemCount(int series) { // all arrays equal length, * Re-map an index, for use in retrieving data. * * @param toFetch the index. + * @param series a ghost variable necessary for the annotations * * @return The translated index. */ - protected int translateGet(int toFetch) { + @SuppressWarnings("index") // internal method that breaks abstraction boundaries + protected @IndexFor("this.pointsInTime") int translateGet(@IndexFor("this.getSeries(#2)") int toFetch, int series) { if (this.oldestAt == 0) { return toFetch; // no translation needed } @@ -509,7 +534,7 @@ protected int translateGet(int toFetch) { * * @return The offset. */ - public int offsetFromNewest(int delta) { + public @IndexFor("this.pointsInTime") int offsetFromNewest(int delta) { return wrapOffset(this.newestAt + delta); } @@ -520,7 +545,7 @@ public int offsetFromNewest(int delta) { * * @return The offset. */ - public int offsetFromOldest(int delta) { + public @IndexFor("this.pointsInTime") int offsetFromOldest(int delta) { return wrapOffset(this.oldestAt + delta); } @@ -531,7 +556,8 @@ public int offsetFromOldest(int delta) { * * @return The offset. */ - protected int wrapOffset(int protoIndex) { + @SuppressWarnings("index") // this method assumes that protoIndex will be within an absolute value of an actual index + protected @IndexFor("this.pointsInTime") int wrapOffset(int protoIndex) { int tmp = protoIndex; if (tmp >= this.historyCount) { tmp -= this.historyCount; @@ -564,7 +590,9 @@ public synchronized RegularTimePeriod advanceTime() { oldMax = this.maxValue.floatValue(); } for (int s = 0; s < getSeriesCount(); s++) { - if (this.valueHistory[s].getData(this.oldestAt) == oldMax) { + @SuppressWarnings("index") // String array bug https://github.com/kelloggm/checker-framework/issues/205 + @LTLengthOf("this.valueHistory[s]") int oldestAt = this.oldestAt; + if (this.valueHistory[s].getData(oldestAt) == oldMax) { extremaChanged = true; } if (extremaChanged) { @@ -577,15 +605,22 @@ public synchronized RegularTimePeriod advanceTime() { // wipe the next (about to be used) set of data slots float wiper = (float) 0.0; for (int s = 0; s < getSeriesCount(); s++) { - this.valueHistory[s].enterData(this.newestAt, wiper); + @SuppressWarnings("index") // String array bug https://github.com/kelloggm/checker-framework/issues/205 + @LTLengthOf("this.valueHistory[s]") int newestAt = this.newestAt; + this.valueHistory[s].enterData(newestAt, wiper); } // Update the array of TimePeriods: this.pointsInTime[this.newestAt] = nextInstant; // Now advance "oldestAt", wrapping at end of the array - this.oldestAt++; + int newOldestAt; if (this.oldestAt >= this.historyCount) { - this.oldestAt = 0; + newOldestAt = 0; + } else { + newOldestAt = this.oldestAt + 1; } + @SuppressWarnings("index") // the check right above ensures this doesn't go out of bounds + @IndexFor("this.pointsInTime") int tmp = newOldestAt; + this.oldestAt = tmp; // Update the domain limits: long startL = this.domainStart.longValue(); //(time is kept in msec) this.domainStart = new Long(startL + this.deltaTime); @@ -615,7 +650,9 @@ protected double findMaxValue() { double max = 0.0f; for (int s = 0; s < getSeriesCount(); s++) { for (int i = 0; i < this.historyCount; i++) { - double tmp = getYValue(s, i); + @SuppressWarnings("index") // String array bug https://github.com/kelloggm/checker-framework/issues/205 + @LTLengthOf("this.getSeries(s)") int i0 = i; + double tmp = getYValue(s, i0); if (tmp > max) { max = tmp; } @@ -631,7 +668,7 @@ protected double findMaxValue() { * * @return The index. */ - public int getOldestIndex() { + public @IndexFor("this.pointsInTime") int getOldestIndex() { return this.oldestAt; } @@ -640,7 +677,7 @@ public int getOldestIndex() { * * @return The index. */ - public int getNewestIndex() { + public @IndexFor("this.pointsInTime") int getNewestIndex() { return this.newestAt; } @@ -662,9 +699,13 @@ public void appendData(float[] newData) { // check whether the "valueHistory" array member exists; if not, // create them: if (this.valueHistory[s] == null) { - this.valueHistory[s] = new ValueSequence(this.historyCount); + @SuppressWarnings("index") // this.historyCount is the length of this.pointsInTime + @SameLen("this.pointsInTime") ValueSequence valueSequence = new ValueSequence(this.historyCount); + this.valueHistory[s] = valueSequence; } - this.valueHistory[s].enterData(this.newestAt, newData[s]); + @SuppressWarnings("index") // String array bug https://github.com/kelloggm/checker-framework/issues/205 + @LTLengthOf("this.valueHistory[s]") int newestAt = this.newestAt; + this.valueHistory[s].enterData(newestAt, newData[s]); } fireSeriesChanged(); } @@ -677,7 +718,7 @@ public void appendData(float[] newData) { * @param refresh value of n in "refresh the display on every nth call" * (ignored if <= 0 ) */ - public void appendData(float[] newData, int insertionIndex, int refresh) { + public void appendData(float[] newData, final @IndexFor("this.pointsInTime") int insertionIndex, int refresh) { int nDataPoints = newData.length; if (nDataPoints > this.valueHistory.length) { throw new IllegalArgumentException( @@ -685,13 +726,17 @@ public void appendData(float[] newData, int insertionIndex, int refresh) { } for (int s = 0; s < nDataPoints; s++) { if (this.valueHistory[s] == null) { - this.valueHistory[s] = new ValueSequence(this.historyCount); + @SuppressWarnings("index") // this.historyCount is the length of this.pointsInTime + @SameLen("this.pointsInTime") ValueSequence valueSequence = new ValueSequence(this.historyCount); + this.valueHistory[s] = valueSequence; } - this.valueHistory[s].enterData(insertionIndex, newData[s]); + @SuppressWarnings("index") // String array bug https://github.com/kelloggm/checker-framework/issues/205 + @LTLengthOf("this.valueHistory[s]") int insertionIndex0 = insertionIndex; + this.valueHistory[s].enterData(insertionIndex0, newData[s]); } if (refresh > 0) { - insertionIndex++; - if (insertionIndex % refresh == 0) { + int insertionIndexPlus = insertionIndex + 1; + if (insertionIndexPlus % refresh == 0) { fireSeriesChanged(); } } @@ -726,8 +771,8 @@ public RegularTimePeriod getOldestTime() { // getXxx() ftns can ignore the "series" argument: // Don't synchronize this!! Instead, synchronize the loop that calls it. @Override - public Number getX(int series, int item) { - RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + RegularTimePeriod tp = this.pointsInTime[translateGet(item, series)]; return new Long(getX(tp)); } @@ -740,11 +785,12 @@ public Number getX(int series, int item) { * @return The value. */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { // Don't synchronize this!! // Instead, synchronize the loop that calls it. + @SuppressWarnings("index") // array-list interop: every other class that implements these interfaces uses a list to store series, so the annotation here is wrong ValueSequence values = this.valueHistory[series]; - return values.getData(translateGet(item)); + return values.getData(translateGet(item, series)); } /** @@ -756,7 +802,7 @@ public double getYValue(int series, int item) { * @return The value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Float(getYValue(series, item)); } @@ -769,8 +815,8 @@ public Number getY(int series, int item) { * @return The value. */ @Override - public Number getStartX(int series, int item) { - RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + RegularTimePeriod tp = this.pointsInTime[translateGet(item, series)]; return new Long(tp.getFirstMillisecond(this.workingCalendar)); } @@ -783,8 +829,8 @@ public Number getStartX(int series, int item) { * @return The value. */ @Override - public Number getEndX(int series, int item) { - RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + RegularTimePeriod tp = this.pointsInTime[translateGet(item, series)]; return new Long(tp.getLastMillisecond(this.workingCalendar)); } @@ -797,7 +843,7 @@ public Number getEndX(int series, int item) { * @return The value. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -810,17 +856,17 @@ public Number getStartY(int series, int item) { * @return The value. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } /* // "Extras" found useful when analyzing/verifying class behavior: - public Number getUntranslatedXValue(int series, int item) + public Number getUntranslatedXValue(int series, @NonNegative int item) { return super.getXValue(series, item); } - public float getUntranslatedY(int series, int item) + public float getUntranslatedY(int series, @NonNegative int item) { return super.getY(series, item); } */ @@ -833,7 +879,8 @@ public float getUntranslatedY(int series, int item) * @return The key. */ @Override - public Comparable getSeriesKey(int series) { + @SuppressWarnings("index") // array-list interop: every other class that implements this interface backs the series with a list, so the annotation on this class don't correspond to the ones on the interface + public Comparable getSeriesKey(@IndexFor("this.seriesKeys") int series) { return this.seriesKeys[series]; } diff --git a/src/main/java/org/jfree/data/time/Hour.java b/src/main/java/org/jfree/data/time/Hour.java index 9048ed7d9..53df9796f 100644 --- a/src/main/java/org/jfree/data/time/Hour.java +++ b/src/main/java/org/jfree/data/time/Hour.java @@ -66,6 +66,8 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; + import java.io.Serializable; import java.util.Calendar; import java.util.Date; @@ -92,7 +94,7 @@ public class Hour extends RegularTimePeriod implements Serializable { private Day day; /** The hour. */ - private byte hour; + @IntRange(from = 0, to = 23) private byte hour; /** The first millisecond. */ private long firstMillisecond; @@ -113,7 +115,7 @@ public Hour() { * @param hour the hour (in the range 0 to 23). * @param day the day ({@code null} not permitted). */ - public Hour(int hour, Day day) { + public Hour(@IntRange(from = 0, to = 23) int hour, Day day) { Args.nullNotPermitted(day, "day"); this.hour = (byte) hour; this.day = day; @@ -128,7 +130,7 @@ public Hour(int hour, Day day) { * @param month the month (1-12). * @param year the year (1900-9999). */ - public Hour(int hour, int day, int month, int year) { + public Hour(@IntRange(from = 0, to = 23) int hour, @IntRange(from = 1, to = 31) int day, @IntRange(from = 1, to = 12) int month, @IntRange(from = 1900, to = 9999) int year) { this(hour, new Day(day, month, year)); } @@ -155,6 +157,7 @@ public Hour(Date time) { * * @since 1.0.13 */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Hour(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); @@ -171,7 +174,7 @@ public Hour(Date time, TimeZone zone, Locale locale) { * * @return The hour (0 <= hour <= 23). */ - public int getHour() { + public @IntRange(from = 0, to = 23) int getHour() { return this.hour; } @@ -189,7 +192,7 @@ public Day getDay() { * * @return The year. */ - public int getYear() { + public @IntRange(from = 1900, to = 9999) int getYear() { return this.day.getYear(); } @@ -198,7 +201,7 @@ public int getYear() { * * @return The month. */ - public int getMonth() { + public @IntRange(from = 1, to = 12) int getMonth() { return this.day.getMonth(); } @@ -207,7 +210,7 @@ public int getMonth() { * * @return The day-of-the-month. */ - public int getDayOfMonth() { + public @IntRange(from = 1, to = 31) int getDayOfMonth() { return this.day.getDayOfMonth(); } diff --git a/src/main/java/org/jfree/data/time/Millisecond.java b/src/main/java/org/jfree/data/time/Millisecond.java index 864f0d3ca..beeba0933 100644 --- a/src/main/java/org/jfree/data/time/Millisecond.java +++ b/src/main/java/org/jfree/data/time/Millisecond.java @@ -60,6 +60,8 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; + import java.io.Serializable; import java.util.Calendar; import java.util.Date; @@ -85,16 +87,16 @@ public class Millisecond extends RegularTimePeriod implements Serializable { private Day day; /** The hour in the day. */ - private byte hour; + @IntRange(from = 0, to = 23) private byte hour; /** The minute. */ - private byte minute; + @IntRange(from = 0, to = 59) private byte minute; /** The second. */ - private byte second; + @IntRange(from = 0, to = 59) private byte second; /** The millisecond. */ - private int millisecond; + @IntRange(from = 0, to = 999) private int millisecond; /** * The pegged millisecond. @@ -114,7 +116,7 @@ public Millisecond() { * @param millisecond the millisecond (0-999). * @param second the second. */ - public Millisecond(int millisecond, Second second) { + public Millisecond(@IntRange(from = 0, to = 999) int millisecond, Second second) { this.millisecond = millisecond; this.second = (byte) second.getSecond(); this.minute = (byte) second.getMinute().getMinute(); @@ -134,8 +136,8 @@ public Millisecond(int millisecond, Second second) { * @param month the month (1-12). * @param year the year (1900-9999). */ - public Millisecond(int millisecond, int second, int minute, int hour, - int day, int month, int year) { + public Millisecond(@IntRange(from = 0, to = 999) int millisecond, @IntRange(from = 0, to = 59) int second, @IntRange(from = 0, to = 59) int minute, @IntRange(from = 0, to = 23) int hour, + @IntRange(from = 1, to = 31) int day, @IntRange(from = 1, to = 12) int month, @IntRange(from = 1900, to = 9999) int year) { this(millisecond, new Second(second, minute, hour, day, month, year)); @@ -161,6 +163,7 @@ public Millisecond(Date time) { * * @since 1.0.13 */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Millisecond(Date time, TimeZone zone, Locale locale) { Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); diff --git a/src/main/java/org/jfree/data/time/Minute.java b/src/main/java/org/jfree/data/time/Minute.java index 1e7d2316b..0801251e6 100644 --- a/src/main/java/org/jfree/data/time/Minute.java +++ b/src/main/java/org/jfree/data/time/Minute.java @@ -67,6 +67,8 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; + import java.io.Serializable; import java.util.Calendar; import java.util.Date; @@ -93,10 +95,10 @@ public class Minute extends RegularTimePeriod implements Serializable { private Day day; /** The hour in which the minute falls. */ - private byte hour; + @IntRange(from = 0, to = 23) private byte hour; /** The minute. */ - private byte minute; + @IntRange(from = 0, to = 59) private byte minute; /** The first millisecond. */ private long firstMillisecond; @@ -117,7 +119,7 @@ public Minute() { * @param minute the minute (0 to 59). * @param hour the hour ({@code null} not permitted). */ - public Minute(int minute, Hour hour) { + public Minute(@IntRange(from = 0, to = 59) int minute, Hour hour) { Args.nullNotPermitted(hour, "hour"); this.minute = (byte) minute; this.hour = (byte) hour.getHour(); @@ -147,6 +149,7 @@ public Minute(Date time) { * * @since 1.0.13 */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Minute(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); @@ -169,7 +172,7 @@ public Minute(Date time, TimeZone zone, Locale locale) { * @param month the month (1-12). * @param year the year (1900-9999). */ - public Minute(int minute, int hour, int day, int month, int year) { + public Minute(@IntRange(from = 0, to = 59) int minute, @IntRange(from = 0, to = 23) int hour, @IntRange(from = 1, to = 31) int day, @IntRange(from = 1, to = 12) int month, @IntRange(from = 1900, to = 9999) int year) { this(minute, new Hour(hour, new Day(day, month, year))); } @@ -200,7 +203,7 @@ public Hour getHour() { * * @since 1.0.3 */ - public int getHourValue() { + public @IntRange(from = 0, to = 23) int getHourValue() { return this.hour; } @@ -209,7 +212,7 @@ public int getHourValue() { * * @return The minute. */ - public int getMinute() { + public @IntRange(from = 0, to = 59) int getMinute() { return this.minute; } diff --git a/src/main/java/org/jfree/data/time/Month.java b/src/main/java/org/jfree/data/time/Month.java index cc0c30f6b..76e1ffeaa 100644 --- a/src/main/java/org/jfree/data/time/Month.java +++ b/src/main/java/org/jfree/data/time/Month.java @@ -66,6 +66,8 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; + import java.io.Serializable; import java.util.Calendar; import java.util.Date; @@ -84,10 +86,10 @@ public class Month extends RegularTimePeriod implements Serializable { private static final long serialVersionUID = -5090216912548722570L; /** The month (1-12). */ - private int month; + private @IntRange(from = 1, to = 12) int month; /** The year in which the month falls. */ - private int year; + private @IntRange(from = 0, to = 9999) int year; /** The first millisecond. */ private long firstMillisecond; @@ -108,7 +110,7 @@ public Month() { * @param month the month (in the range 1 to 12). * @param year the year. */ - public Month(int month, int year) { + public Month(@IntRange(from = 1, to = 12) int month, @IntRange(from = 0, to = 9999) int year) { if ((month < 1) || (month > 12)) { throw new IllegalArgumentException("Month outside valid range."); } @@ -123,7 +125,7 @@ public Month(int month, int year) { * @param month the month (in the range 1 to 12). * @param year the year. */ - public Month(int month, Year year) { + public Month(@IntRange(from = 1, to = 12) int month, Year year) { if ((month < 1) || (month > 12)) { throw new IllegalArgumentException("Month outside valid range."); } @@ -154,6 +156,7 @@ public Month(Date time) { * * @since 1.0.12 */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Month(Date time, TimeZone zone, Locale locale) { Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); @@ -439,6 +442,7 @@ public long getLastMillisecond(Calendar calendar) { * @return {@code null} if the string is not parseable, the month * otherwise. */ + @SuppressWarnings({"value", "index"}) // This method repeatedly assumes properties of the String s, especially that it is "wellformed" according to the documentation (i.e. either YYYY-MM or MM-YYYY) public static Month parseMonth(String s) { Month result = null; if (s == null) { diff --git a/src/main/java/org/jfree/data/time/MovingAverage.java b/src/main/java/org/jfree/data/time/MovingAverage.java index c45ab2e49..075bb74c4 100644 --- a/src/main/java/org/jfree/data/time/MovingAverage.java +++ b/src/main/java/org/jfree/data/time/MovingAverage.java @@ -50,6 +50,11 @@ package org.jfree.data.time; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; @@ -74,7 +79,7 @@ public class MovingAverage { * @return A collection of moving average time series. */ public static TimeSeriesCollection createMovingAverage( - TimeSeriesCollection source, String suffix, int periodCount, + TimeSeriesCollection source, String suffix, @Positive int periodCount, int skip) { Args.nullNotPermitted(source, "source"); @@ -108,7 +113,7 @@ public static TimeSeriesCollection createMovingAverage( * @return The moving average series. */ public static TimeSeries createMovingAverage(TimeSeries source, - String name, int periodCount, int skip) { + String name, @Positive int periodCount, int skip) { Args.nullNotPermitted(source, "source"); if (periodCount < 1) { @@ -140,9 +145,10 @@ public static TimeSeries createMovingAverage(TimeSeries source, boolean finished = false; while ((offset < periodCount) && (!finished)) { - if ((i - offset) >= 0) { + int itemIndex = i - offset; + if (itemIndex >= 0) { TimeSeriesDataItem item = source.getRawDataItem( - i - offset); + itemIndex); RegularTimePeriod p = item.getPeriod(); Number v = item.getValue(); long currentIndex = p.getSerialIndex(); @@ -189,7 +195,7 @@ public static TimeSeries createMovingAverage(TimeSeries source, * @return The moving average series. */ public static TimeSeries createPointMovingAverage(TimeSeries source, - String name, int pointCount) { + String name, @Positive int pointCount) { Args.nullNotPermitted(source, "source"); if (pointCount < 2) { @@ -206,15 +212,16 @@ public static TimeSeries createPointMovingAverage(TimeSeries source, // FIXME: what if value is null on next line? rollingSumForPeriod += current.getValue().doubleValue(); - if (i > pointCount - 1) { + int startIndex = i - pointCount; + if (startIndex >= 0) { // remove the point i-periodCount out of the rolling sum. TimeSeriesDataItem startOfMovingAvg = source.getRawDataItem( - i - pointCount); + startIndex); rollingSumForPeriod -= startOfMovingAvg.getValue() .doubleValue(); result.add(period, rollingSumForPeriod / pointCount); } - else if (i == pointCount - 1) { + else if (startIndex == - 1) { result.add(period, rollingSumForPeriod / pointCount); } } @@ -280,7 +287,7 @@ public static XYDataset createMovingAverage(XYDataset source, * @return The dataset. */ public static XYSeries createMovingAverage(XYDataset source, - int series, String name, double period, double skip) { + @NonNegative int series, String name, double period, double skip) { Args.nullNotPermitted(source, "source"); if (period < Double.MIN_VALUE) { @@ -296,6 +303,7 @@ public static XYSeries createMovingAverage(XYDataset source, // if the initial averaging period is to be excluded, then // calculate the lowest x-value to have an average calculated... + @SuppressWarnings("index") // item count was checked just above to be greater than zero, so zero is an index double first = source.getXValue(series, 0) + skip; for (int i = source.getItemCount(series) - 1; i >= 0; i--) { @@ -312,9 +320,10 @@ public static XYSeries createMovingAverage(XYDataset source, boolean finished = false; while (!finished) { - if ((i - offset) >= 0) { - double xx = source.getXValue(series, i - offset); - Number yy = source.getY(series, i - offset); + int ioff = i - offset; + if (ioff >= 0) { + double xx = source.getXValue(series, ioff); + Number yy = source.getY(series, ioff); if (xx > limit) { if (yy != null) { sum = sum + yy.doubleValue(); diff --git a/src/main/java/org/jfree/data/time/Quarter.java b/src/main/java/org/jfree/data/time/Quarter.java index 2097a0d4d..5f867d4b9 100644 --- a/src/main/java/org/jfree/data/time/Quarter.java +++ b/src/main/java/org/jfree/data/time/Quarter.java @@ -61,6 +61,9 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import java.util.Calendar; import java.util.Date; @@ -86,22 +89,22 @@ public class Quarter extends RegularTimePeriod implements Serializable { public static final int LAST_QUARTER = 4; /** The first month in each quarter. */ - public static final int[] FIRST_MONTH_IN_QUARTER = { + public static final int @ArrayLen(5) [] FIRST_MONTH_IN_QUARTER = { 0, MonthConstants.JANUARY, MonthConstants.APRIL, MonthConstants.JULY, MonthConstants.OCTOBER }; /** The last month in each quarter. */ - public static final int[] LAST_MONTH_IN_QUARTER = { + public static final @IntRange(from = 0, to = 12) int @ArrayLen(5) [] LAST_MONTH_IN_QUARTER = { 0, MonthConstants.MARCH, MonthConstants.JUNE, MonthConstants.SEPTEMBER, MonthConstants.DECEMBER }; /** The year in which the quarter falls. */ - private short year; + @IntRange(from = 0, to = 9999) private short year; /** The quarter (1-4). */ - private byte quarter; + private @IntVal({1,2,3,4}) byte quarter; /** The first millisecond. */ private long firstMillisecond; @@ -122,7 +125,7 @@ public Quarter() { * @param year the year (1900 to 9999). * @param quarter the quarter (1 to 4). */ - public Quarter(int quarter, int year) { + public Quarter(@IntVal({1,2,3,4}) int quarter, @IntRange(from=0, to = 9999)int year) { if ((quarter < FIRST_QUARTER) || (quarter > LAST_QUARTER)) { throw new IllegalArgumentException("Quarter outside valid range."); } @@ -137,7 +140,7 @@ public Quarter(int quarter, int year) { * @param quarter the quarter (1 to 4). * @param year the year (1900 to 9999). */ - public Quarter(int quarter, Year year) { + public Quarter(@IntVal({1,2,3,4}) int quarter, Year year) { if ((quarter < FIRST_QUARTER) || (quarter > LAST_QUARTER)) { throw new IllegalArgumentException("Quarter outside valid range."); } @@ -168,6 +171,7 @@ public Quarter(Date time) { * * @since 1.0.12 */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Quarter(Date time, TimeZone zone, Locale locale) { Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); @@ -182,7 +186,7 @@ public Quarter(Date time, TimeZone zone, Locale locale) { * * @return The quarter. */ - public int getQuarter() { + public @IntVal({1,2,3,4}) int getQuarter() { return this.quarter; } @@ -202,7 +206,7 @@ public Year getYear() { * * @since 1.0.3 */ - public int getYearValue() { + public @IntRange(from = -9999, to = 9999) int getYearValue() { return this.year; } @@ -418,7 +422,8 @@ public String toString() { */ @Override public long getFirstMillisecond(Calendar calendar) { - int month = Quarter.FIRST_MONTH_IN_QUARTER[this.quarter]; + @SuppressWarnings("index") // month >= 1, since months are 1 to 12. this.quarter is 1 to 4, so it can't access the 0th value in FIRST_MONTH_IN_QUARTER, which is the only non positive value + @Positive int month = Quarter.FIRST_MONTH_IN_QUARTER[this.quarter]; calendar.set(this.year, month - 1, 1, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTimeInMillis(); @@ -437,7 +442,8 @@ public long getFirstMillisecond(Calendar calendar) { */ @Override public long getLastMillisecond(Calendar calendar) { - int month = Quarter.LAST_MONTH_IN_QUARTER[this.quarter]; + @SuppressWarnings({"index", "value"}) // this.quarter is always 1, 2, 3, or 4 - and only the 0th element of this array is out of the range 1-12 + @IntRange(from = 1, to = 12) int month = Quarter.LAST_MONTH_IN_QUARTER[this.quarter]; int eom = SerialDate.lastDayOfMonth(month, this.year); calendar.set(this.year, month - 1, eom, 23, 59, 59); calendar.set(Calendar.MILLISECOND, 999); @@ -453,6 +459,7 @@ public long getLastMillisecond(Calendar calendar) { * * @return The quarter. */ + @SuppressWarnings({"index", "value"}) // parse method relies on string being properly formatted public static Quarter parseQuarter(String s) { // find the Q and the integer following it (remove both from the diff --git a/src/main/java/org/jfree/data/time/RegularTimePeriod.java b/src/main/java/org/jfree/data/time/RegularTimePeriod.java index a396266bf..2f7f473ff 100644 --- a/src/main/java/org/jfree/data/time/RegularTimePeriod.java +++ b/src/main/java/org/jfree/data/time/RegularTimePeriod.java @@ -54,6 +54,8 @@ package org.jfree.data.time; +import org.checkerframework.dataflow.qual.Pure; + import java.lang.reflect.Constructor; import java.util.Calendar; import java.util.Date; @@ -177,6 +179,7 @@ else if (c.equals(Second.class)) { * @see #getFirstMillisecond() */ @Override + @Pure public Date getStart() { return new Date(getFirstMillisecond()); } diff --git a/src/main/java/org/jfree/data/time/Second.java b/src/main/java/org/jfree/data/time/Second.java index 47beeb956..14f2e29b0 100644 --- a/src/main/java/org/jfree/data/time/Second.java +++ b/src/main/java/org/jfree/data/time/Second.java @@ -62,6 +62,8 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; + import java.io.Serializable; import java.util.Calendar; import java.util.Date; @@ -88,13 +90,13 @@ public class Second extends RegularTimePeriod implements Serializable { private Day day; /** The hour of the day. */ - private byte hour; + @IntRange(from = 0, to = 23) private byte hour; /** The minute. */ - private byte minute; + @IntRange(from = 0, to = 59) private byte minute; /** The second. */ - private byte second; + @IntRange(from = 0, to = 59) private byte second; /** * The first millisecond. We don't store the last millisecond, because it @@ -115,7 +117,7 @@ public Second() { * @param second the second (0 to 59). * @param minute the minute ({@code null} not permitted). */ - public Second(int second, Minute minute) { + public Second(@IntRange(from = 0, to = 59) int second, Minute minute) { Args.requireInRange(second, "second", Second.FIRST_SECOND_IN_MINUTE, Second.LAST_SECOND_IN_MINUTE); Args.nullNotPermitted(minute, "minute"); @@ -136,8 +138,8 @@ public Second(int second, Minute minute) { * @param month the month (1-12). * @param year the year (1900-9999). */ - public Second(int second, int minute, int hour, - int day, int month, int year) { + public Second(@IntRange(from = 0, to = 59) int second, @IntRange(from = 0, to = 59) int minute, @IntRange(from = 0, to = 23) int hour, + @IntRange(from = 1, to = 31) int day, @IntRange(from = 1, to = 12) int month, @IntRange(from = 1900, to = 9999) int year) { this(second, new Minute(minute, hour, day, month, year)); } @@ -162,6 +164,7 @@ public Second(Date time) { * * @since 1.0.13 */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Second(Date time, TimeZone zone, Locale locale) { Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); @@ -177,7 +180,7 @@ public Second(Date time, TimeZone zone, Locale locale) { * * @return The second (0 - 59). */ - public int getSecond() { + public @IntRange(from = 0, to = 59) int getSecond() { return this.second; } diff --git a/src/main/java/org/jfree/data/time/SimpleTimePeriod.java b/src/main/java/org/jfree/data/time/SimpleTimePeriod.java index 9e081a1c5..d4e3769da 100644 --- a/src/main/java/org/jfree/data/time/SimpleTimePeriod.java +++ b/src/main/java/org/jfree/data/time/SimpleTimePeriod.java @@ -46,6 +46,8 @@ package org.jfree.data.time; +import org.checkerframework.dataflow.qual.Pure; + import java.io.Serializable; import java.util.Date; @@ -97,6 +99,7 @@ public SimpleTimePeriod(Date start, Date end) { * @return The start date/time (never {@code null}). */ @Override + @Pure public Date getStart() { return new Date(this.start); } diff --git a/src/main/java/org/jfree/data/time/TimePeriod.java b/src/main/java/org/jfree/data/time/TimePeriod.java index f1b1a7ec7..3bbdc37e9 100644 --- a/src/main/java/org/jfree/data/time/TimePeriod.java +++ b/src/main/java/org/jfree/data/time/TimePeriod.java @@ -42,6 +42,8 @@ package org.jfree.data.time; +import org.checkerframework.dataflow.qual.Pure; + import java.util.Date; /** @@ -56,6 +58,7 @@ public interface TimePeriod extends Comparable { * * @return The start date/time (never {@code null}). */ + @Pure public Date getStart(); /** diff --git a/src/main/java/org/jfree/data/time/TimePeriodValue.java b/src/main/java/org/jfree/data/time/TimePeriodValue.java index b403f9fb6..9615d9b2b 100644 --- a/src/main/java/org/jfree/data/time/TimePeriodValue.java +++ b/src/main/java/org/jfree/data/time/TimePeriodValue.java @@ -43,6 +43,8 @@ package org.jfree.data.time; +import org.checkerframework.dataflow.qual.Pure; + import java.io.Serializable; import org.jfree.chart.util.Args; @@ -91,6 +93,7 @@ public TimePeriodValue(TimePeriod period, double value) { * * @return The time period (never {@code null}). */ + @Pure public TimePeriod getPeriod() { return this.period; } diff --git a/src/main/java/org/jfree/data/time/TimePeriodValues.java b/src/main/java/org/jfree/data/time/TimePeriodValues.java index a9119bebd..daa6d1fd5 100644 --- a/src/main/java/org/jfree/data/time/TimePeriodValues.java +++ b/src/main/java/org/jfree/data/time/TimePeriodValues.java @@ -48,6 +48,11 @@ package org.jfree.data.time; +import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.checker.index.qual.GTENegativeOne; +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.index.qual.LessThan; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -87,22 +92,22 @@ public class TimePeriodValues extends Series implements Serializable { private List data; /** Index of the time period with the minimum start milliseconds. */ - private int minStartIndex = -1; + private @GTENegativeOne int minStartIndex = -1; /** Index of the time period with the maximum start milliseconds. */ - private int maxStartIndex = -1; + private @GTENegativeOne int maxStartIndex = -1; /** Index of the time period with the minimum middle milliseconds. */ - private int minMiddleIndex = -1; + private @GTENegativeOne int minMiddleIndex = -1; /** Index of the time period with the maximum middle milliseconds. */ - private int maxMiddleIndex = -1; + private @GTENegativeOne int maxMiddleIndex = -1; /** Index of the time period with the minimum end milliseconds. */ - private int minEndIndex = -1; + private @GTENegativeOne int minEndIndex = -1; /** Index of the time period with the maximum end milliseconds. */ - private int maxEndIndex = -1; + private @GTENegativeOne int maxEndIndex = -1; /** * Creates a new (empty) collection of time period values. @@ -189,7 +194,7 @@ public void setRangeDescription(String description) { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.data.size(); } @@ -201,7 +206,8 @@ public int getItemCount() { * * @return One data item for the series. */ - public TimePeriodValue getDataItem(int index) { + @Pure + public TimePeriodValue getDataItem(@NonNegative int index) { return (TimePeriodValue) this.data.get(index); } @@ -215,7 +221,7 @@ public TimePeriodValue getDataItem(int index) { * * @see #getDataItem(int) */ - public TimePeriod getTimePeriod(int index) { + public TimePeriod getTimePeriod(@NonNegative int index) { return getDataItem(index).getPeriod(); } @@ -229,7 +235,7 @@ public TimePeriod getTimePeriod(int index) { * * @see #getDataItem(int) */ - public Number getValue(int index) { + public Number getValue(@NonNegative int index) { return getDataItem(index).getValue(); } @@ -242,7 +248,9 @@ public Number getValue(int index) { public void add(TimePeriodValue item) { Args.nullNotPermitted(item, "item"); this.data.add(item); - updateBounds(item.getPeriod(), this.data.size() - 1); + @SuppressWarnings("index") // data.size is positive because we just added an item to data on the previous line + @NonNegative int lastIndex = this.data.size() - 1; + updateBounds(item.getPeriod(), lastIndex); fireSeriesChanged(); } @@ -252,7 +260,7 @@ public void add(TimePeriodValue item) { * @param period the time period. * @param index the index of the time period. */ - private void updateBounds(TimePeriod period, int index) { + private void updateBounds(TimePeriod period, @NonNegative int index) { long start = period.getStart().getTime(); long end = period.getEnd().getTime(); @@ -279,11 +287,13 @@ private void updateBounds(TimePeriod period, int index) { else { this.maxStartIndex = index; } - - if (this.minMiddleIndex >= 0) { - long s = getDataItem(this.minMiddleIndex).getPeriod().getStart() + + // don't use field directly to satisfy typechecker that field is not side-effected + int minMiddleIndex = this.minMiddleIndex; + if (minMiddleIndex >= 0) { + long s = getDataItem(minMiddleIndex).getPeriod().getStart() .getTime(); - long e = getDataItem(this.minMiddleIndex).getPeriod().getEnd() + long e = getDataItem(minMiddleIndex).getPeriod().getEnd() .getTime(); long minMiddle = s + (e - s) / 2; if (middle < minMiddle) { @@ -293,11 +303,13 @@ private void updateBounds(TimePeriod period, int index) { else { this.minMiddleIndex = index; } - - if (this.maxMiddleIndex >= 0) { - long s = getDataItem(this.maxMiddleIndex).getPeriod().getStart() + + // don't use field directly to satisfy typechecker that field is not side-effected + int maxMiddleIndex = this.maxMiddleIndex; + if (maxMiddleIndex >= 0) { + long s = getDataItem(maxMiddleIndex).getPeriod().getStart() .getTime(); - long e = getDataItem(this.maxMiddleIndex).getPeriod().getEnd() + long e = getDataItem(maxMiddleIndex).getPeriod().getEnd() .getTime(); long maxMiddle = s + (e - s) / 2; if (middle > maxMiddle) { @@ -381,7 +393,7 @@ public void add(TimePeriod period, Number value) { * @param index the index of the data item to update. * @param value the new value ({@code null} not permitted). */ - public void update(int index, Number value) { + public void update(@NonNegative int index, Number value) { TimePeriodValue item = getDataItem(index); item.setValue(value); fireSeriesChanged(); @@ -394,7 +406,7 @@ public void update(int index, Number value) { * @param start the index of the first period to delete. * @param end the index of the last period to delete. */ - public void delete(int start, int end) { + public void delete(@NonNegative @LessThan("#2 + 1") int start, @NonNegative int end) { for (int i = 0; i <= (end - start); i++) { this.data.remove(start); } @@ -493,7 +505,7 @@ public Object clone() throws CloneNotSupportedException { * * @throws CloneNotSupportedException if there is a cloning problem. */ - public TimePeriodValues createCopy(int start, int end) + public TimePeriodValues createCopy(@NonNegative int start, @GTENegativeOne int end) throws CloneNotSupportedException { TimePeriodValues copy = (TimePeriodValues) super.clone(); @@ -520,7 +532,7 @@ public TimePeriodValues createCopy(int start, int end) * * @return The index. */ - public int getMinStartIndex() { + public @GTENegativeOne int getMinStartIndex() { return this.minStartIndex; } @@ -529,7 +541,7 @@ public int getMinStartIndex() { * * @return The index. */ - public int getMaxStartIndex() { + public @GTENegativeOne int getMaxStartIndex() { return this.maxStartIndex; } @@ -539,7 +551,7 @@ public int getMaxStartIndex() { * * @return The index. */ - public int getMinMiddleIndex() { + public @GTENegativeOne int getMinMiddleIndex() { return this.minMiddleIndex; } @@ -549,7 +561,7 @@ public int getMinMiddleIndex() { * * @return The index. */ - public int getMaxMiddleIndex() { + public @GTENegativeOne int getMaxMiddleIndex() { return this.maxMiddleIndex; } @@ -558,7 +570,7 @@ public int getMaxMiddleIndex() { * * @return The index. */ - public int getMinEndIndex() { + public @GTENegativeOne int getMinEndIndex() { return this.minEndIndex; } @@ -567,7 +579,7 @@ public int getMinEndIndex() { * * @return The index. */ - public int getMaxEndIndex() { + public @GTENegativeOne int getMaxEndIndex() { return this.maxEndIndex; } diff --git a/src/main/java/org/jfree/data/time/TimePeriodValuesCollection.java b/src/main/java/org/jfree/data/time/TimePeriodValuesCollection.java index aef830e63..8e0b5632a 100644 --- a/src/main/java/org/jfree/data/time/TimePeriodValuesCollection.java +++ b/src/main/java/org/jfree/data/time/TimePeriodValuesCollection.java @@ -50,6 +50,11 @@ package org.jfree.data.time; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Iterator; import java.util.List; @@ -135,7 +140,7 @@ public void setXPosition(TimePeriodAnchor position) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -146,7 +151,7 @@ public int getSeriesCount() { * * @return The series. */ - public TimePeriodValues getSeries(int series) { + public TimePeriodValues getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Index 'series' out of range."); } @@ -161,7 +166,7 @@ public TimePeriodValues getSeries(int series) { * @return The key for a series. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // defer argument checking return getSeries(series).getKey(); } @@ -198,7 +203,7 @@ public void removeSeries(TimePeriodValues series) { * * @param index the series index (zero-based). */ - public void removeSeries(int index) { + public void removeSeries(@NonNegative int index) { TimePeriodValues series = getSeries(index); if (series != null) { removeSeries(series); @@ -215,7 +220,8 @@ public void removeSeries(int index) { * @return The number of items in the specified series. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: getSeries(series).getItemCount is LengthOf(this.getSeries) + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return getSeries(series).getItemCount(); } @@ -228,7 +234,7 @@ public int getItemCount(int series) { * @return The x-value for the specified series and item. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimePeriodValues ts = (TimePeriodValues) this.data.get(series); TimePeriodValue dp = ts.getDataItem(item); TimePeriod period = dp.getPeriod(); @@ -269,7 +275,7 @@ else if (this.xPosition == TimePeriodAnchor.END) { * @return The starting X value for the specified series and item. */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimePeriodValues ts = (TimePeriodValues) this.data.get(series); TimePeriodValue dp = ts.getDataItem(item); return new Long(dp.getPeriod().getStart().getTime()); @@ -284,7 +290,7 @@ public Number getStartX(int series, int item) { * @return The ending X value for the specified series and item. */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimePeriodValues ts = (TimePeriodValues) this.data.get(series); TimePeriodValue dp = ts.getDataItem(item); return new Long(dp.getPeriod().getEnd().getTime()); @@ -299,7 +305,7 @@ public Number getEndX(int series, int item) { * @return The y-value for the specified series and item. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimePeriodValues ts = (TimePeriodValues) this.data.get(series); TimePeriodValue dp = ts.getDataItem(item); return dp.getValue(); @@ -314,7 +320,7 @@ public Number getY(int series, int item) { * @return The starting Y value for the specified series and item. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -327,7 +333,7 @@ public Number getStartY(int series, int item) { * @return The ending Y value for the specified series and item. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -376,6 +382,7 @@ public double getDomainUpperBound(boolean includeInterval) { * @return The range. */ @Override + @SuppressWarnings("index") // guaranteed index: every call to getXIndex in this method (there are a lot) is guarded by the if (count > 0) check, which ensures that those values can't be negative. public Range getDomainBounds(boolean includeInterval) { boolean interval = includeInterval; Range result = null; diff --git a/src/main/java/org/jfree/data/time/TimeSeries.java b/src/main/java/org/jfree/data/time/TimeSeries.java index 3a2da9e13..216ac82f3 100644 --- a/src/main/java/org/jfree/data/time/TimeSeries.java +++ b/src/main/java/org/jfree/data/time/TimeSeries.java @@ -91,6 +91,10 @@ package org.jfree.data.time; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -141,7 +145,7 @@ public class TimeSeries extends Series implements Cloneable, Serializable { protected List data; /** The maximum number of items for the series. */ - private int maximumItemCount; + private @NonNegative int maximumItemCount; /** * The maximum age of items for the series, specified as a number of @@ -256,7 +260,7 @@ public void setRangeDescription(String description) { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.data.size(); } @@ -279,7 +283,7 @@ public List getItems() { * * @see #setMaximumItemCount(int) */ - public int getMaximumItemCount() { + public @NonNegative int getMaximumItemCount() { return this.maximumItemCount; } @@ -294,14 +298,18 @@ public int getMaximumItemCount() { * * @see #getMaximumItemCount() */ - public void setMaximumItemCount(int maximum) { + public void setMaximumItemCount(@NonNegative int maximum) { if (maximum < 0) { throw new IllegalArgumentException("Negative 'maximum' argument."); } this.maximumItemCount = maximum; int count = this.data.size(); if (count > maximum) { - delete(0, count - maximum - 1); + @SuppressWarnings({"index","value"}) // https://github.com/kelloggm/checker-framework/issues/158 + @IntRange(from=0) int deleteIndex = count - maximum - 1; + // Extra assignment to kill the dataflow refinement. + deleteIndex = deleteIndex; + delete(0, deleteIndex); } } @@ -476,7 +484,7 @@ public Class getTimePeriodClass() { * * @return The data item. */ - public TimeSeriesDataItem getDataItem(int index) { + public TimeSeriesDataItem getDataItem(@NonNegative int index) { TimeSeriesDataItem item = (TimeSeriesDataItem) this.data.get(index); return (TimeSeriesDataItem) item.clone(); } @@ -514,7 +522,7 @@ public TimeSeriesDataItem getDataItem(RegularTimePeriod period) { * * @since 1.0.14 */ - TimeSeriesDataItem getRawDataItem(int index) { + TimeSeriesDataItem getRawDataItem(@NonNegative int index) { return (TimeSeriesDataItem) this.data.get(index); } @@ -546,7 +554,7 @@ TimeSeriesDataItem getRawDataItem(RegularTimePeriod period) { * * @return The time period. */ - public RegularTimePeriod getTimePeriod(int index) { + public RegularTimePeriod getTimePeriod(@NonNegative int index) { return getRawDataItem(index).getPeriod(); } @@ -557,6 +565,7 @@ public RegularTimePeriod getTimePeriod(int index) { * @return The next time period. */ public RegularTimePeriod getNextTimePeriod() { + @SuppressWarnings("index") // bug: requires there to be at least one time period, but documentation doesn't state it RegularTimePeriod last = getTimePeriod(getItemCount() - 1); return last.next(); } @@ -616,7 +625,7 @@ public int getIndex(RegularTimePeriod period) { * * @return The value (possibly {@code null}). */ - public Number getValue(int index) { + public Number getValue(@NonNegative int index) { return getRawDataItem(index).getValue(); } @@ -679,7 +688,7 @@ else if (!this.timePeriodClass.equals(c)) { added = true; } else { - RegularTimePeriod last = getTimePeriod(getItemCount() - 1); + RegularTimePeriod last = getTimePeriod(count - 1); if (item.getPeriod().compareTo(last) > 0) { this.data.add(item); added = true; @@ -687,7 +696,9 @@ else if (!this.timePeriodClass.equals(c)) { else { int index = Collections.binarySearch(this.data, item); if (index < 0) { - this.data.add(-index - 1, item); + @SuppressWarnings("index") // binary search on list, which SearchIndex does not handle because lists are not fixed-size data structure + @NonNegative int index1 = -index - 1; + this.data.add(index1, item); added = true; } else { @@ -809,7 +820,7 @@ public void update(RegularTimePeriod period, Number value) { * @param index the index of the data item. * @param value the new value ({@code null} permitted). */ - public void update(int index, Number value) { + public void update(@NonNegative int index, Number value) { TimeSeriesDataItem item = (TimeSeriesDataItem) this.data.get(index); boolean iterate = false; Number oldYN = item.getValue(); @@ -936,7 +947,9 @@ else if (item.getValue() != null) { } else { item = (TimeSeriesDataItem) item.clone(); - this.data.add(-index - 1, item); + @SuppressWarnings("index") // binary search on list, which SearchIndex does not handle because lists are not fixed-size data structure + @NonNegative int index1 = -index - 1; + this.data.add(index1, item); updateBoundsForAddedItem(item); // check if this addition will exceed the maximum item count... @@ -964,8 +977,9 @@ else if (item.getValue() != null) { public void removeAgedItems(boolean notify) { // check if there are any values earlier than specified by the history // count... - if (getItemCount() > 1) { - long latest = getTimePeriod(getItemCount() - 1).getSerialIndex(); + int count = getItemCount(); + if (count > 1) { + long latest = getTimePeriod(count - 1).getSerialIndex(); boolean removed = false; while ((latest - getTimePeriod(0).getSerialIndex()) > this.maximumItemAge) { @@ -1073,7 +1087,7 @@ public void delete(RegularTimePeriod period) { * @param start the index of the first period to delete. * @param end the index of the last period to delete. */ - public void delete(int start, int end) { + public void delete(@NonNegative @LessThan("#2 + 1") int start, @NonNegative int end) { delete(start, end, true); } @@ -1086,7 +1100,7 @@ public void delete(int start, int end) { * * @since 1.0.14 */ - public void delete(int start, int end, boolean notify) { + public void delete(@NonNegative @LessThan("#2 + 1") int start, @NonNegative int end, boolean notify) { if (end < start) { throw new IllegalArgumentException("Requires start <= end."); } @@ -1136,7 +1150,7 @@ public Object clone() throws CloneNotSupportedException { * * @throws CloneNotSupportedException if there is a cloning problem. */ - public TimeSeries createCopy(int start, int end) + public TimeSeries createCopy(@NonNegative int start, @NonNegative int end) throws CloneNotSupportedException { if (start < 0) { throw new IllegalArgumentException("Requires start >= 0."); @@ -1207,6 +1221,10 @@ public TimeSeries createCopy(RegularTimePeriod start, RegularTimePeriod end) copy.data = new java.util.ArrayList(); return copy; } + @SuppressWarnings("index") // if endIndex < 0, emptyRange is true, so the function returns before we get here. + @NonNegative int endIndex1 = endIndex; + endIndex = endIndex1; + return createCopy(startIndex, endIndex); } diff --git a/src/main/java/org/jfree/data/time/TimeSeriesCollection.java b/src/main/java/org/jfree/data/time/TimeSeriesCollection.java index 05e502888..dca8b64df 100644 --- a/src/main/java/org/jfree/data/time/TimeSeriesCollection.java +++ b/src/main/java/org/jfree/data/time/TimeSeriesCollection.java @@ -1,3 +1,4 @@ + /* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== @@ -88,6 +89,11 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; @@ -238,7 +244,7 @@ public List getSeries() { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -252,7 +258,7 @@ public int getSeriesCount() { * * @since 1.0.6 */ - public int indexOf(TimeSeries series) { + public @GTENegativeOne int indexOf(TimeSeries series) { Args.nullNotPermitted(series, "series"); return this.data.indexOf(series); } @@ -264,7 +270,7 @@ public int indexOf(TimeSeries series) { * * @return The series. */ - public TimeSeries getSeries(int series) { + public TimeSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException( "The 'series' argument is out of bounds (" + series + ")."); @@ -301,7 +307,7 @@ public TimeSeries getSeries(Comparable key) { * @return The key for a series. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // check arguments...delegated // fetch the series name... return getSeries(series).getKey(); @@ -317,7 +323,7 @@ public Comparable getSeriesKey(int series) { * * @since 1.0.17 */ - public int getSeriesIndex(Comparable key) { + public @GTENegativeOne int getSeriesIndex(Comparable key) { Args.nullNotPermitted(key, "key"); int seriesCount = getSeriesCount(); for (int i = 0; i < seriesCount; i++) { @@ -362,7 +368,7 @@ public void removeSeries(TimeSeries series) { * * @param index the series index (zero-based). */ - public void removeSeries(int index) { + public void removeSeries(@NonNegative int index) { TimeSeries series = getSeries(index); if (series != null) { removeSeries(series); @@ -398,7 +404,8 @@ public void removeAllSeries() { * @return The item count. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: getSeries(series).getItemCount is LengthOf(this.getSeries) + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return getSeries(series).getItemCount(); } @@ -411,7 +418,7 @@ public int getItemCount(int series) { * @return The x-value. */ @Override - public double getXValue(int series, int item) { + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimeSeries s = (TimeSeries) this.data.get(series); RegularTimePeriod period = s.getTimePeriod(item); return getX(period); @@ -426,7 +433,7 @@ public double getXValue(int series, int item) { * @return The value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimeSeries ts = (TimeSeries) this.data.get(series); RegularTimePeriod period = ts.getTimePeriod(item); return new Long(getX(period)); @@ -462,7 +469,7 @@ else if (this.xPosition == TimePeriodAnchor.END) { * @return The value. */ @Override - public synchronized Number getStartX(int series, int item) { + public synchronized Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimeSeries ts = (TimeSeries) this.data.get(series); return new Long(ts.getTimePeriod(item).getFirstMillisecond( this.workingCalendar)); @@ -477,7 +484,7 @@ public synchronized Number getStartX(int series, int item) { * @return The value. */ @Override - public synchronized Number getEndX(int series, int item) { + public synchronized Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimeSeries ts = (TimeSeries) this.data.get(series); return new Long(ts.getTimePeriod(item).getLastMillisecond( this.workingCalendar)); @@ -492,7 +499,7 @@ public synchronized Number getEndX(int series, int item) { * @return The value (possibly {@code null}). */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimeSeries ts = (TimeSeries) this.data.get(series); return ts.getValue(item); } @@ -506,7 +513,7 @@ public Number getY(int series, int item) { * @return The value (possibly {@code null}). */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -519,7 +526,7 @@ public Number getStartY(int series, int item) { * @return The value (possibly {@code null}). */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -534,10 +541,9 @@ public Number getEndY(int series, int item) { * @return An array containing the (two) indices of the items surrounding * the time. */ - public int[] getSurroundingItems(int series, long milliseconds) { + public int @ArrayLen(2) [] getSurroundingItems(@NonNegative int series, long milliseconds) { int[] result = new int[] {-1, -1}; - TimeSeries timeSeries = getSeries(series); - for (int i = 0; i < timeSeries.getItemCount(); i++) { + for (int i = 0; i < this.getItemCount(series); i++) { Number x = getX(series, i); long m = x.longValue(); if (m <= milliseconds) { diff --git a/src/main/java/org/jfree/data/time/TimeSeriesTableModel.java b/src/main/java/org/jfree/data/time/TimeSeriesTableModel.java index b1e1ec33b..dd302a4b9 100644 --- a/src/main/java/org/jfree/data/time/TimeSeriesTableModel.java +++ b/src/main/java/org/jfree/data/time/TimeSeriesTableModel.java @@ -43,6 +43,8 @@ package org.jfree.data.time; +import org.checkerframework.checker.index.qual.NonNegative; + import javax.swing.table.AbstractTableModel; import org.jfree.data.general.SeriesChangeEvent; @@ -102,7 +104,7 @@ public TimeSeriesTableModel(TimeSeries series, boolean editable) { * @return The column count. */ @Override - public int getColumnCount() { + public @NonNegative int getColumnCount() { return 2; } @@ -114,7 +116,7 @@ public int getColumnCount() { * @return The column class in the table model. */ @Override - public Class getColumnClass(int column) { + public Class getColumnClass(@NonNegative int column) { if (column == 0) { return String.class; } @@ -136,7 +138,7 @@ public Class getColumnClass(int column) { * @return The name of a column. */ @Override - public String getColumnName(int column) { + public String getColumnName(@NonNegative int column) { if (column == 0) { return "Period:"; @@ -158,7 +160,7 @@ public String getColumnName(int column) { * @return The row count. */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { return this.series.getItemCount(); } @@ -171,7 +173,7 @@ public int getRowCount() { * @return The data value for a cell in the table model. */ @Override - public Object getValueAt(int row, int column) { + public Object getValueAt(@NonNegative int row, @NonNegative int column) { if (row < this.series.getItemCount()) { if (column == 0) { @@ -211,7 +213,7 @@ public Object getValueAt(int row, int column) { * @return {@code true} if the specified cell is editable. */ @Override - public boolean isCellEditable(int row, int column) { + public boolean isCellEditable(@NonNegative int row, @NonNegative int column) { if (this.editable) { if ((column == 0) || (column == 1)) { return true; @@ -233,7 +235,7 @@ public boolean isCellEditable(int row, int column) { * @param column the column. */ @Override - public void setValueAt(Object value, int row, int column) { + public void setValueAt(Object value, @NonNegative int row, @NonNegative int column) { if (row < this.series.getItemCount()) { diff --git a/src/main/java/org/jfree/data/time/TimeTableXYDataset.java b/src/main/java/org/jfree/data/time/TimeTableXYDataset.java index 0cb32d047..4acf4d87a 100644 --- a/src/main/java/org/jfree/data/time/TimeTableXYDataset.java +++ b/src/main/java/org/jfree/data/time/TimeTableXYDataset.java @@ -57,6 +57,11 @@ package org.jfree.data.time; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Calendar; import java.util.List; import java.util.Locale; @@ -298,7 +303,7 @@ public void clear() { * * @return The time period. */ - public TimePeriod getTimePeriod(int item) { + public TimePeriod getTimePeriod(@NonNegative int item) { return (TimePeriod) this.values.getRowKey(item); } @@ -308,7 +313,7 @@ public TimePeriod getTimePeriod(int item) { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.values.getRowCount(); } @@ -322,7 +327,8 @@ public int getItemCount() { * @return The number of items within the series. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: series is ignored and the underlying rep here is a list + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return getItemCount(); } @@ -332,7 +338,7 @@ public int getItemCount(int series) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.values.getColumnCount(); } @@ -344,7 +350,7 @@ public int getSeriesCount() { * @return The key for the series. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { return this.values.getColumnKey(series); } @@ -359,7 +365,7 @@ public Comparable getSeriesKey(int series) { * @return The x-value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getXValue(series, item)); } @@ -372,7 +378,7 @@ public Number getX(int series, int item) { * @return The value. */ @Override - public double getXValue(int series, int item) { + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimePeriod period = (TimePeriod) this.values.getRowKey(item); return getXValue(period); } @@ -388,7 +394,7 @@ public double getXValue(int series, int item) { * @see #getStartXValue(int, int) */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getStartXValue(series, item)); } @@ -402,7 +408,7 @@ public Number getStartX(int series, int item) { * @return The value. */ @Override - public double getStartXValue(int series, int item) { + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimePeriod period = (TimePeriod) this.values.getRowKey(item); return period.getStart().getTime(); } @@ -418,7 +424,7 @@ public double getStartXValue(int series, int item) { * @see #getEndXValue(int, int) */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getEndXValue(series, item)); } @@ -432,7 +438,7 @@ public Number getEndX(int series, int item) { * @return The value. */ @Override - public double getEndXValue(int series, int item) { + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { TimePeriod period = (TimePeriod) this.values.getRowKey(item); return period.getEnd().getTime(); } @@ -446,7 +452,7 @@ public double getEndXValue(int series, int item) { * @return The y-value (possibly {@code null}). */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.values.getValue(item, series); } @@ -459,7 +465,7 @@ public Number getY(int series, int item) { * @return The starting Y value for the specified series and item. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -472,7 +478,7 @@ public Number getStartY(int series, int item) { * @return The ending Y value for the specified series and item. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -551,7 +557,9 @@ public Range getDomainBounds(boolean includeInterval) { } TimePeriod first = (TimePeriod) keys.get(0); - TimePeriod last = (TimePeriod) keys.get(keys.size() - 1); + @SuppressWarnings("index") // keys.isEmpty() call above establishes that this is safe + @NonNegative int lastIndex = keys.size() - 1; + TimePeriod last = (TimePeriod) keys.get(lastIndex); if (!includeInterval || this.domainIsPointsInTime) { return new Range(getXValue(first), getXValue(last)); diff --git a/src/main/java/org/jfree/data/time/Week.java b/src/main/java/org/jfree/data/time/Week.java index 7504115f8..82536cd1a 100644 --- a/src/main/java/org/jfree/data/time/Week.java +++ b/src/main/java/org/jfree/data/time/Week.java @@ -74,6 +74,8 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; + import java.io.Serializable; import java.util.Calendar; import java.util.Date; @@ -103,10 +105,10 @@ public class Week extends RegularTimePeriod implements Serializable { public static final int LAST_WEEK_IN_YEAR = 53; /** The year in which the week falls. */ - private short year; + private @IntRange(from = 0, to = 9999) short year; /** The week (1-53). */ - private byte week; + private @IntRange(from = 1, to = 53) byte week; /** The first millisecond. */ private long firstMillisecond; @@ -128,7 +130,7 @@ public Week() { * @param week the week (1 to 53). * @param year the year (1900 to 9999). */ - public Week(int week, int year) { + public Week(@IntRange(from = 1, to = 53) int week, @IntRange(from = 0, to = 9999) int year) { if ((week < FIRST_WEEK_IN_YEAR) && (week > LAST_WEEK_IN_YEAR)) { throw new IllegalArgumentException( "The 'week' argument must be in the range 1 - 53."); @@ -144,7 +146,7 @@ public Week(int week, int year) { * @param week the week (1 to 53). * @param year the year (1900 to 9999). */ - public Week(int week, Year year) { + public Week(@IntRange(from = 1, to = 53) int week, Year year) { if ((week < FIRST_WEEK_IN_YEAR) && (week > LAST_WEEK_IN_YEAR)) { throw new IllegalArgumentException( "The 'week' argument must be in the range 1 - 53."); @@ -179,6 +181,7 @@ public Week(Date time) { * * @since 1.0.7 */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Week(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); @@ -223,7 +226,7 @@ public Year getYear() { * * @return The year. */ - public int getYearValue() { + public @IntRange(from = -9999, to = 9999) int getYearValue() { return this.year; } @@ -232,7 +235,7 @@ public int getYearValue() { * * @return The week. */ - public int getWeek() { + public @IntRange(from = 1, to = 53) int getWeek() { return this.week; } @@ -301,8 +304,10 @@ public RegularTimePeriod previous() { int yy = this.year - 1; Calendar prevYearCalendar = Calendar.getInstance(); prevYearCalendar.set(yy, Calendar.DECEMBER, 31); - result = new Week(prevYearCalendar.getActualMaximum( + @SuppressWarnings({"index", "value"}) // calendar get: calendar.getActualMaximum is a combined getter for various calendar fields, and therefore has no sensical annotation + Week resultTmp = new Week(prevYearCalendar.getActualMaximum( Calendar.WEEK_OF_YEAR), yy); + result = resultTmp; } else { result = null; @@ -331,7 +336,8 @@ public RegularTimePeriod next() { else { Calendar calendar = Calendar.getInstance(); calendar.set(this.year, Calendar.DECEMBER, 31); - int actualMaxWeek + @SuppressWarnings({"index", "value"}) // calendar get: calendar.getActualMaximum is a combined getter for various calendar fields, and therefore has no sensical annotation + @IntRange(from = 52, to = 53) int actualMaxWeek = calendar.getActualMaximum(Calendar.WEEK_OF_YEAR); if (this.week < actualMaxWeek) { result = new Week(this.week + 1, this.year); @@ -522,6 +528,7 @@ else if (o1 instanceof RegularTimePeriod) { * @return {@code null} if the string is not parseable, the week * otherwise. */ + @SuppressWarnings({"index", "value"}) // parsing code relies on wellformedness of the String passed in public static Week parseWeek(String s) { Week result = null; diff --git a/src/main/java/org/jfree/data/time/Year.java b/src/main/java/org/jfree/data/time/Year.java index 67a941512..30ad3d573 100644 --- a/src/main/java/org/jfree/data/time/Year.java +++ b/src/main/java/org/jfree/data/time/Year.java @@ -62,6 +62,8 @@ package org.jfree.data.time; +import org.checkerframework.common.value.qual.*; + import java.io.Serializable; import java.util.Calendar; import java.util.Date; @@ -92,7 +94,7 @@ public class Year extends RegularTimePeriod implements Serializable { private static final long serialVersionUID = -7659990929736074836L; /** The year. */ - private short year; + private @IntRange(from = -9999, to = 9999) short year; /** The first millisecond. */ private long firstMillisecond; @@ -112,7 +114,7 @@ public Year() { * * @param year the year. */ - public Year(int year) { + public Year(@IntRange(from = -9999, to = 9999) int year) { if ((year < Year.MINIMUM_YEAR) || (year > Year.MAXIMUM_YEAR)) { throw new IllegalArgumentException( "Year constructor: year (" + year + ") outside valid range."); @@ -143,6 +145,7 @@ public Year(Date time) { * * @since 1.0.12 */ + @SuppressWarnings({"index", "value"}) // calendar get: calendar.get is a combined getter for various calendar fields, and therefore has no sensical annotation public Year(Date time, TimeZone zone, Locale locale) { Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); @@ -155,7 +158,8 @@ public Year(Date time, TimeZone zone, Locale locale) { * * @return The year. */ - public int getYear() { + @SuppressWarnings({"index", "value"}) // this is a bug. The calendar API which this interacts with expects years to be nonnegative (it uses an era field to represent AD/BC) + public @IntRange(from = 0, to = 9999) int getYear() { return this.year; } @@ -259,6 +263,7 @@ public long getSerialIndex() { * {@code null}. */ @Override + @SuppressWarnings({"index", "value"}) // this is a bug in this class. Years cannot be negative from a Calendar's perspective public long getFirstMillisecond(Calendar calendar) { calendar.set(this.year, Calendar.JANUARY, 1, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); @@ -277,6 +282,7 @@ public long getFirstMillisecond(Calendar calendar) { * {@code null}. */ @Override + @SuppressWarnings({"index", "value"}) // this is a bug in this class. Years cannot be negative from a Calendar's perspective public long getLastMillisecond(Calendar calendar) { calendar.set(this.year, Calendar.DECEMBER, 31, 23, 59, 59); calendar.set(Calendar.MILLISECOND, 999); @@ -383,6 +389,7 @@ public String toString() { * @return {@code null} if the string is not parseable, the year * otherwise. */ + @SuppressWarnings({"index", "value"}) // parsing method is intended to throw an exception if the year isn't valid public static Year parseYear(String s) { // parse the string... diff --git a/src/main/java/org/jfree/data/time/ohlc/OHLCSeries.java b/src/main/java/org/jfree/data/time/ohlc/OHLCSeries.java index 81c25be40..a8acd1906 100644 --- a/src/main/java/org/jfree/data/time/ohlc/OHLCSeries.java +++ b/src/main/java/org/jfree/data/time/ohlc/OHLCSeries.java @@ -42,6 +42,8 @@ package org.jfree.data.time.ohlc; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.chart.util.Args; import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; @@ -74,7 +76,7 @@ public OHLCSeries(Comparable key) { * * @return The time period. */ - public RegularTimePeriod getPeriod(int index) { + public RegularTimePeriod getPeriod(@NonNegative int index) { OHLCItem item = (OHLCItem) getDataItem(index); return item.getPeriod(); } @@ -87,7 +89,7 @@ public RegularTimePeriod getPeriod(int index) { * @return The data item. */ @Override - public ComparableObjectItem getDataItem(int index) { + public ComparableObjectItem getDataItem(@NonNegative int index) { return super.getDataItem(index); } @@ -136,7 +138,7 @@ public void add(OHLCItem item) { * @since 1.0.14 */ @Override - public ComparableObjectItem remove(int index) { + public ComparableObjectItem remove(@NonNegative int index) { return super.remove(index); } diff --git a/src/main/java/org/jfree/data/time/ohlc/OHLCSeriesCollection.java b/src/main/java/org/jfree/data/time/ohlc/OHLCSeriesCollection.java index f14315fcb..c28eab112 100644 --- a/src/main/java/org/jfree/data/time/ohlc/OHLCSeriesCollection.java +++ b/src/main/java/org/jfree/data/time/ohlc/OHLCSeriesCollection.java @@ -44,6 +44,10 @@ package org.jfree.data.time.ohlc; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.List; @@ -125,7 +129,7 @@ public void addSeries(OHLCSeries series) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -139,7 +143,7 @@ public int getSeriesCount() { * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ - public OHLCSeries getSeries(int series) { + public OHLCSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -158,7 +162,7 @@ public OHLCSeries getSeries(int series) { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // defer argument checking return getSeries(series).getKey(); } @@ -174,7 +178,8 @@ public Comparable getSeriesKey(int series) { * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: the underlying data structure here is a list, but this method's annotation expects an array + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { // defer argument checking return getSeries(series).getItemCount(); } @@ -209,7 +214,7 @@ else if (this.xPosition == TimePeriodAnchor.END) { * @return The x-value. */ @Override - public double getXValue(int series, int item) { + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); RegularTimePeriod period = di.getPeriod(); @@ -225,7 +230,7 @@ public double getXValue(int series, int item) { * @return The x-value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getXValue(series, item)); } @@ -238,7 +243,7 @@ public Number getX(int series, int item) { * @return The y-value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return new Double(di.getYValue()); @@ -253,7 +258,7 @@ public Number getY(int series, int item) { * @return The open-value. */ @Override - public double getOpenValue(int series, int item) { + public double getOpenValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getOpenValue(); @@ -268,7 +273,7 @@ public double getOpenValue(int series, int item) { * @return The open-value. */ @Override - public Number getOpen(int series, int item) { + public Number getOpen(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getOpenValue(series, item)); } @@ -281,7 +286,7 @@ public Number getOpen(int series, int item) { * @return The close-value. */ @Override - public double getCloseValue(int series, int item) { + public double getCloseValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getCloseValue(); @@ -296,7 +301,7 @@ public double getCloseValue(int series, int item) { * @return The close-value. */ @Override - public Number getClose(int series, int item) { + public Number getClose(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getCloseValue(series, item)); } @@ -309,7 +314,7 @@ public Number getClose(int series, int item) { * @return The high-value. */ @Override - public double getHighValue(int series, int item) { + public double getHighValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getHighValue(); @@ -324,7 +329,7 @@ public double getHighValue(int series, int item) { * @return The high-value. */ @Override - public Number getHigh(int series, int item) { + public Number getHigh(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getHighValue(series, item)); } @@ -337,7 +342,7 @@ public Number getHigh(int series, int item) { * @return The low-value. */ @Override - public double getLowValue(int series, int item) { + public double getLowValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getLowValue(); @@ -352,7 +357,7 @@ public double getLowValue(int series, int item) { * @return The low-value. */ @Override - public Number getLow(int series, int item) { + public Number getLow(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getLowValue(series, item)); } @@ -366,7 +371,7 @@ public Number getLow(int series, int item) { * @return {@code null}. */ @Override - public Number getVolume(int series, int item) { + public Number getVolume(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return null; } @@ -380,7 +385,7 @@ public Number getVolume(int series, int item) { * @return {@code Double.NaN}. */ @Override - public double getVolumeValue(int series, int item) { + public double getVolumeValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return Double.NaN; } @@ -392,7 +397,7 @@ public double getVolumeValue(int series, int item) { * * @since 1.0.14 */ - public void removeSeries(int index) { + public void removeSeries(@NonNegative int index) { OHLCSeries series = getSeries(index); if (series != null) { removeSeries(series); diff --git a/src/main/java/org/jfree/data/xml/KeyHandler.java b/src/main/java/org/jfree/data/xml/KeyHandler.java index 4c480b998..ca79d04c3 100644 --- a/src/main/java/org/jfree/data/xml/KeyHandler.java +++ b/src/main/java/org/jfree/data/xml/KeyHandler.java @@ -40,6 +40,9 @@ package org.jfree.data.xml; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; @@ -134,7 +137,8 @@ public void endElement(String namespaceURI, * @param length the length of the valid character data. */ @Override - public void characters(char[] ch, int start, int length) { + @SuppressWarnings("index") // need xml annotations + public void characters(char[] ch, @IndexOrHigh("#1") int start, @IndexOrHigh("#1") int length) { if (this.currentText != null) { this.currentText.append(String.copyValueOf(ch, start, length)); } diff --git a/src/main/java/org/jfree/data/xml/RootHandler.java b/src/main/java/org/jfree/data/xml/RootHandler.java index de6fe59cb..1350a42e5 100644 --- a/src/main/java/org/jfree/data/xml/RootHandler.java +++ b/src/main/java/org/jfree/data/xml/RootHandler.java @@ -40,6 +40,9 @@ package org.jfree.data.xml; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.util.Stack; import org.xml.sax.SAXException; @@ -79,7 +82,8 @@ public Stack getSubHandlers() { * @throws SAXException for errors. */ @Override - public void characters(char[] ch, int start, int length) + @SuppressWarnings("index") // need xml annotations + public void characters(char[] ch, @IndexOrHigh("#1") int start, @IndexOrHigh("#1") int length) throws SAXException { DefaultHandler handler = getCurrentHandler(); if (handler != this) { diff --git a/src/main/java/org/jfree/data/xml/ValueHandler.java b/src/main/java/org/jfree/data/xml/ValueHandler.java index f387e1c8f..b228b6ad0 100644 --- a/src/main/java/org/jfree/data/xml/ValueHandler.java +++ b/src/main/java/org/jfree/data/xml/ValueHandler.java @@ -41,6 +41,9 @@ package org.jfree.data.xml; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; @@ -139,7 +142,8 @@ public void endElement(String namespaceURI, * @param length the length of the valid character data. */ @Override - public void characters(char[] ch, int start, int length) { + @SuppressWarnings("index") // need xml annotations + public void characters(char[] ch, @IndexOrHigh("#1") int start, @IndexOrHigh("#1") int length) { if (this.currentText != null) { this.currentText.append(String.copyValueOf(ch, start, length)); } diff --git a/src/main/java/org/jfree/data/xy/AbstractIntervalXYDataset.java b/src/main/java/org/jfree/data/xy/AbstractIntervalXYDataset.java index c8e97a9cb..18667261e 100644 --- a/src/main/java/org/jfree/data/xy/AbstractIntervalXYDataset.java +++ b/src/main/java/org/jfree/data/xy/AbstractIntervalXYDataset.java @@ -43,6 +43,9 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; /** * An base class that you can use to create new implementations of the @@ -61,7 +64,7 @@ public abstract class AbstractIntervalXYDataset extends AbstractXYDataset * @return The value. */ @Override - public double getStartXValue(int series, int item) { + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number x = getStartX(series, item); if (x != null) { @@ -80,7 +83,7 @@ public double getStartXValue(int series, int item) { * @return The value. */ @Override - public double getEndXValue(int series, int item) { + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number x = getEndX(series, item); if (x != null) { @@ -99,7 +102,7 @@ public double getEndXValue(int series, int item) { * @return The value. */ @Override - public double getStartYValue(int series, int item) { + public double getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number y = getStartY(series, item); if (y != null) { @@ -118,7 +121,7 @@ public double getStartYValue(int series, int item) { * @return The value. */ @Override - public double getEndYValue(int series, int item) { + public double getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number y = getEndY(series, item); if (y != null) { @@ -126,5 +129,4 @@ public double getEndYValue(int series, int item) { } return result; } - } diff --git a/src/main/java/org/jfree/data/xy/AbstractXYDataset.java b/src/main/java/org/jfree/data/xy/AbstractXYDataset.java index 45bca666c..4c4325743 100644 --- a/src/main/java/org/jfree/data/xy/AbstractXYDataset.java +++ b/src/main/java/org/jfree/data/xy/AbstractXYDataset.java @@ -43,6 +43,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.DomainOrder; import org.jfree.data.general.AbstractSeriesDataset; @@ -72,7 +76,7 @@ public DomainOrder getDomainOrder() { * @return The value. */ @Override - public double getXValue(int series, int item) { + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number x = getX(series, item); if (x != null) { @@ -90,7 +94,7 @@ public double getXValue(int series, int item) { * @return The value. */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number y = getY(series, item); if (y != null) { @@ -98,5 +102,4 @@ public double getYValue(int series, int item) { } return result; } - } diff --git a/src/main/java/org/jfree/data/xy/AbstractXYZDataset.java b/src/main/java/org/jfree/data/xy/AbstractXYZDataset.java index 859a60e58..a72ab04d4 100644 --- a/src/main/java/org/jfree/data/xy/AbstractXYZDataset.java +++ b/src/main/java/org/jfree/data/xy/AbstractXYZDataset.java @@ -41,6 +41,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + /** * An base class that you can use to create new implementations of the * {@link XYZDataset} interface. @@ -57,7 +61,7 @@ public abstract class AbstractXYZDataset extends AbstractXYDataset * @return The z-value. */ @Override - public double getZValue(int series, int item) { + public double getZValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number z = getZ(series, item); if (z != null) { diff --git a/src/main/java/org/jfree/data/xy/CategoryTableXYDataset.java b/src/main/java/org/jfree/data/xy/CategoryTableXYDataset.java index 0c2431513..69dc5d955 100644 --- a/src/main/java/org/jfree/data/xy/CategoryTableXYDataset.java +++ b/src/main/java/org/jfree/data/xy/CategoryTableXYDataset.java @@ -49,6 +49,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DefaultKeyedValues2D; import org.jfree.data.DomainInfo; @@ -159,7 +163,7 @@ public void clear() { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.values.getColumnCount(); } @@ -171,7 +175,7 @@ public int getSeriesCount() { * @return The key for a series. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { return this.values.getColumnKey(series); } @@ -181,7 +185,7 @@ public Comparable getSeriesKey(int series) { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.values.getRowCount(); } @@ -194,9 +198,11 @@ public int getItemCount() { * @return The item count. */ @Override - public int getItemCount(int series) { - return getItemCount(); // all series have the same number of items in + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { + @SuppressWarnings("index") // The annotation on this method's return type isn't quite sensical, because there's no way to express the correct invariant here. Warnings are suppressed throughout these classes, but callers have a good interface. + @LengthOf("this.getSeries(#1)") int result = getItemCount(); // all series have the same number of items in // this dataset + return result; } /** @@ -208,7 +214,7 @@ public int getItemCount(int series) { * @return The value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return (Number) this.values.getRowKey(item); } @@ -221,7 +227,8 @@ public Number getX(int series, int item) { * @return The starting X value. */ @Override - public Number getStartX(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212: interval delegate and this.getSeries have the same conceptual length + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.intervalDelegate.getStartX(series, item); } @@ -234,7 +241,8 @@ public Number getStartX(int series, int item) { * @return The ending X value. */ @Override - public Number getEndX(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212: interval delegate and this.getSeries have the same conceptual length + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.intervalDelegate.getEndX(series, item); } @@ -247,7 +255,7 @@ public Number getEndX(int series, int item) { * @return The y value (possibly {@code null}). */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.values.getValue(item, series); } @@ -260,7 +268,7 @@ public Number getY(int series, int item) { * @return The starting Y value. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -273,7 +281,7 @@ public Number getStartY(int series, int item) { * @return The ending Y value. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } diff --git a/src/main/java/org/jfree/data/xy/DefaultHighLowDataset.java b/src/main/java/org/jfree/data/xy/DefaultHighLowDataset.java index 1b3ed72c8..16d4d14fd 100644 --- a/src/main/java/org/jfree/data/xy/DefaultHighLowDataset.java +++ b/src/main/java/org/jfree/data/xy/DefaultHighLowDataset.java @@ -49,6 +49,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + import java.util.Arrays; import java.util.Date; import org.jfree.chart.util.Args; @@ -66,22 +68,22 @@ public class DefaultHighLowDataset extends AbstractXYDataset private Comparable seriesKey; /** Storage for the dates. */ - private Date[] date; + private Date @SameLen({"this.high", "this.date", "this.low", "this.open", "this.close", "this.volume"}) [] date; /** Storage for the high values. */ - private Number[] high; + private Number @SameLen({"this.high", "this.date", "this.low", "this.open", "this.close", "this.volume"}) [] high; /** Storage for the low values. */ - private Number[] low; + private Number @SameLen({"this.high", "this.date", "this.low", "this.open", "this.close", "this.volume"}) [] low; /** Storage for the open values. */ - private Number[] open; + private Number @SameLen({"this.high", "this.date", "this.low", "this.open", "this.close", "this.volume"}) [] open; /** Storage for the close values. */ - private Number[] close; + private Number @SameLen({"this.high", "this.date", "this.low", "this.open", "this.close", "this.volume"}) [] close; /** Storage for the volume values. */ - private Number[] volume; + private Number @SameLen({"this.high", "this.date", "this.low", "this.open", "this.close", "this.volume"}) [] volume; /** * Constructs a new high/low/open/close dataset. @@ -98,9 +100,11 @@ public class DefaultHighLowDataset extends AbstractXYDataset * @param close the close values ({@code null} not permitted). * @param volume the volume values ({@code null} not permitted). */ - public DefaultHighLowDataset(Comparable seriesKey, Date[] date, - double[] high, double[] low, double[] open, double[] close, - double[] volume) { + @SuppressWarnings("samelen") // While initializing object, SameLen invariants between the fields will be broken (because one field must be initialized before the others). The annotations on the parameters guarantee the invariant holds after the constructor finishes executing. + public DefaultHighLowDataset(Comparable seriesKey, Date @SameLen({"#2", "#3", "#4", "#5", "#6", "#7"}) [] date, + double @SameLen({"#2", "#3", "#4", "#5", "#6", "#7"}) [] high, double @SameLen({"#2", "#3", "#4", "#5", "#6", "#7"}) [] low, + double @SameLen({"#2", "#3", "#4", "#5", "#6", "#7"}) [] open, double @SameLen({"#2", "#3", "#4", "#5", "#6", "#7"}) [] close, + double @SameLen({"#2", "#3", "#4", "#5", "#6", "#7"}) [] volume) { Args.nullNotPermitted(seriesKey, "seriesKey"); Args.nullNotPermitted(date, "date"); @@ -123,7 +127,7 @@ public DefaultHighLowDataset(Comparable seriesKey, Date[] date, * @return The series key (never {@code null}). */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { return this.seriesKey; } @@ -142,8 +146,10 @@ public Comparable getSeriesKey(int series) { * @see #getXDate(int, int) */ @Override - public Number getX(int series, int item) { - return new Long(this.date[item].getTime()); + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // array-list interop: the annotation on this method cannot expose the implementation detail of this class being backed by an array + Number result = new Long(this.date[item].getTime()); + return result; } /** @@ -158,8 +164,10 @@ public Number getX(int series, int item) { * * @see #getX(int, int) */ - public Date getXDate(int series, int item) { - return this.date[item]; + public Date getXDate(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // array-list interop: the annotation on this method cannot expose the implementation detail of this class being backed by an array + Date result = this.date[item]; + return result; } /** @@ -176,7 +184,7 @@ public Date getXDate(int series, int item) { * @see #getYValue(int, int) */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getClose(series, item); } @@ -191,8 +199,10 @@ public Number getY(int series, int item) { * @see #getHighValue(int, int) */ @Override - public Number getHigh(int series, int item) { - return this.high[item]; + public Number getHigh(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // array-list interop: the annotation on this method cannot expose the implementation detail of this class being backed by an array + Number result = this.high[item]; + return result; } /** @@ -207,7 +217,7 @@ public Number getHigh(int series, int item) { * @see #getHigh(int, int) */ @Override - public double getHighValue(int series, int item) { + public double getHighValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number h = getHigh(series, item); if (h != null) { @@ -227,8 +237,10 @@ public double getHighValue(int series, int item) { * @see #getLowValue(int, int) */ @Override - public Number getLow(int series, int item) { - return this.low[item]; + public Number getLow(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // array-list interop: the annotation on this method cannot expose the implementation detail of this class being backed by an array + Number result = this.low[item]; + return result; } /** @@ -243,7 +255,7 @@ public Number getLow(int series, int item) { * @see #getLow(int, int) */ @Override - public double getLowValue(int series, int item) { + public double getLowValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number l = getLow(series, item); if (l != null) { @@ -263,8 +275,10 @@ public double getLowValue(int series, int item) { * @see #getOpenValue(int, int) */ @Override - public Number getOpen(int series, int item) { - return this.open[item]; + public Number getOpen(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // array-list interop: the annotation on this method cannot expose the implementation detail of this class being backed by an array + Number result = this.open[item]; + return result; } /** @@ -279,7 +293,7 @@ public Number getOpen(int series, int item) { * @see #getOpen(int, int) */ @Override - public double getOpenValue(int series, int item) { + public double getOpenValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number open = getOpen(series, item); if (open != null) { @@ -299,8 +313,10 @@ public double getOpenValue(int series, int item) { * @see #getCloseValue(int, int) */ @Override - public Number getClose(int series, int item) { - return this.close[item]; + public Number getClose(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // array-list interop: the annotation on this method cannot expose the implementation detail of this class being backed by an array + Number result = this.close[item]; + return result; } /** @@ -315,7 +331,7 @@ public Number getClose(int series, int item) { * @see #getClose(int, int) */ @Override - public double getCloseValue(int series, int item) { + public double getCloseValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number c = getClose(series, item); if (c != null) { @@ -335,8 +351,10 @@ public double getCloseValue(int series, int item) { * @see #getVolumeValue(int, int) */ @Override - public Number getVolume(int series, int item) { - return this.volume[item]; + public Number getVolume(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // array-list interop: the annotation on this method cannot expose the implementation detail of this class being backed by an array + Number result = this.volume[item]; + return result; } /** @@ -351,7 +369,7 @@ public Number getVolume(int series, int item) { * @see #getVolume(int, int) */ @Override - public double getVolumeValue(int series, int item) { + public double getVolumeValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number v = getVolume(series, item); if (v != null) { @@ -368,7 +386,7 @@ public double getVolumeValue(int series, int item) { * @return The number of series. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return 1; } @@ -380,8 +398,10 @@ public int getSeriesCount() { * @return The number of items in the specified series. */ @Override - public int getItemCount(int series) { - return this.date.length; + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { + @SuppressWarnings("index") // the annotation on this method isn't quite sensical, but it's the closest we can get, and is safe. + @LengthOf("this.getSeries(#1)") int result = this.date.length; + return result; } /** @@ -432,7 +452,7 @@ public boolean equals(Object obj) { * * @return The data as an array of Number objects. */ - public static Number[] createNumberArray(double[] data) { + public static Number @SameLen("#1") [] createNumberArray(double[] data) { Number[] result = new Number[data.length]; for (int i = 0; i < data.length; i++) { result[i] = new Double(data[i]); diff --git a/src/main/java/org/jfree/data/xy/DefaultIntervalXYDataset.java b/src/main/java/org/jfree/data/xy/DefaultIntervalXYDataset.java index 9003e532e..6abcc0c56 100644 --- a/src/main/java/org/jfree/data/xy/DefaultIntervalXYDataset.java +++ b/src/main/java/org/jfree/data/xy/DefaultIntervalXYDataset.java @@ -45,6 +45,9 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -76,7 +79,7 @@ public class DefaultIntervalXYDataset extends AbstractIntervalXYDataset * order of the series is significant. This list must be kept in sync * with the seriesKeys list. */ - private List seriesList; + private List<double @ArrayLen(6) [][]> seriesList; /** * Creates a new {@code DefaultIntervalXYDataset} instance, initially @@ -93,7 +96,7 @@ public DefaultIntervalXYDataset() { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.seriesList.size(); } @@ -109,7 +112,7 @@ public int getSeriesCount() { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -128,12 +131,16 @@ public Comparable getSeriesKey(int series) { * specified range. */ @Override - public int getItemCount(int series) { + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } - double[][] seriesArray = (double[][]) this.seriesList.get(series); - return seriesArray[0].length; + double @ArrayLen(6) [][] seriesArray = + (double @ArrayLen(6) [][]) + this.seriesList.get(series); + @SuppressWarnings("index") // The annotation on this method's return type isn't quite sensical, because there's no way to express the correct invariant here. Warnings are suppressed throughout these classes, but callers have a good interface. + @LengthOf("this.getSeries(#1)") int result = seriesArray[0].length; + return result; } /** @@ -154,9 +161,11 @@ public int getItemCount(int series) { * @see #getX(int, int) */ @Override - public double getXValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[0][item]; + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(6) [][] seriesData = (double @ArrayLen(6) [][]) this.seriesList.get(series); + @SuppressWarnings({"index", "value"}) // array-list interop + @IndexFor("seriesData[0]") int itemIndex = item; + return seriesData[0][itemIndex]; } /** @@ -177,9 +186,11 @@ public double getXValue(int series, int item) { * @see #getY(int, int) */ @Override - public double getYValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[3][item]; + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(6) [][] seriesData = (double @ArrayLen(6) [][]) this.seriesList.get(series); + @SuppressWarnings({"index", "value"}) // array-list interop + @IndexFor("seriesData[3]") int itemIndex = item; + return seriesData[3][itemIndex]; } /** @@ -200,9 +211,11 @@ public double getYValue(int series, int item) { * @see #getStartX(int, int) */ @Override - public double getStartXValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[1][item]; + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(6) [][] seriesData = (double @ArrayLen(6) [][]) this.seriesList.get(series); + @SuppressWarnings({"index", "value"}) // array-list interop + @IndexFor("seriesData[1]") int itemIndex = item; + return seriesData[1][itemIndex]; } /** @@ -223,9 +236,11 @@ public double getStartXValue(int series, int item) { * @see #getEndX(int, int) */ @Override - public double getEndXValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[2][item]; + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(6) [][] seriesData = (double @ArrayLen(6) [][]) this.seriesList.get(series); + @SuppressWarnings({"index", "value"}) // array-list interop + @IndexFor("seriesData[2]") int itemIndex = item; + return seriesData[2][itemIndex]; } /** @@ -246,9 +261,11 @@ public double getEndXValue(int series, int item) { * @see #getStartY(int, int) */ @Override - public double getStartYValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[4][item]; + public double getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(6) [][] seriesData = (double @ArrayLen(6) [][]) this.seriesList.get(series); + @SuppressWarnings({"index", "value"}) // array-list interop + @IndexFor("seriesData[4]") int itemIndex = item; + return seriesData[4][itemIndex]; } /** @@ -269,9 +286,11 @@ public double getStartYValue(int series, int item) { * @see #getEndY(int, int) */ @Override - public double getEndYValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[5][item]; + public double getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(6) [][] seriesData = (double @ArrayLen(6) [][]) this.seriesList.get(series); + @SuppressWarnings({"index", "value"}) // array-list interop + @IndexFor("seriesData[5]") int itemIndex = item; + return seriesData[5][itemIndex]; } /** @@ -292,7 +311,7 @@ public double getEndYValue(int series, int item) { * @see #getEndXValue(int, int) */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getEndXValue(series, item)); } @@ -314,7 +333,7 @@ public Number getEndX(int series, int item) { * @see #getEndYValue(int, int) */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getEndYValue(series, item)); } @@ -336,7 +355,7 @@ public Number getEndY(int series, int item) { * @see #getStartXValue(int, int) */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getStartXValue(series, item)); } @@ -358,7 +377,7 @@ public Number getStartX(int series, int item) { * @see #getStartYValue(int, int) */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getStartYValue(series, item)); } @@ -380,7 +399,7 @@ public Number getStartY(int series, int item) { * @see #getXValue(int, int) */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getXValue(series, item)); } @@ -402,7 +421,7 @@ public Number getX(int series, int item) { * @see #getYValue(int, int) */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getYValue(series, item)); } @@ -417,7 +436,7 @@ public Number getY(int series, int item) { * (x, xLow and xHigh) and the last three containing the y-values * (y, yLow and yHigh)). */ - public void addSeries(Comparable seriesKey, double[][] data) { + public void addSeries(Comparable seriesKey, double @ArrayLen(6) [][] data) { if (seriesKey == null) { throw new IllegalArgumentException( "The 'seriesKey' cannot be null."); @@ -476,8 +495,8 @@ public boolean equals(Object obj) { return false; } for (int i = 0; i < this.seriesList.size(); i++) { - double[][] d1 = (double[][]) this.seriesList.get(i); - double[][] d2 = (double[][]) that.seriesList.get(i); + double[][] d1 = (double @ArrayLen(6) [][]) this.seriesList.get(i); + double[][] d2 = (double @ArrayLen(6) [][]) that.seriesList.get(i); double[] d1x = d1[0]; double[] d2x = d2[0]; if (!Arrays.equals(d1x, d2x)) { @@ -540,7 +559,7 @@ public Object clone() throws CloneNotSupportedException { clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); clone.seriesList = new ArrayList(this.seriesList.size()); for (int i = 0; i < this.seriesList.size(); i++) { - double[][] data = (double[][]) this.seriesList.get(i); + double[][] data = (double @ArrayLen(6) [][]) this.seriesList.get(i); double[] x = data[0]; double[] xStart = data[1]; double[] xEnd = data[2]; diff --git a/src/main/java/org/jfree/data/xy/DefaultOHLCDataset.java b/src/main/java/org/jfree/data/xy/DefaultOHLCDataset.java index 61d564a99..d1a87d579 100644 --- a/src/main/java/org/jfree/data/xy/DefaultOHLCDataset.java +++ b/src/main/java/org/jfree/data/xy/DefaultOHLCDataset.java @@ -45,6 +45,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.Arrays; import java.util.Date; import org.jfree.chart.util.PublicCloneable; @@ -81,7 +85,7 @@ public DefaultOHLCDataset(Comparable key, OHLCDataItem[] data) { * @return The series key. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { return this.key; } @@ -94,8 +98,10 @@ public Comparable getSeriesKey(int series) { * @return The x-value. */ @Override - public Number getX(int series, int item) { - return new Long(this.data[item].getDate().getTime()); + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // guaranteed index: There is only one series for an OHLC dataset, so this is always safe. + Number result = new Long(this.data[item].getDate().getTime()); + return result; } /** @@ -106,8 +112,10 @@ public Number getX(int series, int item) { * * @return The x-value as a date. */ - public Date getXDate(int series, int item) { - return this.data[item].getDate(); + public Date getXDate(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // guaranteed index: There is only one series for an OHLC dataset, so this is always safe. + Date result = this.data[item].getDate(); + return result; } /** @@ -119,7 +127,7 @@ public Date getXDate(int series, int item) { * @return The y value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getClose(series, item); } @@ -132,8 +140,10 @@ public Number getY(int series, int item) { * @return The high value. */ @Override - public Number getHigh(int series, int item) { - return this.data[item].getHigh(); + public Number getHigh(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // guaranteed index: There is only one series for an OHLC dataset, so this is always safe. + Number result = this.data[item].getHigh(); + return result; } /** @@ -146,7 +156,7 @@ public Number getHigh(int series, int item) { * @return The high-value. */ @Override - public double getHighValue(int series, int item) { + public double getHighValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number high = getHigh(series, item); if (high != null) { @@ -164,8 +174,10 @@ public double getHighValue(int series, int item) { * @return The low value. */ @Override - public Number getLow(int series, int item) { - return this.data[item].getLow(); + public Number getLow(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // guaranteed index: There is only one series for an OHLC dataset, so this is always safe. + Number result = this.data[item].getLow(); + return result; } /** @@ -178,7 +190,7 @@ public Number getLow(int series, int item) { * @return The low-value. */ @Override - public double getLowValue(int series, int item) { + public double getLowValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number low = getLow(series, item); if (low != null) { @@ -196,8 +208,10 @@ public double getLowValue(int series, int item) { * @return The open value. */ @Override - public Number getOpen(int series, int item) { - return this.data[item].getOpen(); + public Number getOpen(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // guaranteed index: There is only one series for an OHLC dataset, so this is always safe. + Number result = this.data[item].getOpen(); + return result; } /** @@ -210,7 +224,7 @@ public Number getOpen(int series, int item) { * @return The open-value. */ @Override - public double getOpenValue(int series, int item) { + public double getOpenValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number open = getOpen(series, item); if (open != null) { @@ -228,8 +242,10 @@ public double getOpenValue(int series, int item) { * @return The close value. */ @Override - public Number getClose(int series, int item) { - return this.data[item].getClose(); + public Number getClose(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // guaranteed index: There is only one series for an OHLC dataset, so this is always safe. + Number result = this.data[item].getClose(); + return result; } /** @@ -242,7 +258,7 @@ public Number getClose(int series, int item) { * @return The close-value. */ @Override - public double getCloseValue(int series, int item) { + public double getCloseValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number close = getClose(series, item); if (close != null) { @@ -260,8 +276,10 @@ public double getCloseValue(int series, int item) { * @return The trading volume. */ @Override - public Number getVolume(int series, int item) { - return this.data[item].getVolume(); + public Number getVolume(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + @SuppressWarnings("index") // guaranteed index: There is only one series for an OHLC dataset, so this is always safe. + Number result = this.data[item].getVolume(); + return result; } /** @@ -274,7 +292,7 @@ public Number getVolume(int series, int item) { * @return The volume-value. */ @Override - public double getVolumeValue(int series, int item) { + public double getVolumeValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { double result = Double.NaN; Number volume = getVolume(series, item); if (volume != null) { @@ -289,7 +307,7 @@ public double getVolumeValue(int series, int item) { * @return 1. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return 1; } @@ -301,7 +319,8 @@ public int getSeriesCount() { * @return The item count. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // SameLen to a method with any argument https://github.com/kelloggm/checker-framework/issues/209: this.data is the same length as the conceptual getSeries called with any argument, but no SameLen annotation can express that due to the argument needed for getSeries + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return this.data.length; } diff --git a/src/main/java/org/jfree/data/xy/DefaultTableXYDataset.java b/src/main/java/org/jfree/data/xy/DefaultTableXYDataset.java index 3481a774f..531c577f6 100644 --- a/src/main/java/org/jfree/data/xy/DefaultTableXYDataset.java +++ b/src/main/java/org/jfree/data/xy/DefaultTableXYDataset.java @@ -63,6 +63,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -211,7 +215,7 @@ public void updateXPoints() { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -221,7 +225,7 @@ public int getSeriesCount() { * @return The number of x values in the dataset. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { if (this.xPoints == null) { return 0; } @@ -237,7 +241,7 @@ public int getItemCount() { * * @return The series (never {@code null}). */ - public XYSeries getSeries(int series) { + public XYSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Index outside valid range."); } @@ -252,7 +256,7 @@ public XYSeries getSeries(int series) { * @return The key for a series. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // check arguments...delegated return getSeries(series).getKey(); } @@ -265,9 +269,11 @@ public Comparable getSeriesKey(int series) { * @return The number of items in the specified series. */ @Override - public int getItemCount(int series) { + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { // check arguments...delegated - return getSeries(series).getItemCount(); + @SuppressWarnings("index") // conflict between ghost method and real method + @LengthOf("this.getSeries(#1)") int result = getSeries(series).getItemCount(); + return result; } /** @@ -279,7 +285,7 @@ public int getItemCount(int series) { * @return The x-value for the specified series and item. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XYSeries s = (XYSeries) this.data.get(series); return s.getX(item); @@ -294,7 +300,8 @@ public Number getX(int series, int item) { * @return The starting X value. */ @Override - public Number getStartX(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212: interval delegate and this.getSeries have the same conceptual length + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.intervalDelegate.getStartX(series, item); } @@ -307,7 +314,8 @@ public Number getStartX(int series, int item) { * @return The ending X value. */ @Override - public Number getEndX(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212: interval delegate and this.getSeries have the same conceptual length + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.intervalDelegate.getEndX(series, item); } @@ -321,7 +329,7 @@ public Number getEndX(int series, int item) { * {@code null}). */ @Override - public Number getY(int series, int index) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int index) { XYSeries s = (XYSeries) this.data.get(series); return s.getY(index); } @@ -335,7 +343,7 @@ public Number getY(int series, int index) { * @return The starting Y value. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -348,7 +356,7 @@ public Number getStartY(int series, int item) { * @return The ending Y value. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -395,7 +403,7 @@ public void removeSeries(XYSeries series) { * * @param series the series (zero based index). */ - public void removeSeries(int series) { + public void removeSeries(@NonNegative int series) { // check arguments... if ((series < 0) || (series > getSeriesCount())) { @@ -442,6 +450,7 @@ public void removeAllValuesForX(Number x) { * * @return A boolean. */ + @SuppressWarnings("index") // documentation bug: this method assumes that x is in the dataset. This is a documentation bug. protected boolean canPrune(Number x) { for (int s = 0; s < this.data.size(); s++) { XYSeries series = (XYSeries) this.data.get(s); diff --git a/src/main/java/org/jfree/data/xy/DefaultWindDataset.java b/src/main/java/org/jfree/data/xy/DefaultWindDataset.java index 155d0daa1..e392b1f15 100644 --- a/src/main/java/org/jfree/data/xy/DefaultWindDataset.java +++ b/src/main/java/org/jfree/data/xy/DefaultWindDataset.java @@ -47,6 +47,11 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.common.value.qual.ArrayLen; + import java.io.Serializable; import java.util.Arrays; import java.util.Collections; @@ -84,7 +89,7 @@ public DefaultWindDataset() { * * @throws NullPointerException if {@code data} is {@code null}. */ - public DefaultWindDataset(Object[][][] data) { + public DefaultWindDataset(Object[][] @ArrayLen(3) [] data) { this(seriesNameListFromDataArray(data), data); } @@ -97,7 +102,7 @@ public DefaultWindDataset(Object[][][] data) { * * @throws NullPointerException if {@code seriesNames} is {@code null}. */ - public DefaultWindDataset(String[] seriesNames, Object[][][] data) { + public DefaultWindDataset(String[] seriesNames, Object[][] @ArrayLen(3) [] data) { this(Arrays.asList(seriesNames), data); } @@ -125,7 +130,7 @@ public DefaultWindDataset(String[] seriesNames, Object[][][] data) { * match the number of series in the array. * @throws NullPointerException if {@code data} is {@code null}. */ - public DefaultWindDataset(List seriesKeys, Object[][][] data) { + public DefaultWindDataset(List seriesKeys, Object[][] @ArrayLen(3) [] data) { Args.nullNotPermitted(seriesKeys, "seriesKeys"); if (seriesKeys.size() != data.length) { throw new IllegalArgumentException("The number of series keys does " @@ -172,7 +177,7 @@ public DefaultWindDataset(List seriesKeys, Object[][][] data) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.allSeriesData.size(); } @@ -184,7 +189,7 @@ public int getSeriesCount() { * @return The item count. */ @Override - public int getItemCount(int series) { + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { if (series < 0 || series >= getSeriesCount()) { throw new IllegalArgumentException("Invalid series index: " + series); @@ -201,7 +206,7 @@ public int getItemCount(int series) { * @return The series key. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { if (series < 0 || series >= getSeriesCount()) { throw new IllegalArgumentException("Invalid series index: " + series); @@ -220,7 +225,7 @@ public Comparable getSeriesKey(int series) { * @return The x-value for the item within the series. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { List oneSeriesData = (List) this.allSeriesData.get(series); WindDataItem windItem = (WindDataItem) oneSeriesData.get(item); return windItem.getX(); @@ -237,7 +242,7 @@ public Number getX(int series, int item) { * @return The y-value for the item within the series. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getWindForce(series, item); } @@ -251,7 +256,7 @@ public Number getY(int series, int item) { * @return The wind direction for the item within the series. */ @Override - public Number getWindDirection(int series, int item) { + public Number getWindDirection(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { List oneSeriesData = (List) this.allSeriesData.get(series); WindDataItem windItem = (WindDataItem) oneSeriesData.get(item); return windItem.getWindDirection(); @@ -267,7 +272,7 @@ public Number getWindDirection(int series, int item) { * @return The wind force for the item within the series. */ @Override - public Number getWindForce(int series, int item) { + public Number getWindForce(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { List oneSeriesData = (List) this.allSeriesData.get(series); WindDataItem windItem = (WindDataItem) oneSeriesData.get(item); return windItem.getWindForce(); diff --git a/src/main/java/org/jfree/data/xy/DefaultXYDataset.java b/src/main/java/org/jfree/data/xy/DefaultXYDataset.java index 8f027540b..46fec7d4c 100644 --- a/src/main/java/org/jfree/data/xy/DefaultXYDataset.java +++ b/src/main/java/org/jfree/data/xy/DefaultXYDataset.java @@ -43,6 +43,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.common.value.qual.ArrayLen; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -71,7 +75,7 @@ public class DefaultXYDataset extends AbstractXYDataset * order of the series is significant. This list must be kept in sync * with the seriesKeys list. */ - private List seriesList; + private List<double @ArrayLen(2) [][]> seriesList; /** * Creates a new {@code DefaultXYDataset} instance, initially @@ -88,7 +92,7 @@ public DefaultXYDataset() { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.seriesList.size(); } @@ -104,7 +108,7 @@ public int getSeriesCount() { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -120,7 +124,7 @@ public Comparable getSeriesKey(int series) { * @return The index, or -1. */ @Override - public int indexOf(Comparable seriesKey) { + public @GTENegativeOne int indexOf(Comparable seriesKey) { return this.seriesKeys.indexOf(seriesKey); } @@ -148,11 +152,14 @@ public DomainOrder getDomainOrder() { * specified range. */ @Override - public int getItemCount(int series) { + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } - double[][] seriesArray = (double[][]) this.seriesList.get(series); + @SuppressWarnings("index") //array-list interop: this.getSeries is the logical ghost variable representing this array + double @ArrayLen(2) [] @SameLen("this.getSeries(#1)") [] seriesArray = + (double @ArrayLen(2) [][]) + this.seriesList.get(series); return seriesArray[0].length; } @@ -174,9 +181,11 @@ public int getItemCount(int series) { * @see #getX(int, int) */ @Override - public double getXValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[0][item]; + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(2) [][] seriesData = (double @ArrayLen(2) [][]) this.seriesList.get(series); + @SuppressWarnings("index") // this.seriesList.get is equivalent to this.getSeries + @IndexFor("seriesData[0]") int itemIndex = item; + return seriesData[0][itemIndex]; } /** @@ -197,7 +206,7 @@ public double getXValue(int series, int item) { * @see #getXValue(int, int) */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getXValue(series, item)); } @@ -219,9 +228,11 @@ public Number getX(int series, int item) { * @see #getY(int, int) */ @Override - public double getYValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[1][item]; + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(2) [][] seriesData = (double @ArrayLen(2) [][]) this.seriesList.get(series); + @SuppressWarnings("index") // this.seriesList.get is equivalent to ghost method this.getSeries + @IndexFor("seriesData[1]") int itemIndex = item; + return seriesData[1][itemIndex]; } /** @@ -242,7 +253,7 @@ public double getYValue(int series, int item) { * @see #getX(int, int) */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getYValue(series, item)); } @@ -256,7 +267,7 @@ public Number getY(int series, int item) { * arrays of equal length, the first containing the x-values and the * second containing the y-values). */ - public void addSeries(Comparable seriesKey, double[][] data) { + public void addSeries(Comparable seriesKey, double @ArrayLen(2) [][] data) { if (seriesKey == null) { throw new IllegalArgumentException( "The 'seriesKey' cannot be null."); @@ -327,8 +338,8 @@ public boolean equals(Object obj) { return false; } for (int i = 0; i < this.seriesList.size(); i++) { - double[][] d1 = (double[][]) this.seriesList.get(i); - double[][] d2 = (double[][]) that.seriesList.get(i); + double[][] d1 = (double @ArrayLen(2) [][]) this.seriesList.get(i); + double[][] d2 = (double @ArrayLen(2) [][]) that.seriesList.get(i); double[] d1x = d1[0]; double[] d2x = d2[0]; if (!Arrays.equals(d1x, d2x)) { @@ -371,7 +382,7 @@ public Object clone() throws CloneNotSupportedException { clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); clone.seriesList = new ArrayList(this.seriesList.size()); for (int i = 0; i < this.seriesList.size(); i++) { - double[][] data = (double[][]) this.seriesList.get(i); + double[][] data = (double @ArrayLen(2) [][]) this.seriesList.get(i); double[] x = data[0]; double[] y = data[1]; double[] xx = new double[x.length]; diff --git a/src/main/java/org/jfree/data/xy/DefaultXYZDataset.java b/src/main/java/org/jfree/data/xy/DefaultXYZDataset.java index 580cd05d4..a4adce92d 100644 --- a/src/main/java/org/jfree/data/xy/DefaultXYZDataset.java +++ b/src/main/java/org/jfree/data/xy/DefaultXYZDataset.java @@ -44,6 +44,9 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.common.value.qual.*; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -72,7 +75,7 @@ public class DefaultXYZDataset extends AbstractXYZDataset * order of the series is significant. This list must be kept in sync * with the seriesKeys list. */ - private List seriesList; + private List<double @ArrayLen(3) [][]> seriesList; /** * Creates a new {@code DefaultXYZDataset} instance, initially @@ -89,7 +92,7 @@ public DefaultXYZDataset() { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.seriesList.size(); } @@ -105,7 +108,7 @@ public int getSeriesCount() { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -121,7 +124,7 @@ public Comparable getSeriesKey(int series) { * @return The index, or -1. */ @Override - public int indexOf(Comparable seriesKey) { + public @GTENegativeOne int indexOf(Comparable seriesKey) { return this.seriesKeys.indexOf(seriesKey); } @@ -149,11 +152,13 @@ public DomainOrder getDomainOrder() { * specified range. */ @Override - public int getItemCount(int series) { + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } - double[][] seriesArray = (double[][]) this.seriesList.get(series); + @SuppressWarnings("index") // array-list interop: this.getSeries is the logical ghost variable representing this array + double @ArrayLen(3) [] @SameLen("this.getSeries(#1)") [] seriesArray = + (double @ArrayLen(3) [][]) this.seriesList.get(series); return seriesArray[0].length; } @@ -175,9 +180,11 @@ public int getItemCount(int series) { * @see #getX(int, int) */ @Override - public double getXValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[0][item]; + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(3) [][] seriesData = (double @ArrayLen(3) [][]) this.seriesList.get(series); + @SuppressWarnings("index") // this.seriesList.get is equivalent to this.getSeries + @IndexFor("seriesData[0]") int itemIndex = item; + return seriesData[0][itemIndex]; } /** @@ -198,7 +205,7 @@ public double getXValue(int series, int item) { * @see #getXValue(int, int) */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getXValue(series, item)); } @@ -220,9 +227,11 @@ public Number getX(int series, int item) { * @see #getY(int, int) */ @Override - public double getYValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[1][item]; + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(3) [][] seriesData = (double @ArrayLen(3) [][]) this.seriesList.get(series); + @SuppressWarnings("index") // this.seriesList.get is equivalent to this.getSeries + @IndexFor("seriesData[1]") int itemIndex = item; + return seriesData[1][itemIndex]; } /** @@ -243,7 +252,7 @@ public double getYValue(int series, int item) { * @see #getX(int, int) */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getYValue(series, item)); } @@ -265,9 +274,11 @@ public Number getY(int series, int item) { * @see #getZ(int, int) */ @Override - public double getZValue(int series, int item) { - double[][] seriesData = (double[][]) this.seriesList.get(series); - return seriesData[2][item]; + public double getZValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { + double @ArrayLen(3) [][] seriesData = (double @ArrayLen(3) [][]) this.seriesList.get(series); + @SuppressWarnings("index") // this.seriesList.get is equivalent to this.getSeries + @IndexFor("seriesData[2]") int itemIndex = item; + return seriesData[2][itemIndex]; } /** @@ -288,7 +299,7 @@ public double getZValue(int series, int item) { * @see #getZ(int, int) */ @Override - public Number getZ(int series, int item) { + public Number getZ(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getZValue(series, item)); } @@ -303,7 +314,7 @@ public Number getZ(int series, int item) { * second containing the y-values and the third containing the * z-values). */ - public void addSeries(Comparable seriesKey, double[][] data) { + public void addSeries(Comparable seriesKey, double @ArrayLen(3) [][] data) { if (seriesKey == null) { throw new IllegalArgumentException( "The 'seriesKey' cannot be null."); @@ -375,8 +386,8 @@ public boolean equals(Object obj) { return false; } for (int i = 0; i < this.seriesList.size(); i++) { - double[][] d1 = (double[][]) this.seriesList.get(i); - double[][] d2 = (double[][]) that.seriesList.get(i); + double[][] d1 = (double @ArrayLen(3) [][]) this.seriesList.get(i); + double[][] d2 = (double @ArrayLen(3) [][]) that.seriesList.get(i); double[] d1x = d1[0]; double[] d2x = d2[0]; if (!Arrays.equals(d1x, d2x)) { @@ -424,7 +435,7 @@ public Object clone() throws CloneNotSupportedException { clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); clone.seriesList = new ArrayList(this.seriesList.size()); for (int i = 0; i < this.seriesList.size(); i++) { - double[][] data = (double[][]) this.seriesList.get(i); + double[][] data = (double @ArrayLen(3) [][]) this.seriesList.get(i); double[] x = data[0]; double[] y = data[1]; double[] z = data[2]; diff --git a/src/main/java/org/jfree/data/xy/IntervalXYDataset.java b/src/main/java/org/jfree/data/xy/IntervalXYDataset.java index 8483e976c..922c30333 100644 --- a/src/main/java/org/jfree/data/xy/IntervalXYDataset.java +++ b/src/main/java/org/jfree/data/xy/IntervalXYDataset.java @@ -43,6 +43,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + /** * An extension of the {@link XYDataset} interface that allows an x-interval * and a y-interval to be defined. Note that the x and y values defined @@ -62,7 +66,7 @@ public interface IntervalXYDataset extends XYDataset { * * @return The lower bound of the x-interval ({@code null} permitted). */ - public Number getStartX(int series, int item); + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the lower bound of the x-interval (as a double primitive) for @@ -75,7 +79,7 @@ public interface IntervalXYDataset extends XYDataset { * * @see #getStartX(int, int) */ - public double getStartXValue(int series, int item); + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the upper bound of the x-interval for the specified series and @@ -87,7 +91,7 @@ public interface IntervalXYDataset extends XYDataset { * * @return The upper bound of the x-interval ({@code null} permitted). */ - public Number getEndX(int series, int item); + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the upper bound of the x-interval (as a double primitive) for @@ -100,7 +104,7 @@ public interface IntervalXYDataset extends XYDataset { * * @see #getEndX(int, int) */ - public double getEndXValue(int series, int item); + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the lower bound of the y-interval for the specified series and @@ -112,7 +116,7 @@ public interface IntervalXYDataset extends XYDataset { * * @return The lower bound of the y-interval ({@code null} permitted). */ - public Number getStartY(int series, int item); + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the lower bound of the y-interval (as a double primitive) for @@ -125,7 +129,7 @@ public interface IntervalXYDataset extends XYDataset { * * @see #getStartY(int, int) */ - public double getStartYValue(int series, int item); + public double getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the upper bound of the y-interval for the specified series and @@ -137,7 +141,7 @@ public interface IntervalXYDataset extends XYDataset { * * @return The upper bound of the y-interval ({@code null} permitted). */ - public Number getEndY(int series, int item); + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the upper bound of the y-interval (as a double primitive) for @@ -150,6 +154,6 @@ public interface IntervalXYDataset extends XYDataset { * * @see #getEndY(int, int) */ - public double getEndYValue(int series, int item); + public double getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); } diff --git a/src/main/java/org/jfree/data/xy/IntervalXYDelegate.java b/src/main/java/org/jfree/data/xy/IntervalXYDelegate.java index d1c67ba55..f86afa4cc 100644 --- a/src/main/java/org/jfree/data/xy/IntervalXYDelegate.java +++ b/src/main/java/org/jfree/data/xy/IntervalXYDelegate.java @@ -53,6 +53,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import org.jfree.chart.HashUtils; @@ -257,7 +261,7 @@ public double getIntervalWidth() { * * @see #getStartXValue(int, int) */ - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.dataset.getSeries(#1)") int item) { Number startX = null; Number x = this.dataset.getX(series, item); if (x != null) { @@ -277,7 +281,7 @@ public Number getStartX(int series, int item) { * * @see #getStartX(int, int) */ - public double getStartXValue(int series, int item) { + public double getStartXValue(@NonNegative int series, @IndexFor("this.dataset.getSeries(#1)") int item) { return this.dataset.getXValue(series, item) - getIntervalPositionFactor() * getIntervalWidth(); } @@ -292,7 +296,7 @@ public double getStartXValue(int series, int item) { * * @see #getEndXValue(int, int) */ - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.dataset.getSeries(#1)") int item) { Number endX = null; Number x = this.dataset.getX(series, item); if (x != null) { @@ -312,7 +316,7 @@ public Number getEndX(int series, int item) { * * @see #getEndX(int, int) */ - public double getEndXValue(int series, int item) { + public double getEndXValue(@NonNegative int series, @IndexFor("this.dataset.getSeries(#1)") int item) { return this.dataset.getXValue(series, item) + (1.0 - getIntervalPositionFactor()) * getIntervalWidth(); } @@ -415,10 +419,11 @@ private double recalculateInterval() { * * @return The interval width. */ - private double calculateIntervalForSeries(int series) { + private double calculateIntervalForSeries(@NonNegative int series) { double result = Double.POSITIVE_INFINITY; int itemCount = this.dataset.getItemCount(series); if (itemCount > 1) { + @SuppressWarnings("index") // itemCount is the length of this series, so 0 is safe here double prev = this.dataset.getXValue(series, 0); for (int item = 1; item < itemCount; item++) { double x = this.dataset.getXValue(series, item); diff --git a/src/main/java/org/jfree/data/xy/IntervalXYZDataset.java b/src/main/java/org/jfree/data/xy/IntervalXYZDataset.java index 2a217747d..f5d01c3cf 100644 --- a/src/main/java/org/jfree/data/xy/IntervalXYZDataset.java +++ b/src/main/java/org/jfree/data/xy/IntervalXYZDataset.java @@ -40,6 +40,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + /** * An extension of the {@link XYZDataset} interface that allows a range of data * to be defined for any of the X values, the Y values, and the Z values. @@ -54,7 +58,7 @@ public interface IntervalXYZDataset extends XYZDataset { * * @return The starting X value for the specified series and item. */ - public Number getStartXValue(int series, int item); + public Number getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the ending X value for the specified series and item. @@ -64,7 +68,7 @@ public interface IntervalXYZDataset extends XYZDataset { * * @return The ending X value for the specified series and item. */ - public Number getEndXValue(int series, int item); + public Number getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the starting Y value for the specified series and item. @@ -74,7 +78,7 @@ public interface IntervalXYZDataset extends XYZDataset { * * @return The starting Y value for the specified series and item. */ - public Number getStartYValue(int series, int item); + public Number getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the ending Y value for the specified series and item. @@ -84,7 +88,7 @@ public interface IntervalXYZDataset extends XYZDataset { * * @return The ending Y value for the specified series and item. */ - public Number getEndYValue(int series, int item); + public Number getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the starting Z value for the specified series and item. @@ -94,7 +98,7 @@ public interface IntervalXYZDataset extends XYZDataset { * * @return The starting Z value for the specified series and item. */ - public Number getStartZValue(int series, int item); + public Number getStartZValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the ending Z value for the specified series and item. @@ -104,6 +108,6 @@ public interface IntervalXYZDataset extends XYZDataset { * * @return The ending Z value for the specified series and item. */ - public Number getEndZValue(int series, int item); + public Number getEndZValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); } diff --git a/src/main/java/org/jfree/data/xy/MatrixSeries.java b/src/main/java/org/jfree/data/xy/MatrixSeries.java index 22d774e64..033203eac 100644 --- a/src/main/java/org/jfree/data/xy/MatrixSeries.java +++ b/src/main/java/org/jfree/data/xy/MatrixSeries.java @@ -47,6 +47,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import org.jfree.data.general.Series; @@ -61,7 +63,7 @@ public class MatrixSeries extends Series implements Serializable { private static final long serialVersionUID = 7934188527308315704L; /** Series matrix values */ - protected double[][] data; + protected double @SameLen("this") [] @SameLen("this.data") [] data; /** * Constructs a new matrix series. @@ -73,7 +75,8 @@ public class MatrixSeries extends Series implements Serializable { * @param rows the number of rows. * @param columns the number of columns. */ - public MatrixSeries(String name, int rows, int columns) { + @SuppressWarnings("index") // SameLen on custom collections requires a suppressed warning to establish representation invariant https://github.com/kelloggm/checker-framework/issues/213 + public MatrixSeries(String name, @NonNegative int rows, @NonNegative int columns) { super(name); this.data = new double[rows][columns]; zeroAll(); @@ -84,7 +87,8 @@ public MatrixSeries(String name, int rows, int columns) { * * @return The number of columns in this matrix series. */ - public int getColumnsCount() { + @SuppressWarnings("index") // documentation bug: this method assumes that there is at least one row, but the documentation doesn't say so. This is a documentation bug. + public @LengthOf("this.data") int getColumnsCount() { return this.data[0].length; } @@ -99,7 +103,7 @@ public int getColumnsCount() { * * @see #get(int, int) */ - public Number getItem(int itemIndex) { + public Number getItem(@NonNegative int itemIndex) { int i = getItemRow(itemIndex); int j = getItemColumn(itemIndex); @@ -116,7 +120,7 @@ public Number getItem(int itemIndex) { * * @return The column of the specified item. */ - public int getItemColumn(int itemIndex) { + public @IndexFor("this.data") int getItemColumn(@NonNegative int itemIndex) { //assert itemIndex >= 0 && itemIndex < getItemCount(); return itemIndex % getColumnsCount(); } @@ -128,7 +132,7 @@ public int getItemColumn(int itemIndex) { * @return The item count. */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return getRowCount() * getColumnsCount(); } @@ -140,7 +144,8 @@ public int getItemCount() { * * @return The row of the specified item. */ - public int getItemRow(int itemIndex) { + @SuppressWarnings("index") // dividing the index by the column count results in a row index + public @IndexFor("this") int getItemRow(@NonNegative int itemIndex) { //assert itemIndex >= 0 && itemIndex < getItemCount(); return itemIndex / getColumnsCount(); } @@ -151,7 +156,7 @@ public int getItemRow(int itemIndex) { * * @return The number of rows in this matrix series. */ - public int getRowCount() { + public @LengthOf("this") int getRowCount() { return this.data.length; } @@ -167,7 +172,7 @@ public int getRowCount() { * @see #getItem(int) * @see #update(int, int, double) */ - public double get(int i, int j) { + public double get(@IndexFor("this") int i, @IndexFor("this.data") int j) { return this.data[i][j]; } @@ -181,7 +186,7 @@ public double get(int i, int j) { * * @see #get(int, int) */ - public void update(int i, int j, double mij) { + public void update(@IndexFor("this") int i, @IndexFor("this.data") int j, double mij) { this.data[i][j] = mij; fireSeriesChanged(); } @@ -228,7 +233,9 @@ public boolean equals(Object obj) { } for (int r = 0; r < getRowCount(); r++) { for (int c = 0; c < getColumnsCount(); c++) { - if (get(r, c) != that.get(r, c)) { + @SuppressWarnings("index") // row and column counts have to be equal, as checked above + boolean test = get(r, c) != that.get(r, c); + if (test) { return false; } } diff --git a/src/main/java/org/jfree/data/xy/MatrixSeriesCollection.java b/src/main/java/org/jfree/data/xy/MatrixSeriesCollection.java index 351762d77..760ba1e46 100644 --- a/src/main/java/org/jfree/data/xy/MatrixSeriesCollection.java +++ b/src/main/java/org/jfree/data/xy/MatrixSeriesCollection.java @@ -45,6 +45,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import java.util.List; import org.jfree.chart.util.ObjectUtils; @@ -96,7 +98,8 @@ public MatrixSeriesCollection(MatrixSeries series) { * @return The number of items in the specified series. */ @Override - public int getItemCount(int seriesIndex) { + @SuppressWarnings("index") // array-list interop: getSeries.getItemCount cannot be annotated correctly because some Series are mutable length + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int seriesIndex) { return getSeries(seriesIndex).getItemCount(); } @@ -108,7 +111,7 @@ public int getItemCount(int seriesIndex) { * * @return The series. */ - public MatrixSeries getSeries(int seriesIndex) { + public MatrixSeries getSeries(@NonNegative int seriesIndex) { if ((seriesIndex < 0) || (seriesIndex > getSeriesCount())) { throw new IllegalArgumentException("Index outside valid range."); } @@ -123,7 +126,7 @@ public MatrixSeries getSeries(int seriesIndex) { * @return The number of series in the collection. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.seriesList.size(); } @@ -136,7 +139,7 @@ public int getSeriesCount() { * @return The key for a series. */ @Override - public Comparable getSeriesKey(int seriesIndex) { + public Comparable getSeriesKey(@NonNegative int seriesIndex) { return getSeries(seriesIndex).getKey(); } @@ -153,7 +156,7 @@ public Comparable getSeriesKey(int seriesIndex) { * @see org.jfree.data.xy.XYDataset#getXValue(int, int) */ @Override - public Number getX(int seriesIndex, int itemIndex) { + public Number getX(@NonNegative int seriesIndex, @IndexFor("this.getSeries(#1)") int itemIndex) { MatrixSeries series = (MatrixSeries) this.seriesList.get(seriesIndex); int x = series.getItemColumn(itemIndex); @@ -173,7 +176,7 @@ public Number getX(int seriesIndex, int itemIndex) { * @see org.jfree.data.xy.XYDataset#getYValue(int, int) */ @Override - public Number getY(int seriesIndex, int itemIndex) { + public Number getY(@NonNegative int seriesIndex, @IndexFor("this.getSeries(#1)") int itemIndex) { MatrixSeries series = (MatrixSeries) this.seriesList.get(seriesIndex); int y = series.getItemRow(itemIndex); @@ -193,7 +196,7 @@ public Number getY(int seriesIndex, int itemIndex) { * @see org.jfree.data.xy.XYZDataset#getZValue(int, int) */ @Override - public Number getZ(int seriesIndex, int itemIndex) { + public Number getZ(@NonNegative int seriesIndex, @IndexFor("this.getSeries(#1)") int itemIndex) { MatrixSeries series = (MatrixSeries) this.seriesList.get(seriesIndex); Number z = series.getItem(itemIndex); return z; @@ -314,7 +317,7 @@ public void removeSeries(MatrixSeries series) { * * @param seriesIndex the series (zero based index). */ - public void removeSeries(int seriesIndex) { + public void removeSeries(@NonNegative int seriesIndex) { // check arguments... if ((seriesIndex < 0) || (seriesIndex > getSeriesCount())) { throw new IllegalArgumentException("Index outside valid range."); diff --git a/src/main/java/org/jfree/data/xy/NormalizedMatrixSeries.java b/src/main/java/org/jfree/data/xy/NormalizedMatrixSeries.java index 2adbc2678..2b95c84ce 100644 --- a/src/main/java/org/jfree/data/xy/NormalizedMatrixSeries.java +++ b/src/main/java/org/jfree/data/xy/NormalizedMatrixSeries.java @@ -41,6 +41,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + /** * Represents a dense normalized matrix M[i,j] where each Mij item of the * matrix has a value (default is 0). When a matrix item is observed using @@ -68,7 +70,7 @@ public class NormalizedMatrixSeries extends MatrixSeries { * @param rows the number of rows. * @param columns the number of columns. */ - public NormalizedMatrixSeries(String name, int rows, int columns) { + public NormalizedMatrixSeries(String name, @NonNegative int rows, @NonNegative int columns) { super(name, rows, columns); /* @@ -90,7 +92,7 @@ public NormalizedMatrixSeries(String name, int rows, int columns) { * @see org.jfree.data.xy.MatrixSeries#getItem(int) */ @Override - public Number getItem(int itemIndex) { + public Number getItem(@NonNegative int itemIndex) { int i = getItemRow(itemIndex); int j = getItemColumn(itemIndex); @@ -135,7 +137,7 @@ public double getScaleFactor() { * @see #get(int, int) */ @Override - public void update(int i, int j, double mij) { + public void update(@IndexFor("this") int i, @IndexFor("this.data") int j, double mij) { this.m_totalSum -= get(i, j); this.m_totalSum += mij; diff --git a/src/main/java/org/jfree/data/xy/OHLCDataset.java b/src/main/java/org/jfree/data/xy/OHLCDataset.java index c75943223..3cdbfc132 100644 --- a/src/main/java/org/jfree/data/xy/OHLCDataset.java +++ b/src/main/java/org/jfree/data/xy/OHLCDataset.java @@ -48,6 +48,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + /** * An interface that defines data in the form of (x, high, low, open, close) * tuples. @@ -62,7 +66,7 @@ public interface OHLCDataset extends XYDataset { * * @return The value. */ - public Number getHigh(int series, int item); + public Number getHigh(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the high-value (as a double primitive) for an item within a @@ -73,7 +77,7 @@ public interface OHLCDataset extends XYDataset { * * @return The high-value. */ - public double getHighValue(int series, int item); + public double getHighValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the low-value for the specified series and item. @@ -83,7 +87,7 @@ public interface OHLCDataset extends XYDataset { * * @return The value. */ - public Number getLow(int series, int item); + public Number getLow(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the low-value (as a double primitive) for an item within a @@ -94,7 +98,7 @@ public interface OHLCDataset extends XYDataset { * * @return The low-value. */ - public double getLowValue(int series, int item); + public double getLowValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the open-value for the specified series and item. @@ -104,7 +108,7 @@ public interface OHLCDataset extends XYDataset { * * @return The value. */ - public Number getOpen(int series, int item); + public Number getOpen(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the open-value (as a double primitive) for an item within a @@ -115,7 +119,7 @@ public interface OHLCDataset extends XYDataset { * * @return The open-value. */ - public double getOpenValue(int series, int item); + public double getOpenValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the y-value for the specified series and item. @@ -125,7 +129,7 @@ public interface OHLCDataset extends XYDataset { * * @return The value. */ - public Number getClose(int series, int item); + public Number getClose(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the close-value (as a double primitive) for an item within a @@ -136,7 +140,7 @@ public interface OHLCDataset extends XYDataset { * * @return The close-value. */ - public double getCloseValue(int series, int item); + public double getCloseValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the volume for the specified series and item. @@ -146,7 +150,7 @@ public interface OHLCDataset extends XYDataset { * * @return The value. */ - public Number getVolume(int series, int item); + public Number getVolume(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the volume-value (as a double primitive) for an item within a @@ -157,6 +161,6 @@ public interface OHLCDataset extends XYDataset { * * @return The volume-value. */ - public double getVolumeValue(int series, int item); + public double getVolumeValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); } diff --git a/src/main/java/org/jfree/data/xy/TableXYDataset.java b/src/main/java/org/jfree/data/xy/TableXYDataset.java index b9878ec6d..0e6e95615 100644 --- a/src/main/java/org/jfree/data/xy/TableXYDataset.java +++ b/src/main/java/org/jfree/data/xy/TableXYDataset.java @@ -42,6 +42,7 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; /** * A dataset containing one or more data series containing (x, y) data items, @@ -50,6 +51,7 @@ * x-values between series). This is used primarily by the * {@link org.jfree.chart.renderer.xy.StackedXYAreaRenderer}. */ +@SuppressWarnings("index") // I think this is a documentation bug. The annotation on getItemCount below is correct, I believe, but it's inconsistent with the documentation on this interface. public interface TableXYDataset extends XYDataset { /** @@ -57,6 +59,6 @@ public interface TableXYDataset extends XYDataset { * * @return The item count. */ - public int getItemCount(); + public @NonNegative int getItemCount(); } diff --git a/src/main/java/org/jfree/data/xy/VectorSeries.java b/src/main/java/org/jfree/data/xy/VectorSeries.java index f6d21cdf1..f53583406 100644 --- a/src/main/java/org/jfree/data/xy/VectorSeries.java +++ b/src/main/java/org/jfree/data/xy/VectorSeries.java @@ -46,6 +46,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.general.SeriesChangeEvent; @@ -117,7 +119,7 @@ public void add(VectorDataItem item, boolean notify) { * @return The item removed. */ @Override - public ComparableObjectItem remove(int index) { + public ComparableObjectItem remove(@NonNegative int index) { VectorDataItem result = (VectorDataItem) this.data.remove(index); fireSeriesChanged(); return result; @@ -130,7 +132,7 @@ public ComparableObjectItem remove(int index) { * * @return The x-value. */ - public double getXValue(int index) { + public double getXValue(@NonNegative int index) { VectorDataItem item = (VectorDataItem) this.getDataItem(index); return item.getXValue(); } @@ -142,7 +144,7 @@ public double getXValue(int index) { * * @return The y-value. */ - public double getYValue(int index) { + public double getYValue(@NonNegative int index) { VectorDataItem item = (VectorDataItem) getDataItem(index); return item.getYValue(); } @@ -154,7 +156,7 @@ public double getYValue(int index) { * * @return The x-component of the vector. */ - public double getVectorXValue(int index) { + public double getVectorXValue(@NonNegative int index) { VectorDataItem item = (VectorDataItem) getDataItem(index); return item.getVectorX(); } @@ -166,7 +168,7 @@ public double getVectorXValue(int index) { * * @return The y-component of the vector. */ - public double getVectorYValue(int index) { + public double getVectorYValue(@NonNegative int index) { VectorDataItem item = (VectorDataItem) getDataItem(index); return item.getVectorY(); } @@ -179,7 +181,7 @@ public double getVectorYValue(int index) { * @return The data item. */ @Override - public ComparableObjectItem getDataItem(int index) { + public ComparableObjectItem getDataItem(@NonNegative int index) { // overridden to make public return super.getDataItem(index); } diff --git a/src/main/java/org/jfree/data/xy/VectorSeriesCollection.java b/src/main/java/org/jfree/data/xy/VectorSeriesCollection.java index 5b838b37f..ba9dde846 100644 --- a/src/main/java/org/jfree/data/xy/VectorSeriesCollection.java +++ b/src/main/java/org/jfree/data/xy/VectorSeriesCollection.java @@ -45,6 +45,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.List; import org.jfree.chart.util.ObjectUtils; @@ -128,7 +132,7 @@ public void removeAllSeries() { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -142,7 +146,7 @@ public int getSeriesCount() { * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ - public VectorSeries getSeries(int series) { + public VectorSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -161,7 +165,7 @@ public VectorSeries getSeries(int series) { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // defer argument checking return getSeries(series).getKey(); } @@ -174,7 +178,7 @@ public Comparable getSeriesKey(int series) { * * @return The series index. */ - public int indexOf(VectorSeries series) { + public @GTENegativeOne int indexOf(VectorSeries series) { Args.nullNotPermitted(series, "series"); return this.data.indexOf(series); } @@ -190,7 +194,8 @@ public int indexOf(VectorSeries series) { * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: getSeries.getItemCount cannot be annotated because some series are mutable + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { // defer argument checking return getSeries(series).getItemCount(); } @@ -204,7 +209,7 @@ public int getItemCount(int series) { * @return The x-value. */ @Override - public double getXValue(int series, int item) { + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getXValue(); @@ -221,7 +226,7 @@ public double getXValue(int series, int item) { * @return The x-value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getXValue(series, item)); } @@ -234,7 +239,7 @@ public Number getX(int series, int item) { * @return The y-value. */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getYValue(); @@ -251,7 +256,7 @@ public double getYValue(int series, int item) { * @return The y-value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getYValue(series, item)); } @@ -264,7 +269,7 @@ public Number getY(int series, int item) { * @return The vector (possibly {@code null}). */ @Override - public Vector getVector(int series, int item) { + public Vector getVector(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getVector(); @@ -279,7 +284,7 @@ public Vector getVector(int series, int item) { * @return The x-component of the vector. */ @Override - public double getVectorXValue(int series, int item) { + public double getVectorXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getVectorX(); @@ -294,7 +299,7 @@ public double getVectorXValue(int series, int item) { * @return The y-component of the vector. */ @Override - public double getVectorYValue(int series, int item) { + public double getVectorYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getVectorY(); diff --git a/src/main/java/org/jfree/data/xy/VectorXYDataset.java b/src/main/java/org/jfree/data/xy/VectorXYDataset.java index 6d170722b..7c2682efe 100644 --- a/src/main/java/org/jfree/data/xy/VectorXYDataset.java +++ b/src/main/java/org/jfree/data/xy/VectorXYDataset.java @@ -44,6 +44,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + /** * An extension of the {@link XYDataset} interface that allows a vector to be * defined at a specific (x, y) location. @@ -60,7 +62,7 @@ public interface VectorXYDataset extends XYDataset { * * @return The x-component of the vector. */ - public double getVectorXValue(int series, int item); + public double getVectorXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the y-component of the vector for an item in a series. @@ -70,7 +72,7 @@ public interface VectorXYDataset extends XYDataset { * * @return The y-component of the vector. */ - public double getVectorYValue(int series, int item); + public double getVectorYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the vector for an item in a series. Depending on the particular @@ -84,6 +86,6 @@ public interface VectorXYDataset extends XYDataset { * * @return The vector (possibly {@code null}). */ - public Vector getVector(int series, int item); + public Vector getVector(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); } diff --git a/src/main/java/org/jfree/data/xy/WindDataset.java b/src/main/java/org/jfree/data/xy/WindDataset.java index 0d59ef22b..33c9b3d6f 100644 --- a/src/main/java/org/jfree/data/xy/WindDataset.java +++ b/src/main/java/org/jfree/data/xy/WindDataset.java @@ -41,6 +41,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + /** * Interface for a dataset that supplies wind intensity and direction values * observed at various points in time. @@ -58,7 +62,7 @@ public interface WindDataset extends XYDataset { * * @return The wind direction. */ - public Number getWindDirection(int series, int item); + public Number getWindDirection(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the wind force on the Beaufort scale (0 to 12). See: @@ -72,6 +76,6 @@ public interface WindDataset extends XYDataset { * * @return The wind force. */ - public Number getWindForce(int series, int item); + public Number getWindForce(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); } diff --git a/src/main/java/org/jfree/data/xy/XIntervalSeries.java b/src/main/java/org/jfree/data/xy/XIntervalSeries.java index 9d5b4caef..75123ffdf 100644 --- a/src/main/java/org/jfree/data/xy/XIntervalSeries.java +++ b/src/main/java/org/jfree/data/xy/XIntervalSeries.java @@ -42,6 +42,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.general.SeriesChangeEvent; @@ -114,7 +116,7 @@ public void add(XIntervalDataItem item, boolean notify) { * * @return The x-value (never {@code null}). */ - public Number getX(int index) { + public Number getX(@NonNegative int index) { XIntervalDataItem item = (XIntervalDataItem) getDataItem(index); return item.getX(); } @@ -128,7 +130,7 @@ public Number getX(int index) { * * @since 1.0.10 */ - public double getXLowValue(int index) { + public double getXLowValue(@NonNegative int index) { XIntervalDataItem item = (XIntervalDataItem) getDataItem(index); return item.getXLowValue(); } @@ -142,7 +144,7 @@ public double getXLowValue(int index) { * * @since 1.0.10 */ - public double getXHighValue(int index) { + public double getXHighValue(@NonNegative int index) { XIntervalDataItem item = (XIntervalDataItem) getDataItem(index); return item.getXHighValue(); } @@ -154,7 +156,7 @@ public double getXHighValue(int index) { * * @return The y-value. */ - public double getYValue(int index) { + public double getYValue(@NonNegative int index) { XIntervalDataItem item = (XIntervalDataItem) getDataItem(index); return item.getYValue(); } @@ -167,7 +169,7 @@ public double getYValue(int index) { * @return The data item. */ @Override - public ComparableObjectItem getDataItem(int index) { + public ComparableObjectItem getDataItem(@NonNegative int index) { return super.getDataItem(index); } diff --git a/src/main/java/org/jfree/data/xy/XIntervalSeriesCollection.java b/src/main/java/org/jfree/data/xy/XIntervalSeriesCollection.java index 36ce99a37..c9aa62921 100644 --- a/src/main/java/org/jfree/data/xy/XIntervalSeriesCollection.java +++ b/src/main/java/org/jfree/data/xy/XIntervalSeriesCollection.java @@ -44,6 +44,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.List; import org.jfree.chart.util.ObjectUtils; @@ -91,7 +95,7 @@ public void addSeries(XIntervalSeries series) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -105,7 +109,7 @@ public int getSeriesCount() { * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ - public XIntervalSeries getSeries(int series) { + public XIntervalSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -124,7 +128,7 @@ public XIntervalSeries getSeries(int series) { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // defer argument checking return getSeries(series).getKey(); } @@ -140,7 +144,8 @@ public Comparable getSeriesKey(int series) { * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: getSeries.getItemCount can't be annotated because some series are mutable + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { // defer argument checking return getSeries(series).getItemCount(); } @@ -154,7 +159,7 @@ public int getItemCount(int series) { * @return The x-value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); return di.getX(); @@ -170,7 +175,7 @@ public Number getX(int series, int item) { * @return The value. */ @Override - public double getStartXValue(int series, int item) { + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); return s.getXLowValue(item); } @@ -185,7 +190,7 @@ public double getStartXValue(int series, int item) { * @return The value. */ @Override - public double getEndXValue(int series, int item) { + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); return s.getXHighValue(item); } @@ -200,7 +205,7 @@ public double getEndXValue(int series, int item) { * @return The value. */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); return s.getYValue(item); } @@ -214,7 +219,7 @@ public double getYValue(int series, int item) { * @return The y-value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); return new Double(di.getYValue()); @@ -229,7 +234,7 @@ public Number getY(int series, int item) { * @return The x-value. */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); return new Double(di.getXLowValue()); @@ -244,7 +249,7 @@ public Number getStartX(int series, int item) { * @return The x-value. */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); return new Double(di.getXHighValue()); @@ -260,7 +265,7 @@ public Number getEndX(int series, int item) { * @return The start y-value. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -274,7 +279,7 @@ public Number getStartY(int series, int item) { * @return The end y-value. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -286,7 +291,7 @@ public Number getEndY(int series, int item) { * * @since 1.0.10 */ - public void removeSeries(int series) { + public void removeSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds."); } diff --git a/src/main/java/org/jfree/data/xy/XYBarDataset.java b/src/main/java/org/jfree/data/xy/XYBarDataset.java index 5763f1cc1..730abd47d 100644 --- a/src/main/java/org/jfree/data/xy/XYBarDataset.java +++ b/src/main/java/org/jfree/data/xy/XYBarDataset.java @@ -49,6 +49,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; @@ -122,7 +126,7 @@ public void setBarWidth(double barWidth) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.underlying.getSeriesCount(); } @@ -135,7 +139,7 @@ public int getSeriesCount() { * @return The series key. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { return this.underlying.getSeriesKey(series); } @@ -147,7 +151,8 @@ public Comparable getSeriesKey(int series) { * @return The item count. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { return this.underlying.getItemCount(series); } @@ -162,7 +167,8 @@ public int getItemCount(int series) { * @see #getXValue(int, int) */ @Override - public Number getX(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.underlying.getX(series, item); } @@ -177,7 +183,8 @@ public Number getX(int series, int item) { * @see #getX(int, int) */ @Override - public double getXValue(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.underlying.getXValue(series, item); } @@ -192,7 +199,8 @@ public double getXValue(int series, int item) { * @see #getYValue(int, int) */ @Override - public Number getY(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.underlying.getY(series, item); } @@ -207,7 +215,8 @@ public Number getY(int series, int item) { * @see #getY(int, int) */ @Override - public double getYValue(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.underlying.getYValue(series, item); } @@ -220,7 +229,8 @@ public double getYValue(int series, int item) { * @return The value. */ @Override - public Number getStartX(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; Number xnum = this.underlying.getX(series, item); if (xnum != null) { @@ -241,7 +251,7 @@ public Number getStartX(int series, int item) { * @see #getXValue(int, int) */ @Override - public double getStartXValue(int series, int item) { + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getXValue(series, item) - this.barWidth / 2.0; } @@ -254,7 +264,8 @@ public double getStartXValue(int series, int item) { * @return The value. */ @Override - public Number getEndX(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { Number result = null; Number xnum = this.underlying.getX(series, item); if (xnum != null) { @@ -275,7 +286,7 @@ public Number getEndX(int series, int item) { * @see #getXValue(int, int) */ @Override - public double getEndXValue(int series, int item) { + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getXValue(series, item) + this.barWidth / 2.0; } @@ -288,7 +299,8 @@ public double getEndXValue(int series, int item) { * @return The value. */ @Override - public Number getStartY(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.underlying.getY(series, item); } @@ -304,7 +316,7 @@ public Number getStartY(int series, int item) { * @see #getYValue(int, int) */ @Override - public double getStartYValue(int series, int item) { + public double getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getYValue(series, item); } @@ -317,7 +329,8 @@ public double getStartYValue(int series, int item) { * @return The value. */ @Override - public Number getEndY(int series, int item) { + @SuppressWarnings("index") // retain information when casting https://github.com/kelloggm/checker-framework/issues/212 + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.underlying.getY(series, item); } @@ -333,7 +346,7 @@ public Number getEndY(int series, int item) { * @see #getYValue(int, int) */ @Override - public double getEndYValue(int series, int item) { + public double getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getYValue(series, item); } diff --git a/src/main/java/org/jfree/data/xy/XYDataset.java b/src/main/java/org/jfree/data/xy/XYDataset.java index 4306de7dc..f8ff68bbb 100644 --- a/src/main/java/org/jfree/data/xy/XYDataset.java +++ b/src/main/java/org/jfree/data/xy/XYDataset.java @@ -47,6 +47,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.DomainOrder; import org.jfree.data.general.SeriesDataset; @@ -74,7 +78,7 @@ public interface XYDataset extends SeriesDataset { * * @return The item count. */ - public int getItemCount(int series); + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series); /** * Returns the x-value for an item within a series. The x-values may or @@ -88,7 +92,7 @@ public interface XYDataset extends SeriesDataset { * * @return The x-value (never {@code null}). */ - public Number getX(int series, int item); + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the x-value for an item within a series. @@ -100,7 +104,7 @@ public interface XYDataset extends SeriesDataset { * * @return The x-value. */ - public double getXValue(int series, int item); + public double getXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the y-value for an item within a series. @@ -112,7 +116,7 @@ public interface XYDataset extends SeriesDataset { * * @return The y-value (possibly {@code null}). */ - public Number getY(int series, int item); + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the y-value (as a double primitive) for an item within a series. @@ -124,6 +128,6 @@ public interface XYDataset extends SeriesDataset { * * @return The y-value. */ - public double getYValue(int series, int item); + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); } diff --git a/src/main/java/org/jfree/data/xy/XYDatasetTableModel.java b/src/main/java/org/jfree/data/xy/XYDatasetTableModel.java index 843dcb783..4ddf89850 100644 --- a/src/main/java/org/jfree/data/xy/XYDatasetTableModel.java +++ b/src/main/java/org/jfree/data/xy/XYDatasetTableModel.java @@ -44,6 +44,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.NonNegative; + import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; @@ -103,7 +105,7 @@ public void setModel(TableXYDataset dataset) { * @return The row count. */ @Override - public int getRowCount() { + public @NonNegative int getRowCount() { if (this.model == null) { return 0; } @@ -116,7 +118,7 @@ public int getRowCount() { * @return The number of columns in the model. */ @Override - public int getColumnCount() { + public @NonNegative int getColumnCount() { if (this.model == null) { return 0; } @@ -131,7 +133,7 @@ public int getColumnCount() { * @return The column name. */ @Override - public String getColumnName(int column) { + public String getColumnName(@NonNegative int column) { if (this.model == null) { return super.getColumnName(column); } @@ -153,7 +155,8 @@ public String getColumnName(int column) { * @return The value of the specified cell. */ @Override - public Object getValueAt(int row, int column) { + @SuppressWarnings("index") // array-list interop: this method relies on row being an index into a particular data series in the underlying dataset. The annotations can't neatly express that fact (not all implementations of this interface would even have an underlying dataset...), so I'm not sure how I'd verify this. + public Object getValueAt(@NonNegative int row, @NonNegative int column) { if (this.model == null) { return null; } @@ -186,7 +189,7 @@ public void datasetChanged(DatasetChangeEvent event) { * @return {@code true} if the specified cell is editable. */ @Override - public boolean isCellEditable(int row, int column) { + public boolean isCellEditable(@NonNegative int row, @NonNegative int column) { return false; } @@ -198,7 +201,7 @@ public boolean isCellEditable(int row, int column) { * @param column the column. */ @Override - public void setValueAt(Object value, int row, int column) { + public void setValueAt(Object value, @NonNegative int row, @NonNegative int column) { if (isCellEditable(row, column)) { // XYDataset only provides methods for reading a dataset... } diff --git a/src/main/java/org/jfree/data/xy/XYIntervalSeries.java b/src/main/java/org/jfree/data/xy/XYIntervalSeries.java index 6b8616a11..1bacad1f1 100644 --- a/src/main/java/org/jfree/data/xy/XYIntervalSeries.java +++ b/src/main/java/org/jfree/data/xy/XYIntervalSeries.java @@ -42,6 +42,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.general.SeriesChangeEvent; @@ -117,7 +119,7 @@ public void add(XYIntervalDataItem item, boolean notify) { * * @return The x-value (never {@code null}). */ - public Number getX(int index) { + public Number getX(@NonNegative int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getX(); } @@ -132,7 +134,7 @@ public Number getX(int index) { * * @since 1.0.5 */ - public double getXLowValue(int index) { + public double getXLowValue(@NonNegative int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getXLowValue(); } @@ -147,7 +149,7 @@ public double getXLowValue(int index) { * * @since 1.0.5 */ - public double getXHighValue(int index) { + public double getXHighValue(@NonNegative int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getXHighValue(); } @@ -159,7 +161,7 @@ public double getXHighValue(int index) { * * @return The y-value. */ - public double getYValue(int index) { + public double getYValue(@NonNegative int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getYValue(); } @@ -174,7 +176,7 @@ public double getYValue(int index) { * * @since 1.0.5 */ - public double getYLowValue(int index) { + public double getYLowValue(@NonNegative int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getYLowValue(); } @@ -189,7 +191,7 @@ public double getYLowValue(int index) { * * @since 1.0.5 */ - public double getYHighValue(int index) { + public double getYHighValue(@NonNegative int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getYHighValue(); } @@ -202,7 +204,7 @@ public double getYHighValue(int index) { * @return The data item. */ @Override - public ComparableObjectItem getDataItem(int index) { + public ComparableObjectItem getDataItem(@NonNegative int index) { return super.getDataItem(index); } diff --git a/src/main/java/org/jfree/data/xy/XYIntervalSeriesCollection.java b/src/main/java/org/jfree/data/xy/XYIntervalSeriesCollection.java index 13efceff2..f361b542b 100644 --- a/src/main/java/org/jfree/data/xy/XYIntervalSeriesCollection.java +++ b/src/main/java/org/jfree/data/xy/XYIntervalSeriesCollection.java @@ -46,6 +46,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.List; import org.jfree.chart.util.ObjectUtils; @@ -93,7 +97,7 @@ public void addSeries(XYIntervalSeries series) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -107,7 +111,7 @@ public int getSeriesCount() { * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ - public XYIntervalSeries getSeries(int series) { + public XYIntervalSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -126,7 +130,7 @@ public XYIntervalSeries getSeries(int series) { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // defer argument checking return getSeries(series).getKey(); } @@ -142,7 +146,8 @@ public Comparable getSeriesKey(int series) { * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: getSeries.getItemCount cannot be annotated because some series are mutable + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { // defer argument checking return getSeries(series).getItemCount(); } @@ -156,7 +161,7 @@ public int getItemCount(int series) { * @return The x-value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getX(item); } @@ -171,7 +176,7 @@ public Number getX(int series, int item) { * @return The value. */ @Override - public double getStartXValue(int series, int item) { + public double getStartXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getXLowValue(item); } @@ -186,7 +191,7 @@ public double getStartXValue(int series, int item) { * @return The value. */ @Override - public double getEndXValue(int series, int item) { + public double getEndXValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getXHighValue(item); } @@ -201,7 +206,7 @@ public double getEndXValue(int series, int item) { * @return The value. */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getYValue(item); } @@ -216,7 +221,7 @@ public double getYValue(int series, int item) { * @return The value. */ @Override - public double getStartYValue(int series, int item) { + public double getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getYLowValue(item); } @@ -231,7 +236,7 @@ public double getStartYValue(int series, int item) { * @return The value. */ @Override - public double getEndYValue(int series, int item) { + public double getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getYHighValue(item); } @@ -245,7 +250,7 @@ public double getEndYValue(int series, int item) { * @return The y-value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getYValue(series, item)); } @@ -258,7 +263,7 @@ public Number getY(int series, int item) { * @return The x-value. */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getStartXValue(series, item)); } @@ -271,7 +276,7 @@ public Number getStartX(int series, int item) { * @return The x-value. */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getEndXValue(series, item)); } @@ -285,7 +290,7 @@ public Number getEndX(int series, int item) { * @return The start y-value. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getStartYValue(series, item)); } @@ -299,7 +304,7 @@ public Number getStartY(int series, int item) { * @return The end y-value. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return new Double(getEndYValue(series, item)); } @@ -311,7 +316,7 @@ public Number getEndY(int series, int item) { * * @since 1.0.10 */ - public void removeSeries(int series) { + public void removeSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds."); } diff --git a/src/main/java/org/jfree/data/xy/XYItemKey.java b/src/main/java/org/jfree/data/xy/XYItemKey.java index 7710663d9..39252bd5a 100644 --- a/src/main/java/org/jfree/data/xy/XYItemKey.java +++ b/src/main/java/org/jfree/data/xy/XYItemKey.java @@ -40,6 +40,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + import java.io.Serializable; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; @@ -69,7 +71,7 @@ public class XYItemKey<S extends Comparable<S>> implements ItemKey, * @param seriesKey the series key. * @param itemIndex the item index. */ - public XYItemKey(S seriesKey, int itemIndex) { + public XYItemKey(S seriesKey, @NonNegative int itemIndex) { Args.nullNotPermitted(seriesKey, "seriesKey"); this.seriesKey = seriesKey; this.itemIndex = itemIndex; diff --git a/src/main/java/org/jfree/data/xy/XYSeries.java b/src/main/java/org/jfree/data/xy/XYSeries.java index 040112b57..044a61f8e 100644 --- a/src/main/java/org/jfree/data/xy/XYSeries.java +++ b/src/main/java/org/jfree/data/xy/XYSeries.java @@ -80,6 +80,9 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.Collections; import java.util.Iterator; @@ -111,7 +114,7 @@ public class XYSeries extends Series implements Cloneable, Serializable { protected List data; /** The maximum number of items for the series. */ - private int maximumItemCount = Integer.MAX_VALUE; + private @NonNegative int maximumItemCount = Integer.MAX_VALUE; /** * A flag that controls whether the items are automatically sorted @@ -265,6 +268,7 @@ private void updateBoundsForAddedItem(XYDataItem item) { * * @since 1.0.13 */ + @SuppressWarnings("index") // documentation bug: this method cannot be called unless there is at least one more item beyond the parameter in this dataset: I believe this is a bug private void updateBoundsForRemovedItem(XYDataItem item) { boolean itemContributesToXBounds = false; boolean itemContributesToYBounds = false; @@ -343,7 +347,7 @@ public boolean getAllowDuplicateXValues() { * @see #getItems() */ @Override - public int getItemCount() { + public @NonNegative int getItemCount() { return this.data.size(); } @@ -365,7 +369,7 @@ public List getItems() { * * @see #setMaximumItemCount(int) */ - public int getMaximumItemCount() { + public @NonNegative int getMaximumItemCount() { return this.maximumItemCount; } @@ -383,7 +387,7 @@ public int getMaximumItemCount() { * * @param maximum the maximum number of items for the series. */ - public void setMaximumItemCount(int maximum) { + public void setMaximumItemCount(@NonNegative int maximum) { this.maximumItemCount = maximum; int remove = this.data.size() - maximum; if (remove > 0) { @@ -509,7 +513,9 @@ public void add(XYDataItem item, boolean notify) { if (this.autoSort) { int index = Collections.binarySearch(this.data, item); if (index < 0) { - this.data.add(-index - 1, item); + @SuppressWarnings("index") // binary search on list, which SearchIndex does not handle because lists are not fixed-size data structure + @NonNegative int newIndex = -index - 1; + this.data.add(newIndex, item); } else { if (this.allowDuplicateXValues) { @@ -559,7 +565,7 @@ public void add(XYDataItem item, boolean notify) { * @param start the start index (zero-based). * @param end the end index (zero-based). */ - public void delete(int start, int end) { + public void delete(@NonNegative int start, @NonNegative int end) { this.data.subList(start, end + 1).clear(); findBoundsByIteration(); fireSeriesChanged(); @@ -573,7 +579,7 @@ public void delete(int start, int end) { * * @return The item removed. */ - public XYDataItem remove(int index) { + public XYDataItem remove(@NonNegative int index) { XYDataItem removed = (XYDataItem) this.data.remove(index); updateBoundsForRemovedItem(removed); fireSeriesChanged(); @@ -590,6 +596,7 @@ public XYDataItem remove(int index) { * @return The item removed. */ + @SuppressWarnings("index") // this is a bug? The documentation doesn't say it, but a precondition of this method must be that x is in this XYSeries public XYDataItem remove(Number x) { return remove(indexOf(x)); } @@ -616,7 +623,7 @@ public void clear() { * * @return The data item with the specified index. */ - public XYDataItem getDataItem(int index) { + public XYDataItem getDataItem(@NonNegative int index) { XYDataItem item = (XYDataItem) this.data.get(index); return (XYDataItem) item.clone(); } @@ -630,7 +637,7 @@ public XYDataItem getDataItem(int index) { * * @since 1.0.14 */ - XYDataItem getRawDataItem(int index) { + XYDataItem getRawDataItem(@NonNegative int index) { return (XYDataItem) this.data.get(index); } @@ -641,7 +648,7 @@ XYDataItem getRawDataItem(int index) { * * @return The x-value (never {@code null}). */ - public Number getX(int index) { + public Number getX(@NonNegative int index) { return getRawDataItem(index).getX(); } @@ -652,7 +659,7 @@ public Number getX(int index) { * * @return The y-value (possibly {@code null}). */ - public Number getY(int index) { + public Number getY(@NonNegative int index) { return getRawDataItem(index).getY(); } @@ -703,7 +710,7 @@ private double maxIgnoreNaN(double a, double b) { * * @since 1.0.1 */ - public void updateByIndex(int index, Number y) { + public void updateByIndex(@NonNegative int index, Number y) { XYDataItem item = getRawDataItem(index); // figure out if we need to iterate through all the y-values @@ -820,7 +827,9 @@ else if (item.getY() != null) { // append the value to the list... item = (XYDataItem) item.clone(); if (this.autoSort) { - this.data.add(-index - 1, item); + @SuppressWarnings("index") // binary search on list, which SearchIndex does not handle because lists are not fixed-size data structure + @NonNegative int addIndex = -index - 1; + this.data.add(addIndex, item); } else { this.data.add(item); @@ -869,6 +878,8 @@ public int indexOf(Number x) { * * @since 1.0.4 */ + @SuppressWarnings("index") // every access of the form result[0][i] or result[1][i] in this method issues an error despite + // being safe, since both result[0] and result[1] have length of itemCount. public double[][] toArray() { int itemCount = getItemCount(); double[][] result = new double[2][itemCount]; @@ -909,7 +920,7 @@ public Object clone() throws CloneNotSupportedException { * * @throws CloneNotSupportedException if there is a cloning problem. */ - public XYSeries createCopy(int start, int end) + public XYSeries createCopy(@NonNegative int start, @NonNegative int end) throws CloneNotSupportedException { XYSeries copy = (XYSeries) super.clone(); diff --git a/src/main/java/org/jfree/data/xy/XYSeriesCollection.java b/src/main/java/org/jfree/data/xy/XYSeriesCollection.java index a55625036..01f9ac5f2 100644 --- a/src/main/java/org/jfree/data/xy/XYSeriesCollection.java +++ b/src/main/java/org/jfree/data/xy/XYSeriesCollection.java @@ -65,6 +65,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; @@ -170,7 +174,7 @@ public void addSeries(XYSeries series) { * * @param series the series index (zero-based). */ - public void removeSeries(int series) { + public void removeSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds."); } @@ -220,7 +224,7 @@ public void removeAllSeries() { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -243,7 +247,7 @@ public List getSeries() { * * @since 1.0.6 */ - public int indexOf(XYSeries series) { + public @GTENegativeOne int indexOf(XYSeries series) { Args.nullNotPermitted(series, "series"); return this.data.indexOf(series); } @@ -258,7 +262,7 @@ public int indexOf(XYSeries series) { * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ - public XYSeries getSeries(int series) { + public XYSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -301,7 +305,7 @@ public XYSeries getSeries(Comparable key) { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // defer argument checking return getSeries(series).getKey(); } @@ -316,7 +320,7 @@ public Comparable getSeriesKey(int series) { * * @since 1.0.14 */ - public int getSeriesIndex(Comparable key) { + public @GTENegativeOne int getSeriesIndex(Comparable key) { Args.nullNotPermitted(key, "key"); int seriesCount = getSeriesCount(); for (int i = 0; i < seriesCount; i++) { @@ -339,7 +343,8 @@ public int getSeriesIndex(Comparable key) { * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: getSeries.getItemCount cannot be annotated because some series are mutable + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { // defer argument checking return getSeries(series).getItemCount(); } @@ -353,7 +358,7 @@ public int getItemCount(int series) { * @return The value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { XYSeries s = (XYSeries) this.data.get(series); return s.getX(item); } @@ -367,7 +372,8 @@ public Number getX(int series, int item) { * @return The starting X value. */ @Override - public Number getStartX(int series, int item) { + @SuppressWarnings("index") // array-list interop: getSeries.getItemCount cannot be annotated because some series are mutable + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.intervalDelegate.getStartX(series, item); } @@ -380,7 +386,8 @@ public Number getStartX(int series, int item) { * @return The ending X value. */ @Override - public Number getEndX(int series, int item) { + @SuppressWarnings("index") // array-list interop: getSeries.getItemCount cannot be annotated because some series are mutable + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return this.intervalDelegate.getEndX(series, item); } @@ -393,7 +400,7 @@ public Number getEndX(int series, int item) { * @return The value (possibly {@code null}). */ @Override - public Number getY(int series, int index) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int index) { XYSeries s = (XYSeries) this.data.get(series); return s.getY(index); } @@ -407,7 +414,7 @@ public Number getY(int series, int index) { * @return The starting Y value. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } @@ -420,7 +427,7 @@ public Number getStartY(int series, int item) { * @return The ending Y value. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getY(series, item); } diff --git a/src/main/java/org/jfree/data/xy/XYZDataset.java b/src/main/java/org/jfree/data/xy/XYZDataset.java index 15adba35f..ef6bdf2bd 100644 --- a/src/main/java/org/jfree/data/xy/XYZDataset.java +++ b/src/main/java/org/jfree/data/xy/XYZDataset.java @@ -43,6 +43,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + /** * The interface through which JFreeChart obtains data in the form of (x, y, z) * items - used for XY and XYZ plots. @@ -57,7 +61,7 @@ public interface XYZDataset extends XYDataset { * * @return The z-value (possibly {@code null}). */ - public Number getZ(int series, int item); + public Number getZ(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); /** * Returns the z-value (as a double primitive) for an item within a series. @@ -67,6 +71,6 @@ public interface XYZDataset extends XYDataset { * * @return The z-value. */ - public double getZValue(int series, int item); + public double getZValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item); } diff --git a/src/main/java/org/jfree/data/xy/XisSymbolic.java b/src/main/java/org/jfree/data/xy/XisSymbolic.java index 5ae72153e..3c3a2aad8 100644 --- a/src/main/java/org/jfree/data/xy/XisSymbolic.java +++ b/src/main/java/org/jfree/data/xy/XisSymbolic.java @@ -42,6 +42,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + /** * Represent a data set where X is a symbolic values. Each symbolic value is * linked with an Integer. @@ -64,7 +68,7 @@ public interface XisSymbolic { * * @return The symbolic value. */ - public String getXSymbolicValue(int series, int item); + public String getXSymbolicValue(@NonNegative int series, @NonNegative int item); /** * Returns the symbolic value linked with the specified {@code Integer}. diff --git a/src/main/java/org/jfree/data/xy/YIntervalSeries.java b/src/main/java/org/jfree/data/xy/YIntervalSeries.java index d991c170f..8ee9fefc2 100644 --- a/src/main/java/org/jfree/data/xy/YIntervalSeries.java +++ b/src/main/java/org/jfree/data/xy/YIntervalSeries.java @@ -42,6 +42,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.NonNegative; + import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.general.SeriesChangeEvent; @@ -114,7 +116,7 @@ public void add(YIntervalDataItem item, boolean notify) { * * @return The x-value (never {@code null}). */ - public Number getX(int index) { + public Number getX(@NonNegative int index) { YIntervalDataItem item = (YIntervalDataItem) getDataItem(index); return item.getX(); } @@ -126,7 +128,7 @@ public Number getX(int index) { * * @return The y-value. */ - public double getYValue(int index) { + public double getYValue(@NonNegative int index) { YIntervalDataItem item = (YIntervalDataItem) getDataItem(index); return item.getYValue(); } @@ -141,7 +143,7 @@ public double getYValue(int index) { * * @since 1.0.5 */ - public double getYLowValue(int index) { + public double getYLowValue(@NonNegative int index) { YIntervalDataItem item = (YIntervalDataItem) getDataItem(index); return item.getYLowValue(); } @@ -156,7 +158,7 @@ public double getYLowValue(int index) { * * @since 1.0.5 */ - public double getYHighValue(int index) { + public double getYHighValue(@NonNegative int index) { YIntervalDataItem item = (YIntervalDataItem) getDataItem(index); return item.getYHighValue(); } @@ -169,7 +171,7 @@ public double getYHighValue(int index) { * @return The data item. */ @Override - public ComparableObjectItem getDataItem(int index) { + public ComparableObjectItem getDataItem(@NonNegative int index) { return super.getDataItem(index); } diff --git a/src/main/java/org/jfree/data/xy/YIntervalSeriesCollection.java b/src/main/java/org/jfree/data/xy/YIntervalSeriesCollection.java index c136cf57b..1d9591985 100644 --- a/src/main/java/org/jfree/data/xy/YIntervalSeriesCollection.java +++ b/src/main/java/org/jfree/data/xy/YIntervalSeriesCollection.java @@ -46,6 +46,10 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + +import org.checkerframework.checker.index.qual.NonNegative; + import java.io.Serializable; import java.util.List; import org.jfree.chart.util.ObjectUtils; @@ -93,7 +97,7 @@ public void addSeries(YIntervalSeries series) { * @return The series count. */ @Override - public int getSeriesCount() { + public @NonNegative int getSeriesCount() { return this.data.size(); } @@ -107,7 +111,7 @@ public int getSeriesCount() { * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ - public YIntervalSeries getSeries(int series) { + public YIntervalSeries getSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } @@ -126,7 +130,7 @@ public YIntervalSeries getSeries(int series) { * specified range. */ @Override - public Comparable getSeriesKey(int series) { + public Comparable getSeriesKey(@NonNegative int series) { // defer argument checking return getSeries(series).getKey(); } @@ -142,7 +146,8 @@ public Comparable getSeriesKey(int series) { * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override - public int getItemCount(int series) { + @SuppressWarnings("index") // array-list interop: getSeries.getItemCount cannot be annotated because some series are mutable + public @LengthOf("this.getSeries(#1)") int getItemCount(@NonNegative int series) { // defer argument checking return getSeries(series).getItemCount(); } @@ -156,7 +161,7 @@ public int getItemCount(int series) { * @return The x-value. */ @Override - public Number getX(int series, int item) { + public Number getX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getX(item); } @@ -171,7 +176,7 @@ public Number getX(int series, int item) { * @return The value. */ @Override - public double getYValue(int series, int item) { + public double getYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYValue(item); } @@ -186,7 +191,7 @@ public double getYValue(int series, int item) { * @return The value. */ @Override - public double getStartYValue(int series, int item) { + public double getStartYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYLowValue(item); } @@ -201,7 +206,7 @@ public double getStartYValue(int series, int item) { * @return The value. */ @Override - public double getEndYValue(int series, int item) { + public double getEndYValue(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYHighValue(item); } @@ -215,7 +220,7 @@ public double getEndYValue(int series, int item) { * @return The y-value. */ @Override - public Number getY(int series, int item) { + public Number getY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return new Double(s.getYValue(item)); } @@ -230,7 +235,7 @@ public Number getY(int series, int item) { * @return The x-value. */ @Override - public Number getStartX(int series, int item) { + public Number getStartX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getX(series, item); } @@ -244,7 +249,7 @@ public Number getStartX(int series, int item) { * @return The x-value. */ @Override - public Number getEndX(int series, int item) { + public Number getEndX(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { return getX(series, item); } @@ -257,7 +262,7 @@ public Number getEndX(int series, int item) { * @return The start y-value. */ @Override - public Number getStartY(int series, int item) { + public Number getStartY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return new Double(s.getYLowValue(item)); } @@ -271,7 +276,7 @@ public Number getStartY(int series, int item) { * @return The end y-value. */ @Override - public Number getEndY(int series, int item) { + public Number getEndY(@NonNegative int series, @IndexFor("this.getSeries(#1)") int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return new Double(s.getYHighValue(item)); } @@ -284,7 +289,7 @@ public Number getEndY(int series, int item) { * * @since 1.0.10 */ - public void removeSeries(int series) { + public void removeSeries(@NonNegative int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds."); } diff --git a/src/main/java/org/jfree/data/xy/YisSymbolic.java b/src/main/java/org/jfree/data/xy/YisSymbolic.java index bd5763e39..99c7796de 100644 --- a/src/main/java/org/jfree/data/xy/YisSymbolic.java +++ b/src/main/java/org/jfree/data/xy/YisSymbolic.java @@ -43,6 +43,8 @@ package org.jfree.data.xy; +import org.checkerframework.checker.index.qual.*; + /** * Represent a data set where Y is a symbolic values. Each symbolic value is * linked with an Integer. @@ -65,7 +67,7 @@ public interface YisSymbolic { * * @return The symbolic value. */ - public String getYSymbolicValue(int series, int item); + public String getYSymbolicValue(@NonNegative int series, @NonNegative int item); /** * Returns the symbolic value linked with the specified {@code Integer}. diff --git a/src/test/java/org/jfree/data/statistics/DefaultStatisticalCategoryDatasetTest.java b/src/test/java/org/jfree/data/statistics/DefaultStatisticalCategoryDatasetTest.java index 6d6553972..221f92c93 100644 --- a/src/test/java/org/jfree/data/statistics/DefaultStatisticalCategoryDatasetTest.java +++ b/src/test/java/org/jfree/data/statistics/DefaultStatisticalCategoryDatasetTest.java @@ -43,6 +43,7 @@ */ package org.jfree.data.statistics; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; diff --git a/src/test/java/org/jfree/data/time/TimeSeriesCollectionTest.java b/src/test/java/org/jfree/data/time/TimeSeriesCollectionTest.java index cee454dcd..1e8ba2615 100644 --- a/src/test/java/org/jfree/data/time/TimeSeriesCollectionTest.java +++ b/src/test/java/org/jfree/data/time/TimeSeriesCollectionTest.java @@ -2,7 +2,7 @@ * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * - * (C) Copyright 2000-2016, by Object Refinery Limited and Contributors. + * (C) Copyright 2000-2018, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * @@ -27,19 +27,11 @@ * ----------------------------- * TimeSeriesCollectionTest.java * ----------------------------- - * (C) Copyright 2003-2016, by Object Refinery Limited. + * (C) Copyright 2003-2018, by Object Refinery Limited. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): -; * - * Changes - * ------- - * 01-May-2003 : Version 1 (DG); - * 04-Dec-2003 : Added a test for the getSurroundingItems() method (DG); - * 08-May-2007 : Added testIndexOf() method (DG); - * 18-May-2009 : Added testFindDomainBounds() (DG); - * 08-Jan-2012 : Added testBug3445507() (DG); - * */ package org.jfree.data.time; @@ -58,7 +50,6 @@ import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.TestUtils; - import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.junit.Test; @@ -309,6 +300,10 @@ public void testIndexOf() { */ @Test public void testFindDomainBounds() { + // store the current time zone + TimeZone saved = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris")); + TimeSeriesCollection dataset = new TimeSeriesCollection(); List visibleSeriesKeys = new java.util.ArrayList(); Range r = DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, @@ -321,10 +316,6 @@ public void testFindDomainBounds() { r = DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, true); assertNull(r); - // store the current time zone - TimeZone saved = TimeZone.getDefault(); - TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris")); - s1.add(new Year(2008), 8.0); r = DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, true); assertEquals(1199142000000.0, r.getLowerBound(), EPSILON);