TypeSystem.java

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

package gov.nist.secauto.metaschema.core.metapath.item;

import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider;
import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDayTimeDurationItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDurationItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IEmailAddressItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IHostnameItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIPv4AddressItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIPv6AddressItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.INcNameItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.INonNegativeIntegerItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IPositiveIntegerItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.ITokenItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUriReferenceItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUuidItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem;
import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem;
import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem;
import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
import gov.nist.secauto.metaschema.core.metapath.item.node.IFieldNodeItem;
import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem;
import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Stream;

import javax.xml.namespace.QName;

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

@SuppressWarnings({ "removal", "deprecation" })
public final class TypeSystem {
  private static final Map<Class<? extends IItem>, QName> ITEM_CLASS_TO_QNAME_MAP;

  static {
    ITEM_CLASS_TO_QNAME_MAP = Collections.unmodifiableMap(Map.ofEntries(
        register(IItem.class, "item"),
        register(INodeItem.class, "node"),
        register(IDocumentNodeItem.class, "document-node"),
        register(IAssemblyNodeItem.class, "assembly-node"),
        register(IFieldNodeItem.class, "field-node"),
        register(IFlagNodeItem.class, "flag-node"),
        register(IArrayItem.class, "array"),
        register(IMapItem.class, "map"),
        register(IAnyAtomicItem.class, "any-atomic-type"),
        register(INumericItem.class, "numeric"),
        register(IDurationItem.class, "duration"),
        register(IBase64BinaryItem.class, MetaschemaDataTypeProvider.BASE64),
        register(IBooleanItem.class, MetaschemaDataTypeProvider.BOOLEAN),
        register(IDateItem.class, MetaschemaDataTypeProvider.DATE),
        // register(IDate.class, MetaschemaDataTypeProvider.DATE_WITH_TZ),
        register(IDateTimeItem.class, MetaschemaDataTypeProvider.DATE_TIME),
        // register(IBooleanItem.class, MetaschemaDataTypeProvider.DATE_TIME_WITH_TZ),
        register(IIPv4AddressItem.class, MetaschemaDataTypeProvider.IP_V4_ADDRESS),
        register(IIPv6AddressItem.class, MetaschemaDataTypeProvider.IP_V6_ADDRESS),
        register(IAnyUriItem.class, MetaschemaDataTypeProvider.URI),
        register(IUriReferenceItem.class, MetaschemaDataTypeProvider.URI_REFERENCE),
        register(IUuidItem.class, MetaschemaDataTypeProvider.UUID),
        register(IDayTimeDurationItem.class, MetaschemaDataTypeProvider.DAY_TIME_DURATION),
        register(IYearMonthDurationItem.class, MetaschemaDataTypeProvider.YEAR_MONTH_DURATION),
        register(IDecimalItem.class, MetaschemaDataTypeProvider.DECIMAL),
        register(IIntegerItem.class, MetaschemaDataTypeProvider.INTEGER),
        register(INonNegativeIntegerItem.class, MetaschemaDataTypeProvider.NON_NEGATIVE_INTEGER),
        register(IPositiveIntegerItem.class, MetaschemaDataTypeProvider.POSITIVE_INTEGER),
        register(IEmailAddressItem.class, MetaschemaDataTypeProvider.EMAIL_ADDRESS),
        register(IHostnameItem.class, MetaschemaDataTypeProvider.HOSTNAME),
        register(INcNameItem.class, MetaschemaDataTypeProvider.NCNAME),
        register(IStringItem.class, MetaschemaDataTypeProvider.STRING),
        register(ITokenItem.class, MetaschemaDataTypeProvider.TOKEN)));
  }

  private static Map.Entry<Class<? extends IItem>, QName> register(
      @NonNull Class<? extends IItem> clazz,
      @NonNull IDataTypeAdapter<?> adapter) {
    return Map.entry(clazz, adapter.getPreferredName());
  }

  private static Map.Entry<Class<? extends IItem>, QName> register(
      @NonNull Class<? extends IItem> clazz,
      @NonNull String typeName) {
    return Map.entry(clazz, new QName(MetapathConstants.NS_METAPATH.toASCIIString(), typeName));
  }

  @NonNull
  private static Stream<Class<? extends IItem>> getItemInterfaces(@NonNull Class<?> clazz) {
    @SuppressWarnings("unchecked")
    Stream<Class<? extends IItem>> retval = IItem.class.isAssignableFrom(clazz)
        ? Stream.of((Class<? extends IItem>) clazz)
        : Stream.empty();

    Class<?>[] interfaces = clazz.getInterfaces();
    if (interfaces.length > 0) {
      retval = Stream.concat(retval, Arrays.stream(interfaces).flatMap(TypeSystem::getItemInterfaces));
    }

    return ObjectUtils.notNull(retval);
  }

  /**
   * Get the human-friendly data type name for the provided Metapath item class.
   *
   * @param clazz
   *          the Metapath item class to get the name for
   * @return the name or {@code null} if no name is registered for the item class
   */
  public static String getName(@NonNull Class<? extends IItem> clazz) {
    Class<? extends IItem> itemClass = getItemInterfaces(clazz).findFirst().orElse(null);

    QName qname = ITEM_CLASS_TO_QNAME_MAP.get(itemClass);
    return qname == null ? clazz.getName() : asPrefixedName(qname);
  }

  private static String asPrefixedName(@NonNull QName qname) {
    String namespace = qname.getNamespaceURI();
    String prefix = namespace.isEmpty() ? null : StaticContext.getWellKnownPrefixForUri(namespace);
    return prefix == null ? qname.toString() : prefix + ":" + qname.getLocalPart();
  }

  private TypeSystem() {
    // disable construction
  }
}