dbb29dcdfd41ab814ea4d475f5bbab7f863590b9
[smo/teiv.git] /
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2024 Ericsson
4  *  Modifications Copyright (C) 2024 OpenInfra Foundation Europe
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  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, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21 package org.oran.smo.yangtools.parser.findings;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Objects;
28 import java.util.Set;
29 import java.util.stream.Collectors;
30
31 import org.oran.smo.yangtools.parser.model.YangModel;
32 import org.oran.smo.yangtools.parser.model.statements.AbstractStatement;
33 import org.oran.smo.yangtools.parser.model.yangdom.YangDomElement;
34
35 /**
36  * Holds on to all findings issued during processing.
37  *
38  * @author Mark Hollmann
39  */
40 public class FindingsManager {
41
42     private final Set<Finding> findings = new HashSet<>();
43
44     /*
45      * The severity calculator for findings.
46      */
47     private final FindingSeverityCalculator findingSeverityCalculator;
48     /*
49      * This is the predicate to handle the global "suppress all"
50      */
51     private final SuppressAllFilterPredicate suppressAllFilterPredicate;
52     /*
53      * This is the predicate to handle findings suppressed due to severity calculator.
54      */
55     private final SeverityCalculatorFilterPredicate severityCalculatorFilterPredicate;
56     /*
57      * A number of predicates used to filter-out findings.
58      */
59     private final List<FindingFilterPredicate> filterPredicates = new ArrayList<>();
60     /*
61      * Finding types that can never be suppressed.
62      */
63     private final Set<String> nonSuppressableFindingTypes = new HashSet<>();
64
65     public FindingsManager(final FindingSeverityCalculator findingSeverityCalculator) {
66
67         this.findingSeverityCalculator = findingSeverityCalculator;
68
69         this.suppressAllFilterPredicate = new SuppressAllFilterPredicate();
70         this.filterPredicates.add(suppressAllFilterPredicate);
71
72         this.severityCalculatorFilterPredicate = new SeverityCalculatorFilterPredicate(findingSeverityCalculator);
73         this.filterPredicates.add(severityCalculatorFilterPredicate);
74     }
75
76     public FindingSeverityCalculator getFindingSeverityCalculator() {
77         return findingSeverityCalculator;
78     }
79
80     /**
81      * Add a finding unless it should be filtered.
82      */
83     public void addFinding(final Finding finding) {
84         if (!nonSuppressableFindingTypes.contains(finding.getFindingType()) && shouldSuppress(finding)) {
85             return;
86         }
87
88         findings.add(finding);
89
90         /*
91          * The finding also gets attached to various objects. This is useful for downstream tooling;
92          * for example, for a tool that displays findings on statements.
93          */
94         if (finding.getStatement() != null) {
95             finding.getStatement().addFinding(finding);
96         }
97
98         if (finding.getDataDomNode() != null) {
99             finding.getDataDomNode().addFinding(finding);
100         }
101
102         if (finding.getYangModel() != null) {
103             finding.getYangModel().addFinding(finding);
104         }
105
106         if (finding.getYangData() != null) {
107             finding.getYangData().addFinding(finding);
108         }
109     }
110
111     public void addFindings(final Collection<Finding> findings) {
112         findings.forEach(this::addFinding);
113     }
114
115     public Set<Finding> getAllFindings() {
116         return findings;
117     }
118
119     /**
120      * Removes all findings from this FindingsManager.
121      */
122     public void clear() {
123         findings.clear();
124     }
125
126     /**
127      * Returns whether the finding would be suppressed, based on the supplied finding type. May be
128      * used as performance improvement to avoid complex processing that may result in findings being
129      * issued, just for these to be subsequently suppressed.
130      */
131     public boolean isFindingTypeGloballySuppressed(final String findingType) {
132         return suppressAllFilterPredicate.allSuppressed() || severityCalculatorFilterPredicate.findingTypeSuppressed(
133                 findingType);
134     }
135
136     /**
137      * Adds a finding type that cannot be suppressed. Any finding added to this findings
138      * manager of any of the non-suppressable findings will never be filtered-out.
139      */
140     public void addNonSuppressableFindingType(final String findingType) {
141         nonSuppressableFindingTypes.add(Objects.requireNonNull(findingType));
142     }
143
144     /**
145      * Adds a custom filter predicate to this findings manager.
146      */
147     public void addFilterPredicate(final FindingFilterPredicate pred) {
148         filterPredicates.add(Objects.requireNonNull(pred));
149     }
150
151     /**
152      * If true, will cause all findings to be suppressed. However, certain findings are considered so serious
153      * that it is not possible to suppress these, and any attempt to do so will be ignored.
154      */
155     public void setSuppressAll(final boolean val) {
156         suppressAllFilterPredicate.setSuppressAll(val);
157     }
158
159     /**
160      * Applies the filter currently set in this FindingsManager to the supplied findings.
161      * Only findings passing the filter will be returned.
162      */
163     public Set<Finding> getFilteredFindings(final Set<Finding> findingsToFilter) {
164         Objects.requireNonNull(findingsToFilter);
165         return findingsToFilter.stream().filter(f -> !shouldSuppress(f)).collect(Collectors.toSet());
166     }
167
168     /*
169      * Whether a finding should be suppressed. May not reliably work where a finding
170      * is issued very early during the parse phase when the identity of a module is
171      * not known yet.
172      */
173     private boolean shouldSuppress(final Finding finding) {
174
175         for (final FindingFilterPredicate pred : filterPredicates) {
176             if (pred.test(finding)) {
177                 return true;
178             }
179         }
180
181         return false;
182     }
183
184     /**
185      * Removes all findings from this FindingsManager, except those of the finding type(s) supplied.
186      */
187     public void retainFindingsOfType(final List<String> findingTypesToRetain) {
188         Objects.requireNonNull(findingTypesToRetain);
189         final Set<Finding> retainedFindings = findings.stream().filter(f -> findingTypesToRetain.contains(f
190                 .getFindingType())).collect(Collectors.toSet());
191         findings.clear();
192         findings.addAll(retainedFindings);
193     }
194
195     /**
196      * Returns whether a finding of the specified type exists in this FindingsManager.
197      */
198     public boolean hasFindingOfType(final String findingType) {
199         Objects.requireNonNull(findingType);
200         return findings.stream().anyMatch(f -> f.getFindingType().equals(findingType));
201     }
202
203     /**
204      * Returns whether a finding of any of the specified types exist in this FindingsManager.
205      */
206     public boolean hasFindingOfAnyOf(final List<String> findingTypes) {
207         Objects.requireNonNull(findingTypes);
208         return findings.stream().anyMatch(f -> findingTypes.contains(f.getFindingType()));
209     }
210
211     /**
212      * Removes any finding that has been reported against the supplied YANG DOM element. Returns
213      * true if at least a single finding was removed.
214      */
215     public boolean removeFindingsOnYangDomElement(final YangDomElement domElement) {
216
217         List<Finding> toBeRemoved = null;
218
219         for (final Finding f : findings) {
220
221             final YangDomElement findingDomElement = f.getDomElement();
222             if (domElement != findingDomElement) {
223                 continue;
224             }
225
226             /*
227              * We record the finding as to-be-removed.
228              */
229             if (toBeRemoved == null) {
230                 toBeRemoved = new ArrayList<>();
231             }
232             toBeRemoved.add(f);
233         }
234
235         /*
236          * We also need to remove the finding from the statement or the input, if so attached,
237          * to avoid it showing up in other places.
238          */
239         if (toBeRemoved != null) {
240             for (final Finding f : toBeRemoved) {
241
242                 final AbstractStatement onStatement = f.getStatement();
243                 if (onStatement != null) {
244                     onStatement.removeFinding(f);
245                 }
246
247                 final YangModel onYangModel = f.getYangModel();
248                 if (onYangModel != null) {
249                     onYangModel.removeFinding(f);
250                 }
251
252                 findings.remove(f);
253             }
254         }
255
256         return toBeRemoved != null;
257     }
258 }