1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.core.model;
7   
8   import org.apache.logging.log4j.LogManager;
9   import org.apache.logging.log4j.Logger;
10  
11  import java.util.Collection;
12  import java.util.LinkedHashSet;
13  import java.util.Objects;
14  import java.util.Set;
15  import java.util.function.Function;
16  
17  import edu.umd.cs.findbugs.annotations.NonNull;
18  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19  
20  /**
21   * Supports walking a portion of a metaschema model collecting a set of
22   * definitions that match the provided filter. For a definition to be collected,
23   * the filter must return {@code true}.
24   */
25  public abstract class DefinitionCollectingModelWalker
26      extends ModelWalker<Void> {
27    private static final Logger LOGGER = LogManager.getLogger(DefinitionCollectingModelWalker.class);
28  
29    private final Function<IDefinition, Boolean> filter;
30    @NonNull
31    private final Set<IDefinition> definitions = new LinkedHashSet<>();
32  
33    @Override
34    protected Void getDefaultData() { // NOPMD - intentional
35      return null;
36    }
37  
38    /**
39     * Construct a new walker using the provided filter.
40     *
41     * @param filter
42     *          the filter to match definitions against
43     */
44    protected DefinitionCollectingModelWalker(Function<IDefinition, Boolean> filter) {
45      Objects.requireNonNull(filter, "filter");
46      this.filter = filter;
47    }
48  
49    /**
50     * Retrieves the filter used for matching.
51     *
52     * @return the filter
53     */
54    protected Function<IDefinition, Boolean> getFilter() {
55      return filter;
56    }
57  
58    /**
59     * Return the collection of definitions matching the configured filter.
60     *
61     * @return the collection of definitions
62     */
63    @NonNull
64    @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "interface doesn't allow modification")
65    public Collection<? extends IDefinition> getDefinitions() {
66      return definitions;
67    }
68  
69    @Override
70    protected void visit(IFlagDefinition def, Void data) {
71      if (LOGGER.isTraceEnabled()) {
72        LOGGER.trace("visiting flag definition '{}'", def.toCoordinates());
73      }
74      if (getFilter().apply(def)) {
75        definitions.add(def);
76      }
77    }
78  
79    @Override
80    protected boolean visit(IFieldDefinition def, Void data) {
81      if (LOGGER.isTraceEnabled()) {
82        LOGGER.trace("visiting field definition '{}'", def.toCoordinates());
83      }
84      boolean retval;
85      if (definitions.contains(def)) {
86        // no need to visit, since this has already been seen
87        retval = false;
88      } else {
89        if (getFilter().apply(def)) {
90          definitions.add(def);
91        }
92        retval = true;
93      }
94      return retval;
95    }
96  
97    @Override
98    protected boolean visit(IAssemblyDefinition def, Void data) {
99      if (LOGGER.isTraceEnabled()) {
100       LOGGER.trace("visiting assembly definition '{}'", def.toCoordinates());
101     }
102     boolean retval;
103     if (definitions.contains(def)) {
104       // no need to visit, since this has already been seen
105       retval = false;
106     } else {
107       if (getFilter().apply(def)) {
108         definitions.add(def);
109       }
110       retval = true;
111     }
112     return retval;
113   }
114 }