All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gedcomx.rt.GedcomNamespaceManager Maven / Gradle / Ivy

There is a newer version: 3.41.0
Show newest version
/**
 * Copyright Intellectual Reserve, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gedcomx.rt;

import org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper;
import org.gedcomx.rt.json.JsonElementWrapper;

import javax.xml.XMLConstants;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlSchema;
import jakarta.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import java.beans.Introspector;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;

/**
 * A nice namespace prefix mapper that can be used to make XML and JSON output as pretty as it can be.
 *
 * @author Ryan Heaton
 */
public class GedcomNamespaceManager extends NamespacePrefixMapper {

  private final String defaultns;
  private final Map ns2prefix;

  private static boolean INITIALIZED = false;
  private static final Map KNOWN_PREFIXES = new HashMap();
  private static final Map RUNTIME_VERSIONS = new HashMap();
  private static final Map> WRAPPED_JSON_TYPES_BY_NAME = new HashMap>();
  private static final Map QNAME_WRAPPER_JSON_NAMES = new HashMap();
  private static final Map> KNOWN_JSON_TYPES_BY_NAME = new HashMap>();
  private static final Map> KNOWN_JSON_TYPES_BY_TYPE_ID = new HashMap>();

  public GedcomNamespaceManager(Class rootClass) {
    this(getDefaultNamespace(rootClass));
  }

  private static String getDefaultNamespace(Class rootClass) {
    DefaultNamespace defaultNs = rootClass.getAnnotation(DefaultNamespace.class);
    if (defaultNs == null && rootClass.getPackage() != null) {
      defaultNs = rootClass.getPackage().getAnnotation(DefaultNamespace.class);
    }

    String defaultNamespace = "";
    if (defaultNs != null) {
      defaultNamespace = defaultNs.value();
    }
    else {
      XmlRootElement rootElement = rootClass.getAnnotation(XmlRootElement.class);
      if (rootElement != null) {
        if ("##default".equals(rootElement.namespace())) {
          if (rootClass.getPackage() != null) {
            XmlSchema xmlSchema = rootClass.getPackage().getAnnotation(XmlSchema.class);
            if (xmlSchema != null) {
              defaultNamespace = xmlSchema.namespace();
            }
          }
        }
        else {
          defaultNamespace = rootElement.namespace();
        }
      }
    }

    return defaultNamespace;
  }

  public GedcomNamespaceManager(Map overrides, String defaultns) {
    Map ns2prefix = getKnownPrefixes();
    if (overrides != null) {
      ns2prefix.putAll(overrides);
    }
    this.ns2prefix = Collections.unmodifiableMap(ns2prefix);
    if (defaultns == null) {
      defaultns = "";
    }
    this.defaultns = defaultns;
  }

  public GedcomNamespaceManager(String defaultns) {
    this(null, defaultns);
  }

  public GedcomNamespaceManager(Map overrides) {
    this(overrides, null);
  }

  public GedcomNamespaceManager() {
    this(null, null);
  }

  /**
   * The known set of namespace-to-prefix mappings.
   *
   * @return The known set of namespace-to-prefix mappings.
   */
  public static Map getKnownPrefixes() {
    if (INITIALIZED) {
      return KNOWN_PREFIXES;    // if already initialized no need to wait for a synchronized method to tell us it is initialized - just return it immediately.
    }
    init(Thread.currentThread().getContextClassLoader());
    return KNOWN_PREFIXES;
  }

  protected static synchronized void init(ClassLoader loader) {
    if (INITIALIZED) {
      return;
    }

    Map namespacePrefixes = new HashMap();
    namespacePrefixes.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
    namespacePrefixes.put("http://gedcomx.org/record/v1/", "gxr");
    namespacePrefixes.put("http://gedcomx.org/conclusion/v1/", "gxc");

    Map wrapperJsonNames = new HashMap();
    Map> wrappedJsonTypes = new HashMap>();

    Set> modelClasses = new HashSet>();

    try {
      Enumeration resources = loader.getResources("META-INF/gedcomx.models");
      while (resources.hasMoreElements()) {
        try {
          URL resource = resources.nextElement();
          BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream()));
          String classname = reader.readLine();
          while (classname != null) {
            modelClasses.add(Class.forName(classname, true, loader));
            classname = reader.readLine();
          }
        }
        catch (Exception e) {
          //no-op...
        }
      }
    }
    catch (IOException e) {
      //no-op.
    }

    for (Class modelClass : modelClasses) {
      MediaTypeDefinition mediaTypeInfo = modelClass.getAnnotation(MediaTypeDefinition.class);
      for (Model model : mediaTypeInfo.models()) {
        namespacePrefixes.put(model.namespace(), model.id());
      }
    }

    KNOWN_PREFIXES.putAll(namespacePrefixes);
    QNAME_WRAPPER_JSON_NAMES.putAll(wrapperJsonNames);
    WRAPPED_JSON_TYPES_BY_NAME.putAll(wrappedJsonTypes);
    INITIALIZED = true;
  }

  public static String nameFromQName(String namespaceUri, String localPart) {
    if (namespaceUri == null) {
      namespaceUri = "";
    }

    StringBuilder nameBuilder = new StringBuilder(namespaceUri);
    if (XMLConstants.XML_NS_URI.equals(namespaceUri)) {
      nameBuilder.append('#');
    }
    nameBuilder.append(localPart);
    return nameBuilder.toString();
  }

  @Override
  public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
    String choice = this.ns2prefix.get(namespaceUri);
    if (!requirePrefix && this.defaultns.equals(namespaceUri)) {
      //(01/12/12, heatonra) I looked into why 'requirePrefix' is always 'true'. It's because
      // attributeFormDefault = "qualified" so attributes always have to have a prefix, so
      // requirePrefix is always true.
      return "";
    }
    return choice;
  }

  /**
   * Get the json wrapper name for the specified XML element name.
   *
   * @param wrapperName The identifier for the wrapper name.
   * @return The json wrapper name, or null if none.
   */
  public static String getJsonNameForWrapperName(QName wrapperName) {
    init(Thread.currentThread().getContextClassLoader());
    return QNAME_WRAPPER_JSON_NAMES.get(wrapperName);
  }

  /**
   * Get the wrapper name for the specified json name.
   *
   * @param jsonName The json name.
   * @return The wrapper qname.
   */
  public static QName findWrapperNameForJsonName(String jsonName) {
    init(Thread.currentThread().getContextClassLoader());
    for (Map.Entry entry : QNAME_WRAPPER_JSON_NAMES.entrySet()) {
      if (entry.getValue().equals(jsonName)) {
        return entry.getKey();
      }
    }

    return null;
  }

  /**
   * Get the type of the wrapped object for the specified json name.
   *
   * @param jsonName The json name.
   * @return The wrapped type.
   */
  public static Class getWrappedTypeForJsonName(String jsonName) {
    init(Thread.currentThread().getContextClassLoader());
    return WRAPPED_JSON_TYPES_BY_NAME.get(jsonName);
  }

  /**
   * Register a known JSON type. It must be annotated with either @JsonElementWrapper or @XmlRootElement.
   *
   * @param type the type to register.
   */
  public static void registerKnownJsonType(Class type) {
    String jsonName = getJsonName(type);
    if (jsonName != null) {
      KNOWN_JSON_TYPES_BY_NAME.put(jsonName, type);
    }
    KNOWN_JSON_TYPES_BY_TYPE_ID.put(getTypeIdName(type), type);
  }

  /**
   * Get the JSON name for the specified type.
   *
   * @param type The type.
   * @return The json name.
   */
  public static String getJsonName(Class type) {
    if (type.isAnnotationPresent(JsonElementWrapper.class)) {
      //support custom json element name
      JsonElementWrapper ext = type.getAnnotation(JsonElementWrapper.class);
      return nameFromQName(ext.namespace(), ext.name());
    }
    else if (type.isAnnotationPresent(XmlRootElement.class)) {
      XmlRootElement rootElement = type.getAnnotation(XmlRootElement.class);
      if (rootElement != null) {
        String localPart = rootElement.name();
        if ("##default".equals(localPart)) {
          localPart = Introspector.decapitalize(type.getSimpleName());
        }
        String namespaceURI = rootElement.namespace();
        if ("##default".equals(namespaceURI)) {
          Package pkg = type.getPackage();
          if (pkg != null && pkg.isAnnotationPresent(XmlSchema.class)) {
            namespaceURI = pkg.getAnnotation(XmlSchema.class).namespace();
          }
          else {
            namespaceURI = "";
          }
        }

        return nameFromQName(namespaceURI, localPart);
      }
    }

    return null;
  }

  /**
   * Get the id for the specified type.
   *
   * @param type The type.
   * @return The type id.
   */
  public static String getTypeIdName(Class type) {
    String ns = "";
    if (type.getPackage() != null && type.getPackage().isAnnotationPresent(XmlSchema.class)) {
      ns = type.getPackage().getAnnotation(XmlSchema.class).namespace();
    }

    String name = Introspector.decapitalize(type.getSimpleName());
    if (type.isAnnotationPresent(XmlType.class)) {
      XmlType typeMeta = type.getAnnotation(XmlType.class);
      if (!"##default".equals(typeMeta.name())) {
        name = typeMeta.name();
      }

      if (!"##default".equals(typeMeta.namespace())) {
        ns = typeMeta.namespace();
      }
    }

    return ns + name;
  }

  /**
   * Get the known type for the given JSON name.
   *
   * @param jsonName The json name.
   * @return The known type, or null if not known.
   */
  public static Class getKnownJsonType(String jsonName) {
    return KNOWN_JSON_TYPES_BY_NAME.get(jsonName);
  }

  /**
   * Get the known type for the given data type.
   *
   * @param typeId The type id.
   * @return The known type, or null if not known.
   */
  public static Class getKnownTypeById(String typeId) {
    return KNOWN_JSON_TYPES_BY_TYPE_ID.get(typeId);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy