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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
21 package org.oran.smo.yangtools.parser.findings;
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;
29 import java.util.stream.Collectors;
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;
36 * Holds on to all findings issued during processing.
38 * @author Mark Hollmann
40 public class FindingsManager {
42 private final Set<Finding> findings = new HashSet<>();
45 * The severity calculator for findings.
47 private final FindingSeverityCalculator findingSeverityCalculator;
49 * This is the predicate to handle the global "suppress all"
51 private final SuppressAllFilterPredicate suppressAllFilterPredicate;
53 * This is the predicate to handle findings suppressed due to severity calculator.
55 private final SeverityCalculatorFilterPredicate severityCalculatorFilterPredicate;
57 * A number of predicates used to filter-out findings.
59 private final List<FindingFilterPredicate> filterPredicates = new ArrayList<>();
61 * Finding types that can never be suppressed.
63 private final Set<String> nonSuppressableFindingTypes = new HashSet<>();
65 public FindingsManager(final FindingSeverityCalculator findingSeverityCalculator) {
67 this.findingSeverityCalculator = findingSeverityCalculator;
69 this.suppressAllFilterPredicate = new SuppressAllFilterPredicate();
70 this.filterPredicates.add(suppressAllFilterPredicate);
72 this.severityCalculatorFilterPredicate = new SeverityCalculatorFilterPredicate(findingSeverityCalculator);
73 this.filterPredicates.add(severityCalculatorFilterPredicate);
76 public FindingSeverityCalculator getFindingSeverityCalculator() {
77 return findingSeverityCalculator;
81 * Add a finding unless it should be filtered.
83 public void addFinding(final Finding finding) {
84 if (!nonSuppressableFindingTypes.contains(finding.getFindingType()) && shouldSuppress(finding)) {
88 findings.add(finding);
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.
94 if (finding.getStatement() != null) {
95 finding.getStatement().addFinding(finding);
98 if (finding.getDataDomNode() != null) {
99 finding.getDataDomNode().addFinding(finding);
102 if (finding.getYangModel() != null) {
103 finding.getYangModel().addFinding(finding);
106 if (finding.getYangData() != null) {
107 finding.getYangData().addFinding(finding);
111 public void addFindings(final Collection<Finding> findings) {
112 findings.forEach(this::addFinding);
115 public Set<Finding> getAllFindings() {
120 * Removes all findings from this FindingsManager.
122 public void clear() {
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.
131 public boolean isFindingTypeGloballySuppressed(final String findingType) {
132 return suppressAllFilterPredicate.allSuppressed() || severityCalculatorFilterPredicate.findingTypeSuppressed(
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.
140 public void addNonSuppressableFindingType(final String findingType) {
141 nonSuppressableFindingTypes.add(Objects.requireNonNull(findingType));
145 * Adds a custom filter predicate to this findings manager.
147 public void addFilterPredicate(final FindingFilterPredicate pred) {
148 filterPredicates.add(Objects.requireNonNull(pred));
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.
155 public void setSuppressAll(final boolean val) {
156 suppressAllFilterPredicate.setSuppressAll(val);
160 * Applies the filter currently set in this FindingsManager to the supplied findings.
161 * Only findings passing the filter will be returned.
163 public Set<Finding> getFilteredFindings(final Set<Finding> findingsToFilter) {
164 Objects.requireNonNull(findingsToFilter);
165 return findingsToFilter.stream().filter(f -> !shouldSuppress(f)).collect(Collectors.toSet());
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
173 private boolean shouldSuppress(final Finding finding) {
175 for (final FindingFilterPredicate pred : filterPredicates) {
176 if (pred.test(finding)) {
185 * Removes all findings from this FindingsManager, except those of the finding type(s) supplied.
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());
192 findings.addAll(retainedFindings);
196 * Returns whether a finding of the specified type exists in this FindingsManager.
198 public boolean hasFindingOfType(final String findingType) {
199 Objects.requireNonNull(findingType);
200 return findings.stream().anyMatch(f -> f.getFindingType().equals(findingType));
204 * Returns whether a finding of any of the specified types exist in this FindingsManager.
206 public boolean hasFindingOfAnyOf(final List<String> findingTypes) {
207 Objects.requireNonNull(findingTypes);
208 return findings.stream().anyMatch(f -> findingTypes.contains(f.getFindingType()));
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.
215 public boolean removeFindingsOnYangDomElement(final YangDomElement domElement) {
217 List<Finding> toBeRemoved = null;
219 for (final Finding f : findings) {
221 final YangDomElement findingDomElement = f.getDomElement();
222 if (domElement != findingDomElement) {
227 * We record the finding as to-be-removed.
229 if (toBeRemoved == null) {
230 toBeRemoved = new ArrayList<>();
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.
239 if (toBeRemoved != null) {
240 for (final Finding f : toBeRemoved) {
242 final AbstractStatement onStatement = f.getStatement();
243 if (onStatement != null) {
244 onStatement.removeFinding(f);
247 final YangModel onYangModel = f.getYangModel();
248 if (onYangModel != null) {
249 onYangModel.removeFinding(f);
256 return toBeRemoved != null;