1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.rat.report.claim;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.ArrayList;
25 import java.util.Comparator;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30
31 import javax.xml.parsers.DocumentBuilder;
32
33 import org.apache.commons.io.function.IOSupplier;
34 import org.apache.commons.lang3.StringUtils;
35 import org.apache.rat.api.Document;
36 import org.apache.rat.configuration.XMLConfigurationReader;
37 import org.apache.rat.report.xml.writer.XmlWriter;
38 import org.apache.rat.utils.StandardXmlFactory;
39 import org.xml.sax.SAXException;
40
41
42
43
44
45 public class ClaimStatistic {
46
47
48 public enum Counter {
49
50 APPROVED("A count of approved licenses.", -1, 0),
51
52 ARCHIVES("A count of archive files.", -1, 0),
53
54 BINARIES("A count of binary files.", -1, 0),
55
56 DOCUMENT_TYPES("A count of distinct document types.", -1, 1),
57
58 IGNORED("A count of ignored files.", -1, 0),
59
60 LICENSE_CATEGORIES("A count of distinct license categories.", -1, 1),
61
62 LICENSE_NAMES("A count of distinct license names.", -1, 1),
63
64 NOTICES("A count of notice files.", -1, 0),
65
66 STANDARDS("A count of standard files.", -1, 1),
67
68 UNAPPROVED("A count of unapproved licenses.", 0, 0),
69
70 UNKNOWN("A count of unknown file types.", -1, 0);
71
72
73 private final String description;
74
75 private final int defaultMaxValue;
76
77 private final int defaultMinValue;
78
79 Counter(final String description, final int defaultMaxValue, final int defaultMinValue) {
80 this.description = description;
81 this.defaultMaxValue = defaultMaxValue;
82 this.defaultMinValue = defaultMinValue;
83 }
84
85
86
87
88
89 public String getDescription() {
90 return description;
91 }
92
93
94
95
96
97 public int getDefaultMaxValue() {
98 return defaultMaxValue;
99 }
100
101
102
103
104 public int getDefaultMinValue() {
105 return defaultMinValue;
106 }
107
108
109
110
111
112 public String displayName() {
113 return StringUtils.capitalize(name().replace("_", " ").toLowerCase(Locale.ROOT));
114 }
115 }
116
117
118 private final ConcurrentHashMap<String, IntCounter> licenseNameMap = new ConcurrentHashMap<>();
119
120 private final ConcurrentHashMap<String, IntCounter> licenseFamilyCategoryMap = new ConcurrentHashMap<>();
121
122 private final ConcurrentHashMap<Document.Type, IntCounter> documentTypeMap = new ConcurrentHashMap<>();
123
124 private final ConcurrentHashMap<ClaimStatistic.Counter, IntCounter> counterMap = new ConcurrentHashMap<>();
125
126 public SerDes serDes() {
127 return new SerDes();
128 }
129
130
131
132
133
134
135 private int getValue(final IntCounter counter) {
136 return counter == null ? 0 : counter.value();
137 }
138
139
140
141
142
143
144 public int getCounter(final Counter counter) {
145 return getValue(counterMap.get(counter));
146 }
147
148
149
150
151
152
153 public void incCounter(final Counter counter, final int value) {
154 counterMap.compute(counter, (k, v) -> v == null ? new IntCounter().increment(value) : v.increment(value));
155 }
156
157
158
159
160
161
162 public void setCounter(final Counter counter, final int value) {
163 counterMap.put(counter, new IntCounter().increment(value));
164 }
165
166
167
168
169
170
171 public int getCounter(final Document.Type documentType) {
172 return getValue(documentTypeMap.get(documentType));
173 }
174
175
176
177
178
179 public List<Document.Type> getDocumentTypes() {
180 List<Document.Type> result = new ArrayList<>(documentTypeMap.keySet());
181 result.sort(Comparator.comparing(Enum::name));
182 return result;
183 }
184
185
186
187
188
189
190 public void incCounter(final Document.Type documentType, final int value) {
191 documentTypeMap.compute(documentType, (k, v) -> updateCounter(Counter.DOCUMENT_TYPES, v, value));
192 switch (documentType) {
193 case STANDARD -> incCounter(Counter.STANDARDS, value);
194 case ARCHIVE -> incCounter(Counter.ARCHIVES, value);
195 case BINARY -> incCounter(Counter.BINARIES, value);
196 case NOTICE -> incCounter(Counter.NOTICES, value);
197 case UNKNOWN -> incCounter(Counter.UNKNOWN, value);
198 case IGNORED -> incCounter(Counter.IGNORED, value);
199 }
200 }
201
202
203
204
205
206
207 public int getLicenseCategoryCount(final String licenseFamilyCategory) {
208 return getValue(licenseFamilyCategoryMap.get(licenseFamilyCategory));
209 }
210
211
212
213
214
215
216 public int getLicenseNameCount(final String licenseName) {
217 return getValue(licenseNameMap.get(licenseName));
218 }
219
220
221
222
223
224
225
226
227
228 private IntCounter updateCounter(final Counter counter, final IntCounter intCounter, final int value) {
229 if (intCounter == null) {
230 incCounter(counter, 1);
231 return new IntCounter().increment(value);
232 } else {
233 return intCounter.increment(value);
234 }
235 }
236
237
238
239
240
241
242 public void incLicenseCategoryCount(final String licenseFamilyCategory, final int value) {
243 licenseFamilyCategoryMap.compute(licenseFamilyCategory, (k, v) -> updateCounter(Counter.LICENSE_CATEGORIES, v, value));
244 }
245
246
247
248
249
250 public List<String> getLicenseFamilyCategories() {
251 List<String> result = new ArrayList<>(licenseFamilyCategoryMap.keySet());
252 result.sort(String::compareTo);
253 return result;
254 }
255
256
257
258
259
260 public List<String> getLicenseNames() {
261 List<String> result = new ArrayList<>(licenseNameMap.keySet());
262 result.sort(String::compareTo);
263 return result;
264 }
265
266
267
268
269
270
271 public void incLicenseNameCount(final String licenseName, final int value) {
272 licenseNameMap.compute(licenseName, (k, v) -> updateCounter(Counter.LICENSE_NAMES, v, value));
273 }
274
275
276
277
278 static class IntCounter {
279
280
281
282 private int value;
283
284
285
286
287
288
289 public IntCounter increment(final int count) {
290 value += count;
291 return this;
292 }
293
294
295
296
297
298 public int value() {
299 return value;
300 }
301
302 @Override
303 public String toString() {
304 return String.valueOf(value);
305 }
306 }
307
308
309
310
311 public class SerDes {
312
313 private static final String COUNT = "count";
314
315 private static final String NAME = "name";
316
317
318
319
320
321
322 public void serialize(final Appendable appendable) throws IOException {
323 try (XmlWriter writer = new XmlWriter(appendable)) {
324 writer.startDocument().startElement("ClaimStatistic")
325 .startElement("licenseNameMap");
326 for (Map.Entry<String, IntCounter> entry : licenseNameMap.entrySet()) {
327 if (entry.getValue().value > 0) {
328 writer.startElement("licenseName")
329 .attribute(COUNT, entry.getValue().toString())
330 .attribute(NAME, entry.getKey()).closeElement();
331 }
332 }
333 writer.closeElement()
334 .startElement("licenseFamilyCategoryMap");
335 for (Map.Entry<String, IntCounter> entry : licenseFamilyCategoryMap.entrySet()) {
336 if (entry.getValue().value > 0) {
337 writer.startElement("familyCategory")
338 .attribute(COUNT, entry.getValue().toString())
339 .attribute(NAME, entry.getKey()).closeElement();
340 }
341 }
342 writer.closeElement()
343 .startElement("documentTypeMap");
344 for (Map.Entry<Document.Type, IntCounter> entry : documentTypeMap.entrySet()) {
345 if (entry.getValue().value > 0) {
346 writer.startElement("documentType")
347 .attribute(COUNT, entry.getValue().toString())
348 .attribute(NAME, entry.getKey().name()).closeElement();
349 }
350 }
351 writer.closeElement()
352 .startElement("counterMap");
353 for (Map.Entry<ClaimStatistic.Counter, IntCounter> entry : counterMap.entrySet()) {
354 if (entry.getValue().value > 0) {
355 writer.startElement("counter")
356 .attribute(COUNT, entry.getValue().toString())
357 .attribute(NAME, entry.getKey().name()).closeElement();
358 }
359 }
360 writer.closeElement();
361 }
362 }
363
364
365
366
367
368
369 public void deserialize(final IOSupplier<InputStream> inputStreamSupplier) throws IOException {
370 DocumentBuilder builder = StandardXmlFactory.documentBuilder();
371 org.w3c.dom.Document document;
372
373 try (InputStream stream = inputStreamSupplier.get()) {
374 document = builder.parse(stream);
375 } catch (SAXException e) {
376 throw new IOException("Unable to read input", e);
377 }
378
379 XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("licenseName"), node -> {
380 Map<String, String> attributes = XMLConfigurationReader.attributes(node);
381 incLicenseNameCount(attributes.get(NAME), Integer.parseInt(attributes.get(COUNT)));
382 });
383
384 XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("familyCategory"), node -> {
385 Map<String, String> attributes = XMLConfigurationReader.attributes(node);
386 incLicenseCategoryCount(attributes.get(NAME), Integer.parseInt(attributes.get(COUNT)));
387 });
388
389 XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("documentType"), node -> {
390 Map<String, String> attributes = XMLConfigurationReader.attributes(node);
391 Document.Type type = Document.Type.valueOf(attributes.get(NAME));
392 incCounter(type, Integer.parseInt(attributes.get(COUNT)));
393 });
394
395 XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("counter"), node -> {
396 Map<String, String> attributes = XMLConfigurationReader.attributes(node);
397 Counter type = Counter.valueOf(attributes.get(NAME));
398 setCounter(type, Integer.parseInt(attributes.get(COUNT)));
399 });
400 }
401 }
402 }