24
24
import java .security .SecureRandom ;
25
25
import java .text .DecimalFormat ;
26
26
import java .util .ArrayList ;
27
- import java .util .Arrays ;
28
27
import java .util .HashMap ;
29
28
import java .util .List ;
30
- import java .util .Map .Entry ;
31
29
import java .util .Set ;
32
30
import org .jfree .chart .ChartFactory ;
33
31
import org .jfree .chart .JFreeChart ;
34
32
import org .jfree .chart .annotations .XYLineAnnotation ;
35
- import org .jfree .chart .annotations .XYTextAnnotation ;
36
33
import org .jfree .chart .plot .PlotOrientation ;
37
34
import org .jfree .chart .plot .XYPlot ;
38
- import org .jfree .chart .ui .TextAnchor ;
39
35
import org .jfree .data .xy .XYDataItem ;
40
36
import org .jfree .data .xy .XYSeries ;
41
37
import org .jfree .data .xy .XYSeriesCollection ;
44
40
import org .owasp .benchmarkutils .score .ToolResults ;
45
41
46
42
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.
50
46
private final String focus ;
51
47
static final char INITIAL_LABEL = 'A' ;
52
48
@@ -101,23 +97,23 @@ private JFreeChart display(String title, Set<Tool> tools) {
101
97
}
102
98
103
99
for (double d : averageCommercialFalseRates ) {
104
- afr += d ;
100
+ this . commercialAveFPR += d ;
105
101
}
106
- afr = afr / averageCommercialFalseRates .size ();
102
+ this . commercialAveFPR = this . commercialAveFPR / averageCommercialFalseRates .size ();
107
103
108
104
for (double d : averageCommercialTrueRates ) {
109
- atr += d ;
105
+ this . commercialAveTPR += d ;
110
106
}
111
- atr = atr / averageCommercialTrueRates .size ();
107
+ this . commercialAveTPR = this . commercialAveTPR / averageCommercialTrueRates .size ();
112
108
113
109
if (commercialToolCount > 1
114
110
|| (BenchmarkScore .showAveOnlyMode && commercialToolCount == 1 )) {
115
- series .add (afr * 100 , atr * 100 );
111
+ series .add (commercialAveFPR * 100 , commercialAveTPR * 100 );
116
112
}
117
113
118
114
dataset .addSeries (series );
119
115
120
- chart =
116
+ this . chart =
121
117
ChartFactory .createScatterPlot (
122
118
title ,
123
119
"False Positive Rate" ,
@@ -127,15 +123,19 @@ private JFreeChart display(String title, Set<Tool> tools) {
127
123
true ,
128
124
true ,
129
125
false );
130
- theme .apply (chart );
131
- initializePlot (chart );
126
+ theme .apply (this . chart );
127
+ initializePlot (this . chart );
132
128
133
- XYPlot xyplot = chart .getXYPlot ();
129
+ XYPlot xyplot = this . chart .getXYPlot ();
134
130
addGenerationDate (xyplot );
135
131
132
+ // List the Key value (i.e., A, B, C) next to each plot point.
136
133
makeDataLabels (tools , xyplot );
134
+ // List all the tools on the right, along with their scores
137
135
makeLegend (tools , 103 , 100.5 , dataset , xyplot );
138
136
137
+ // Create the dashed lines from the baseline 0 axis line to the plotted score dot on the
138
+ // chart for each tool plotted
139
139
for (XYDataItem item : (List <XYDataItem >) series .getItems ()) {
140
140
double x = item .getX ().doubleValue ();
141
141
double y = item .getY ().doubleValue ();
@@ -144,39 +144,19 @@ private JFreeChart display(String title, Set<Tool> tools) {
144
144
xyplot .addAnnotation (score );
145
145
}
146
146
147
- return chart ;
147
+ return this . chart ;
148
148
}
149
149
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
+ */
150
157
private void makeDataLabels (Set <Tool > tools , XYPlot xyplot ) {
151
158
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 );
180
160
}
181
161
182
162
private static SecureRandom sr = new SecureRandom ();
@@ -230,49 +210,20 @@ private HashMap<Point2D, String> makePointList(Set<Tool> tools) {
230
210
|| (BenchmarkScore .showAveOnlyMode && commercialToolCount == 1 )) {
231
211
Point2D ap =
232
212
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 ;
236
216
map .put (ap , "" + ch );
237
217
}
238
218
239
- dedupify (map );
219
+ dedupifyPlotPoints (map );
240
220
return map ;
241
221
}
242
222
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
-
271
223
private void makeLegend (
272
224
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 ;
276
227
int i = -2 ; // Used to keep track of which row in the key we are processing. Helps calculate
277
228
// the Y axis location where to put the Key entry
278
229
@@ -284,13 +235,7 @@ private void makeLegend(
284
235
if (!r .isCommercial ()) {
285
236
// print non-commercial label if there is at least one non-commercial tool
286
237
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" );
294
239
i ++;
295
240
printedNonCommercialLabel = true ;
296
241
}
@@ -299,49 +244,28 @@ private void makeLegend(
299
244
String label = (ch == 'I' || ch == 'i' ? ch + ": " : ch + ": " );
300
245
// Another hack to make it line up better if the letter is a 'J' or 'j'
301
246
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 );
333
247
248
+ addEntryToKey (
249
+ xyplot ,
250
+ Color .blue ,
251
+ x ,
252
+ y ,
253
+ i ,
254
+ label ,
255
+ r .getToolNameAndVersion (),
256
+ or .getTruePositiveRate (),
257
+ or .getFalsePositiveRate ());
334
258
i ++;
335
259
// Weak hack if there are more than 26 tools scored. This will only get us to 52.
336
260
if (ch == 'Z' ) ch = 'a' ;
337
261
else ch ++;
338
262
}
339
263
}
340
264
341
- // commercial tools
342
- double totalScore = 0 ;
265
+ // commercial tools - Their averages have already been calculated
343
266
boolean printedCommercialLabel = false ;
344
267
int commercialToolCount = 0 ;
268
+ final DecimalFormat DF = new DecimalFormat ("#0.0" );
345
269
346
270
for (Tool r : tools ) {
347
271
@@ -350,13 +274,7 @@ private void makeLegend(
350
274
351
275
// print commercial label if there is at least one commercial tool
352
276
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" );
360
278
i ++;
361
279
printedCommercialLabel = true ;
362
280
}
@@ -366,50 +284,25 @@ private void makeLegend(
366
284
String label = (ch == 'I' || ch == 'i' ? ch + ": " : ch + ": " );
367
285
// Another hack to make it line up better if the letter is a 'J' or 'j'
368
286
label = (ch == 'J' || ch == 'j' ? ch + ": " : label );
369
- double score = or .getOverallScore () * 100 ;
370
287
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 ());
405
298
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.
408
300
if (ch == 'Z' ) ch = 'a' ;
409
301
else ch ++;
410
302
}
411
- totalScore += score ;
412
303
}
304
+
305
+ // This highlights the tool of focus, making that plot point green. Rarely used.
413
306
if (r .getToolName ().replace (' ' , '_' ).equalsIgnoreCase (focus )) {
414
307
ToolResults orc = r .getOverallResults ();
415
308
Point2D focusPoint =
@@ -423,24 +316,20 @@ private void makeLegend(
423
316
// commercial average
424
317
if (commercialToolCount > 1
425
318
|| (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 );
444
333
makePoint (xyplot , averagePoint , 3 , Color .magenta );
445
334
}
446
335
}
0 commit comments