/*
* 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.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.converters.sharedUtils.stats;
//~--- JDK imports ------------------------------------------------------------
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.Map;
import java.util.UUID;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sh.isaac.MetaData;
import sh.isaac.api.component.concept.ConceptSpecification;
import sh.isaac.api.constants.DynamicSememeConstants;
import sh.isaac.api.util.UuidT3Generator;
import sh.isaac.api.util.UuidT5Generator;
import sh.isaac.converters.sharedUtils.ConsoleUtil;
import sh.isaac.converters.sharedUtils.ConverterBaseMojo;
//~--- classes ----------------------------------------------------------------
/**
* A utility class for generating UUIDs which keeps track of what was used to generate the UUIDs - which
* can then be dumped to disk (or looked up by UUID)
*
* The in-memory map can be disabled by setting the static flag here - or - with loaders that extend {@link ConverterBaseMojo}
* by setting the system property skipUUIDDebug to true - or in maven speak - '-DskipUUIDDebug' on the command line.
*
* @author darmbrust
* TODO: evaluate the utility of this class. KEC
*/
public class ConverterUUID {
/** The Constant LOG. */
private static final Logger LOG = LogManager.getLogger();
/** The disable UUID map flag. */
public static boolean disableUUIDMap = false; // Some loaders need to disable this due to memory constraints
/** The master UUID map. */
private static Hashtable<UUID, String> masterUUIDMap = new Hashtable<UUID, String>();
/** The namespace. */
private static NAMESPACE namespace = null;
/** The constants. */
private static ConceptSpecification[] constants = new ConceptSpecification[] {
MetaData.IS_A, MetaData.SYNONYM, MetaData.FULLY_SPECIFIED_NAME, MetaData.DEFINITION_DESCRIPTION_TYPE,
MetaData.US_ENGLISH_DIALECT, MetaData.GB_ENGLISH_DIALECT, MetaData.CONVERTED_IBDF_ARTIFACT_CLASSIFIER,
MetaData.CONVERTED_IBDF_ARTIFACT_VERSION, MetaData.CONVERTER_VERSION, MetaData.SOURCE_ARTIFACT_VERSION,
MetaData.SOURCE_RELEASE_DATE, MetaData.SCTID, DynamicSememeConstants.get().DYNAMIC_SEMEME_EXTENSION_DEFINITION,
DynamicSememeConstants.get().DYNAMIC_SEMEME_INDEX_CONFIGURATION,
DynamicSememeConstants.get().DYNAMIC_SEMEME_REFERENCED_COMPONENT_RESTRICTION,
DynamicSememeConstants.get().DYNAMIC_SEMEME_DEFINITION_DESCRIPTION,
DynamicSememeConstants.get().DYNAMIC_SEMEME_ASSOCIATION_SEMEME
};
//~--- enums ---------------------------------------------------------------
public enum NAMESPACE {
SNOMED,
LOINC,
RXNORM;
UUID namespaceUuid;
//~--- constructors -----------------------------------------------------
NAMESPACE() {
try {
this.namespaceUuid = UUID.nameUUIDFromBytes((NAMESPACE.class.getName() +
name()).getBytes(UuidT3Generator.ENCODING_FOR_UUID_GENERATION));
} catch (UnsupportedEncodingException ex) {
LOG.error(ex.getLocalizedMessage(), ex);
throw new RuntimeException(ex);
}
}
}
//~--- methods -------------------------------------------------------------
/**
* Allow this map to be updated with UUIDs that were not generated via this utility class.
*
* @param value the value
* @param uuid the uuid
*/
public static void addMapping(String value, UUID uuid) {
if (!disableUUIDMap) {
final String putResult = masterUUIDMap.put(uuid, value);
if (putResult != null) {
throw new RuntimeException("Just made a duplicate UUID! '" + value + "' -> " + uuid);
}
}
}
/**
* Clear cache.
*/
public static void clearCache() {
masterUUIDMap.clear();
}
/**
* Configure namespace.
*
* @param namespace the namespace
*/
public static void configureNamespace(NAMESPACE namespace) {
if ((ConverterUUID.namespace != null) &&!ConverterUUID.namespace.equals(namespace)) {
throw new RuntimeException("Reconfiguring Namespace not allowed: " + namespace);
}
ConverterUUID.namespace = namespace;
}
/**
* Create a new Type5 UUID using the provided name as the seed in the configured namespace.
*
* Throws a runtime exception if the namespace has not been configured.
*
* @param name the name
* @return the uuid
*/
public static UUID createNamespaceUUIDFromString(String name) {
return createNamespaceUUIDFromString(name, false);
}
/**
* Create a new Type5 UUID using the provided namespace, and provided name as the seed.
*
* @param namespace the namespace
* @param name the name
* @return the uuid
*/
public static UUID createNamespaceUUIDFromString(NAMESPACE namespace, String name) {
return createNamespaceUUIDFromString(namespace, name, false);
}
/**
* Create a new Type5 UUID using the provided name as the seed in the configured namespace.
*
* Throws a runtime exception if the namespace has not been configured.
*
* @param name the name
* @param skipDupeCheck can be used to bypass the duplicate checking function - useful in cases where you know
* you are creating the same UUID more than once. Normally, this method throws a runtime exception
* if the same UUID is generated more than once.
* @return the uuid
*/
public static UUID createNamespaceUUIDFromString(String name, boolean skipDupeCheck) {
initCheck();
return createNamespaceUUIDFromString(namespace, name, skipDupeCheck);
}
/**
* Create a new Type5 UUID using the provided namespace, and provided name as the seed.
*
* @param namespace the namespace
* @param name the name
* @param skipDupeCheck can be used to bypass the duplicate checking function - useful in cases where you know
* you are creating the same UUID more than once. Normally, this method throws a runtime exception
* if the same UUID is generated more than once.
* @return the uuid
*/
public static UUID createNamespaceUUIDFromString(NAMESPACE namespace, String name, boolean skipDupeCheck) {
UUID uuid;
try {
uuid = UuidT5Generator.get(namespace.namespaceUuid, name);
} catch (final Exception e) {
throw new RuntimeException("Unexpected error configuring UUID generator");
}
if (!disableUUIDMap) {
final String putResult = masterUUIDMap.put(uuid, name);
if (!skipDupeCheck && (putResult != null)) {
throw new RuntimeException("Just made a duplicate UUID! '" + name + "' -> " + uuid);
}
}
return uuid;
}
/**
* Create a new Type5 UUID using the provided name as the seed in the configured namespace.
*
* Throws a runtime exception if the namespace has not been configured.
*
* @param values the values
* @return the uuid
*/
public static UUID createNamespaceUUIDFromStrings(String... values) {
final StringBuilder uuidKey = new StringBuilder();
for (final String s: values) {
if (s != null) {
uuidKey.append(s);
uuidKey.append("|");
}
}
if (uuidKey.length() > 1) {
uuidKey.setLength(uuidKey.length() - 1);
} else {
throw new RuntimeException("No string provided!");
}
return createNamespaceUUIDFromString(uuidKey.toString());
}
/**
* Write out a debug file with all of the UUID - String mappings.
*
* @param outputDirectory the output directory
* @param prefix the prefix
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void dump(File outputDirectory, String prefix)
throws IOException {
try (BufferedWriter br = new BufferedWriter(new FileWriter(new File(outputDirectory,
prefix + "DebugMap.txt")));) {
if (disableUUIDMap) {
ConsoleUtil.println("UUID Debug map was disabled");
br.write("Note - the UUID debug feature was disabled, this file is incomplete" +
System.getProperty("line.separator"));
}
for (final Map.Entry<UUID, String> entry: masterUUIDMap.entrySet()) {
br.write(entry.getKey() + " - " + entry.getValue() + System.getProperty("line.separator"));
}
}
}
/**
* In some scenarios, it isn't desireable to cache every creation string - allow the removal in these cases.
*
* @param uuid the uuid
*/
public static void removeMapping(UUID uuid) {
masterUUIDMap.remove(uuid);
}
/**
* Inits the check.
*/
private static void initCheck() {
if (namespace == null) {
throw new RuntimeException("Namespace UUID has not yet been initialized");
}
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the namespace.
*
* @return the namespace
*/
public static NAMESPACE getNamespace() {
return namespace;
}
/**
* Return the string that was used to generate this UUID (if available - null if not).
*
* @param uuid the uuid
* @return the UUID creation string
*/
public static String getUUIDCreationString(UUID uuid) {
if (uuid == null) {
return null;
}
final String found = masterUUIDMap.get(uuid);
if (found == null) {
for (final ConceptSpecification cs: constants) {
if (uuid.equals(cs.getPrimordialUuid())) {
return cs.getConceptDescriptionText();
}
}
}
return found;
}
}