View Javadoc
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 }