Java tutorial
/** * Copyright Notice * * This is a work of the U.S. Government and is not subject to copyright * protection in the United States. Foreign copyrights may apply. * * 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 gov.va.isaac.util; import gov.va.isaac.AppContext; import gov.va.isaac.ExtendedAppContext; import gov.va.isaac.config.generated.StatedInferredOptions; import gov.va.isaac.config.profiles.UserProfile; import gov.va.isaac.config.profiles.UserProfileManager; import gov.va.isaac.config.users.InvalidUserException; import gov.vha.isaac.cradle.Builder; import gov.vha.isaac.cradle.sememe.SememeProvider; import gov.vha.isaac.metadata.coordinates.StampCoordinates; import gov.vha.isaac.metadata.coordinates.ViewCoordinates; import gov.vha.isaac.metadata.source.IsaacMetadataAuxiliaryBinding; import gov.vha.isaac.ochre.api.IdentifierService; import gov.vha.isaac.ochre.api.LookupService; import gov.vha.isaac.ochre.api.chronicle.LatestVersion; import gov.vha.isaac.ochre.model.sememe.version.StringSememeImpl; import gov.vha.isaac.ochre.util.UuidFactory; import java.io.IOException; import java.text.Format; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.ihtsdo.otf.tcc.api.blueprint.ConceptCB; import org.ihtsdo.otf.tcc.api.blueprint.DescriptionCAB; import org.ihtsdo.otf.tcc.api.blueprint.IdDirective; import org.ihtsdo.otf.tcc.api.blueprint.InvalidCAB; import org.ihtsdo.otf.tcc.api.blueprint.RefexCAB; import org.ihtsdo.otf.tcc.api.blueprint.RefexDirective; import org.ihtsdo.otf.tcc.api.blueprint.RefexDynamicCAB; import org.ihtsdo.otf.tcc.api.blueprint.RelationshipCAB; import org.ihtsdo.otf.tcc.api.blueprint.TerminologyBuilderBI; import org.ihtsdo.otf.tcc.api.chronicle.ComponentChronicleBI; import org.ihtsdo.otf.tcc.api.chronicle.ComponentVersionBI; import org.ihtsdo.otf.tcc.api.conattr.ConceptAttributeVersionBI; import org.ihtsdo.otf.tcc.api.concept.ConceptChronicleBI; import org.ihtsdo.otf.tcc.api.concept.ConceptVersionBI; import org.ihtsdo.otf.tcc.api.contradiction.ContradictionException; import org.ihtsdo.otf.tcc.api.coordinate.EditCoordinate; import org.ihtsdo.otf.tcc.api.coordinate.Status; import org.ihtsdo.otf.tcc.api.coordinate.ViewCoordinate; import org.ihtsdo.otf.tcc.api.description.DescriptionChronicleBI; import org.ihtsdo.otf.tcc.api.description.DescriptionVersionBI; import org.ihtsdo.otf.tcc.api.lang.LanguageCode; import org.ihtsdo.otf.tcc.api.metadata.ComponentType; import org.ihtsdo.otf.tcc.api.metadata.binding.Snomed; import org.ihtsdo.otf.tcc.api.refex.RefexChronicleBI; import org.ihtsdo.otf.tcc.api.refex.RefexType; import org.ihtsdo.otf.tcc.api.refex.RefexVersionBI; import org.ihtsdo.otf.tcc.api.refexDynamic.RefexDynamicChronicleBI; import org.ihtsdo.otf.tcc.api.refexDynamic.RefexDynamicVersionBI; import org.ihtsdo.otf.tcc.api.relationship.RelationshipChronicleBI; import org.ihtsdo.otf.tcc.api.relationship.RelationshipType; import org.ihtsdo.otf.tcc.api.relationship.RelationshipVersionBI; import org.ihtsdo.otf.tcc.api.spec.ConceptSpec; import org.ihtsdo.otf.tcc.api.spec.ValidationException; import org.ihtsdo.otf.tcc.api.store.TerminologyStoreDI; import org.ihtsdo.otf.tcc.ddo.concept.ConceptChronicleDdo; import org.ihtsdo.otf.tcc.ddo.concept.component.description.DescriptionChronicleDdo; import org.ihtsdo.otf.tcc.ddo.concept.component.description.DescriptionVersionDdo; import org.ihtsdo.otf.tcc.ddo.concept.component.refex.RefexChronicleDdo; import org.ihtsdo.otf.tcc.ddo.concept.component.refex.type_comp.RefexCompVersionDdo; import org.ihtsdo.otf.tcc.model.cc.refex.type_membership.MembershipMember; import org.ihtsdo.otf.tcc.model.cc.refex.type_nid.NidMember; import org.ihtsdo.otf.tcc.model.cc.termstore.PersistentStoreI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * {@link OTFUtility} * * Utility for accessing OTF APIs. * * @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a> * @author ocarlsen * @author jefron */ public class OTFUtility { private static final Logger LOG = LoggerFactory.getLogger(OTFUtility.class); private static final UUID FSN_UUID = IsaacMetadataAuxiliaryBinding.FULLY_SPECIFIED_NAME.getPrimodialUuid(); private static final UUID PREFERRED_UUID = IsaacMetadataAuxiliaryBinding.PREFERRED.getPrimodialUuid(); private static final UUID SYNONYM_UUID = IsaacMetadataAuxiliaryBinding.SYNONYM.getPrimodialUuid(); private static Integer fsnNid = null; private static Integer preferredNid = null; private static Integer synonymNid = null; private static Integer langTypeNid = null; private static Integer snomedAssemblageNid = null; private static TerminologyStoreDI dataStore = ExtendedAppContext.getDataStore(); private static final Format format = new SimpleDateFormat("yyyy MM dd HH:mm:ss"); public static TerminologyBuilderBI getBuilder() { return new Builder(getEditCoordinate(), getViewCoordinateAllowInactive(), AppContext.getService(PersistentStoreI.class)); } public static TerminologyBuilderBI getBuilder(EditCoordinate ec, ViewCoordinate vc) { return new Builder(ec, vc, AppContext.getService(PersistentStoreI.class)); } public static ViewCoordinate getSystemViewCoordinate() { return ViewCoordinateFactory.getSystemViewCoordinate(); } public static ViewCoordinate getViewCoordinate() { ViewCoordinate vc = null; try { UserProfile userProfile = ExtendedAppContext.getCurrentlyLoggedInUserProfile(); if (userProfile == null) { LOG.warn( "User profile not available yet during call to getViewCoordinate - configuring automation mode!"); try { LookupService.getService(UserProfileManager.class).configureAutomationMode(null); userProfile = ExtendedAppContext.getCurrentlyLoggedInUserProfile(); } catch (InvalidUserException e) { throw new RuntimeException("Problem configuring automation mode!"); } } StatedInferredOptions relAssertionType = userProfile.getStatedInferredPolicy(); UUID path = userProfile.getViewCoordinatePath(); Set<Status> statuses = userProfile.getViewCoordinateStatuses(); long time = userProfile.getViewCoordinateTime(); Set<UUID> modules = userProfile.getViewCoordinateModules(); vc = ViewCoordinateFactory.getViewCoordinate(path, relAssertionType, statuses, time, modules); //LOG.info("Using ViewCoordinate policy={}, path nid={}, uuid={}, desc={}", policy, pathNid, pathUuid, OTFUtility.getDescription(pathChronicle)); } catch (RuntimeException e) { LOG.error("Failed fetching ViewCoordinate. Caught " + e.getClass().getName() + " " + e.getLocalizedMessage(), e); throw e; } return vc; } public static ViewCoordinate getViewCoordinateAllowInactive() { ViewCoordinate vc = getViewCoordinate(); vc.getAllowedStatus().add(Status.INACTIVE); vc.getAllowedStatus().add(Status.ACTIVE); return vc; } public static EditCoordinate getEditCoordinate() { try { UserProfile userProfile = ExtendedAppContext.getCurrentlyLoggedInUserProfile(); int authorNid = dataStore .getNidForUuids(ExtendedAppContext.getCurrentlyLoggedInUserProfile().getConceptUUID()); int module = Snomed.CORE_MODULE.getLenient().getNid(); int pathNid = 0; ConceptChronicleBI pathChronicle = null; UUID pathUuid = userProfile.getEditCoordinatePath(); if (pathUuid != null && (pathChronicle = dataStore.getConcept(pathUuid)) != null) { pathNid = pathChronicle.getNid(); } else { pathNid = IsaacMetadataAuxiliaryBinding.DEVELOPMENT.getLenient().getConceptNid(); pathChronicle = dataStore.getConcept(pathNid); pathUuid = pathChronicle.getPrimordialUuid(); } LOG.info("Using EditCoordinate path nid={}, uuid={}, desc={}", pathNid, pathUuid, OTFUtility.getDescription(pathChronicle)); // Override edit path return new EditCoordinate(authorNid, module, pathNid); } catch (NullPointerException e) { LOG.error("Edit path UUID does not exist", e); } catch (IOException e) { LOG.error("error configuring edit coordinate", e); } return null; } /** * Returns null if no concept exists with this nid */ public static String getDescriptionIfConceptExists(UUID uuid, ViewCoordinate vc) { ConceptVersionBI result = getConceptVersion(uuid, vc); return (result == null ? null : getDescription(result)); } public static String getDescriptionIfConceptExists(UUID uuid) { return getDescriptionIfConceptExists(uuid, getViewCoordinate()); } public static String getDescription(UUID uuid, ViewCoordinate vc) { try { ConceptVersionBI conceptVersion = dataStore.getConceptVersion(vc, uuid); return getDescription(conceptVersion); } catch (Exception ex) { LOG.warn("Unexpected error looking up description", ex); return null; } } public static String getDescription(UUID uuid) { return getDescription(uuid, getViewCoordinate()); } /** * Returns null if no concept exists with this nid */ public static String getDescriptionIfConceptExists(int nid, ViewCoordinate vc) { ConceptVersionBI result = getConceptVersion(nid, vc); return (result == null ? null : getDescription(result)); } public static String getDescriptionIfConceptExists(int nid) { return getDescriptionIfConceptExists(nid, getViewCoordinate()); } public static String getDescription(int nid, ViewCoordinate vc) { try { if (!dataStore.hasConcept(nid)) { return null; } ConceptVersionBI conceptVersion = dataStore.getConceptVersion(vc, nid); return getDescription(conceptVersion); } catch (Exception ex) { LOG.warn("Unexpected error looking up description", ex); return null; } } public static String getDescription(int nid) { return getDescription(nid, getViewCoordinate()); } /** * Note, this method isn't smart enough to work with multiple versions properly.... * assumes you only pass in a concept with current values. */ public static String getDescription(ConceptChronicleBI concept) { String fsn = null; String preferred = null; String bestFound = null; try { if (concept.getDescriptions() != null) { for (DescriptionChronicleBI desc : concept.getDescriptions()) { int versionCount = desc.getVersions().size(); DescriptionVersionBI<?> descVer = desc.getVersions() .toArray(new DescriptionVersionBI[versionCount])[versionCount - 1]; if (descVer.getTypeNid() == getFSNNid()) { if (descVer.getStatus() == Status.ACTIVE) { if (ExtendedAppContext.getCurrentlyLoggedInUserProfile().getDisplayFSN()) { return descVer.getText(); } else { fsn = descVer.getText(); } } else { bestFound = descVer.getText(); } } else if ((descVer.getTypeNid() == getSynonymTypeNid()) && isPreferred(descVer.getAnnotations())) { if (descVer.getStatus() == Status.ACTIVE) { if (!ExtendedAppContext.getCurrentlyLoggedInUserProfile().getDisplayFSN()) { return descVer.getText(); } else { preferred = descVer.getText(); } } else { bestFound = descVer.getText(); } } } } } catch (IOException e) { // noop } // If we get here, we didn't find what they were looking for. Pick // something.... String returnValue = (fsn != null ? fsn : (preferred != null ? preferred : (bestFound != null ? bestFound : concept.toUserString()))); return returnValue; } public static String getFullySpecifiedName(ConceptChronicleBI concept) { try { if (concept.getDescriptions() != null) { for (DescriptionChronicleBI desc : concept.getDescriptions()) { int versionCount = desc.getVersions().size(); DescriptionVersionBI<?> descVer = desc.getVersions() .toArray(new DescriptionVersionBI[versionCount])[versionCount - 1]; if (descVer.getTypeNid() == getFSNNid()) { if (descVer.getStatus() == Status.ACTIVE) { return descVer.getText(); } } } } } catch (IOException e) { // noop } return null; } private static int getFSNNid() { if (fsnNid == null) { fsnNid = dataStore.getNidForUuids(FSN_UUID); } return fsnNid; } private static int getSynonymTypeNid() { // Lazily load. if (synonymNid == null) { synonymNid = dataStore.getNidForUuids(SYNONYM_UUID); } return synonymNid; } public static int getLangTypeNid() { // Lazily load. if (langTypeNid == null) { langTypeNid = dataStore.getNidForUuids(Snomed.LANGUAGE_REFEX.getPrimodialUuid()); } return langTypeNid; } private static int getPreferredTypeNid() { // Lazily load. if (preferredNid == null) { preferredNid = dataStore.getNidForUuids(PREFERRED_UUID); } return preferredNid; } public static int getSnomedAssemblageNid() { if (snomedAssemblageNid == null) { snomedAssemblageNid = IsaacMetadataAuxiliaryBinding.SNOMED_INTEGER_ID.getNid(); } return snomedAssemblageNid; } /** * Pass in the annotations on a description component to determine if one of the * annotations is the isPreferred annotation */ public static boolean isPreferred(Collection<? extends RefexChronicleBI<?>> collection) { for (RefexChronicleBI<?> rc : collection) { if (rc.getRefexType() == RefexType.CID) { int nid1 = ((NidMember) rc).getNid1(); // RefexType.CID means NidMember. if (nid1 == getPreferredTypeNid()) { return true; } } } return false; } public static String getDescription(ConceptChronicleDdo concept) { // Go hunting for a FSN if (concept == null) { return null; } if (concept.getDescriptions() == null) { if (concept.getConceptReference() == null) { return null; } return concept.getConceptReference().getText(); } String fsn = null; String preferred = null; String bestFound = null; for (DescriptionChronicleDdo d : concept.getDescriptions()) { DescriptionVersionDdo dv = d.getVersions().get(d.getVersions().size() - 1); if (dv.getTypeReference().getUuid().equals(FSN_UUID)) { if (dv.getStatus() == Status.ACTIVE) { if (ExtendedAppContext.getCurrentlyLoggedInUserProfile().getDisplayFSN()) { return dv.getText(); } else { fsn = dv.getText(); } } else { bestFound = dv.getText(); } } else if (dv.getTypeReference().getUuid().equals(SYNONYM_UUID)) { if ((dv.getStatus() == Status.ACTIVE) && isPreferred(dv.getAnnotations())) { if (!ExtendedAppContext.getCurrentlyLoggedInUserProfile().getDisplayFSN()) { return dv.getText(); } else { preferred = dv.getText(); } } else { bestFound = dv.getText(); } } } // If we get here, we didn't find what they were looking for. Pick // something.... return (fsn != null ? fsn : (preferred != null ? preferred : (bestFound != null ? bestFound : concept.getConceptReference().getText()))); } private static boolean isPreferred(List<RefexChronicleDdo<?, ?>> annotations) { for (RefexChronicleDdo<?, ?> frc : annotations) { for (Object version : frc.getVersions()) { if (version instanceof RefexCompVersionDdo) { UUID uuid = ((RefexCompVersionDdo<?, ?>) version).getComp1Ref().getUuid(); return uuid.equals(PREFERRED_UUID); } } } return false; } /** * If the passed in value is a {@link UUID}, calls {@link #getConceptVersion(UUID)} * Next, if no hit, if the passed in value is parseable as a int < 0 (a nid), calls {@link #getConceptVersion(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 converts that to UUID and then calls {@link #getConceptVersion(UUID)}. 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 - then a * runtime exception is thrown. * Finally, if it is a positive integer, it treats is as a sequence identity, converts it to a nid, then looks up the nid. * * */ public static ConceptVersionBI lookupIdentifier(String identifier, ViewCoordinate vc) { LOG.debug("WB DB String Lookup '{}'", identifier); if (StringUtils.isBlank(identifier)) { return null; } String localIdentifier = identifier.trim(); UUID uuid = Utility.getUUID(localIdentifier); if (uuid != null) { return getConceptVersion(uuid, vc); } //if it is a negative integer, assume nid Optional<Integer> nid = Utility.getNID(localIdentifier); if (nid.isPresent()) { return getConceptVersion(nid.get(), vc); } if (SctId.isValidSctId(localIdentifier)) { //Note that some sequence IDs may still look like valid SCTIDs... which would mis-match... UUID alternateUUID = UuidFactory.getUuidFromAlternateId( IsaacMetadataAuxiliaryBinding.SNOMED_INTEGER_ID.getPrimodialUuid(), localIdentifier); LOG.debug("WB DB String Lookup as SCTID converted to UUID {}", alternateUUID); ConceptVersionBI cv = getConceptVersion(alternateUUID, vc); if (cv != null) { //sanity check: if (Utility.isInt(localIdentifier)) { int nidFromSequence = LookupService.getService(IdentifierService.class) .getConceptNid(Integer.parseInt(localIdentifier)); if (nidFromSequence != 0) { throw new RuntimeException("Cannot distinguish " + localIdentifier + ". Appears to be valid as a SCTID and a sequence identifier."); } } return cv; } } else if (Utility.isInt(localIdentifier)) { //Must be a postive integer, which wasn't a valid SCTID - it may be a sequence ID. int nidFromSequence = LookupService.getService(IdentifierService.class) .getConceptNid(Integer.parseInt(localIdentifier)); if (nidFromSequence != 0) { return getConceptVersion(nidFromSequence, vc); } } return null; } public static ConceptVersionBI lookupIdentifier(String identifier) { return lookupIdentifier(identifier, getViewCoordinate()); } /** * If the passed in value is a {@link UUID}, calls {@link #getConceptVersion(UUID)} * Next, if no hit, if the passed in value is parseable as a long, treats it as an SCTID and converts that to UUID and * then calls {@link #getConceptVersion(UUID)} * Next, if no hit, if the passed in value is parseable as a int, calls {@link #getConceptVersion(int)} * * All done in a background thread, method 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 */ public static void lookupIdentifier(final String identifier, final ConceptLookupCallback callback, final Integer callId, ViewCoordinate vc) { LOG.debug("Threaded Lookup: '{}'", identifier); final long submitTime = System.currentTimeMillis(); Runnable r = new Runnable() { @Override public void run() { ConceptVersionBI c = lookupIdentifier(identifier, vc); callback.lookupComplete(c, submitTime, callId); } }; Utility.execute(r); } public static void lookupIdentifier(final String identifier, final ConceptLookupCallback callback, final Integer callId) { lookupIdentifier(identifier, callback, callId, getViewCoordinate()); } /** * * All done in a background thread, method returns immediately * * @param identifier - The NID 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 */ public static void getConceptVersion(final int nid, final ConceptLookupCallback callback, final Integer callId, ViewCoordinate vc) { LOG.debug("Threaded Lookup: '{}'", nid); final long submitTime = System.currentTimeMillis(); Runnable r = new Runnable() { @Override public void run() { ConceptVersionBI c = getConceptVersion(nid, vc); callback.lookupComplete(c, submitTime, callId); } }; Utility.execute(r); } public static void getConceptVersion(final int nid, final ConceptLookupCallback callback, final Integer callId) { getConceptVersion(nid, callback, callId, getViewCoordinate()); } /** * Get the ConceptVersion identified by UUID on the ViewCoordinate configured by {@link #getViewCoordinate()} but * only if the concept exists at that point. Returns null otherwise. */ public static ConceptVersionBI getConceptVersion(UUID uuid, ViewCoordinate vc) { LOG.debug("Get ConceptVersion: '{}'", uuid); if (uuid == null) { return null; } try { ConceptVersionBI result = dataStore.getConceptVersion(vc, uuid); // Nothing like an undocumented getter which, rather than returning null when // the thing you are asking for doesn't exist - it goes off and returns // essentially a new, empty, useless node. Sigh. if (result.getUuidList().size() == 0) { return null; } return result; } catch (IOException ex) { LOG.error("Trouble getting concept: " + uuid, ex); } return null; } public static ConceptVersionBI getConceptVersion(UUID uuid) { return getConceptVersion(uuid, getViewCoordinate()); } /** * Get the ConceptVersion identified by NID on the ViewCoordinate configured by {@link #getViewCoordinate()} but * only if the concept exists at that point. Returns null otherwise. */ public static ConceptVersionBI getConceptVersion(int nid) { return getConceptVersion(nid, getViewCoordinate()); } /** * Get the ConceptVersion identified by NID on the ViewCoordinate configured by {@link #getViewCoordinate()} but * only if the concept exists at that point. Returns null otherwise. */ public static ConceptVersionBI getConceptVersion(int nid, ViewCoordinate vc) { LOG.debug("Get concept by nid: '{}'", nid); if (nid == 0) { return null; } try { ConceptVersionBI result = dataStore.getConceptVersion(vc, nid); // Nothing like an undocumented getter which, rather than returning null when // the thing you are asking for doesn't exist - it goes off and returns // essentially a new, empty, useless node. Sigh. if (result.getUuidList().size() == 0) { return null; } return result; } catch (IOException ex) { LOG.error("Trouble getting concept: " + nid, ex); } return null; } /** * Get the ComponentVersionBI identified by NID on the ViewCoordinate configured by {@link #getViewCoordinate()} but * only if the Component exists at that point. Returns null otherwise. */ public static ComponentVersionBI getComponentVersion(int nid, ViewCoordinate vc) { LOG.debug("Get component by nid: '{}'", nid); if (nid == 0) { return null; } try { ComponentChronicleBI<?> componentChronicle = getComponentChronicle(nid); Optional<? extends ComponentVersionBI> componentVersion = componentChronicle.getVersion(vc); // Nothing like an undocumented getter which, rather than returning null when // the thing you are asking for doesn't exist - it goes off and returns // essentially a new, empty, useless node. Sigh. if (!componentVersion.isPresent() || componentVersion.get().getUuidList().size() == 0) { return null; } else { return componentVersion.get(); } } catch (ContradictionException e) { LOG.error("Trouble getting concept " + nid + ". Caught " + e.getClass().getName() + " " + e.getLocalizedMessage(), e); return null; } } public static ComponentVersionBI getComponentVersion(int nid) { return getComponentVersion(nid, getViewCoordinate()); } /** * Get the ComponentVersionBI identified by NID on the ViewCoordinate configured by {@link #getViewCoordinate()} but * only if the Component exists at that point. Returns null otherwise. */ public static ComponentVersionBI getComponentVersion(UUID uuid, ViewCoordinate vc) { LOG.debug("Get component by nid: '{}'", uuid); try { ComponentChronicleBI<?> componentChronicle = getComponentChronicle(uuid); Optional<? extends ComponentVersionBI> componentVersion = componentChronicle.getVersion(vc); // Nothing like an undocumented getter which, rather than returning null when // the thing you are asking for doesn't exist - it goes off and returns // essentially a new, empty, useless node. Sigh. if (!componentVersion.isPresent() || componentVersion.get().getUuidList().size() == 0) { return null; } else { return componentVersion.get(); } } catch (ContradictionException e) { LOG.error("Trouble getting concept " + uuid + ". Caught " + e.getClass().getName() + " " + e.getLocalizedMessage(), e); return null; } } public static ComponentVersionBI getComponentVersion(UUID uuid) { return getComponentVersion(uuid, getViewCoordinate()); } /** * Get the Component identified by NID on the ViewCoordinate configured by {@link #getViewCoordinate()} but * only if it exists at that point. Returns null otherwise. */ public static ComponentChronicleBI<?> getComponentChronicle(int nid) { LOG.debug("Get component chronicle by nid: '{}'", nid); if (nid == 0) { return null; } try { ComponentChronicleBI<?> result = dataStore.getComponent(nid); if (result == null) { return null; } // Nothing like an undocumented getter which, rather than returning null when // the thing you are asking for doesn't exist - it goes off and returns // essentially a new, empty, useless node. Sigh. if (result.getUuidList().size() == 0) { return null; } return result; } catch (IOException ex) { LOG.error("Trouble getting component: " + nid, ex); } return null; } /** * Get the Component identified by NID on the ViewCoordinate configured by {@link #getViewCoordinate()} but * only if it exists at that point. Returns null otherwise. */ public static ComponentChronicleBI<?> getComponentChronicle(UUID uuid) { LOG.debug("Get component chronicle by uuid: '{}'", uuid); if (uuid == null) { return null; } try { ComponentChronicleBI<?> result = dataStore.getComponent(uuid); // Nothing like an undocumented getter which, rather than returning null when // the thing you are asking for doesn't exist - it goes off and returns // essentially a new, empty, useless node. Sigh. if (result.getUuidList().size() == 0) { return null; } return result; } catch (IOException ex) { LOG.error("Trouble getting component: " + uuid, ex); return null; } } /** * Returns an empty optional, if the member isn't on the path (or if it is an invalid nid alltogether) */ public static Optional<? extends RefexVersionBI<?>> getRefsetMember(int nid) { try { RefexChronicleBI<?> refexChron = (RefexChronicleBI<?>) dataStore.getComponent(nid); if (refexChron != null) { ViewCoordinate tempVc = getViewCoordinate(); tempVc.getAllowedStatus().add(Status.INACTIVE); return refexChron.getVersion(tempVc); } } catch (Exception ex) { LOG.warn("perhaps unexpected?", ex); } return Optional.empty(); } public static RefexChronicleBI<?> getAllVersionsRefsetMember(int nid) { try { return (RefexChronicleBI<?>) dataStore.getComponent(nid); } catch (Exception ex) { LOG.warn("perhaps unexpected?", ex); } return null; } // TODO OCHRE getUncommittedConcepts() is unsupported so isUncommittened(con) will always throw exception public static boolean isUncommittened(ConceptVersionBI con) { return dataStore.getUncommittedConcepts().contains(con.getChronicle()); } /** * Recursively find the leaf nodes of a concept hierarchy * @param nid - starting concept */ public static Set<ConceptVersionBI> getAllLeafChildrenOfConcept(int nid, ViewCoordinate vc) throws IOException, ContradictionException { return getAllChildrenOfConcept(new HashSet<>(), getConceptVersion(nid, vc), vc, true, true); } public static Set<ConceptVersionBI> getAllLeafChildrenOfConcept(int nid) throws IOException, ContradictionException { return getAllLeafChildrenOfConcept(nid, getViewCoordinate()); } /** * Recursively get Is a children of a concept */ public static Set<ConceptVersionBI> getAllChildrenOfConcept(int nid, ViewCoordinate vc, boolean recursive) throws IOException, ContradictionException { return getAllChildrenOfConcept(new HashSet<>(), getConceptVersion(nid, vc), recursive, false); } public static Set<ConceptVersionBI> getAllChildrenOfConcept(int nid, boolean recursive) throws IOException, ContradictionException { return getAllChildrenOfConcept(nid, getViewCoordinate(), recursive); } /** * Recursively get Is a children of a concept */ public static Set<ConceptVersionBI> getAllChildrenOfConcept(ConceptVersionBI concept, ViewCoordinate vc, boolean recursive) throws IOException, ContradictionException { return getAllChildrenOfConcept(new HashSet<>(), concept, vc, recursive, false); } public static Set<ConceptVersionBI> getAllChildrenOfConcept(ConceptVersionBI concept, boolean recursive) throws IOException, ContradictionException { return getAllChildrenOfConcept(concept, getViewCoordinate(), recursive); } /** * Recursively get Is a children of a concept */ private static Set<ConceptVersionBI> getAllChildrenOfConcept(Set<Integer> handledConceptNids, ConceptVersionBI concept, ViewCoordinate vc, boolean recursive, boolean leafOnly) throws IOException, ContradictionException { Set<ConceptVersionBI> results = new HashSet<>(); // This both prevents infinite recursion and avoids processing or returning of duplicates if (handledConceptNids.contains(concept.getNid())) { LOG.debug( "Encountered already-handled concept \"{}\". May be result of OTF-returned duplicate or source of potential infinite loop", OTFUtility.getDescription(concept.getNid(), vc)); return results; } //TODO OTF Bug - OTF is broken, this returns all kinds of duplicates https://jira.ihtsdotools.org/browse/OTFISSUE-21 int size = 0; for (RelationshipVersionBI<?> r : concept.getRelationshipsIncomingActiveIsa()) { size++; if (handledConceptNids.contains(r.getOriginNid())) { // avoids processing or returning of duplicates LOG.debug( "Encountered already-handled ORIGIN child concept \"{}\". May be result of OTF-returned duplicate or source of potential infinite loop", OTFUtility.getDescription(r.getOriginNid(), vc)); continue; } ConceptVersionBI originConcept = getConceptVersion(r.getOriginNid(), vc); if (!leafOnly) { results.add(originConcept); } if (recursive) { results.addAll(getAllChildrenOfConcept(handledConceptNids, originConcept, vc, recursive, leafOnly)); } } if (leafOnly && size == 0 && !handledConceptNids.contains(concept.getNid())) { results.add(concept); } handledConceptNids.add(concept.getNid()); return results; } private static Set<ConceptVersionBI> getAllChildrenOfConcept(Set<Integer> handledConceptNids, ConceptVersionBI concept, boolean recursive, boolean leafOnly) throws IOException, ContradictionException { return getAllChildrenOfConcept(handledConceptNids, concept, getViewCoordinate(), recursive, leafOnly); } public static Set<Integer> getAllChildrenOfConceptAsNids(Integer conceptNid, ViewCoordinate vc, boolean recursive) throws IOException, ContradictionException { Set<ConceptVersionBI> resultsAsConceptVersions = getAllChildrenOfConcept(conceptNid, vc, recursive); Set<Integer> results = new HashSet<>(); for (ConceptVersionBI conceptVersion : resultsAsConceptVersions) { results.add(conceptVersion.getNid()); } return results; } public static Set<Integer> getAllChildrenOfConceptAsNids(Integer conceptNid, boolean recursive) throws IOException, ContradictionException { return getAllChildrenOfConceptAsNids(conceptNid, getViewCoordinate(), recursive); } /** * Recursively get Is a parents of a concept */ public static Set<ConceptVersionBI> getConceptAncestors(int nid, ViewCoordinate vc) throws IOException, ContradictionException { return getConceptAncestors(getConceptVersion(nid, vc), vc); } public static Set<ConceptVersionBI> getConceptAncestors(int nid) throws IOException, ContradictionException { return getConceptAncestors(nid, getViewCoordinate()); } /** * Recursively get Is a parents of a concept */ public static Set<ConceptVersionBI> getConceptAncestors(ConceptVersionBI concept, ViewCoordinate vc) throws IOException, ContradictionException { Set<Integer> handledNids = new HashSet<>(); return getConceptAncestors(handledNids, concept, vc); } public static Set<ConceptVersionBI> getConceptAncestors(ConceptVersionBI concept) throws IOException, ContradictionException { return getConceptAncestors(concept, getViewCoordinate()); } private static Set<ConceptVersionBI> getConceptAncestors(Set<Integer> handledNids, ConceptVersionBI concept, ViewCoordinate vc) throws IOException, ContradictionException { Set<ConceptVersionBI> results = new HashSet<>(); // This both prevents infinite recursion and avoids processing or returning of duplicates if (handledNids.contains(concept.getNid())) { LOG.debug( "Encountered already-handled concept \"{}\". May be result of OTF-returned duplicate or source of potential infinite loop", OTFUtility.getDescription(concept.getNid(), vc)); return results; } //TODO OTF Bug - OTF is broken, this returns all kinds of duplicates https://jira.ihtsdotools.org/browse/OTFISSUE-21 for (RelationshipVersionBI<?> r : concept.getRelationshipsOutgoingActiveIsa()) { if (handledNids.contains(r.getDestinationNid())) { // avoids processing or returning of duplicates LOG.debug( "Encountered already-handled DESTINATION ancestor concept \"{}\". May be result of OTF-returned duplicate or source of potential infinite loop", OTFUtility.getDescription(r.getDestinationNid(), vc)); continue; } ConceptVersionBI destConcept = getConceptVersion(r.getDestinationNid(), vc); results.add(destConcept); results.addAll(getConceptAncestors(handledNids, destConcept, vc)); } handledNids.add(concept.getNid()); return results; } private static Set<ConceptVersionBI> getConceptAncestors(Set<Integer> handledNids, ConceptVersionBI concept) throws IOException, ContradictionException { return getConceptAncestors(handledNids, concept, getViewCoordinate()); } /** * Finds just the concept's parents */ public static Set<ConceptVersionBI> getConceptParents(ConceptVersionBI concept, ViewCoordinate vc) throws IOException, ContradictionException { Set<ConceptVersionBI> results = new HashSet<>(); for (RelationshipVersionBI<?> r : concept.getRelationshipsOutgoingActiveIsa()) { results.add(getConceptVersion(r.getDestinationNid(), vc)); } return results; } public static Set<ConceptVersionBI> getConceptParents(ConceptVersionBI concept) throws IOException, ContradictionException { return getConceptParents(concept, getViewCoordinate()); } public static ConceptChronicleBI createNewConcept(ConceptChronicleBI parent, String fsn, String prefTerm) throws IOException, InvalidCAB, ContradictionException { ConceptCB newConCB = createNewConceptBlueprint(parent, fsn, prefTerm); ConceptChronicleBI newCon = getBuilder().construct(newConCB); return newCon; } public static ConceptChronicleBI createAndCommitNewConcept(ConceptChronicleBI parent, String fsn, String prefTerm) throws IOException, InvalidCAB, ContradictionException { ConceptCB newConCB = createNewConceptBlueprint(parent, fsn, prefTerm); ConceptChronicleBI newCon = getBuilder().construct(newConCB); ExtendedAppContext.getDataStore().addUncommitted(newCon); ExtendedAppContext.getDataStore().commit(); return newCon; } public static ConceptCB createNewConceptBlueprint(ConceptChronicleBI parent, String fsn, String prefTerm) throws ValidationException, IOException, InvalidCAB, ContradictionException { LanguageCode lc = LanguageCode.EN_US; UUID isA = IsaacMetadataAuxiliaryBinding.IS_A.getPrimodialUuid(); IdDirective idDir = IdDirective.GENERATE_HASH; UUID module = Snomed.CORE_MODULE.getLenient().getPrimordialUuid(); UUID parentUUIDs[] = new UUID[1]; parentUUIDs[0] = parent.getPrimordialUuid(); return new ConceptCB(fsn, prefTerm, lc, isA, idDir, module, IsaacMetadataAuxiliaryBinding.DEVELOPMENT.getPrimodialUuid(), parentUUIDs); } // public static void commit(int nid) throws IOException { // ConceptVersionBI con = getConceptVersion(nid); // commit(/* con */); // } public static void cancel() throws IOException { dataStore.cancel(); } public static String getConPrefTerm(int nid, ViewCoordinate vc) { try { ConceptVersionBI cv = OTFUtility.getConceptVersion(nid, vc); if (cv == null) { return nid + " NOT ON PATH"; } else { DescriptionVersionBI<?> dv = cv.getPreferredDescription(); if (dv == null) { return nid + " NO DESC FOUND"; } else { return dv.getText(); } } } catch (IOException | ContradictionException e) { LOG.error("Unable to identify description. Points to larger problem", e); return "ERROR"; } } public static String getConPrefTerm(int nid) { return getConPrefTerm(nid, getViewCoordinate()); } public static String getStatusString(ComponentVersionBI comp) { return comp.getStatus() == Status.ACTIVE ? "Active" : "Inactive"; } public static String getAuthorString(ComponentVersionBI comp, ViewCoordinate vc) { return getConPrefTerm(comp.getAuthorNid(), vc); } public static String getAuthorString(ComponentVersionBI comp) { return getAuthorString(comp, getViewCoordinate()); } public static String getModuleString(ComponentVersionBI comp, ViewCoordinate vc) { return getConPrefTerm(comp.getModuleNid(), vc); } public static String getModuleString(ComponentVersionBI comp) { return getModuleString(comp, getViewCoordinate()); } public static String getPathString(ComponentVersionBI comp, ViewCoordinate vc) { return getConPrefTerm(comp.getPathNid(), vc); } public static String getPathString(ComponentVersionBI comp) { return getPathString(comp, getViewCoordinate()); } public static String getTimeString(ComponentVersionBI comp) { if (comp.getTime() != Long.MAX_VALUE) { Date date = new Date(comp.getTime()); return format.format(date); } else { return "Uncommitted"; } } public static void createNewDescription(int conNid, int typeNid, LanguageCode lang, String term, boolean isInitial) throws IOException, InvalidCAB, ContradictionException { DescriptionCAB newDesc = new DescriptionCAB(conNid, typeNid, lang, term, isInitial, IdDirective.GENERATE_HASH); getBuilder().construct(newDesc); dataStore.addUncommitted(dataStore.getConceptForNid(conNid)); } public static void createNewRelationship(int conNid, int typeNid, int targetNid, int group, RelationshipType type) throws IOException, InvalidCAB, ContradictionException { RelationshipCAB newRel = new RelationshipCAB(conNid, typeNid, targetNid, group, type, IdDirective.GENERATE_HASH); getBuilder().construct(newRel); dataStore.addUncommitted(dataStore.getConceptForNid(conNid)); } public static void createNewDescription(int conNid, String term) throws IOException, InvalidCAB, ContradictionException { DescriptionCAB newDesc = new DescriptionCAB(conNid, IsaacMetadataAuxiliaryBinding.SYNONYM.getNid(), LanguageCode.EN_US, term, false, IdDirective.GENERATE_HASH); getBuilder().construct(newDesc); dataStore.addUncommitted(dataStore.getConceptForNid(conNid)); } public static void createNewRole(int conNid, int typeNid, int targetNid) throws IOException, InvalidCAB, ContradictionException { RelationshipCAB newRel = new RelationshipCAB(conNid, typeNid, targetNid, 0, RelationshipType.STATED_ROLE, IdDirective.GENERATE_HASH); getBuilder().construct(newRel); dataStore.addUncommitted(dataStore.getConceptForNid(conNid)); } public static void createNewParent(int conNid, int targetNid) throws ValidationException, IOException, InvalidCAB, ContradictionException { RelationshipCAB newRel = new RelationshipCAB(conNid, IsaacMetadataAuxiliaryBinding.IS_A.getNid(), targetNid, 0, RelationshipType.STATED_HIERARCHY, IdDirective.GENERATE_HASH); getBuilder().construct(newRel); dataStore.addUncommitted(dataStore.getConceptForNid(conNid)); } public static List<ConceptChronicleBI> getPathConcepts() throws ValidationException, IOException, ContradictionException { ConceptChronicleBI pathRefset = dataStore .getConcept(IsaacMetadataAuxiliaryBinding.PATHS_ASSEMBLAGE.getLenient().getPrimordialUuid()); Collection<? extends RefexChronicleBI<?>> members = pathRefset.getRefsetMembers(); List<ConceptChronicleBI> pathConcepts = new ArrayList<>(); for (RefexChronicleBI<?> member : members) { if (member instanceof MembershipMember) { MembershipMember membershipMember = (MembershipMember) member; int pathNid = membershipMember.getReferencedComponentNid(); ConceptChronicleBI pathConcept = dataStore.getConcept(pathNid); pathConcepts.add(pathConcept); } else { LOG.warn("While loading paths expecting MembershipMember but encountered {}: {}", member.getClass().getName(), member); } } if (pathConcepts.size() == 0) { LOG.error("No paths loaded based on membership in {}", IsaacMetadataAuxiliaryBinding.PATHS_ASSEMBLAGE); } else { LOG.debug("Loaded {} paths: {}", pathConcepts.size(), pathConcepts); } return pathConcepts; } public static ComponentVersionBI getLastCommittedVersion(ComponentChronicleBI<?> chronicle) { // Strictly Time-Based sorting. Should suffice until a) Path setup changes or b) Proper implementation added to tcc @SuppressWarnings("unchecked") Collection<ComponentVersionBI> versions = (Collection<ComponentVersionBI>) chronicle.getVersions(); ComponentVersionBI latestVersion = null; for (ComponentVersionBI v : versions) { if ((v.getTime() != Long.MAX_VALUE) && (latestVersion == null || v.getTime() > latestVersion.getTime())) { latestVersion = v; } } return latestVersion; } public static void addToPromotionPath(UUID compUuid) throws IOException, ContradictionException, InvalidCAB { // Setup Edit Path to be promotion path ConceptVersionBI pp = getConceptVersion( AppContext.getAppConfiguration().getCurrentWorkflowPromotionPathUuidAsUUID()); ConceptSpec cs = new ConceptSpec(pp.getNid()); List<ConceptSpec> editPaths = new ArrayList<ConceptSpec>(); editPaths.add(cs); EditCoordinate editCoord = getEditCoordinate(); editCoord.setEditPathListSpecs(editPaths); TerminologyBuilderBI builder = getBuilder(editCoord, getViewCoordinate()); // Create new version of all uncommitted components in concept ConceptVersionBI conceptWithComp = OTFUtility .getConceptVersion(getComponentVersion(compUuid).getAssociatedConceptNid()); Set<ComponentVersionBI> componentsInConcept = getConceptComponents(conceptWithComp); int devPathNid = IsaacMetadataAuxiliaryBinding.DEVELOPMENT.getNid(); for (ComponentVersionBI comp : componentsInConcept) { if (comp.getPathNid() == devPathNid) { ComponentType type = ComponentType.getComponentVersionType(comp); @SuppressWarnings("unused") ComponentChronicleBI<?> cbi = null; if (type == ComponentType.CONCEPT) { ConceptCB cab = (ConceptCB) comp.makeBlueprint(getViewCoordinate(), IdDirective.PRESERVE, RefexDirective.EXCLUDE); cbi = builder.construct(cab); } else if (type == ComponentType.DESCRIPTION) { DescriptionCAB cab = (DescriptionCAB) comp.makeBlueprint(getViewCoordinate(), IdDirective.PRESERVE, RefexDirective.EXCLUDE); cbi = builder.construct(cab); } else if (type == ComponentType.RELATIONSHIP) { RelationshipCAB cab = (RelationshipCAB) comp.makeBlueprint(getViewCoordinate(), IdDirective.PRESERVE, RefexDirective.EXCLUDE); cbi = builder.construct(cab); } else if (type == ComponentType.SEMEME) { RefexCAB cab = (RefexCAB) comp.makeBlueprint(getViewCoordinate(), IdDirective.PRESERVE, RefexDirective.EXCLUDE); cbi = builder.construct(cab); } else if (type == ComponentType.SEMEME_DYNAMIC) { RefexDynamicCAB cab = (RefexDynamicCAB) comp.makeBlueprint(getViewCoordinate(), IdDirective.PRESERVE, RefexDirective.EXCLUDE); cbi = builder.construct(cab); } } } ExtendedAppContext.getDataStore().commit(/* conceptWithComp.getVersion(getViewCoordinate()) */); } public static Set<ComponentVersionBI> getConceptComponents(ConceptVersionBI conceptWithComp) throws IOException, ContradictionException { Set<ComponentVersionBI> retSet = new HashSet<>(); retSet.add(conceptWithComp); for (DescriptionChronicleBI desc : conceptWithComp.getDescriptions()) { desc.getVersion(conceptWithComp.getViewCoordinate()).ifPresent((dv) -> retSet.add(dv)); } for (RelationshipChronicleBI rel : conceptWithComp.getRelationshipsOutgoing()) { rel.getVersion(conceptWithComp.getViewCoordinate()).ifPresent((rv) -> retSet.add(rv)); } for (RefexChronicleBI<?> refsetMember : conceptWithComp.getRefsetMembers()) { refsetMember.getVersion(conceptWithComp.getViewCoordinate()).ifPresent((rm) -> retSet.add(rm)); } for (RefexDynamicChronicleBI<?> dynRef : conceptWithComp.getRefexesDynamic()) { dynRef.getVersion(conceptWithComp.getViewCoordinate()).ifPresent((rdv) -> retSet.add(rdv)); } return retSet; } public static ConceptAttributeVersionBI<?> getLatestAttributes( @SuppressWarnings("rawtypes") Collection<? extends ConceptAttributeVersionBI> collection) { ConceptAttributeVersionBI<?> newest = null; long newestTime = Long.MIN_VALUE; for (ConceptAttributeVersionBI<?> x : collection) { if (x.getTime() > newestTime) { newest = x; newestTime = x.getTime(); } } return newest; } public static DescriptionVersionBI<?> getLatestDescVersion( @SuppressWarnings("rawtypes") Collection<? extends DescriptionVersionBI> collection) { DescriptionVersionBI<?> newest = null; long newestTime = Long.MIN_VALUE; for (DescriptionVersionBI<?> x : collection) { if (x.getTime() > newestTime) { newest = x; newestTime = x.getTime(); } } return newest; } public static RefexVersionBI<?> getLatestRefexVersion( @SuppressWarnings("rawtypes") Collection<? extends RefexVersionBI> collection) { RefexVersionBI<?> newest = null; ; long newestTime = Long.MIN_VALUE; for (RefexVersionBI<?> x : collection) { if (x.getTime() > newestTime) { newest = x; newestTime = x.getTime(); } } return newest; } public static RefexDynamicVersionBI<?> getLatestDynamicRefexVersion( @SuppressWarnings("rawtypes") Collection<? extends RefexDynamicVersionBI> collection) { RefexDynamicVersionBI<?> newest = null; ; long newestTime = Long.MIN_VALUE; for (RefexDynamicVersionBI<?> x : collection) { if (x.getTime() > newestTime) { newest = x; newestTime = x.getTime(); } } return newest; } /** * Returns the uuid for fsn and pt based on the ConceptCB assignment algorithm. * * @param fsn the fsn * @param pt the pt * @return the uuid for fsn */ public static UUID getUuidForFsn(String fsn, String pt) { List<String> fsns = new ArrayList<>(); fsns.add(fsn); List<String> pts = new ArrayList<>(); pts.add(pt); return ConceptCB.computeComponentUuid(IdDirective.GENERATE_REFEX_CONTENT_HASH, fsns, pts, null); } public static Optional<Long> getSctId(int componentNid) { try { Optional<LatestVersion<StringSememeImpl>> sememe = AppContext.getService(SememeProvider.class) .getSnapshot(StringSememeImpl.class, StampCoordinates.getDevelopmentLatest()) .getLatestSememeVersionsForComponentFromAssemblage(componentNid, getSnomedAssemblageNid()) .findFirst(); if (sememe.isPresent()) { String temp = sememe.get().value().getString(); try { return Optional.of(Long.parseLong(temp)); } catch (NumberFormatException e) { LOG.error("The found string '" + temp + "' isn't parseable as a long - as an SCTID should be - in nid " + componentNid); } } } catch (Exception e) { LOG.warn("Unexpected error trying to find SCTID for nid " + componentNid, e); } return Optional.empty(); } }