Skip to content

Commit 6a2f0da

Browse files
committed
Major elimination of duplicate code in the various Scatter*.java classes.
1 parent 99f5395 commit 6a2f0da

File tree

5 files changed

+362
-491
lines changed

5 files changed

+362
-491
lines changed

plugin/src/main/java/org/owasp/benchmarkutils/score/report/ScatterHome.java

+72-183
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,14 @@
2424
import java.security.SecureRandom;
2525
import java.text.DecimalFormat;
2626
import java.util.ArrayList;
27-
import java.util.Arrays;
2827
import java.util.HashMap;
2928
import java.util.List;
30-
import java.util.Map.Entry;
3129
import java.util.Set;
3230
import org.jfree.chart.ChartFactory;
3331
import org.jfree.chart.JFreeChart;
3432
import org.jfree.chart.annotations.XYLineAnnotation;
35-
import org.jfree.chart.annotations.XYTextAnnotation;
3633
import org.jfree.chart.plot.PlotOrientation;
3734
import org.jfree.chart.plot.XYPlot;
38-
import org.jfree.chart.ui.TextAnchor;
3935
import org.jfree.data.xy.XYDataItem;
4036
import org.jfree.data.xy.XYSeries;
4137
import org.jfree.data.xy.XYSeriesCollection;
@@ -44,9 +40,9 @@
4440
import org.owasp.benchmarkutils.score.ToolResults;
4541

4642
public class ScatterHome extends ScatterPlot {
47-
private static char averageLabel;
48-
private double afr = 0;
49-
private double atr = 0;
43+
44+
private double commercialAveFPR = 0; // The average FPR for commercial tools.
45+
private double commercialAveTPR = 0; // The average TPR. These are range 0-1.
5046
private final String focus;
5147
static final char INITIAL_LABEL = 'A';
5248

@@ -101,23 +97,23 @@ private JFreeChart display(String title, Set<Tool> tools) {
10197
}
10298

10399
for (double d : averageCommercialFalseRates) {
104-
afr += d;
100+
this.commercialAveFPR += d;
105101
}
106-
afr = afr / averageCommercialFalseRates.size();
102+
this.commercialAveFPR = this.commercialAveFPR / averageCommercialFalseRates.size();
107103

108104
for (double d : averageCommercialTrueRates) {
109-
atr += d;
105+
this.commercialAveTPR += d;
110106
}
111-
atr = atr / averageCommercialTrueRates.size();
107+
this.commercialAveTPR = this.commercialAveTPR / averageCommercialTrueRates.size();
112108

113109
if (commercialToolCount > 1
114110
|| (BenchmarkScore.showAveOnlyMode && commercialToolCount == 1)) {
115-
series.add(afr * 100, atr * 100);
111+
series.add(commercialAveFPR * 100, commercialAveTPR * 100);
116112
}
117113

118114
dataset.addSeries(series);
119115

120-
chart =
116+
this.chart =
121117
ChartFactory.createScatterPlot(
122118
title,
123119
"False Positive Rate",
@@ -127,15 +123,19 @@ private JFreeChart display(String title, Set<Tool> tools) {
127123
true,
128124
true,
129125
false);
130-
theme.apply(chart);
131-
initializePlot(chart);
126+
theme.apply(this.chart);
127+
initializePlot(this.chart);
132128

133-
XYPlot xyplot = chart.getXYPlot();
129+
XYPlot xyplot = this.chart.getXYPlot();
134130
addGenerationDate(xyplot);
135131

132+
// List the Key value (i.e., A, B, C) next to each plot point.
136133
makeDataLabels(tools, xyplot);
134+
// List all the tools on the right, along with their scores
137135
makeLegend(tools, 103, 100.5, dataset, xyplot);
138136

137+
// Create the dashed lines from the baseline 0 axis line to the plotted score dot on the
138+
// chart for each tool plotted
139139
for (XYDataItem item : (List<XYDataItem>) series.getItems()) {
140140
double x = item.getX().doubleValue();
141141
double y = item.getY().doubleValue();
@@ -144,39 +144,19 @@ private JFreeChart display(String title, Set<Tool> tools) {
144144
xyplot.addAnnotation(score);
145145
}
146146

147-
return chart;
147+
return this.chart;
148148
}
149149

150+
/**
151+
* Add the letter, from the key on the right, next to the plot point on the chart for for each
152+
* tool to the supplied xyplot.
153+
*
154+
* @param tools - THe set of tool results.
155+
* @param xyplot - The chart to make the Data labels on.
156+
*/
150157
private void makeDataLabels(Set<Tool> tools, XYPlot xyplot) {
151158
HashMap<Point2D, String> map = makePointList(tools);
152-
for (Entry<Point2D, String> e : map.entrySet()) {
153-
if (e.getValue() != null) {
154-
Point2D p = e.getKey();
155-
String label = sort(e.getValue());
156-
XYTextAnnotation annotation = new XYTextAnnotation(label, p.getX(), p.getY());
157-
annotation.setTextAnchor(
158-
p.getX() < 3 ? TextAnchor.TOP_LEFT : TextAnchor.TOP_CENTER);
159-
annotation.setBackgroundPaint(Color.white);
160-
if (label.toCharArray()[0] == averageLabel) {
161-
annotation.setPaint(Color.magenta);
162-
} else {
163-
annotation.setPaint(Color.blue);
164-
}
165-
annotation.setFont(theme.getRegularFont());
166-
xyplot.addAnnotation(annotation);
167-
}
168-
}
169-
}
170-
171-
private static String sort(String value) {
172-
String[] parts = value.split(",");
173-
Arrays.sort(parts);
174-
StringBuilder sb = new StringBuilder();
175-
for (int i = 0; i < parts.length; i++) {
176-
sb.append(parts[i]);
177-
if (i < parts.length - 1) sb.append(",");
178-
}
179-
return sb.toString();
159+
addLabelsToPlotPoints(map, xyplot);
180160
}
181161

182162
private static SecureRandom sr = new SecureRandom();
@@ -230,49 +210,20 @@ private HashMap<Point2D, String> makePointList(Set<Tool> tools) {
230210
|| (BenchmarkScore.showAveOnlyMode && commercialToolCount == 1)) {
231211
Point2D ap =
232212
new Point2D.Double(
233-
afr * 100 + sr.nextDouble() * .000001,
234-
atr * 100 + sr.nextDouble() * .000001 - 1);
235-
averageLabel = ch;
213+
commercialAveFPR * 100 + sr.nextDouble() * .000001,
214+
commercialAveTPR * 100 + sr.nextDouble() * .000001 - 1);
215+
this.averageLabel = ch;
236216
map.put(ap, "" + ch);
237217
}
238218

239-
dedupify(map);
219+
dedupifyPlotPoints(map);
240220
return map;
241221
}
242222

243-
private static void dedupify(HashMap<Point2D, String> map) {
244-
for (Entry<Point2D, String> e1 : map.entrySet()) {
245-
Entry<Point2D, String> e2 = getMatch(map, e1);
246-
while (e2 != null) {
247-
StringBuilder label = new StringBuilder();
248-
if (e1.getValue() != null) label.append(e1.getValue());
249-
if (e1.getValue() != null && e2.getValue() != null) label.append(",");
250-
if (e2.getValue() != null) label.append(e2.getValue());
251-
e1.setValue(label.toString());
252-
e2.setValue(null);
253-
e2 = getMatch(map, e1);
254-
}
255-
}
256-
}
257-
258-
private static Entry<Point2D, String> getMatch(
259-
HashMap<Point2D, String> map, Entry<Point2D, String> e1) {
260-
for (Entry<Point2D, String> e2 : map.entrySet()) {
261-
Double xd = Math.abs(e1.getKey().getX() - e2.getKey().getX());
262-
Double yd = Math.abs(e1.getKey().getY() - e2.getKey().getY());
263-
boolean close = xd < 1 && yd < 3;
264-
if (e1 != e2 && e1.getValue() != null && e2.getValue() != null && close) {
265-
return e2;
266-
}
267-
}
268-
return null;
269-
}
270-
271223
private void makeLegend(
272224
Set<Tool> tools, double x, double y, XYSeriesCollection dataset, XYPlot xyplot) {
273-
char ch =
274-
INITIAL_LABEL; // This is the first label in the Key with all the tools processed by
275-
// this scorecard
225+
// The first label in the Key with all the tools processed by this scorecard
226+
char ch = INITIAL_LABEL;
276227
int i = -2; // Used to keep track of which row in the key we are processing. Helps calculate
277228
// the Y axis location where to put the Key entry
278229

@@ -284,13 +235,7 @@ private void makeLegend(
284235
if (!r.isCommercial()) {
285236
// print non-commercial label if there is at least one non-commercial tool
286237
if (!printedNonCommercialLabel) {
287-
XYTextAnnotation stroketext1 =
288-
new XYTextAnnotation("Non-Commercial", x, y + i * -3.3);
289-
stroketext1.setTextAnchor(TextAnchor.CENTER_LEFT);
290-
stroketext1.setBackgroundPaint(Color.white);
291-
stroketext1.setPaint(Color.black);
292-
stroketext1.setFont(theme.getRegularFont());
293-
xyplot.addAnnotation(stroketext1);
238+
addLabelToKey(xyplot, x, y, i, "Non-Commercial");
294239
i++;
295240
printedNonCommercialLabel = true;
296241
}
@@ -299,49 +244,28 @@ private void makeLegend(
299244
String label = (ch == 'I' || ch == 'i' ? ch + ": " : ch + ": ");
300245
// Another hack to make it line up better if the letter is a 'J' or 'j'
301246
label = (ch == 'J' || ch == 'j' ? ch + ": " : label);
302-
double score = or.getOverallScore() * 100;
303-
final DecimalFormat DF = new DecimalFormat("#0.0");
304-
String TPR = DF.format(100 * or.getTruePositiveRate());
305-
if (TPR.endsWith("0"))
306-
TPR = TPR.substring(0, TPR.length() - 2); // trim off .0 if it ends that way.
307-
String FPR = DF.format(100 * or.getFalsePositiveRate());
308-
if (FPR.endsWith("0")) FPR = FPR.substring(0, FPR.length() - 2);
309-
310-
final String TOOL = "\u25A0 " + label + r.getToolNameAndVersion();
311-
XYTextAnnotation toolLabel = new XYTextAnnotation(TOOL, x, y + i * -3.3);
312-
toolLabel.setTextAnchor(TextAnchor.CENTER_LEFT);
313-
toolLabel.setBackgroundPaint(Color.white);
314-
toolLabel.setPaint(Color.blue);
315-
toolLabel.setFont(theme.getRegularFont());
316-
xyplot.addAnnotation(toolLabel);
317-
final String SCORE = Math.round(score) + "%";
318-
XYTextAnnotation scoreLabel =
319-
new XYTextAnnotation(SCORE, x + COLUMN_1_OFFSET, y + i * -3.3);
320-
scoreLabel.setTextAnchor(TextAnchor.CENTER_RIGHT);
321-
scoreLabel.setBackgroundPaint(Color.white);
322-
scoreLabel.setPaint(Color.blue);
323-
scoreLabel.setFont(theme.getRegularFont());
324-
xyplot.addAnnotation(scoreLabel);
325-
final String CALC = "(" + TPR + "-" + FPR + ")";
326-
XYTextAnnotation calcLabel =
327-
new XYTextAnnotation(CALC, x + COLUMN_2_OFFSET, y + i * -3.3);
328-
calcLabel.setTextAnchor(TextAnchor.CENTER);
329-
calcLabel.setBackgroundPaint(Color.white);
330-
calcLabel.setPaint(Color.gray);
331-
calcLabel.setFont(theme.getSmallFont());
332-
xyplot.addAnnotation(calcLabel);
333247

248+
addEntryToKey(
249+
xyplot,
250+
Color.blue,
251+
x,
252+
y,
253+
i,
254+
label,
255+
r.getToolNameAndVersion(),
256+
or.getTruePositiveRate(),
257+
or.getFalsePositiveRate());
334258
i++;
335259
// Weak hack if there are more than 26 tools scored. This will only get us to 52.
336260
if (ch == 'Z') ch = 'a';
337261
else ch++;
338262
}
339263
}
340264

341-
// commercial tools
342-
double totalScore = 0;
265+
// commercial tools - Their averages have already been calculated
343266
boolean printedCommercialLabel = false;
344267
int commercialToolCount = 0;
268+
final DecimalFormat DF = new DecimalFormat("#0.0");
345269

346270
for (Tool r : tools) {
347271

@@ -350,13 +274,7 @@ private void makeLegend(
350274

351275
// print commercial label if there is at least one commercial tool
352276
if (!printedCommercialLabel) {
353-
XYTextAnnotation stroketext =
354-
new XYTextAnnotation("Commercial", x, y + i * -3.3);
355-
stroketext.setTextAnchor(TextAnchor.CENTER_LEFT);
356-
stroketext.setBackgroundPaint(Color.white);
357-
stroketext.setPaint(Color.black);
358-
stroketext.setFont(theme.getRegularFont());
359-
xyplot.addAnnotation(stroketext);
277+
addLabelToKey(xyplot, x, y, i, "Commercial");
360278
i++;
361279
printedCommercialLabel = true;
362280
}
@@ -366,50 +284,25 @@ private void makeLegend(
366284
String label = (ch == 'I' || ch == 'i' ? ch + ": " : ch + ": ");
367285
// Another hack to make it line up better if the letter is a 'J' or 'j'
368286
label = (ch == 'J' || ch == 'j' ? ch + ": " : label);
369-
double score = or.getOverallScore() * 100;
370287
if (!BenchmarkScore.showAveOnlyMode) {
371-
372-
final DecimalFormat DF = new DecimalFormat("#0.0");
373-
String TPR = DF.format(100 * or.getTruePositiveRate());
374-
if (TPR.endsWith("0"))
375-
TPR =
376-
TPR.substring(
377-
0, TPR.length() - 2); // trim off .0 if it ends that way.
378-
String FPR = DF.format(100 * or.getFalsePositiveRate());
379-
if (FPR.endsWith("0")) FPR = FPR.substring(0, FPR.length() - 2);
380-
381-
final String TOOL = "\u25A0 " + label + r.getToolNameAndVersion();
382-
XYTextAnnotation toolLabel = new XYTextAnnotation(TOOL, x, y + i * -3.3);
383-
toolLabel.setTextAnchor(TextAnchor.CENTER_LEFT);
384-
toolLabel.setBackgroundPaint(Color.white);
385-
toolLabel.setPaint(Color.blue);
386-
toolLabel.setFont(theme.getRegularFont());
387-
xyplot.addAnnotation(toolLabel);
388-
final String SCORE = Math.round(score) + "%";
389-
XYTextAnnotation scoreLabel =
390-
new XYTextAnnotation(SCORE, x + COLUMN_1_OFFSET, y + i * -3.3);
391-
scoreLabel.setTextAnchor(TextAnchor.CENTER_RIGHT);
392-
scoreLabel.setBackgroundPaint(Color.white);
393-
scoreLabel.setPaint(Color.blue);
394-
scoreLabel.setFont(theme.getRegularFont());
395-
xyplot.addAnnotation(scoreLabel);
396-
final String CALC = "(" + TPR + "-" + FPR + ")";
397-
XYTextAnnotation calcLabel =
398-
new XYTextAnnotation(CALC, x + COLUMN_2_OFFSET, y + i * -3.3);
399-
calcLabel.setTextAnchor(TextAnchor.CENTER);
400-
calcLabel.setBackgroundPaint(Color.white);
401-
calcLabel.setPaint(Color.gray);
402-
calcLabel.setFont(theme.getSmallFont());
403-
xyplot.addAnnotation(calcLabel);
404-
288+
addEntryToKey(
289+
xyplot,
290+
Color.blue,
291+
x,
292+
y,
293+
i,
294+
label,
295+
r.getToolNameAndVersion(),
296+
or.getTruePositiveRate(),
297+
or.getFalsePositiveRate());
405298
i++;
406-
// Weak hack if there are more than 26 tools scored. This will only get us to
407-
// 52.
299+
// Weak hack if more than 26 tools scored. This will only get us to 52.
408300
if (ch == 'Z') ch = 'a';
409301
else ch++;
410302
}
411-
totalScore += score;
412303
}
304+
305+
// This highlights the tool of focus, making that plot point green. Rarely used.
413306
if (r.getToolName().replace(' ', '_').equalsIgnoreCase(focus)) {
414307
ToolResults orc = r.getOverallResults();
415308
Point2D focusPoint =
@@ -423,24 +316,20 @@ private void makeLegend(
423316
// commercial average
424317
if (commercialToolCount > 1
425318
|| (BenchmarkScore.showAveOnlyMode && commercialToolCount == 1)) {
426-
double averageScore = totalScore / commercialToolCount;
427-
XYTextAnnotation stroketext2 =
428-
new XYTextAnnotation(
429-
"\u25A0 "
430-
+ ch
431-
+ ": Commercial Average"
432-
+ " ("
433-
+ Math.round(averageScore)
434-
+ "%)",
435-
x,
436-
y + i * -3.3);
437-
stroketext2.setTextAnchor(TextAnchor.CENTER_LEFT);
438-
stroketext2.setBackgroundPaint(Color.white);
439-
stroketext2.setPaint(Color.magenta);
440-
stroketext2.setFont(theme.getRegularFont());
441-
xyplot.addAnnotation(stroketext2);
442-
443-
Point2D averagePoint = new Point2D.Double(afr * 100, atr * 100);
319+
320+
addEntryToKey(
321+
xyplot,
322+
Color.magenta,
323+
x,
324+
y,
325+
i,
326+
ch + ": ",
327+
"Commercial Average",
328+
commercialAveTPR,
329+
commercialAveFPR);
330+
331+
Point2D averagePoint =
332+
new Point2D.Double(this.commercialAveFPR * 100, this.commercialAveTPR * 100);
444333
makePoint(xyplot, averagePoint, 3, Color.magenta);
445334
}
446335
}

0 commit comments

Comments
 (0)