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 setMaxValue(v == null ? new MutableInt(k.getDefaultMaxValue()) : v, value));
151 }
152
153 /**
154 * Gets the max limit for the specific counter.
155 * @param counter the counter to get the limit for.
156 * @return the limit for the counter or 0 if not set.
157 */
158 public int getMax(final ClaimStatistic.Counter counter) {
159 if (counter == null) {
160 DefaultLog.getInstance().warn("`null` passed as argument to getMax() -- returning 0");
161 return 0;
162 }
163 return max.get(counter).intValue();
164 }
165
166 /**
167 * Gets the min limit for the specific counter.
168 * @param counter the counter to get the limit for.
169 * @return the limit for the counter or zero if not set.
170 */
171 public int getMin(final ClaimStatistic.Counter counter) {
172 if (counter == null) {
173 DefaultLog.getInstance().warn("`null` passed as argument to getMin() -- returning 0");
174 return 0;
175 }
176 return min.get(counter).intValue();
177 }
178
179 /**
180 * Determines if the specified count is within the limits for the counter.
181 * @param counter the counter to check.
182 * @param count the limit to check.
183 * @return {@code true} if the count is within the limits, {@code false} otherwise.
184 * @throws NullPointerException if counter is {@code null}.
185 */
186 public boolean isValid(final ClaimStatistic.Counter counter, final int count) {
187 Objects.requireNonNull(counter, "counter must not be null.");
188 boolean result = max.get(counter).intValue() >= count && min.get(counter).intValue() <= count;
189 hasErrors |= !result;
190 return result;
191 }
192
193 /**
194 * Logs all the invalid values as errors.
195 * @param statistic the statistics that contain the run's values.
196 * @throws NullPointerException if statistic is {@code null}.
197 */
198 public void logIssues(final ClaimStatistic statistic) {
199 Objects.requireNonNull(statistic, "statistic must not be null.");
200 for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) {
201 if (!isValid(counter, statistic.getCounter(counter))) {
202 DefaultLog.getInstance().error(format("Unexpected count for %s, limit is [%s,%s]. Count: %s", counter,
203 min.get(counter), max.get(counter), statistic.getCounter(counter)));
204 DefaultLog.getInstance().debug(format("%s (%s) is %s", counter, counter.displayName(),
205 StringUtils.uncapitalize(counter.getDescription())));
206 }
207 }
208 }
209
210 /**
211 * Creates a list of items that have issues.
212 * @param statistic the statistics that contain the run values.
213 * @return a collection of counter names that are invalid.
214 * @throws NullPointerException if statistic is {@code null}.
215 */
216 public List<String> listIssues(final ClaimStatistic statistic) {
217 Objects.requireNonNull(statistic, "statistic must not be null.");
218 List<String> result = new ArrayList<>();
219 for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) {
220 if (!isValid(counter, statistic.getCounter(counter))) {
221 result.add(counter.toString());
222 }
223 }
224 return result;
225 }
226 }