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