ae514ec0e4c18380cba9d36ded8e532e7c4a6614
[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.HashSet;
25 import java.util.List;
26 import java.util.Objects;
27 import java.util.Set;
28 import java.util.regex.Pattern;
29
30 import org.oran.smo.yangtools.parser.model.ModuleIdentity;
31
32 /**
33  * A predicate that takes one or more modules, and one or more severities.
34  *
35  * @Author Mark Hollmann
36  */
37 public class ModuleAndSeverityFilterPredicate implements FindingFilterPredicate {
38
39     /**
40      * Parses the supplied string into an instance of ModuleAndSeverityFilterPredicate.
41      * <p>
42      * Module names are separated from severities by the ";" character.
43      * <p>
44      * Module names are separated by the "," character. Severities are likewise separated by
45      * the "," character. A value must be supplied for both module names and severities.
46      * <p>
47      * The only allowable wildcard character is a "*", denoting any "character sequence".
48      * <p>
49      * Example: The following will suppress all findings of severity INFO and WARNING in any IETF
50      * and IANA modules: "ietf-*,iana-*;INFO,WARNING"
51      */
52     public static ModuleAndSeverityFilterPredicate fromString(final String s,
53             final FindingSeverityCalculator findingSeverityCalculator) {
54
55         final String[] split = s.split(";");
56         if (split.length != 2) {
57             throw new RuntimeException("Invalid string format for ModuleAndSeverityFilterPredicate.");
58         }
59
60         final List<Pattern> moduleNames = new ArrayList<>();
61         if (!split[0].equals("*")) {
62             final String[] moduleNamesSplit = split[0].contains(",") ? split[0].split(",") : new String[] { split[0] };
63             for (final String stringPattern : moduleNamesSplit) {
64                 moduleNames.add(Pattern.compile(stringPattern.trim().replace(".", "[.]").replace("*", ".*")));
65             }
66         }
67
68         final Set<FindingSeverity> severities = new HashSet<>();
69         if (!split[1].equals("*")) {
70             final String[] severitiesSplit = split[1].contains(",") ? split[1].split(",") : new String[] { split[1] };
71             for (final String severity : severitiesSplit) {
72                 severities.add(FindingSeverity.valueOf(severity.trim().toUpperCase()));
73             }
74         }
75
76         return new ModuleAndSeverityFilterPredicate(moduleNames, severities, findingSeverityCalculator);
77     }
78
79     private final List<Pattern> moduleNames;
80     private final Set<FindingSeverity> severities;
81     private final FindingSeverityCalculator findingSeverityCalculator;
82
83     /**
84      * A finding will be filtered if the statement is part of any of the supplied modules,
85      * and has any of the supplied severities.
86      * <p>
87      * More formally, the name of the module in which the offending statement sits must be
88      * matchable against any of the module name patterns, and the severity of the finding must
89      * be part of the supplied set of severities.
90      * <p>
91      * Supplying an empty list for module names, or empty set for severities, will match-all
92      * for that parameter.
93      */
94     public ModuleAndSeverityFilterPredicate(final List<Pattern> moduleNames, final Set<FindingSeverity> severities,
95             final FindingSeverityCalculator findingSeverityCalculator) {
96         this.moduleNames = Objects.requireNonNull(moduleNames);
97         this.severities = Objects.requireNonNull(severities);
98         this.findingSeverityCalculator = Objects.requireNonNull(findingSeverityCalculator);
99     }
100
101     @Override
102     public boolean test(final Finding f) {
103         return matchOnModule(f) && matchOnSeverity(f);
104     }
105
106     private boolean matchOnModule(final Finding finding) {
107
108         if (moduleNames.isEmpty()) {
109             return true;
110         }
111
112         /*
113          * If the finding does not relate to a YAM then obviously we cannot match.
114          */
115         if (finding.getYangModel() == null) {
116             return false;
117         }
118
119         /*
120          * It can happen that we don't have a module identity yet, because a finding was found
121          * before we actually got a chance to extract the module name. In this case we use the
122          * name of the input. This is not foolproof, of course.
123          */
124         final ModuleIdentity moduleIdentity = finding.getYangModel().getModuleIdentity();
125         final String moduleOrSubModuleName = moduleIdentity == null ?
126                 finding.getYangModel().getYangInput().getName() :
127                 moduleIdentity.getModuleName();
128
129         for (final Pattern pattern : moduleNames) {
130             if (pattern.matcher(moduleOrSubModuleName).matches()) {
131                 return true;
132             }
133         }
134
135         return false;
136     }
137
138     private boolean matchOnSeverity(final Finding finding) {
139
140         if (severities.isEmpty()) {
141             return true;
142         }
143
144         return severities.contains(findingSeverityCalculator.calculateSeverity(finding.getFindingType()));
145     }
146 }