DefaultGroupedModelContainerSupport.java

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

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

import gov.nist.secauto.metaschema.core.model.IAssemblyInstanceGrouped;
import gov.nist.secauto.metaschema.core.model.IContainerModelSupport;
import gov.nist.secauto.metaschema.core.model.IFieldInstanceGrouped;
import gov.nist.secauto.metaschema.core.model.INamedModelInstanceGrouped;
import gov.nist.secauto.metaschema.core.util.CollectionUtil;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.xml.namespace.QName;

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

/**
 * Supports grouped model instance operations on assembly model instances.
 * <p>
 * This implementation uses underlying {@link LinkedHashMap} instances to
 * preserve ordering.
 * <p>
 * Since a choice group only contains named model instances (i.e., fields,
 * assemblies), model instance operations are supported by the map returned by
 * {@link #getNamedModelInstanceMap()}.
 *
 * @param <NMI>
 *          the named model instance Java type
 * @param <FI>
 *          the field instance Java type
 * @param <AI>
 *          the assembly instance Java type
 */
public class DefaultGroupedModelContainerSupport<
    NMI extends INamedModelInstanceGrouped,
    FI extends IFieldInstanceGrouped,
    AI extends IAssemblyInstanceGrouped>
    implements IContainerModelSupport<NMI, NMI, FI, AI> {

  @NonNull
  private final Map<QName, NMI> namedModelInstances;
  @NonNull
  private final Map<QName, FI> fieldInstances;
  @NonNull
  private final Map<QName, AI> assemblyInstances;

  /**
   * Construct an empty, mutable container.
   */
  @SuppressWarnings("PMD.UseConcurrentHashMap")
  public DefaultGroupedModelContainerSupport() {
    this(
        new LinkedHashMap<>(),
        new LinkedHashMap<>(),
        new LinkedHashMap<>());
  }

  /**
   * Construct an immutable container from a collection of named model instances.
   *
   * @param instances
   *          the collection of named model instances to add to the new container.
   * @param fieldClass
   *          the Java type for field instances
   * @param assemblyClass
   *          the Java type for assembly instances
   */
  @SuppressWarnings({ "PMD.UseConcurrentHashMap" })
  @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
  private DefaultGroupedModelContainerSupport(
      @NonNull Collection<NMI> instances,
      @NonNull Class<FI> fieldClass,
      @NonNull Class<AI> assemblyClass) {
    assert !fieldClass.isAssignableFrom(assemblyClass) : String.format(
        "The field class '%s' must not be assignment compatible to the assembly class '%s'.",
        fieldClass.getName(),
        assemblyClass.getName());

    Map<QName, NMI> namedModelInstances = new LinkedHashMap<>();
    Map<QName, FI> fieldInstances = new LinkedHashMap<>();
    Map<QName, AI> assemblyInstances = new LinkedHashMap<>();
    for (NMI instance : instances) {
      QName key = instance.getXmlQName();
      namedModelInstances.put(key, instance);

      if (fieldClass.isInstance(instance)) {
        fieldInstances.put(key, fieldClass.cast(instance));
      } else if (assemblyClass.isInstance(instance)) {
        assemblyInstances.put(key, assemblyClass.cast(instance));
      }
    }

    this.namedModelInstances = namedModelInstances.isEmpty()
        ? CollectionUtil.emptyMap()
        : CollectionUtil.unmodifiableMap(namedModelInstances);
    this.fieldInstances = fieldInstances.isEmpty()
        ? CollectionUtil.emptyMap()
        : CollectionUtil.unmodifiableMap(fieldInstances);
    this.assemblyInstances = assemblyInstances.isEmpty()
        ? CollectionUtil.emptyMap()
        : CollectionUtil.unmodifiableMap(assemblyInstances);
  }

  /**
   * Construct an new container using the provided collections.
   *
   * @param namedModelInstances
   *          a collection of named model instances
   * @param fieldInstances
   *          a collection of field instances
   * @param assemblyInstances
   *          a collection of assembly instances
   */
  protected DefaultGroupedModelContainerSupport(
      @NonNull Map<QName, NMI> namedModelInstances,
      @NonNull Map<QName, FI> fieldInstances,
      @NonNull Map<QName, AI> assemblyInstances) {
    this.namedModelInstances = namedModelInstances;
    this.fieldInstances = fieldInstances;
    this.assemblyInstances = assemblyInstances;
  }

  @SuppressWarnings("null")
  @Override
  public Collection<NMI> getModelInstances() {
    return namedModelInstances.values();
  }

  @Override
  public Map<QName, NMI> getNamedModelInstanceMap() {
    return namedModelInstances;
  }

  @Override
  public Map<QName, FI> getFieldInstanceMap() {
    return fieldInstances;
  }

  @Override
  public Map<QName, AI> getAssemblyInstanceMap() {
    return assemblyInstances;
  }
}