/*
* 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.utility;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.inject.Singleton;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jvnet.hk2.annotations.Service;
import sh.isaac.MetaData;
import sh.isaac.api.Get;
import sh.isaac.api.LookupService;
import sh.isaac.api.State;
import sh.isaac.api.bootstrap.TermAux;
import sh.isaac.api.chronicle.LatestVersion;
import sh.isaac.api.chronicle.ObjectChronology;
import sh.isaac.api.chronicle.ObjectChronologyType;
import sh.isaac.api.collections.ConceptSequenceSet;
import sh.isaac.api.collections.LruCache;
import sh.isaac.api.commit.ChangeCheckerMode;
import sh.isaac.api.commit.Stamp;
import sh.isaac.api.component.concept.ConceptBuilder;
import sh.isaac.api.component.concept.ConceptBuilderService;
import sh.isaac.api.component.concept.ConceptChronology;
import sh.isaac.api.component.concept.ConceptService;
import sh.isaac.api.component.concept.ConceptSnapshot;
import sh.isaac.api.component.concept.ConceptSpecification;
import sh.isaac.api.component.concept.ConceptVersion;
import sh.isaac.api.component.concept.description.DescriptionBuilder;
import sh.isaac.api.component.concept.description.DescriptionBuilderService;
import sh.isaac.api.component.sememe.SememeChronology;
import sh.isaac.api.component.sememe.SememeType;
import sh.isaac.api.component.sememe.version.ComponentNidSememe;
import sh.isaac.api.component.sememe.version.DescriptionSememe;
import sh.isaac.api.component.sememe.version.DynamicSememe;
import sh.isaac.api.component.sememe.version.LogicGraphSememe;
import sh.isaac.api.component.sememe.version.MutableDescriptionSememe;
import sh.isaac.api.component.sememe.version.SememeVersion;
import sh.isaac.api.component.sememe.version.StringSememe;
import sh.isaac.api.component.sememe.version.dynamicSememe.DynamicSememeColumnInfo;
import sh.isaac.api.component.sememe.version.dynamicSememe.DynamicSememeColumnUtility;
import sh.isaac.api.component.sememe.version.dynamicSememe.DynamicSememeData;
import sh.isaac.api.component.sememe.version.dynamicSememe.DynamicSememeDataType;
import sh.isaac.api.component.sememe.version.dynamicSememe.DynamicSememeUsageDescription;
import sh.isaac.api.component.sememe.version.dynamicSememe.DynamicSememeUtility;
import sh.isaac.api.constants.DynamicSememeConstants;
import sh.isaac.api.coordinate.EditCoordinate;
import sh.isaac.api.coordinate.LanguageCoordinate;
import sh.isaac.api.coordinate.LogicCoordinate;
import sh.isaac.api.coordinate.StampCoordinate;
import sh.isaac.api.coordinate.StampPosition;
import sh.isaac.api.coordinate.StampPrecedence;
import sh.isaac.api.coordinate.TaxonomyCoordinate;
import sh.isaac.api.externalizable.OchreExternalizableObjectType;
import sh.isaac.api.identity.StampedVersion;
import sh.isaac.api.index.IndexServiceBI;
import sh.isaac.api.index.SearchResult;
import sh.isaac.api.logic.LogicalExpression;
import sh.isaac.api.logic.LogicalExpressionBuilder;
import sh.isaac.api.logic.LogicalExpressionBuilderService;
import sh.isaac.api.logic.NodeSemantic;
import sh.isaac.api.util.NumericUtils;
import sh.isaac.api.util.TaskCompleteCallback;
import sh.isaac.api.util.UUIDUtil;
import sh.isaac.mapping.constants.IsaacMappingConstants;
import sh.isaac.model.concept.ConceptVersionImpl;
import sh.isaac.model.configuration.EditCoordinates;
import sh.isaac.model.configuration.LanguageCoordinates;
import sh.isaac.model.configuration.LogicCoordinates;
import sh.isaac.model.configuration.StampCoordinates;
import sh.isaac.model.coordinate.StampCoordinateImpl;
import sh.isaac.model.coordinate.StampPositionImpl;
import sh.isaac.model.relationship.RelationshipVersionAdaptorImpl;
import sh.isaac.model.sememe.DynamicSememeUsageDescriptionImpl;
import sh.isaac.model.sememe.dataTypes.DynamicSememeStringImpl;
import sh.isaac.model.sememe.dataTypes.DynamicSememeUUIDImpl;
import sh.isaac.model.sememe.version.ComponentNidSememeImpl;
import sh.isaac.model.sememe.version.DescriptionSememeImpl;
import sh.isaac.model.sememe.version.DynamicSememeImpl;
import sh.isaac.model.sememe.version.LogicGraphSememeImpl;
import sh.isaac.model.sememe.version.LongSememeImpl;
import sh.isaac.model.sememe.version.StringSememeImpl;
import static sh.isaac.api.logic.LogicalExpressionBuilder.And;
import static sh.isaac.api.logic.LogicalExpressionBuilder.ConceptAssertion;
import static sh.isaac.api.logic.LogicalExpressionBuilder.NecessarySet;
//~--- classes ----------------------------------------------------------------
/**
* The Class Frills.
*/
//This is a service, simply to implement the DynamicSememeColumnUtility interface. Everythign else is static, and may be used directly
@Service
@Singleton
public class Frills
implements DynamicSememeColumnUtility {
/** The log. */
private static Logger log = LogManager.getLogger(Frills.class);
/** The is association cache. */
private static LruCache<Integer, Boolean> isAssociationCache = new LruCache<>(50);
/** The is mapping cache. */
private static LruCache<Integer, Boolean> isMappingCache = new LruCache<>(50);
//~--- methods -------------------------------------------------------------
/**
* Build, without committing, a new concept using the provided columnName and columnDescription values which is suitable
* for use as a column descriptor within {@link DynamicSememeUsageDescription}.
*
* The new concept will be created under the concept {@link DynamicSememeConstants#DYNAMIC_SEMEME_COLUMNS}
*
* A complete usage pattern (where both the refex assemblage concept and the column name concept needs
* to be created) would look roughly like this:
*
* DynamicSememeUtility.createNewDynamicSememeUsageDescriptionConcept(
* "The name of the Sememe",
* "The description of the Sememe",
* new DynamicSememeColumnInfo[]{new DynamicSememeColumnInfo(
* 0,
* DynamicSememeColumnInfo.createNewDynamicSememeColumnInfoConcept(
* "column name",
* "column description"
* )
* DynamicSememeDataType.STRING,
* new DynamicSememeStringImpl("default value")
* )}
* )
*
* //TODO [REFEX] figure out language details (how we know what language to put on the name/description
*
* @param columnName the column name
* @param columnDescription the column description
* @return the concept chronology<? extends concept version<?>>
* @throws RuntimeException the runtime exception
*/
@SuppressWarnings("deprecation")
public static ConceptChronology<? extends ConceptVersion<?>> buildNewDynamicSememeColumnInfoConcept(
String columnName,
String columnDescription)
throws RuntimeException {
if ((columnName == null) ||
(columnName.length() == 0) ||
(columnDescription == null) ||
(columnDescription.length() == 0)) {
throw new RuntimeException("Both the column name and column description are required");
}
final ConceptBuilderService conceptBuilderService = LookupService.getService(ConceptBuilderService.class);
conceptBuilderService.setDefaultLanguageForDescriptions(MetaData.ENGLISH_LANGUAGE);
conceptBuilderService.setDefaultDialectAssemblageForDescriptions(MetaData.US_ENGLISH_DIALECT);
conceptBuilderService.setDefaultLogicCoordinate(LogicCoordinates.getStandardElProfile());
final DescriptionBuilderService descriptionBuilderService =
LookupService.getService(DescriptionBuilderService.class);
final LogicalExpressionBuilder defBuilder = LookupService.getService(LogicalExpressionBuilderService.class)
.getLogicalExpressionBuilder();
NecessarySet(And(ConceptAssertion(Get.conceptService()
.getConcept(DynamicSememeConstants.get().DYNAMIC_SEMEME_COLUMNS
.getNid()),
defBuilder)));
final LogicalExpression parentDef = defBuilder.build();
final ConceptBuilder builder = conceptBuilderService.getDefaultConceptBuilder(columnName, null, parentDef);
DescriptionBuilder<?, ?> definitionBuilder = descriptionBuilderService.getDescriptionBuilder(columnName,
builder,
MetaData.SYNONYM,
MetaData.ENGLISH_LANGUAGE);
definitionBuilder.addPreferredInDialectAssemblage(MetaData.US_ENGLISH_DIALECT);
builder.addDescription(definitionBuilder);
definitionBuilder = descriptionBuilderService.getDescriptionBuilder(columnDescription,
builder,
MetaData.DEFINITION_DESCRIPTION_TYPE,
MetaData.ENGLISH_LANGUAGE);
definitionBuilder.addPreferredInDialectAssemblage(MetaData.US_ENGLISH_DIALECT);
builder.addDescription(definitionBuilder);
ConceptChronology<? extends ConceptVersion<?>> newCon;
try {
newCon = builder.build(EditCoordinates.getDefaultUserMetadata(), ChangeCheckerMode.ACTIVE, new ArrayList<>())
.get();
} catch (InterruptedException | ExecutionException e) {
final String msg = "Failed building new DynamicSememeColumnInfo concept columnName=\"" + columnName +
"\", columnDescription=\"" + columnDescription + "\"";
log.error(msg, e);
throw new RuntimeException(msg, e);
}
return newCon;
}
/**
* This method returns an uncommitted refexUsageDescriptor concept chronology.
* A DynamicSememeUsageDescription may be constructed by passing it to the DynamicSememeUsageDescriptionImpl ctor.
*
* @param sememeFSN the sememe FSN
* @param sememePreferredTerm the sememe preferred term
* @param sememeDescription the sememe description
* @param columns the columns
* @param parentConceptNidOrSequence the parent concept nid or sequence
* @param referencedComponentRestriction the referenced component restriction
* @param referencedComponentSubRestriction the referenced component sub restriction
* @param editCoord the edit coord
* @return the concept chronology<? extends concept version<?>>
*/
@SuppressWarnings("deprecation")
public static ConceptChronology<? extends ConceptVersion<?>> buildUncommittedNewDynamicSememeUsageDescription(
String sememeFSN,
String sememePreferredTerm,
String sememeDescription,
DynamicSememeColumnInfo[] columns,
Integer parentConceptNidOrSequence,
ObjectChronologyType referencedComponentRestriction,
SememeType referencedComponentSubRestriction,
EditCoordinate editCoord) {
try {
final EditCoordinate localEditCoord = ((editCoord == null) ? Get.configurationService()
.getDefaultEditCoordinate()
: editCoord);
final ConceptBuilderService conceptBuilderService = LookupService.getService(ConceptBuilderService.class);
conceptBuilderService.setDefaultLanguageForDescriptions(MetaData.ENGLISH_LANGUAGE);
conceptBuilderService.setDefaultDialectAssemblageForDescriptions(MetaData.US_ENGLISH_DIALECT);
conceptBuilderService.setDefaultLogicCoordinate(LogicCoordinates.getStandardElProfile());
final DescriptionBuilderService descriptionBuilderService =
LookupService.getService(DescriptionBuilderService.class);
final LogicalExpressionBuilder defBuilder = LookupService.getService(LogicalExpressionBuilderService.class)
.getLogicalExpressionBuilder();
final ConceptChronology<?> parentConcept = Get.conceptService()
.getConcept((parentConceptNidOrSequence == null)
? DynamicSememeConstants.get().DYNAMIC_SEMEME_ASSEMBLAGES
.getNid()
: parentConceptNidOrSequence);
NecessarySet(And(ConceptAssertion(parentConcept, defBuilder)));
final LogicalExpression parentDef = defBuilder.build();
final ConceptBuilder builder = conceptBuilderService.getDefaultConceptBuilder(sememeFSN, null, parentDef);
DescriptionBuilder<? extends SememeChronology<?>, ? extends MutableDescriptionSememe<?>> definitionBuilder =
descriptionBuilderService.getDescriptionBuilder(sememePreferredTerm,
builder,
MetaData.SYNONYM,
MetaData.ENGLISH_LANGUAGE);
definitionBuilder.addPreferredInDialectAssemblage(MetaData.US_ENGLISH_DIALECT);
builder.addDescription(definitionBuilder);
final ConceptChronology<? extends ConceptVersion<?>> newCon = builder.build(localEditCoord,
ChangeCheckerMode.ACTIVE,
new ArrayList<>())
.getNoThrow();
{
// Set up the dynamic sememe 'special' definition
definitionBuilder = descriptionBuilderService.getDescriptionBuilder(sememeDescription,
builder,
MetaData.DEFINITION_DESCRIPTION_TYPE,
MetaData.ENGLISH_LANGUAGE);
definitionBuilder.addPreferredInDialectAssemblage(MetaData.US_ENGLISH_DIALECT);
final SememeChronology<?> definitionSememe = definitionBuilder.build(localEditCoord,
ChangeCheckerMode.ACTIVE)
.getNoThrow();
Get.sememeBuilderService()
.getDynamicSememeBuilder(definitionSememe.getNid(),
DynamicSememeConstants.get().DYNAMIC_SEMEME_DEFINITION_DESCRIPTION
.getSequence(),
null)
.build(localEditCoord, ChangeCheckerMode.ACTIVE)
.getNoThrow();
}
if (columns != null) {
// Ensure that we process in column order - we don't always keep track of that later - we depend on the data being stored in the right order.
final TreeSet<DynamicSememeColumnInfo> sortedColumns = new TreeSet<>(Arrays.asList(columns));
for (final DynamicSememeColumnInfo ci: sortedColumns) {
final DynamicSememeData[] data = LookupService.getService(DynamicSememeUtility.class)
.configureDynamicSememeDefinitionDataForColumn(ci);
Get.sememeBuilderService()
.getDynamicSememeBuilder(newCon.getNid(),
DynamicSememeConstants.get().DYNAMIC_SEMEME_EXTENSION_DEFINITION
.getSequence(),
data)
.build(localEditCoord, ChangeCheckerMode.ACTIVE)
.getNoThrow();
}
}
final DynamicSememeData[] data = LookupService.getService(DynamicSememeUtility.class)
.configureDynamicSememeRestrictionData(
referencedComponentRestriction,
referencedComponentSubRestriction);
if (data != null) {
Get.sememeBuilderService()
.getDynamicSememeBuilder(newCon.getNid(),
DynamicSememeConstants.get().DYNAMIC_SEMEME_REFERENCED_COMPONENT_RESTRICTION
.getSequence(),
data)
.build(localEditCoord, ChangeCheckerMode.ACTIVE)
.getNoThrow();
}
return newCon;
} catch (final IllegalStateException e) {
throw new RuntimeException("Creation of Dynamic Sememe Failed!", e);
}
}
/**
* Create a new concept using the provided columnName and columnDescription values which is suitable
* for use as a column descriptor within {@link DynamicSememeUsageDescription}.
*
* The new concept will be created under the concept {@link DynamicSememeConstants#DYNAMIC_SEMEME_COLUMNS}
*
* A complete usage pattern (where both the refex assemblage concept and the column name concept needs
* to be created) would look roughly like this:
*
* DynamicSememeUtility.createNewDynamicSememeUsageDescriptionConcept(
* "The name of the Sememe",
* "The description of the Sememe",
* new DynamicSememeColumnInfo[]{new DynamicSememeColumnInfo(
* 0,
* DynamicSememeColumnInfo.createNewDynamicSememeColumnInfoConcept(
* "column name",
* "column description"
* )
* DynamicSememeDataType.STRING,
* new DynamicSememeStringImpl("default value")
* )}
* )
*
* //TODO [REFEX] figure out language details (how we know what language to put on the name/description
*
* @param columnName the column name
* @param columnDescription the column description
* @return the concept chronology<? extends concept version<?>>
* @throws RuntimeException the runtime exception
*/
@SuppressWarnings("deprecation")
public static ConceptChronology<? extends ConceptVersion<?>> createNewDynamicSememeColumnInfoConcept(
String columnName,
String columnDescription)
throws RuntimeException {
final ConceptChronology<? extends ConceptVersion<?>> newCon = buildNewDynamicSememeColumnInfoConcept(columnName,
columnDescription);
try {
Get.commitService()
.commit("creating new dynamic sememe column: " + columnName)
.get();
return newCon;
} catch (InterruptedException | ExecutionException e) {
final String msg = "Failed committing new DynamicSememeColumnInfo concept columnName=\"" + columnName +
"\", columnDescription=\"" + columnDescription + "\"";
log.error(msg, e);
throw new RuntimeException(msg, e);
}
}
/**
* See {@link DynamicSememeUsageDescription} for the full details on what this builds.
*
* Does all the work to create a new concept that is suitable for use as an Assemblage Concept for a new style Dynamic Sememe.
*
* The concept will be created under the concept {@link DynamicSememeConstants#DYNAMIC_SEMEME_ASSEMBLAGES} if a parent is not specified
*
* //TODO [REFEX] figure out language details (how we know what language to put on the name/description
*
* @param sememeFSN the sememe FSN
* @param sememePreferredTerm - The preferred term for this refex concept that will be created.
* @param sememeDescription - A user friendly string the explains the overall intended purpose of this sememe (what it means, what it stores)
* @param columns - The column information for this new refex. May be an empty list or null.
* @param parentConceptNidOrSequence - optional - if null, uses {@link DynamicSememeConstants#DYNAMIC_SEMEME_ASSEMBLAGES}
* @param referencedComponentRestriction - optional - may be null - if provided - this restricts the type of object referenced by the nid or
* UUID that is set for the referenced component in an instance of this sememe. If {@link ObjectChronologyType#UNKNOWN_NID} is passed, it is ignored, as
* if it were null.
* @param referencedComponentSubRestriction - optional - may be null - subtype restriction for {@link ObjectChronologyType#SEMEME} restrictions
* @param editCoord - optional - the coordinate to use during create of the sememe concept (and related descriptions) - if not provided, uses system default.
* @return a reference to the newly created sememe item
*/
@SuppressWarnings("deprecation")
public static DynamicSememeUsageDescription createNewDynamicSememeUsageDescriptionConcept(String sememeFSN,
String sememePreferredTerm,
String sememeDescription,
DynamicSememeColumnInfo[] columns,
Integer parentConceptNidOrSequence,
ObjectChronologyType referencedComponentRestriction,
SememeType referencedComponentSubRestriction,
EditCoordinate editCoord) {
final ConceptChronology<? extends ConceptVersion<?>> newDynamicSememeUsageDescriptionConcept =
buildUncommittedNewDynamicSememeUsageDescription(sememeFSN,
sememePreferredTerm,
sememeDescription,
columns,
parentConceptNidOrSequence,
referencedComponentRestriction,
referencedComponentSubRestriction,
editCoord);
try {
Get.commitService()
.commit("creating new dynamic sememe assemblage (DynamicSememeUsageDescription): NID=" +
newDynamicSememeUsageDescriptionConcept.getNid() + ", FSN=" + sememeFSN + ", PT=" +
sememePreferredTerm + ", DESC=" + sememeDescription)
.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException("Commit of Dynamic Sememe Failed!", e);
}
return new DynamicSememeUsageDescriptionImpl(newDynamicSememeUsageDescriptionConcept.getNid());
}
/**
* Defines association.
*
* @param conceptSequence the concept sequence
* @return true, if successful
*/
public static boolean definesAssociation(int conceptSequence) {
if (isAssociationCache.containsKey(conceptSequence)) {
return isAssociationCache.get(conceptSequence);
}
final boolean temp = Get.sememeService()
.getSememesForComponentFromAssemblage(Get.identifierService()
.getConceptNid(conceptSequence),
DynamicSememeConstants.get().DYNAMIC_SEMEME_ASSOCIATION_SEMEME
.getConceptSequence())
.anyMatch(sememe -> true);
isAssociationCache.put(conceptSequence, temp);
return temp;
}
/**
* Defines dynamic sememe.
*
* @param conceptSequence the concept sequence
* @return true, if successful
*/
public static boolean definesDynamicSememe(int conceptSequence) {
return DynamicSememeUsageDescriptionImpl.isDynamicSememe(conceptSequence);
}
/**
* Defines mapping.
*
* @param conceptSequence the concept sequence
* @return true, if successful
*/
public static boolean definesMapping(int conceptSequence) {
if (isMappingCache.containsKey(conceptSequence)) {
return isMappingCache.get(conceptSequence);
}
final boolean temp = Get.sememeService()
.getSememesForComponentFromAssemblage(Get.identifierService()
.getConceptNid(conceptSequence),
IsaacMappingConstants.get().DYNAMIC_SEMEME_MAPPING_SEMEME_TYPE
.getConceptSequence())
.anyMatch(sememe -> true);
isMappingCache.put(conceptSequence, temp);
return temp;
}
/**
* Convenience method to find the nearest concept related to a sememe. Recursively walks referenced components until it finds a concept.
*
* @param nid the nid
* @return the nearest concept sequence, or -1, if no concept can be found.
*/
public static int findConcept(int nid) {
final Optional<? extends ObjectChronology<? extends StampedVersion>> c = Get.identifiedObjectService()
.getIdentifiedObjectChronology(nid);
if (c.isPresent()) {
if (c.get()
.getOchreObjectType() == OchreExternalizableObjectType.SEMEME) {
return findConcept(((SememeChronology<?>) c.get()).getReferencedComponentNid());
} else if (c.get()
.getOchreObjectType() == OchreExternalizableObjectType.CONCEPT) {
return ((ConceptChronology<?>) c.get()).getConceptSequence();
} else {
log.warn("Unexpected object type: " + c.get().getOchreObjectType());
}
}
return -1;
}
/**
* Calls {@link #getConceptForUnknownIdentifier(String)} in a background thread. returns immediately.
*
*
* @param identifier - what to search for
* @param callback - who to inform when lookup completes
* @param callId - An arbitrary identifier that will be returned to the caller when this completes
* @param stampCoord - optional - what stamp to use when returning the ConceptSnapshot (defaults to user prefs)
* @param langCoord - optional - what lang coord to use when returning the ConceptSnapshot (defaults to user prefs)
*/
public static void lookupConceptForUnknownIdentifier(final String identifier,
final TaskCompleteCallback<ConceptSnapshot> callback,
final Integer callId,
final StampCoordinate stampCoord,
final LanguageCoordinate langCoord) {
log.debug("Threaded Lookup: '{}'", identifier);
final long submitTime = System.currentTimeMillis();
final Runnable r = () -> {
ConceptSnapshot result = null;
final Optional<? extends ConceptChronology<? extends ConceptVersion<?>>> c =
getConceptForUnknownIdentifier(identifier);
if (c.isPresent()) {
final Optional<ConceptSnapshot> temp = getConceptSnapshot(c.get()
.getConceptSequence(),
stampCoord,
langCoord);
if (temp.isPresent()) {
result = temp.get();
}
}
callback.taskComplete(result, submitTime, callId);
};
Get.workExecutors()
.getExecutor()
.execute(r);
}
/**
* All done in a background thread, method returns immediately.
*
* @param nid the nid
* @param callback - who to inform when lookup completes
* @param callId - An arbitrary identifier that will be returned to the caller when this completes
* @param stampCoord - optional - what stamp to use when returning the ConceptSnapshot (defaults to user prefs)
* @param langCoord - optional - what lang coord to use when returning the ConceptSnapshot (defaults to user prefs)
*/
public static void lookupConceptSnapshot(final int nid,
final TaskCompleteCallback<ConceptSnapshot> callback,
final Integer callId,
final StampCoordinate stampCoord,
final LanguageCoordinate langCoord) {
log.debug("Threaded Lookup: '{}'", nid);
final long submitTime = System.currentTimeMillis();
final Runnable r = () -> {
final Optional<ConceptSnapshot> c = getConceptSnapshot(nid, stampCoord, langCoord);
callback.taskComplete(c.isPresent() ? c.get()
: null, submitTime, callId);
};
Get.workExecutors()
.getExecutor()
.execute(r);
}
/**
* Make stamp coordinate analog varying by modules only.
*
* @param existingStampCoordinate the existing stamp coordinate
* @param requiredModuleSequence the required module sequence
* @param optionalModuleSequences the optional module sequences
* @return the stamp coordinate
*/
public static StampCoordinate makeStampCoordinateAnalogVaryingByModulesOnly(StampCoordinate existingStampCoordinate,
int requiredModuleSequence,
int... optionalModuleSequences) {
final ConceptSequenceSet moduleSequenceSet = new ConceptSequenceSet();
moduleSequenceSet.add(requiredModuleSequence);
if (optionalModuleSequences != null) {
for (final int seq: optionalModuleSequences) {
moduleSequenceSet.add(seq);
}
}
final EnumSet<State> allowedStates = EnumSet.allOf(State.class);
allowedStates.addAll(existingStampCoordinate.getAllowedStates());
final StampCoordinate newStampCoordinate = new StampCoordinateImpl(existingStampCoordinate.getStampPrecedence(),
existingStampCoordinate.getStampPosition(),
moduleSequenceSet,
allowedStates);
return newStampCoordinate;
}
/**
* Read dynamic sememe column name description.
*
* @param columnDescriptionConcept the column description concept
* @return the string[]
*/
@SuppressWarnings("unchecked")
@Override
public String[] readDynamicSememeColumnNameDescription(UUID columnDescriptionConcept) {
String columnName = null;
String columnDescription = null;
String fsn = null;
String acceptableSynonym = null;
String acceptableDefinition = null;
try {
final ConceptChronology<? extends ConceptVersion<?>> cc = Get.conceptService()
.getConcept(columnDescriptionConcept);
for (final SememeChronology<? extends DescriptionSememe<?>> dc: cc.getConceptDescriptionList()) {
if ((columnName != null) && (columnDescription != null)) {
break;
}
@SuppressWarnings("rawtypes")
final Optional<LatestVersion<DescriptionSememe<?>>> descriptionVersion =
((SememeChronology) dc).getLatestVersion(DescriptionSememe.class,
Get.configurationService()
.getDefaultStampCoordinate()
.makeAnalog(State.ACTIVE,
State.INACTIVE,
State.CANCELED,
State.PRIMORDIAL));
if (descriptionVersion.isPresent()) {
final DescriptionSememe<?> d = descriptionVersion.get()
.value();
if (d.getDescriptionTypeConceptSequence() ==
TermAux.FULLY_SPECIFIED_DESCRIPTION_TYPE.getConceptSequence()) {
fsn = d.getText();
} else if (d.getDescriptionTypeConceptSequence() ==
TermAux.SYNONYM_DESCRIPTION_TYPE.getConceptSequence()) {
if (Frills.isDescriptionPreferred(d.getNid(), null)) {
columnName = d.getText();
} else {
acceptableSynonym = d.getText();
}
} else if (d.getDescriptionTypeConceptSequence() ==
TermAux.DEFINITION_DESCRIPTION_TYPE.getConceptSequence()) {
if (Frills.isDescriptionPreferred(d.getNid(), null)) {
columnDescription = d.getText();
} else {
acceptableDefinition = d.getText();
}
}
}
}
} catch (final Exception e) {
log.warn("Failure reading DynamicSememeColumnInfo '" + columnDescriptionConcept + "'", e);
}
if (columnName == null) {
log.warn("No preferred synonym found on '" + columnDescriptionConcept + "' to use " +
"for the column name - using FSN");
columnName = ((fsn == null) ? "ERROR - see log"
: fsn);
}
if ((columnDescription == null) && (acceptableDefinition != null)) {
columnDescription = acceptableDefinition;
}
if ((columnDescription == null) && (acceptableSynonym != null)) {
columnDescription = acceptableSynonym;
}
if (columnDescription == null) {
log.info("No preferred or acceptable definition or acceptable synonym found on '" + columnDescriptionConcept +
"' to use for the column description- re-using the the columnName, instead.");
columnDescription = columnName;
}
return new String[] { columnName, columnDescription };
}
/**
* Refresh indexes.
*/
public static void refreshIndexes() {
LookupService.get()
.getAllServiceHandles(IndexServiceBI.class)
.forEach(index -> {
// Making a query, with long.maxValue, causes the index to refresh itself, and look at the latest updates, if there have been updates.
index.getService()
.query("hi", null, 1, Long.MAX_VALUE);
});
}
/**
* To string.
*
* @param version toString for StampedVersion
* @return the string
*/
public static String toString(StampedVersion version) {
return version.getClass()
.getSimpleName() + " STAMP=" + version.getStampSequence() + "{state=" + version.getState() +
", time=" + version.getTime() + ", author=" + version.getAuthorSequence() +
", module=" + version.getModuleSequence() + ", path=" +
version.getPathSequence() + "}";
}
//~--- get methods ---------------------------------------------------------
/**
* Returns a Map correlating each dialect sequence for a passed
* descriptionSememeId with its respective acceptability nid (preferred vs
* acceptable).
*
* @param descriptionSememeNid the description sememe nid
* @param stamp - optional - if not provided, uses default from config
* service
* @return the acceptabilities
* @throws RuntimeException If there is inconsistent data (incorrectly)
* attached to the sememe
*/
public static Map<Integer, Integer> getAcceptabilities(int descriptionSememeNid,
StampCoordinate stamp)
throws RuntimeException {
final Map<Integer, Integer> dialectSequenceToAcceptabilityNidMap = new ConcurrentHashMap<>();
Get.sememeService()
.getSememesForComponent(descriptionSememeNid)
.forEach(nestedSememe -> {
if (nestedSememe.getSememeType() == SememeType.COMPONENT_NID) {
final int dialectSequence = nestedSememe.getAssemblageSequence();
@SuppressWarnings({ "rawtypes", "unchecked" })
final Optional<LatestVersion<ComponentNidSememe>> latest =
((SememeChronology) nestedSememe).getLatestVersion(ComponentNidSememe.class,
(stamp == null)
? Get.configurationService()
.getDefaultStampCoordinate()
: stamp);
if (latest.isPresent()) {
if ((latest.get().value().getComponentNid() == MetaData.PREFERRED.getNid()) ||
(latest.get().value().getComponentNid() == MetaData.ACCEPTABLE.getNid())) {
if ((dialectSequenceToAcceptabilityNidMap.get(dialectSequence) != null) &&
(dialectSequenceToAcceptabilityNidMap.get(dialectSequence) !=
latest.get().value().getComponentNid())) {
throw new RuntimeException("contradictory annotations about acceptability!");
} else {
dialectSequenceToAcceptabilityNidMap.put(dialectSequence,
latest.get()
.value()
.getComponentNid());
}
} else {
UUID uuid = null;
String componentDesc = null;
try {
final Optional<UUID> uuidOptional = Get.identifierService()
.getUuidPrimordialForNid(latest.get()
.value()
.getComponentNid());
if (uuidOptional.isPresent()) {
uuid = uuidOptional.get();
}
final Optional<LatestVersion<DescriptionSememe<?>>> desc = Get.conceptService()
.getSnapshot(
StampCoordinates.getDevelopmentLatest(),
LanguageCoordinates.getUsEnglishLanguageFullySpecifiedNameCoordinate())
.getDescriptionOptional(
latest.get()
.value()
.getComponentNid());
componentDesc = desc.isPresent() ? desc.get()
.value()
.getText()
: null;
} catch (final Exception e) {
// NOOP
}
log.warn("Unexpected component " + componentDesc + " (uuid=" + uuid + ", nid=" +
latest.get().value().getComponentNid() + ")");
// throw new RuntimeException("Unexpected component " + componentDesc + " (uuid=" + uuid + ", nid=" + latest.get().value().getComponentNid() + ")");
// dialectSequenceToAcceptabilityNidMap.put(dialectSequence, latest.get().value().getComponentNid());
}
}
}
});
return dialectSequenceToAcceptabilityNidMap;
}
/**
* Get isA children of a concept. Does not return the requested concept in any circumstance.
* @param conceptSequence The concept to look at
* @param recursive recurse down from the concept
* @param leafOnly only return leaf nodes
* @return the set of concept sequence ids that represent the children
*/
public static Set<Integer> getAllChildrenOfConcept(int conceptSequence, boolean recursive, boolean leafOnly) {
final Set<Integer> temp = getAllChildrenOfConcept(new HashSet<>(), conceptSequence, recursive, leafOnly);
if (leafOnly && (temp.size() == 1)) {
temp.remove(conceptSequence);
}
return temp;
}
/**
* Recursively get Is a children of a concept. May inadvertenly return the requested starting sequence when leafOnly is true, and
* there are no children.
*
* @param handledConceptSequenceIds the handled concept sequence ids
* @param conceptSequence the concept sequence
* @param recursive the recursive
* @param leafOnly the leaf only
* @return the all children of concept
*/
private static Set<Integer> getAllChildrenOfConcept(Set<Integer> handledConceptSequenceIds,
int conceptSequence,
boolean recursive,
boolean leafOnly) {
final Set<Integer> results = new HashSet<>();
// This both prevents infinite recursion and avoids processing or returning of duplicates
if (handledConceptSequenceIds.contains(conceptSequence)) {
return results;
}
final AtomicInteger count = new AtomicInteger();
final IntStream children = Get.taxonomyService()
.getTaxonomyChildSequences(conceptSequence);
children.forEach((conSequence) -> {
count.getAndIncrement();
if (!leafOnly) {
results.add(conSequence);
}
if (recursive) {
results.addAll(getAllChildrenOfConcept(handledConceptSequenceIds,
conSequence,
recursive,
leafOnly));
}
});
if (leafOnly && (count.get() == 0)) {
results.add(conceptSequence);
}
handledConceptSequenceIds.add(conceptSequence);
return results;
}
/**
* Convenience method to return sequences of a distinct set of modules in
* which versions of an ObjectChronology have been defined.
*
* @param chronology The ObjectChronology
* @return sequences of a distinct set of modules in which versions of an
* ObjectChronology have been defined
*/
public static Set<Integer> getAllModuleSequences(ObjectChronology<? extends StampedVersion> chronology) {
final Set<Integer> moduleSequences = new HashSet<>();
for (final StampedVersion version: chronology.getVersionList()) {
moduleSequences.add(version.getModuleSequence());
}
return Collections.unmodifiableSet(moduleSequences);
}
/**
* A convenience method to determine if a particular component has 0 or 1 annotations of a particular type. If there is more than one
* annotation of a particular type, this method will throw a runtime exception.
*
* @param componentNid - the component to check for the assemblage
* @param assemblageConceptId - the assemblage type you are interested in (nid or concept sequence)
* @return the annotation sememe
*/
public static Optional<SememeChronology<? extends SememeVersion<?>>> getAnnotationSememe(int componentNid,
int assemblageConceptId) {
final Set<Integer> allowedAssemblages = new HashSet<>();
allowedAssemblages.add(assemblageConceptId);
final Set<SememeChronology<? extends SememeVersion<?>>> sememeSet = Get.sememeService()
.getSememesForComponentFromAssemblages(
componentNid,
allowedAssemblages)
.collect(Collectors.toSet());
switch (sememeSet.size()) {
case 0:
return Optional.empty();
case 1:
return Optional.of(sememeSet.iterator()
.next());
default:
throw new RuntimeException("Component " + componentNid + " has " + sememeSet.size() +
" annotations of type " + Get.conceptDescriptionText(assemblageConceptId) +
" (should only have zero or 1)");
}
}
/**
* Gets the annotation string value.
*
* @param componentId the component id
* @param assemblageConceptId the assemblage concept id
* @param stamp the stamp
* @return the annotation string value
*/
public static Optional<String> getAnnotationStringValue(int componentId,
int assemblageConceptId,
StampCoordinate stamp) {
try {
final Optional<UUID> assemblageConceptUuid = Get.identifierService()
.getUuidPrimordialFromConceptId(assemblageConceptId);
if (!assemblageConceptUuid.isPresent()) {
throw new RuntimeException("getUuidPrimordialFromConceptId() return empty UUID for assemblageConceptId " +
assemblageConceptId);
}
final int componentNid = Get.identifierService()
.getConceptNid(componentId);
final ArrayList<String> values = new ArrayList<>(1);
final int assemblageConceptSequence = Get.identifierService()
.getConceptSequenceForUuids(assemblageConceptUuid.get());
Get.sememeService()
.getSnapshot(SememeVersion.class,
(stamp == null) ? Get.configurationService()
.getDefaultStampCoordinate()
: stamp)
.getLatestSememeVersionsForComponentFromAssemblage(componentNid, assemblageConceptSequence)
.forEach(latestSememe -> {
if (latestSememe.value()
.getChronology()
.getSememeType() == SememeType.STRING) {
values.add(((StringSememeImpl) latestSememe.value()).getString());
} else if (latestSememe.value()
.getChronology()
.getSememeType() == SememeType.COMPONENT_NID) {
values.add(((ComponentNidSememeImpl) latestSememe.value()).getComponentNid() + "");
} else if (latestSememe.value()
.getChronology()
.getSememeType() == SememeType.LONG) {
values.add(((LongSememeImpl) latestSememe.value()).getLongValue() + "");
} else if (latestSememe.value()
.getChronology()
.getSememeType() == SememeType.DYNAMIC) {
final DynamicSememeData[] data = ((DynamicSememeImpl) latestSememe.value()).getData();
if (data.length > 0) {
log.warn("Found multiple (" + data.length + ") dynamic sememe data fields in sememe " +
latestSememe.value().getNid() + " of assemblage type " + assemblageConceptUuid +
" on component " +
Get.identifierService().getUuidPrimordialForNid(componentNid));
}
values.add(data[0].dataToString());
}
});
if (values.size() > 1) {
log.warn("Found multiple (" + values.size() + ") " + assemblageConceptUuid +
" annotation sememes on component " +
Get.identifierService().getUuidPrimordialForNid(componentNid) + ". Using first value \"" +
values.get(0) + "\".");
}
if (values.size() > 0) {
return Optional.of(values.get(0));
}
} catch (final Exception e) {
log.error("Unexpected error trying to find " + assemblageConceptId + " annotation sememe on component " +
componentId,
e);
}
return Optional.empty();
}
/**
* Checks if association.
*
* @param sc the sc
* @return true, if association
*/
public static boolean isAssociation(SememeChronology<? extends SememeVersion<?>> sc) {
return definesAssociation(sc.getAssemblageSequence());
}
/**
* If the passed in value is a {@link UUID}, calls {@link ConceptService#getOptionalConcept(int)} after converting the UUID to nid.
* Next, if no hit, if the passed in value is parseable as a int < 0 (a nid), calls {@link ConceptService#getOptionalConcept(int)}
* Next, if no hit, if the passed in value is parseable as a long, and is a valid SCTID (checksum is valid) - treats it as
* a SCTID and attempts to look up the SCTID in the lucene index. Note that is is possible for some
* sequence identifiers to look like SCTIDs - if a passed in value is valid as both a SCTID and a sequence identifier - it will be
* treated as an SCTID.
* Finally, if it is a positive integer, it treats is as a sequence identity, converts it to a nid, then looks up the nid.
*
* @param identifier the identifier
* @return the concept for unknown identifier
*/
public static Optional<? extends ConceptChronology<? extends ConceptVersion<?>>> getConceptForUnknownIdentifier(
String identifier) {
log.debug("Concept Chronology lookup by string '{}'", identifier);
if (StringUtils.isBlank(identifier)) {
return Optional.empty();
}
final String localIdentifier = identifier.trim();
final Optional<UUID> uuid = UUIDUtil.getUUID(localIdentifier);
if (uuid.isPresent()) {
return Get.conceptService()
.getOptionalConcept(uuid.get());
}
// if it is a negative integer, assume nid
final Optional<Integer> nid = NumericUtils.getNID(localIdentifier);
if (nid.isPresent()) {
return Get.conceptService()
.getOptionalConcept(nid.get());
}
if (SctId.isValidSctId(localIdentifier)) {
final IndexServiceBI si = LookupService.get()
.getService(IndexServiceBI.class, "sememe indexer");
if (si != null) {
// force the prefix algorithm, and add a trailing space - quickest way to do an exact-match type of search
final List<SearchResult> result = si.query(localIdentifier + " ",
true,
new Integer[] { MetaData.SCTID.getConceptSequence() },
5,
Long.MIN_VALUE);
if (result.size() > 0) {
final int componentNid = Get.sememeService()
.getSememe(result.get(0)
.getNid())
.getReferencedComponentNid();
if (Get.identifierService()
.getChronologyTypeForNid(componentNid) == ObjectChronologyType.CONCEPT) {
return Get.conceptService()
.getOptionalConcept(componentNid);
} else {
log.warn("Passed in SCTID is not a Concept ID!");
return Optional.empty();
}
}
} else {
log.warn("Sememe Index not available - can't lookup SCTID");
}
} else if (NumericUtils.isInt(localIdentifier)) {
// Must be a postive integer, which wasn't a valid SCTID - it may be a sequence ID.
final int nidFromSequence = Get.identifierService()
.getConceptNid(Integer.parseInt(localIdentifier));
if (nidFromSequence != 0) {
return Get.conceptService()
.getOptionalConcept(nidFromSequence);
}
}
return Optional.empty();
}
/**
* Checks if concept fully defined.
*
* @param <T> the generic type
* @param lgs The LogicGraphSememe containing the logic graph data
* @return true if the corresponding concept is fully defined, otherwise returns false (for primitive concepts)
*
* Things that are defined with at least one SUFFICIENT_SET node are defined.
* Things that are defined without any SUFFICIENT_SET nodes are primitive.
*/
public static <T extends LogicGraphSememe<T>> boolean isConceptFullyDefined(LogicGraphSememe<T> lgs) {
return lgs.getLogicalExpression()
.contains(NodeSemantic.SUFFICIENT_SET);
}
/**
* Return true for fully defined, false for primitive, or empty for unknown, on the standard logic coordinates / standard development path.
*
* @param conceptNid the concept nid
* @param stated the stated
* @return the optional
*/
public static Optional<Boolean> isConceptFullyDefined(int conceptNid, boolean stated) {
final Optional<SememeChronology<? extends SememeVersion<?>>> sememe = Get.sememeService()
.getSememesForComponentFromAssemblage(
conceptNid,
(stated
? LogicCoordinates.getStandardElProfile()
.getStatedAssemblageSequence()
: LogicCoordinates.getStandardElProfile()
.getInferredAssemblageSequence()))
.findAny();
if (sememe.isPresent()) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Optional<LatestVersion<LogicGraphSememe>> sv =
((SememeChronology) sememe.get()).getLatestVersion(LogicGraphSememe.class,
StampCoordinates.getDevelopmentLatest());
if (sv.isPresent()) {
return Optional.of(isConceptFullyDefined((LogicGraphSememe<?>) sv.get()
.value()));
}
}
return Optional.empty();
}
/**
* Gets the concept snapshot.
*
* @param conceptNidOrSequence the concept nid or sequence
* @param stampCoord - optional - what stamp to use when returning the ConceptSnapshot (defaults to user prefs)
* @param langCoord - optional - what lang coord to use when returning the ConceptSnapshot (defaults to user prefs)
* @return the ConceptSnapshot, or an optional that indicates empty, if the identifier was invalid, or if the concept didn't
* have a version available on the specified stampCoord
*/
public static Optional<ConceptSnapshot> getConceptSnapshot(int conceptNidOrSequence,
StampCoordinate stampCoord,
LanguageCoordinate langCoord) {
final Optional<? extends ConceptChronology<? extends ConceptVersion<?>>> c = Get.conceptService()
.getOptionalConcept(
conceptNidOrSequence);
if (c.isPresent()) {
try {
return Optional.of(Get.conceptService()
.getSnapshot((stampCoord == null) ? Get.configurationService()
.getDefaultStampCoordinate()
: stampCoord,
(langCoord == null) ? Get.configurationService()
.getDefaultLanguageCoordinate()
: langCoord)
.getConceptSnapshot(c.get()
.getConceptSequence()));
} catch (final Exception e) {
// TODO conceptSnapshot APIs are currently broken, provide no means of detecting if a concept doesn't exist on a given coordinate
// See slack convo https://informatics-arch.slack.com/archives/dev-isaac/p1440568057000512
return Optional.empty();
}
}
return Optional.empty();
}
/**
* Gets the concept snapshot.
*
* @param conceptUUID the concept UUID
* @param stampCoord - optional - what stamp to use when returning the ConceptSnapshot (defaults to user prefs)
* @param langCoord - optional - what lang coord to use when returning the ConceptSnapshot (defaults to user prefs)
* @return the ConceptSnapshot, or an optional that indicates empty, if the identifier was invalid, or if the concept didn't
* have a version available on the specified stampCoord
*/
public static Optional<ConceptSnapshot> getConceptSnapshot(UUID conceptUUID,
StampCoordinate stampCoord,
LanguageCoordinate langCoord) {
return getConceptSnapshot(Get.identifierService()
.getNidForUuids(conceptUUID), stampCoord, langCoord);
}
/**
* Utility method to get the best text value description for a concept, according to the user preferences.
* Calls {@link #getDescription(int, LanguageCoordinate, StampCoordinate)} using the default system stamp coordinate,
* modified to return all states (this may return an inactive description)
*
* @param conceptId - either a sequence or a nid
* @return the description
*/
public static Optional<String> getDescription(int conceptId) {
return getDescription(conceptId,
Get.configurationService()
.getDefaultStampCoordinate()
.makeAnalog(State.ACTIVE, State.INACTIVE, State.CANCELED, State.PRIMORDIAL),
null);
}
/**
* Utility method to get the best text value description for a concept, according to the user preferences.
* Calls {@link #getDescription(UUID, LanguageCoordinate, StampCoordinate)} with nulls.
*
* @param conceptUUID - identifier for a concept
* @return the description
*/
public static Optional<String> getDescription(UUID conceptUUID) {
return getDescription(conceptUUID, null, null);
}
/**
* Utility method to get the best text value description for a concept, according to the passed in options,
* or the user preferences. Calls {@link #getDescription(int, LanguageCoordinate, StampCoordinate)} with values
* extracted from the taxonomyCoordinate, or null.
*
* @param conceptId - either a sequence or a nid
* @param taxonomyCoordinate the taxonomy coordinate
* @return the description
*/
public static Optional<String> getDescription(int conceptId, TaxonomyCoordinate taxonomyCoordinate) {
return getDescription(conceptId,
(taxonomyCoordinate == null) ? null
: taxonomyCoordinate.getStampCoordinate(),
(taxonomyCoordinate == null) ? null
: taxonomyCoordinate.getLanguageCoordinate());
}
/**
* Utility method to get the best text value description for a concept, according to the passed in options,
* or the user preferences. Calls {@link #getDescription(UUID, LanguageCoordinate, StampCoordinate)} with values
* extracted from the taxonomyCoordinate, or null.
*
* @param conceptUUID - identifier for a concept
* @param taxonomyCoordinate the taxonomy coordinate
* @return the description
*/
public static Optional<String> getDescription(UUID conceptUUID, TaxonomyCoordinate taxonomyCoordinate) {
return getDescription(conceptUUID,
(taxonomyCoordinate == null) ? null
: taxonomyCoordinate.getStampCoordinate(),
(taxonomyCoordinate == null) ? null
: taxonomyCoordinate.getLanguageCoordinate());
}
/**
* Utility method to get the best text value description for a concept, according to the passed in options,
* or the user preferences.
*
* @param conceptId - either a sequence or a nid
* @param stampCoordinate - optional - if not provided, defaults to system preference values
* @param languageCoordinate - optional - if not provided, defaults to system preferences values
* @return the description
*/
public static Optional<String> getDescription(int conceptId,
StampCoordinate stampCoordinate,
LanguageCoordinate languageCoordinate) {
final Optional<LatestVersion<DescriptionSememe<?>>> desc = Get.conceptService()
.getSnapshot((stampCoordinate == null)
? Get.configurationService()
.getDefaultStampCoordinate()
: stampCoordinate,
(languageCoordinate == null)
? Get.configurationService()
.getDefaultLanguageCoordinate()
: languageCoordinate)
.getDescriptionOptional(conceptId);
return desc.isPresent() ? Optional.of(desc.get()
.value()
.getText())
: Optional.empty();
}
/**
* Utility method to get the best text value description for a concept, according to the passed in options,
* or the user preferences. Calls {@link #getDescription(int, LanguageCoordinate, StampCoordinate)} with values
* extracted from the taxonomyCoordinate, or null.
*
* @param conceptUUID the concept UUID
* @param stampCoordinate - optional - if not provided, defaults to system preference values
* @param languageCoordinate - optional - if not provided, defaults to system preferences values
* @return the description
*/
public static Optional<String> getDescription(UUID conceptUUID,
StampCoordinate stampCoordinate,
LanguageCoordinate languageCoordinate) {
return getDescription(Get.identifierService()
.getConceptSequenceForUuids(conceptUUID),
stampCoordinate,
languageCoordinate);
}
/**
* If this description is flagged as an extended description type, return the type concept of the extension.
*
* @param stampCoordinate the stamp coordinate
* @param descriptionId - the nid or sequence of the description sememe to check for an extended type.
* @return the description extended type concept
*/
public static Optional<UUID> getDescriptionExtendedTypeConcept(StampCoordinate stampCoordinate, int descriptionId) {
final Optional<SememeChronology<? extends SememeVersion<?>>> descriptionExtendedTypeAnnotationSememe =
getAnnotationSememe(Get.identifierService()
.getSememeNid(descriptionId),
DynamicSememeConstants.get().DYNAMIC_SEMEME_EXTENDED_DESCRIPTION_TYPE
.getConceptSequence());
if (descriptionExtendedTypeAnnotationSememe.isPresent()) {
@SuppressWarnings({ "rawtypes", "unchecked" })
final Optional<LatestVersion<DynamicSememeImpl>> optionalLatestSememeVersion =
((SememeChronology) (descriptionExtendedTypeAnnotationSememe.get())).getLatestVersion(
DynamicSememeImpl.class,
(stampCoordinate == null) ? Get.configurationService()
.getDefaultStampCoordinate()
: stampCoordinate);
if (optionalLatestSememeVersion.get().contradictions().isPresent() &&
(optionalLatestSememeVersion.get().contradictions().get().size() > 0)) {
// TODO handle contradictions
log.warn("Component " + descriptionId + " " +
" has DYNAMIC_SEMEME_EXTENDED_DESCRIPTION_TYPE annotation with " +
optionalLatestSememeVersion.get().contradictions().get().size() + " contradictions");
}
final DynamicSememeData[] dataColumns = optionalLatestSememeVersion.get()
.value()
.getData();
if (dataColumns.length != 1) {
throw new RuntimeException(
"Invalidly specified DYNAMIC_SEMEME_EXTENDED_DESCRIPTION_TYPE. Should always have a column size of 1");
}
if (dataColumns[0].getDynamicSememeDataType() == DynamicSememeDataType.UUID) {
return Optional.of(((DynamicSememeUUIDImpl) dataColumns[0]).getDataUUID());
}
// This isn't supposed to happen, but we have some bad data where it did.
else if (dataColumns[0].getDynamicSememeDataType() == DynamicSememeDataType.STRING) {
log.warn("Extended description type data found with type string instead of type UUID!");
return Optional.of(UUID.fromString(((DynamicSememeStringImpl) dataColumns[0]).getDataString()));
}
throw new RuntimeException(
"Failed to find UUID DynamicSememeData type in DYNAMIC_SEMEME_EXTENDED_DESCRIPTION_TYPE annotation dynamic sememe");
}
return Optional.empty();
}
/**
* Determine if a particular description sememe is flagged as preferred IN
* ANY LANGUAGE. Returns false if there is no acceptability sememe.
*
* @param descriptionSememeNid the description sememe nid
* @param stamp - optional - if not provided, uses default from config
* service
* @return true, if description preferred
* @throws RuntimeException If there is unexpected data (incorrectly)
* attached to the sememe
*/
public static boolean isDescriptionPreferred(int descriptionSememeNid,
StampCoordinate stamp)
throws RuntimeException {
final AtomicReference<Boolean> answer = new AtomicReference<>();
// Ignore the language annotation... treat preferred in any language as good enough for our purpose here...
Get.sememeService()
.getSememesForComponent(descriptionSememeNid)
.forEach(nestedSememe -> {
if (nestedSememe.getSememeType() == SememeType.COMPONENT_NID) {
@SuppressWarnings({ "rawtypes", "unchecked" })
final Optional<LatestVersion<ComponentNidSememe>> latest =
((SememeChronology) nestedSememe).getLatestVersion(ComponentNidSememe.class,
(stamp == null)
? Get.configurationService()
.getDefaultStampCoordinate()
: stamp);
if (latest.isPresent()) {
if (latest.get()
.value()
.getComponentNid() == MetaData.PREFERRED.getNid()) {
if ((answer.get() != null) && (answer.get() != true)) {
throw new RuntimeException("contradictory annotations about preferred status!");
}
answer.set(true);
} else if (latest.get()
.value()
.getComponentNid() == MetaData.ACCEPTABLE.getNid()) {
if ((answer.get() != null) && (answer.get() != false)) {
throw new RuntimeException("contradictory annotations about preferred status!");
}
answer.set(false);
} else {
throw new RuntimeException("Unexpected component nid!");
}
}
}
});
if (answer.get() == null) {
log.warn("Description nid {} does not have an acceptability sememe!", descriptionSememeNid);
return false;
}
return answer.get();
}
/**
* Convenience method to extract the latest version of descriptions of the
* requested type.
*
* @param conceptNid The concept to read descriptions for
* @param descriptionType expected to be one of
* {@link MetaData#SYNONYM} or
* {@link MetaData#FULLY_SPECIFIED_NAME} or
* {@link MetaData#DEFINITION_DESCRIPTION_TYPE}
* @param stamp - optional - if not provided gets the default from the
* config service
* @return the descriptions - may be empty, will not be null
*/
public static List<DescriptionSememe<?>> getDescriptionsOfType(int conceptNid,
ConceptSpecification descriptionType,
StampCoordinate stamp) {
final ArrayList<DescriptionSememe<?>> results = new ArrayList<>();
Get.sememeService()
.getSememesForComponent(conceptNid)
.forEach(descriptionC -> {
if (descriptionC.getSememeType() == SememeType.DESCRIPTION) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Optional<LatestVersion<DescriptionSememe<?>>> latest =
((SememeChronology) descriptionC).getLatestVersion(DescriptionSememe.class,
(stamp == null)
? Get.configurationService()
.getDefaultStampCoordinate()
: stamp);
if (latest.isPresent()) {
final DescriptionSememe<?> ds = latest.get()
.value();
if (ds.getDescriptionTypeConceptSequence() == descriptionType.getConceptSequence()) {
results.add(ds);
}
}
}
});
return results;
}
/**
* Gets the extended description types.
*
* @return the extended description types
* @throws IOException Signals that an I/O exception has occurred.
*/
public static List<SimpleDisplayConcept> getExtendedDescriptionTypes()
throws IOException {
Set<Integer> extendedDescriptionTypes;
final ArrayList<SimpleDisplayConcept> temp = new ArrayList<>();
extendedDescriptionTypes =
Frills.getAllChildrenOfConcept(MetaData.DESCRIPTION_TYPE_IN_SOURCE_TERMINOLOGY.getConceptSequence(),
true,
true);
for (final Integer seq: extendedDescriptionTypes) {
temp.add(new SimpleDisplayConcept(seq));
}
Collections.sort(temp);
return temp;
}
/**
* Gets the id info.
*
* @param id int identifier
* @return a IdInfo, the toString() for which will display known identifiers and descriptions associated with the passed id
*
* This method should only be used for logging. The returned data structure is not meant to be parsed.
*/
public static IdInfo getIdInfo(int id) {
return getIdInfo(Integer.toString(id));
}
/**
* Gets the id info.
*
* @param id String identifier may parse to int NID, int sequence or UUID
* @return a IdInfo, the toString() for which will display known identifiers and descriptions associated with the passed id
*
* This method should only be used for logging. The returned data structure is not meant to be parsed.
*/
public static IdInfo getIdInfo(String id) {
return getIdInfo(id,
StampCoordinates.getDevelopmentLatest(),
LanguageCoordinates.getUsEnglishLanguageFullySpecifiedNameCoordinate());
}
/**
* Gets the id info.
*
* @param id UUID identifier
* @return a IdInfo, the toString() for which will display known identifiers and descriptions associated with the passed id
*
* This method should only be used for logging. The returned data structure is not meant to be parsed.
*/
public static IdInfo getIdInfo(UUID id) {
return getIdInfo(id.toString());
}
/**
* Gets the id info.
*
* @param id the id
* @param sc the sc
* @param lc the lc
* @return the id info
*/
public static IdInfo getIdInfo(int id, StampCoordinate sc, LanguageCoordinate lc) {
return getIdInfo(Integer.toString(id), sc, lc);
}
/**
* Gets the id info.
*
* @param id the id
* @param sc the sc
* @param lc the lc
* @return the id info
*/
public static IdInfo getIdInfo(String id, StampCoordinate sc, LanguageCoordinate lc) {
final Map<String, Object> idInfo = new HashMap<>();
Long sctId = null;
Integer seq = null;
Integer nid = null;
UUID[] uuids = null;
ObjectChronologyType typeOfPassedId = null;
try {
final Optional<Integer> intId = NumericUtils.getInt(id);
if (intId.isPresent()) {
// id interpreted as the id of the referenced component
if (intId.get() > 0) {
seq = intId.get();
nid = Get.identifierService()
.getConceptNid(seq);
} else if (intId.get() < 0) {
nid = intId.get();
seq = Get.identifierService()
.getConceptSequence(intId.get());
}
if (nid != null) {
typeOfPassedId = Get.identifierService()
.getChronologyTypeForNid(nid);
uuids = Get.identifierService()
.getUuidArrayForNid(nid);
}
} else {
final Optional<UUID> uuidId = UUIDUtil.getUUID(id);
if (uuidId.isPresent()) {
// id interpreted as the id of either a sememe or a concept
nid = Get.identifierService()
.getNidForUuids(uuidId.get());
typeOfPassedId = Get.identifierService()
.getChronologyTypeForNid(nid);
switch (typeOfPassedId) {
case CONCEPT: {
seq = Get.identifierService()
.getConceptSequenceForUuids(uuidId.get());
break;
}
case SEMEME: {
seq = Get.identifierService()
.getSememeSequenceForUuids(uuidId.get());
break;
}
case UNKNOWN_NID:
default:
}
}
}
if (nid != null) {
idInfo.put("DESC", Get.conceptService()
.getSnapshot(sc, lc)
.conceptDescriptionText(nid));
if (typeOfPassedId == ObjectChronologyType.CONCEPT) {
final Optional<Long> optSctId = Frills.getSctId(nid, sc);
if (optSctId.isPresent()) {
sctId = optSctId.get();
idInfo.put("SCTID", sctId);
}
}
}
} catch (final Exception e) {
log.warn("Problem getting idInfo for \"{}\". Caught {}", e.getClass()
.getName(), e.getLocalizedMessage());
}
idInfo.put("PASSED_ID", id);
idInfo.put("SEQ", seq);
idInfo.put("NID", nid);
idInfo.put("UUIDs", Arrays.toString(uuids));
idInfo.put("TYPE", typeOfPassedId);
return new IdInfo(idInfo);
}
/**
* Gets the id info.
*
* @param id the id
* @param sc the sc
* @param lc the lc
* @return the id info
*/
public static IdInfo getIdInfo(UUID id, StampCoordinate sc, LanguageCoordinate lc) {
return getIdInfo(id.toString(), sc, lc);
}
/**
* Gets the inferred definition chronology.
*
* @param conceptId either a concept nid or sequence.
* @param logicCoordinate LogicCoordinate.
* @return the inferred definition chronology for the specified concept
* according to the default logic coordinate.
*/
public static Optional<SememeChronology<? extends SememeVersion<?>>> getInferredDefinitionChronology(int conceptId,
LogicCoordinate logicCoordinate) {
conceptId = Get.identifierService()
.getConceptNid(conceptId);
return Get.sememeService()
.getSememesForComponentFromAssemblage(conceptId, logicCoordinate.getInferredAssemblageSequence())
.findAny();
}
/**
* Gets the logic graph chronology.
*
* @param id The int sequence or NID of the Concept for which the logic graph is requested
* @param stated boolean indicating stated vs inferred definition chronology should be used
* @return An Optional containing a LogicGraphSememe SememeChronology
*/
public static Optional<SememeChronology<? extends LogicGraphSememe<?>>> getLogicGraphChronology(int id,
boolean stated) {
log.debug("Getting {} logic graph chronology for {}", (stated ? "stated"
: "inferred"), Optional.ofNullable(Frills.getIdInfo(id)));
final Optional<SememeChronology<? extends SememeVersion<?>>> defChronologyOptional =
stated ? Get.statedDefinitionChronology(id)
: Get.inferredDefinitionChronology(id);
if (defChronologyOptional.isPresent()) {
log.debug("Got {} logic graph chronology for {}", (stated ? "stated"
: "inferred"), Optional.ofNullable(Frills.getIdInfo(id)));
@SuppressWarnings("unchecked")
final SememeChronology<? extends LogicGraphSememe<?>> sememeChronology =
(SememeChronology<? extends LogicGraphSememe<?>>) defChronologyOptional.get();
return Optional.of(sememeChronology);
} else {
log.warn("NO {} logic graph chronology for {}", (stated ? "stated"
: "inferred"), Optional.ofNullable(Frills.getIdInfo(id)));
return Optional.empty();
}
}
/**
* Gets the logic graph chronology.
*
* @param id The int sequence or NID of the Concept for which the logic graph is requested
* @param stated boolean indicating stated vs inferred definition chronology should be used
* @param stampCoordinate The StampCoordinate for which the logic graph is requested
* @param languageCoordinate The LanguageCoordinate for which the logic graph is requested
* @param logicCoordinate the LogicCoordinate for which the logic graph is requested
* @return An Optional containing a LogicGraphSememe SememeChronology
*/
public static Optional<SememeChronology<? extends LogicGraphSememe<?>>> getLogicGraphChronology(int id,
boolean stated,
StampCoordinate stampCoordinate,
LanguageCoordinate languageCoordinate,
LogicCoordinate logicCoordinate) {
log.debug("Getting {} logic graph chronology for {}", (stated ? "stated"
: "inferred"), Optional.ofNullable(Frills.getIdInfo(id, stampCoordinate, languageCoordinate)));
final Optional<SememeChronology<? extends SememeVersion<?>>> defChronologyOptional =
stated ? getStatedDefinitionChronology(id,
logicCoordinate)
: getInferredDefinitionChronology(id, logicCoordinate);
if (defChronologyOptional.isPresent()) {
log.debug("Got {} logic graph chronology for {}", (stated ? "stated"
: "inferred"), Optional.ofNullable(Frills.getIdInfo(id, stampCoordinate, languageCoordinate)));
@SuppressWarnings("unchecked")
final SememeChronology<? extends LogicGraphSememe<?>> sememeChronology =
(SememeChronology<? extends LogicGraphSememe<?>>) defChronologyOptional.get();
return Optional.of(sememeChronology);
} else {
log.warn("NO {} logic graph chronology for {}", (stated ? "stated"
: "inferred"), Optional.ofNullable(Frills.getIdInfo(id, stampCoordinate, languageCoordinate)));
return Optional.empty();
}
}
/**
* Gets the logic graph version.
*
* @param logicGraphSememeChronology The SememeChronology<? extends LogicGraphSememe<?>> chronology for which the logic graph version is requested
* @param stampCoordinate StampCoordinate to be used for selecting latest version
* @return An Optional containing a LogicGraphSememe SememeChronology
*/
public static Optional<LatestVersion<LogicGraphSememe<?>>> getLogicGraphVersion(
SememeChronology<? extends LogicGraphSememe<?>> logicGraphSememeChronology,
StampCoordinate stampCoordinate) {
log.debug("Getting logic graph sememe for {}",
Optional.ofNullable(Frills.getIdInfo(logicGraphSememeChronology.getReferencedComponentNid())));
@SuppressWarnings({ "unchecked", "rawtypes" })
final Optional<LatestVersion<LogicGraphSememe<?>>> latest =
((SememeChronology) logicGraphSememeChronology).getLatestVersion(LogicGraphSememe.class,
stampCoordinate);
if (latest.isPresent()) {
log.debug("Got logic graph sememe for {}",
Optional.ofNullable(Frills.getIdInfo(logicGraphSememeChronology.getReferencedComponentNid())));
} else {
log.warn("NO logic graph sememe for {}",
Optional.ofNullable(Frills.getIdInfo(logicGraphSememeChronology.getReferencedComponentNid())));
}
return latest;
}
/**
* Checks if mapping.
*
* @param sc the sc
* @return true, if mapping
*/
public static boolean isMapping(SememeChronology<? extends SememeVersion<?>> sc) {
return definesMapping(sc.getAssemblageSequence());
}
/**
* Determine if Chronology has nested sememes.
*
* @param chronology the chronology
* @return true if there is a nested sememe, false otherwise
*/
public static boolean hasNestedSememe(ObjectChronology<?> chronology) {
return !chronology.getSememeList()
.isEmpty();
}
/**
* Gets the nid for SCTID.
*
* @param sctID the sct ID
* @return the nid for SCTID
*/
public static Optional<Integer> getNidForSCTID(long sctID) {
final IndexServiceBI si = LookupService.get()
.getService(IndexServiceBI.class, "sememe indexer");
if (si != null) {
// force the prefix algorithm, and add a trailing space - quickest way to do an exact-match type of search
final List<SearchResult> result = si.query(sctID + " ",
true,
new Integer[] { MetaData.SCTID.getConceptSequence() },
5,
Long.MIN_VALUE);
if (result.size() > 0) {
return Optional.of(Get.sememeService()
.getSememe(result.get(0)
.getNid())
.getReferencedComponentNid());
}
} else {
log.warn("Sememe Index not available - can't lookup SCTID");
}
return Optional.empty();
}
/**
* Gets the nid for VUID.
*
* @param vuID the vu ID
* @return the nid for VUID
*/
public static Optional<Integer> getNidForVUID(long vuID) {
final IndexServiceBI si = LookupService.get()
.getService(IndexServiceBI.class, "sememe indexer");
if (si != null) {
// force the prefix algorithm, and add a trailing space - quickest way to do an exact-match type of search
final List<SearchResult> result = si.query(vuID + " ",
true,
new Integer[] { MetaData.VUID.getConceptSequence() },
5,
Long.MIN_VALUE);
if (result.size() > 0) {
return Optional.of(Get.sememeService()
.getSememe(result.get(0)
.getNid())
.getReferencedComponentNid());
}
} else {
log.warn("Sememe Index not available - can't lookup VUID");
}
return Optional.empty();
}
/**
* Find the SCTID for a component (if it has one).
*
* @param componentNid the component nid
* @param stamp - optional - if not provided uses default from config
* service
* @return the id, if found, or empty (will not return null)
*/
public static Optional<Long> getSctId(int componentNid, StampCoordinate stamp) {
try {
final Optional<LatestVersion<StringSememeImpl>> sememe = Get.sememeService()
.getSnapshot(StringSememeImpl.class,
(stamp == null) ? Get.configurationService()
.getDefaultStampCoordinate()
: stamp)
.getLatestSememeVersionsForComponentFromAssemblage(
componentNid,
MetaData.SCTID.getConceptSequence())
.findFirst();
if (sememe.isPresent()) {
return Optional.of(Long.parseLong(sememe.get()
.value()
.getString()));
}
} catch (final Exception e) {
log.error("Unexpected error trying to find SCTID for nid " + componentNid, e);
}
return Optional.empty();
}
/**
* Construct a stamp coordinate from an existing stamp coordinate, and the path from the edit coordinate, ensuring that the returned
* stamp coordinate includes the module edit coordinate.
*
* @param stampCoordinate - optional - used to fill in the stamp details not available from the edit coordinate. If not provided,
* uses the system defaults.
* @param editCoordinate - ensure that the returned stamp coordinate includes the module and path from this edit coordinate.
* @return a new stamp coordinate
*/
public static StampCoordinate getStampCoordinateFromEditCoordinate(StampCoordinate stampCoordinate,
EditCoordinate editCoordinate) {
if (stampCoordinate == null) {
stampCoordinate = Get.configurationService()
.getDefaultStampCoordinate();
}
final StampPosition stampPosition = new StampPositionImpl(stampCoordinate.getStampPosition().getTime(),
editCoordinate.getPathSequence());
final StampCoordinateImpl temp = new StampCoordinateImpl(stampCoordinate.getStampPrecedence(),
stampPosition,
stampCoordinate.getModuleSequences(),
stampCoordinate.getAllowedStates());
if (temp.getModuleSequences()
.size() > 0) {
temp.getModuleSequences()
.add(editCoordinate.getModuleSequence());
}
return temp;
}
/**
* Gets the stamp coordinate from stamp.
*
* @param stamp Stamp from which to generate StampCoordinate
* @return StampCoordinate corresponding to Stamp values
*
* StampPrecedence set to StampPrecedence.TIME
*
* Use StampCoordinate.makeAnalog() to customize result
*/
public static StampCoordinate getStampCoordinateFromStamp(Stamp stamp) {
return getStampCoordinateFromStamp(stamp, StampPrecedence.TIME);
}
/**
* Gets the stamp coordinate from stamp.
*
* @param stamp Stamp from which to generate StampCoordinate
* @param precedence Precedence to assign StampCoordinate
* @return StampCoordinate corresponding to Stamp values
*
* Use StampCoordinate.makeAnalog() to customize result
*/
public static StampCoordinate getStampCoordinateFromStamp(Stamp stamp, StampPrecedence precedence) {
final StampPosition stampPosition = new StampPositionImpl(stamp.getTime(), stamp.getPathSequence());
final StampCoordinate stampCoordinate = new StampCoordinateImpl(precedence,
stampPosition,
ConceptSequenceSet.of(stamp.getModuleSequence()),
EnumSet.of(stamp.getStatus()));
log.debug("Created StampCoordinate from Stamp: " + stamp + ": " + stampCoordinate);
return stampCoordinate;
}
/**
* Gets the stamp coordinate from version.
*
* @param version StampedVersion from which to generate StampCoordinate
* @return StampCoordinate corresponding to StampedVersion values
*
* StampPrecedence set to StampPrecedence.TIME
*
* Use StampCoordinate.makeAnalog() to customize result
*/
public static StampCoordinate getStampCoordinateFromVersion(StampedVersion version) {
return getStampCoordinateFromVersion(version, StampPrecedence.TIME);
}
/**
* Gets the stamp coordinate from version.
*
* @param version StampedVersion from which to generate StampCoordinate
* @param precedence the precedence
* @return StampCoordinate corresponding to StampedVersion values
*
* StampPrecedence set to StampPrecedence.TIME
*
* Use StampCoordinate.makeAnalog() to customize result
*/
public static StampCoordinate getStampCoordinateFromVersion(StampedVersion version, StampPrecedence precedence) {
final StampPosition stampPosition = new StampPositionImpl(version.getTime(), version.getPathSequence());
final StampCoordinate stampCoordinate = new StampCoordinateImpl(precedence,
stampPosition,
ConceptSequenceSet.of(
version.getModuleSequence()),
EnumSet.of(version.getState()));
log.debug("Created StampCoordinate from StampedVersion: " + toString(version) + ": " + stampCoordinate);
return stampCoordinate;
}
/**
* Gets the stated definition chronology.
*
* @param conceptId either a concept nid or sequence.
* @param logicCoordinate LogicCoordinate.
* @return the stated definition chronology for the specified concept
* according to the default logic coordinate.
*/
public static Optional<SememeChronology<? extends SememeVersion<?>>> getStatedDefinitionChronology(int conceptId,
LogicCoordinate logicCoordinate) {
conceptId = Get.identifierService()
.getConceptNid(conceptId);
return Get.sememeService()
.getSememesForComponentFromAssemblage(conceptId, logicCoordinate.getStatedAssemblageSequence())
.findAny();
}
/**
* Gets the version type.
*
* @param nid the nid
* @return the version type
*/
public static Class<? extends StampedVersion> getVersionType(int nid) {
final Optional<? extends ObjectChronology<? extends StampedVersion>> obj = Get.identifiedObjectService()
.getIdentifiedObjectChronology(nid);
if (!obj.isPresent()) {
throw new RuntimeException("No StampedVersion object exists with NID=" + nid);
}
return getVersionType(obj.get());
}
/**
* Gets the version type.
*
* @param obj the obj
* @return the version type
*/
public static Class<? extends StampedVersion> getVersionType(ObjectChronology<? extends StampedVersion> obj) {
switch (obj.getOchreObjectType()) {
case SEMEME: {
@SuppressWarnings({ "rawtypes", "unchecked" })
final SememeChronology<? extends SememeVersion> sememeChronology =
(SememeChronology<? extends SememeVersion>) obj;
switch (sememeChronology.getSememeType()) {
case COMPONENT_NID:
return ComponentNidSememeImpl.class;
case DESCRIPTION:
return DescriptionSememeImpl.class;
case DYNAMIC:
return DynamicSememeImpl.class;
case LOGIC_GRAPH:
return LogicGraphSememeImpl.class;
case LONG:
return LongSememeImpl.class;
case STRING:
return StringSememeImpl.class;
case RELATIONSHIP_ADAPTOR:
return RelationshipVersionAdaptorImpl.class;
case UNKNOWN:
case MEMBER:
default:
throw new RuntimeException("Sememe with NID=" + obj.getNid() + " is of unsupported SememeType " +
sememeChronology.getSememeType());
}
}
case CONCEPT:
return ConceptVersionImpl.class;
default:
throw new RuntimeException("Object with NID=" + obj.getNid() +
" is of unsupported OchreExternalizableObjectType " + obj.getOchreObjectType());
}
}
/**
* Find the VUID for a component (if it has one).
*
* @param componentNid the component nid
* @param stamp - optional - if not provided uses default from config
* service
* @return the id, if found, or empty (will not return null)
*/
public static Optional<Long> getVuId(int componentNid, StampCoordinate stamp) {
try {
final ArrayList<Long> vuids = new ArrayList<>(1);
Get.sememeService()
.getSnapshot(SememeVersion.class,
(stamp == null) ? Get.configurationService()
.getDefaultStampCoordinate()
: stamp)
.getLatestSememeVersionsForComponentFromAssemblage(componentNid, MetaData.VUID.getConceptSequence())
.forEach(latestSememe -> {
// expected path
if (latestSememe.value()
.getChronology()
.getSememeType() == SememeType.STRING) {
vuids.add(Long.parseLong(((StringSememe) latestSememe.value()).getString()));
}
// Data model bug path (can go away, after bug is fixed)
else if (latestSememe.value()
.getChronology()
.getSememeType() == SememeType.DYNAMIC) {
vuids.add(Long.parseLong(((DynamicSememe) latestSememe.value()).getData()[0]
.dataToString()));
}
});
if (vuids.size() > 1) {
log.warn("Found multiple VUIDs on component " +
Get.identifierService().getUuidPrimordialForNid(componentNid));
}
if (vuids.size() > 0) {
return Optional.of(vuids.get(0));
}
} catch (final Exception e) {
log.error("Unexpected error trying to find VUID for nid " + componentNid, e);
}
return Optional.empty();
}
//~--- inner classes -------------------------------------------------------
/**
* {@link IdInfo}.
*
* @author <a href="mailto:joel.kniaz.list@gmail.com">Joel Kniaz</a>
*
* Class to contain and hide map generated by getIdInfo(). Only useful method is toString(). The returned String is not meant to be parsed.
*/
public final static class IdInfo {
/** The map. */
private final Map<String, Object> map;
//~--- constructors -----------------------------------------------------
/**
* Instantiates a new id info.
*
* @param map the map
*/
private IdInfo(Map<String, Object> map) {
this.map = map;
}
//~--- methods ----------------------------------------------------------
/**
* To string.
*
* @return the string
*/
@Override
public String toString() {
return this.map.toString();
}
}
;
}