1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.rat.config.results;
20
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Objects;
24 import java.util.concurrent.ConcurrentHashMap;
25
26 import org.apache.commons.lang3.StringUtils;
27 import org.apache.commons.lang3.mutable.MutableInt;
28 import org.apache.rat.report.claim.ClaimStatistic;
29 import org.apache.rat.utils.DefaultLog;
30
31 import static java.lang.String.format;
32
33 /**
34 * Validates the ClaimStatistic results meet the specified requirements.
35 */
36 public final class ClaimValidator {
37 /**
38 * The map of max counter limits.
39 */
40 private final ConcurrentHashMap<ClaimStatistic.Counter, MutableInt> max = new ConcurrentHashMap<>();
41 /**
42 * The map of min counter limits.
43 */
44 private final ConcurrentHashMap<ClaimStatistic.Counter, MutableInt> min = new ConcurrentHashMap<>();
45 /**
46 * {@code true} if errors were detected in the claim.
47 */
48 private boolean hasErrors;
49
50 /**
51 * Constructor.
52 * Visible for testing.
53 * @param withData if {@code true} the max and min are loaded with data from the Counter definitions.
54 */
55 ClaimValidator(final boolean withData) {
56 if (withData) {
57 for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) {
58 max.put(counter,
59 new MutableInt(counter.getDefaultMaxValue() < 0 ? Integer.MAX_VALUE : counter.getDefaultMaxValue()));
60 min.put(counter, new MutableInt(counter.getDefaultMinValue()));
61 }
62 }
63
64 }
65 /**
66 * Constructor.
67 */
68 public ClaimValidator() {
69 this(true);
70 }
71
72 /**
73 * Returns {@code true} if any validation failed.
74 * @return {@code true} if any validation failed.
75 */
76 public boolean hasErrors() {
77 return hasErrors;
78 }
79
80 /**
81 * Sets the value unconditionally.
82 * @param result the MutableInt to set.
83 * @param value the value to set it to.
84 * @return the {@code result} argument
85 */
86 private MutableInt newValue(final MutableInt result, final int value) {
87 result.setValue(value);
88 return result;
89 }
90
91 /**
92 * Sets the {@code result.value} if it is greater than the {@code value} argument.
93 * @param result the MutableInt to set.
94 * @param value the value to set it to if it is less than the {@code result.value}.
95 * @return the {@code result} argument
96 */
97 private MutableInt setMinValue(final MutableInt result, final int value) {
98 if (result.intValue() > value) {
99 result.setValue(value);
100 }
101 return result;
102 }
103
104 /**
105 * Sets the {@code result.value} if it is less than the {@code value} argument.
106 * @param result the MutableInt to set.
107 * @param value the value to set it to if it is greater than the {@code result.value}.
108 * @return the {@code result} argument
109 */
110 private MutableInt setMaxValue(final MutableInt result, final int value) {
111 if (result.intValue() < value) {
112 result.setValue(value);
113 }
114 return result;
115 }
116
117 /**
118 * Sets the max value for the specified counter.
119 * Will adjust the minimum if the minimum is greater than the maximum.
120 * @param counter the counter to set the limit for.
121 * @param value the value to set. A negative value specifies no maximum value.
122 */
123 public void setMax(final ClaimStatistic.Counter counter, final int value) {
124 if (counter == null) {
125 DefaultLog.getInstance().warn("`null` passed as argument to setMax() -- ignoring");
126 return;
127 }
128 int setValue = value < 0 ? Integer.MAX_VALUE : value;
129 MutableInt maxValue = max.compute(counter, (k, v) ->
130 v == null ? new MutableInt(setValue) : newValue(v, setValue));
131 min.compute(counter, (k, v) ->
132 v == null ? new MutableInt(Math.min(k.getDefaultMinValue(), maxValue.intValue())) :
133 setMinValue(v, setValue));
134 }
135
136 /**
137 * Sets the min value for the specified counter.
138 * Will adjust the maximum if the maximum is less than the minimum.
139 * @param counter the counter to set the limit for.
140 * @param value the value to set. A negative value specifies no maximum value.
141 */
142 public void setMin(final ClaimStatistic.Counter counter, final int value) {
143 if (counter == null) {
144 DefaultLog.getInstance().warn("`null` passed as argument to setMin() -- ignoring");
145 return;
146 }
147 min.compute(counter, (k, v) ->
148 v == null ? new MutableInt(value) : newValue(v, value));
149 max.compute(counter, (k, v) ->
150 v == null ? setMaxValue(new MutableInt(k.getDefaultMaxValue()), value) :
151 setMaxValue(v, value));
152 }
153
154 /**
155 * Gets the max limit for the specific counter.
156 * @param counter the counter to get the limit for.
157 * @return the limit for the counter or 0 if not set.
158 */
159 public int getMax(final ClaimStatistic.Counter counter) {
160 if (counter == null) {
161 DefaultLog.getInstance().warn("`null` passed as argument to getMax() -- returning 0");
162 return 0;
163 }
164 return max.get(counter).intValue();
165 }
166
167 /**
168 * Gets the min limit for the specific counter.
169 * @param counter the counter to get the limit for.
170 * @return the limit for the counter or zero if not set.
171 */
172 public int getMin(final ClaimStatistic.Counter counter) {
173 if (counter == null) {
174 DefaultLog.getInstance().warn("`null` passed as argument to getMin() -- returning 0");
175 return 0;
176 }
177 return min.get(counter).intValue();
178 }
179
180 /**
181 * Determines if the specified count is within the limits for the counter.
182 * @param counter the counter to check.
183 * @param count the limit to check.
184 * @return {@code true} if the count is within the limits, {@code false} otherwise.
185 * @throws NullPointerException if counter is {@code null}.
186 */
187 public boolean isValid(final ClaimStatistic.Counter counter, final int count) {
188 Objects.requireNonNull(counter, "counter must not be null.");
189 boolean result = max.get(counter).intValue() >= count && min.get(counter).intValue() <= count;
190 hasErrors |= !result;
191 return result;
192 }
193
194 /**
195 * Logs all the invalid values as errors.
196 * @param statistic the statistics that contain the run's values.
197 * @throws NullPointerException if statistic is {@code null}.
198 */
199 public void logIssues(final ClaimStatistic statistic) {
200 Objects.requireNonNull(statistic, "statistic must not be null.");
201 for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) {
202 if (!isValid(counter, statistic.getCounter(counter))) {
203 DefaultLog.getInstance().error(format("Unexpected count for %s, limit is [%s,%s]. Count: %s", counter,
204 min.get(counter), max.get(counter), statistic.getCounter(counter)));
205 DefaultLog.getInstance().debug(format("%s (%s) is %s", counter, counter.displayName(),
206 StringUtils.uncapitalize(counter.getDescription())));
207 }
208 }
209 }
210
211 /**
212 * Creates a list of items that have issues.
213 * @param statistic the statistics that contain the run values.
214 * @return a collection of counter names that are invalid.
215 * @throws NullPointerException if statistic is {@code null}.
216 */
217 public List<String> listIssues(final ClaimStatistic statistic) {
218 Objects.requireNonNull(statistic, "statistic must not be null.");
219 List<String> result = new ArrayList<>();
220 for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) {
221 if (!isValid(counter, statistic.getCounter(counter))) {
222 result.add(counter.toString());
223 }
224 }
225 return result;
226 }
227 }