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

au.csiro.ontology.snomed.refset.rf2.ModuleDependencyRefset Maven / Gradle / Ivy

The newest version!
/**
 * Copyright CSIRO Australian e-Health Research Centre (http://aehrc.com).
 * All rights reserved. Use is subject to license terms and conditions.
 */
package au.csiro.ontology.snomed.refset.rf2;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.csiro.ontology.input.StructuredLog;

/**
 * This class represents a module dependency reference set.
 *
 * @author Alejandro Metke
 *
 */
public class ModuleDependencyRefset extends Refset implements IModuleDependencyRefset {

    private final static Logger log = LoggerFactory.getLogger(ModuleDependencyRefset.class);

    private final static class M implements Comparable {
        final private String module;
        final private String time;

        M(final String module, final String time) {
            assert null != module;
            assert null != time;

            this.module = module;
            this.time = time;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + module.hashCode();
            result = prime * result + time.hashCode();
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            M other = (M) obj;
            return module.equals(other.module) && time.equals(other.time);
        }

        @Override
        public String toString() {
//            return "http://snomed.info/module/" + module + "/time/" + time;
            return module + "/" + time;
        }

        @Override
        public int compareTo(M other) {
            final int mCmp = module.compareTo(other.module);
            if (mCmp == 0) {
                return parseTime(time).compareTo(parseTime(other.time));
            } else {
                return mCmp;
            }
        }

    }

    protected final Map> dependencies = new HashMap>();

    /**
     * Creates a new module dependency reference set.
     * 

* Requirements: *

    *
  • All MDRS rows are available (actives and inactives) *
  • Result is a map of every valid "Version" defined by the supplied rows *
  • There is a valid Version for every unique combination of moduleId and sourceEffectiveTime *
  • As per the TIG, sourceEffectiveTime and effectiveTime must be equal in every row *
  • The sourceEffectiveTime is used to identify the Version-specific set of modules *
  • targetEffectiveTime is always less-than or equal to (<=) sourceEffectiveTime *
*

* Note, while module dependencies are not explicitly required to be transitively consistent by the * IHTSDO Specifications (as documented in the TIG), it is implicitly required (at least for Core) by the * requirements that the Core is not "changed" by an Extension. *

* Strategy for loading: *

    *
  • Gather all (active and inactive) rows *
  • For each unique (active or inactive) moduleId and sourceEffectiveTime pair: *
      *
    • The set of associated referencedComponentId module and targetEffectiveTime pairs makes up the required modules *
    *
  • Compute the completion (transitive closure) of the dependency relationship *
* @param report * * @param members */ public ModuleDependencyRefset(Set members, boolean validate) throws ValidationException { id = "900000000000534007"; final List problems = new ArrayList(); // Index the dependency rows final Map> index = new HashMap>(); for (ModuleDependencyRow member : members) { final M version = new M(member.getModuleId(), member.getSourceEffectiveTime()); final M requiredModule = new M(member.getReferencedComponentId(), member.getTargetEffectiveTime()); if (!member.isActive()) { StructuredLog.InactiveDependency.info(log, version, requiredModule); continue; } if (member.isMalformed()) { problems.add(StructuredLog.MalformedMDRSEntry.warn(member, log, version, requiredModule)); continue; } Set vals = index.get(version); if (vals == null) { vals = new HashSet(); index.put(version, vals); } vals.add(requiredModule); } if (validate && !problems.isEmpty()) { throw new ValidationException("Malformed Module Dependency Reference Set", problems); } if (log.isTraceEnabled()) { // Use a TreeSet to get the output sorted for (final M version: new TreeSet(index.keySet())) { log.trace("MDRS entry for version: " + version); } } // Compute the transitive closure of the required modules. // Note: //
  • if the MDRS conforms to the spec., then this would not be required //
  • furthermore, if the dependencies are self-consistent, then this should do nothing // tc(index); for (final M version : index.keySet()) { final String srcId = version.module; final String srcVer = version.time; ModuleDependency md = createDependency(version, index); Map verDepMap = dependencies.get(srcId); if (verDepMap == null) { verDepMap = new HashMap(); dependencies.put(srcId, verDepMap); } verDepMap.put(srcVer, md); } } /** * Compute the transitive closure of the dependencies * * @param index */ private static void tc(Map> index) { final Map warnings = new HashMap<>(); for (Entry> entry: index.entrySet()) { final M src = entry.getKey(); final Set dependents = entry.getValue(); final Queue queue = new LinkedList(dependents); while (!queue.isEmpty()) { final M key = queue.poll(); if (!index.containsKey(key)) { continue; } for (M addition: index.get(key)) { if (!dependents.contains(addition)) { dependents.add(addition); queue.add(addition); warnings.put(src + "|" + addition + "|" + key, new Object[] {src, addition, key}); } } } } for (final Object[] args: warnings.values()) { StructuredLog.ImpliedTransitiveDependency.warn(log, args); } } private ModuleDependency createDependency(M version, Map> index) { return createDependency(new HashSet<>(), version, index); } private ModuleDependency createDependency(Collection all, M version, Map> index) { final ModuleDependency md = new ModuleDependency(version.module, version.time); if (all.contains(md)) { StructuredLog.CyclicDependency.warn(md, log); return null; } all.add(md); Set deps = index.get(version); if (deps != null) { for (M dep : deps) { final ModuleDependency childMd = createDependency(all, dep, index); if (null != childMd) { md.getDependencies().add(childMd); } } } all.remove(md); return md; } @Override public Map> getModuleDependencies() { return dependencies; } static Date parseTime(String time) { final SimpleDateFormat ddf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); try { return ddf.parse(time); } catch (ParseException e1) { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); try { return sdf.parse(time); } catch (ParseException e2) { final String message = StructuredLog.InvalidEffectiveTime.error(log, time, e1.getMessage(), e2.getMessage()); throw new RuntimeException(message, e2); } } } }




  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy