FlagContainerBuilder.java

/*
 * SPDX-FileCopyrightText: none
 * SPDX-License-Identifier: CC0-1.0
 */

package gov.nist.secauto.metaschema.core.model;

import gov.nist.secauto.metaschema.core.model.impl.DefaultContainerFlagSupport;
import gov.nist.secauto.metaschema.core.util.CollectionUtil;
import gov.nist.secauto.metaschema.core.util.CustomCollectors;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

public class FlagContainerBuilder<T extends IFlagInstance> implements IFlagContainerBuilder<T> {
  private static final Logger LOGGER = LogManager.getLogger(FlagContainerBuilder.class);

  @Nullable
  private final QName jsonKeyName;
  @NonNull
  private final List<T> flags;

  /**
   * Construct a new flag container using the provided flag qualified name as the
   * JSON key.
   *
   * @param jsonKeyName
   *          the qualified name of the JSON key or {@code null} if no JSON key is
   *          configured
   */
  public FlagContainerBuilder(@Nullable QName jsonKeyName) {
    this.jsonKeyName = jsonKeyName;
    this.flags = new LinkedList<>();
  }

  @Override
  @NonNull
  public IFlagContainerBuilder<T> flag(@NonNull T instance) {
    flags.add(instance);
    return this;
  }

  @Override
  public IContainerFlagSupport<T> build() {
    IContainerFlagSupport<T> retval;
    if (flags.isEmpty()) {
      retval = IContainerFlagSupport.empty();
    } else {
      Map<QName, T> flagMap = CollectionUtil.unmodifiableMap(ObjectUtils.notNull(flags.stream()
          .collect(
              CustomCollectors.toMap(
                  INamed::getXmlQName,
                  CustomCollectors.identity(),
                  FlagContainerBuilder::handleShadowedInstances,
                  LinkedHashMap::new))));

      T jsonKey = jsonKeyName == null ? null : flagMap.get(jsonKeyName);
      retval = new DefaultContainerFlagSupport<>(flagMap, jsonKey);
    }
    return retval;
  }

  private static <INSTANCE extends IFlagInstance> INSTANCE handleShadowedInstances(
      @NonNull QName key,
      @NonNull INSTANCE shadowed,
      @NonNull INSTANCE shadowing) {
    if (!shadowed.equals(shadowing) && LOGGER.isErrorEnabled()) {
      IModelDefinition owningDefinition = shadowing.getContainingDefinition();
      IModule module = owningDefinition.getContainingModule();
      LOGGER.error("Unexpected duplicate flag instance named '%s' in definition '%s' in module name '%s' at '%s'",
          key,
          owningDefinition.getDefinitionQName(),
          module.getShortName(),
          module.getLocation());
    }
    return shadowing;
  }
}