001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.core.model;
007
008import org.apache.logging.log4j.LogManager;
009import org.apache.logging.log4j.Logger;
010
011import java.util.Collection;
012import java.util.LinkedHashSet;
013import java.util.Objects;
014import java.util.Set;
015import java.util.function.Function;
016
017import edu.umd.cs.findbugs.annotations.NonNull;
018import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
019
020/**
021 * Supports walking a portion of a metaschema model collecting a set of
022 * definitions that match the provided filter. For a definition to be collected,
023 * the filter must return {@code true}.
024 */
025public abstract class DefinitionCollectingModelWalker
026    extends ModelWalker<Void> {
027  private static final Logger LOGGER = LogManager.getLogger(DefinitionCollectingModelWalker.class);
028
029  private final Function<IDefinition, Boolean> filter;
030  @NonNull
031  private final Set<IDefinition> definitions = new LinkedHashSet<>();
032
033  @Override
034  protected Void getDefaultData() {
035    return null;
036  }
037
038  /**
039   * Construct a new walker using the provided filter.
040   *
041   * @param filter
042   *          the filter to match definitions against
043   */
044  protected DefinitionCollectingModelWalker(Function<IDefinition, Boolean> filter) {
045    Objects.requireNonNull(filter, "filter");
046    this.filter = filter;
047  }
048
049  /**
050   * Retrieves the filter used for matching.
051   *
052   * @return the filter
053   */
054  protected Function<IDefinition, Boolean> getFilter() {
055    return filter;
056  }
057
058  /**
059   * Return the collection of definitions matching the configured filter.
060   *
061   * @return the collection of definitions
062   */
063  @NonNull
064  @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "interface doesn't allow modification")
065  public Collection<? extends IDefinition> getDefinitions() {
066    return definitions;
067  }
068
069  @Override
070  protected void visit(IFlagDefinition def, Void data) {
071    if (LOGGER.isTraceEnabled()) {
072      LOGGER.trace("visiting flag definition '{}'", def.toCoordinates());
073    }
074    if (getFilter().apply(def)) {
075      definitions.add(def);
076    }
077  }
078
079  @Override
080  protected boolean visit(IFieldDefinition def, Void data) {
081    if (LOGGER.isTraceEnabled()) {
082      LOGGER.trace("visiting field definition '{}'", def.toCoordinates());
083    }
084    boolean retval;
085    if (definitions.contains(def)) {
086      // no need to visit, since this has already been seen
087      retval = false;
088    } else {
089      if (getFilter().apply(def)) {
090        definitions.add(def);
091      }
092      retval = true;
093    }
094    return retval;
095  }
096
097  @Override
098  protected boolean visit(IAssemblyDefinition def, Void data) {
099    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}