gov.nih.nci.evs.browser.utils.TreeUtils.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.evs.browser.utils.TreeUtils.java

Source

/*L
 * Copyright Northrop Grumman Information Technology.
 *
 * Distributed under the OSI-approved BSD 3-Clause License.
 * See http://ncip.github.com/nci-term-browser/LICENSE.txt for details.
 */

package gov.nih.nci.evs.browser.utils;

/*
 * Copyright: (c) 2004-2009 Mayo Foundation for Medical Education and
 * Research (MFMER). All rights reserved. MAYO, MAYO CLINIC, and the
 * triple-shield Mayo logo are trademarks and service marks of MFMER.
 *
 * Except as contained in the copyright notice above, or as used to identify
 * MFMER as the author of this software, the trade names, trademarks, service
 * marks, or product names of the copyright holder shall not be used in
 * advertising, promotion or otherwise in connection with this software without
 * prior written authorization of the copyright holder.
 *
 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html
 *
 */

/**
  * @author EVS Team
  * @version 1.0
  *
  * Note: This class is created based on Mayo's BuildTreForCode.java sample code
  *
  * Modification history
  *     Initial modification kim.ong@ngc.com
  *
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;

import org.LexGrid.LexBIG.DataModel.Collections.AssociatedConceptList;
import org.LexGrid.LexBIG.DataModel.Collections.AssociationList;
import org.LexGrid.LexBIG.DataModel.Collections.LocalNameList;
import org.LexGrid.LexBIG.DataModel.Collections.ResolvedConceptReferenceList;
import org.LexGrid.LexBIG.DataModel.Core.AssociatedConcept;
import org.LexGrid.LexBIG.DataModel.Core.Association;
import org.LexGrid.LexBIG.DataModel.Core.CodingSchemeSummary;
import org.LexGrid.LexBIG.DataModel.Core.CodingSchemeVersionOrTag;
import org.LexGrid.LexBIG.DataModel.Core.ConceptReference;
import org.LexGrid.LexBIG.DataModel.Core.ResolvedConceptReference;
import org.LexGrid.LexBIG.Exceptions.LBException;
import org.LexGrid.LexBIG.Exceptions.LBResourceUnavailableException;
import org.LexGrid.LexBIG.Extensions.Generic.LexBIGServiceConvenienceMethods;
import org.LexGrid.LexBIG.Extensions.Generic.LexBIGServiceConvenienceMethods.HierarchyPathResolveOption;
import org.LexGrid.LexBIG.Impl.LexBIGServiceImpl;
import org.LexGrid.LexBIG.LexBIGService.CodedNodeGraph;
import org.LexGrid.LexBIG.LexBIGService.CodedNodeSet;
import org.LexGrid.LexBIG.LexBIGService.LexBIGService;
import org.LexGrid.LexBIG.Utility.Constructors;
import org.LexGrid.codingSchemes.CodingScheme;
import org.LexGrid.commonTypes.EntityDescription;
import org.LexGrid.naming.SupportedHierarchy;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import org.LexGrid.codingSchemes.Mappings;

/**
 * Attempts to provide a tree, based on a focus code, that includes the
 * following information:
 * <pre>
 * - All paths from the hierarchy root to one or more focus codes.
 * - Immediate children of every node in path to root
 * - Indicator to show whether any unexpanded node can be further expanded
 * </pre>
 *
 * This example accepts two parameters...
 * The first parameter is required, and must contain at least one code
 * in a comma-delimited list. A tree is produced for each code. Time to
 * produce the tree for each code is printed in milliseconds. In order
 * to factor out costs of startup and shutdown, resolving multiple
 * codes may offer a better overall estimate performance.
 *
 * The second parameter is optional, and can indicate a hierarchy ID to
 * navigate when resolving child nodes. If not provided, "is_a" is
 * assumed.
 */
public class TreeUtils {
    private static Logger _logger = Logger.getLogger(TreeUtils.class);
    LocalNameList noopList_ = Constructors.createLocalNameList("_noop_");

    public TreeUtils() {

    }

    /**
     * Prints the tree for an individual code.
     */

    public HashMap getTreePathData(String scheme, String version, String hierarchyID, String code)
            throws LBException {
        LexBIGService lbsvc = RemoteServerUtil.createLexBIGService();
        LexBIGServiceConvenienceMethods lbscm = (LexBIGServiceConvenienceMethods) lbsvc
                .getGenericExtension("LexBIGServiceConvenienceMethods");
        lbscm.setLexBIGService(lbsvc);
        if (hierarchyID == null)
            hierarchyID = "is_a";
        CodingSchemeVersionOrTag csvt = new CodingSchemeVersionOrTag();
        if (version != null)
            csvt.setVersion(version);
        SupportedHierarchy hierarchyDefn = getSupportedHierarchy(lbsvc, scheme, csvt, hierarchyID);
        return getTreePathData(lbsvc, lbscm, scheme, csvt, hierarchyDefn, code);
    }

    public HashMap getTreePathData(LexBIGService lbsvc, LexBIGServiceConvenienceMethods lbscm, String scheme,
            CodingSchemeVersionOrTag csvt, SupportedHierarchy hierarchyDefn, String focusCode) throws LBException {
        HashMap hmap = new HashMap();
        TreeItem ti = new TreeItem("<Root>", "Root node");
        long ms = System.currentTimeMillis();
        int pathsResolved = 0;
        try {

            // Resolve 'is_a' hierarchy info. This example will
            // need to make some calls outside of what is covered
            // by existing convenience methods, but we use the
            // registered hierarchy to prevent need to hard code
            // relationship and direction info used on lookup ...
            String hierarchyID = hierarchyDefn.getLocalId();
            String[] associationsToNavigate = hierarchyDefn.getAssociationIds();
            boolean associationsNavigatedFwd = hierarchyDefn.getIsForwardNavigable();

            // Identify the set of all codes on path from root
            // to the focus code ...
            Map<String, EntityDescription> codesToDescriptions = new HashMap<String, EntityDescription>();
            AssociationList pathsFromRoot = getPathsFromRoot(lbsvc, lbscm, scheme, csvt, hierarchyID, focusCode,
                    codesToDescriptions);

            // Typically there will be one path, but handle multiple just in
            // case.  Each path from root provides a 'backbone', from focus
            // code to root, for additional nodes to hang off of in our
            // printout. For every backbone node, one level of children is
            // printed, along with an indication of whether those nodes can
            // be expanded.
            for (Iterator<Association> paths = pathsFromRoot.iterateAssociation(); paths.hasNext();) {
                addPathFromRoot(ti, lbsvc, lbscm, scheme, csvt, paths.next(), associationsToNavigate,
                        associationsNavigatedFwd, codesToDescriptions);
                pathsResolved++;
            }

        } finally {
            _logger.debug("Run time (milliseconds): " + (System.currentTimeMillis() - ms) + " to resolve "
                    + pathsResolved + " paths from root.");
        }

        hmap.put(focusCode, ti);

        // Print the result ..
        return hmap;

    }

    public void run(String scheme, String version, String hierarchyId, String focusCode) throws LBException {
        HashMap hmap = getTreePathData(scheme, version, hierarchyId, focusCode);
        Set keyset = hmap.keySet();
        Object[] objs = keyset.toArray();
        String code = (String) objs[0];
        TreeItem ti = (TreeItem) hmap.get(code);
        printTree(ti, focusCode, 0);
    }

    public void printTree(HashMap hmap) {
        Object[] objs = hmap.keySet().toArray();
        String code = (String) objs[0];
        TreeItem ti = (TreeItem) hmap.get(code);
        printTree(ti, code, 0);
    }

    /**
     * The given path represents a multi-tier association with associated
     * concepts and targets.  This method expands the association content
     * and adds results to the given tree item, recursing as necessary to
     * process each level in the path.
     * <p>
     * Nodes in the association acts as the backbone for the display.
     * For each backbone node, additional children are resolved one level
     * deep along with an indication of further expandability.
     */
    protected void addPathFromRoot(TreeItem ti, LexBIGService lbsvc, LexBIGServiceConvenienceMethods lbscm,
            String scheme, CodingSchemeVersionOrTag csvt, Association path, String[] associationsToNavigate,
            boolean associationsNavigatedFwd, Map<String, EntityDescription> codesToDescriptions)
            throws LBException {

        // First, add the branch point from the path ...
        ConceptReference branchRoot = path.getAssociationReference();
        String branchCode = branchRoot.getConceptCode();
        String branchCodeDescription = codesToDescriptions.containsKey(branchCode)
                ? codesToDescriptions.get(branchCode).getContent()
                : getCodeDescription(lbsvc, scheme, csvt, branchCode);

        TreeItem branchPoint = new TreeItem(branchCode, branchCodeDescription);
        String branchNavText = getDirectionalLabel(lbscm, scheme, csvt, path, associationsNavigatedFwd);

        // Now process elements in the branch ...
        AssociatedConceptList concepts = path.getAssociatedConcepts();
        for (int i = 0; i < concepts.getAssociatedConceptCount(); i++) {

            // Add all immediate leafs in the branch, and indication of
            // sub-nodes. Do not process codes already in the backbone here;
            // they will be printed in the recursive call ...
            AssociatedConcept concept = concepts.getAssociatedConcept(i);
            String code = concept.getConceptCode();
            TreeItem branchItem = new TreeItem(code, getCodeDescription(concept));
            branchPoint.addChild(branchNavText, branchItem);

            addChildren(branchItem, lbsvc, lbscm, scheme, csvt, code, codesToDescriptions.keySet(),
                    associationsToNavigate, associationsNavigatedFwd);

            // Recurse to process the remainder of the backbone ...
            AssociationList nextLevel = concept.getSourceOf();
            if (nextLevel != null) {
                if (nextLevel.getAssociationCount() != 0) {
                    // More levels left to process ...
                    for (int j = 0; j < nextLevel.getAssociationCount(); j++)
                        addPathFromRoot(branchPoint, lbsvc, lbscm, scheme, csvt, nextLevel.getAssociation(j),
                                associationsToNavigate, associationsNavigatedFwd, codesToDescriptions);
                } else {
                    // End of the line ...
                    // Always add immediate children ot the focus code.
                    addChildren(branchItem, lbsvc, lbscm, scheme, csvt, concept.getConceptCode(),
                            Collections.EMPTY_SET, associationsToNavigate, associationsNavigatedFwd);
                }
            }
        }

        // Add the populated tree item to those tracked from root.
        ti.addChild(branchNavText, branchPoint);
    }

    /**
     * Populate child nodes for a single branch of the tree,
     * and indicates whether further expansion (to grandchildren)
     * is possible.
     */
    protected void addChildren(TreeItem ti, LexBIGService lbsvc, LexBIGServiceConvenienceMethods lbscm,
            String scheme, CodingSchemeVersionOrTag csvt, String branchRootCode, Set<String> codesToExclude,
            String[] associationsToNavigate, boolean associationsNavigatedFwd) throws LBException {

        // Resolve the next branch, representing children of the given
        // code, navigated according to the provided relationship and
        // direction.  Resolve the children as a code graph, looking 2
        // levels deep but leaving the final level unresolved.
        CodedNodeGraph cng = lbsvc.getNodeGraph(scheme, csvt, null);
        ConceptReference focus = Constructors.createConceptReference(branchRootCode, scheme);
        cng = cng.restrictToAssociations(Constructors.createNameAndValueList(associationsToNavigate), null);
        /*
                ResolvedConceptReferenceList branch = cng.resolveAsList(focus, associationsNavigatedFwd,
            !associationsNavigatedFwd, -1, 2, noopList_, null, null, null, -1, true);
        */
        // to be reversed after loading the patch
        ResolvedConceptReferenceList branch = cng.resolveAsList(focus, associationsNavigatedFwd,
                !associationsNavigatedFwd, -1, 2, noopList_, null, null, null, -1, false);

        // The resolved branch will be represented by the first node in
        // the resolved list.  The node will be subdivided by source or
        // target associations (depending on direction). The associated
        // nodes define the children.
        for (Iterator<ResolvedConceptReference> nodes = branch.iterateResolvedConceptReference(); nodes
                .hasNext();) {
            ResolvedConceptReference node = nodes.next();
            AssociationList childAssociationList = associationsNavigatedFwd ? node.getSourceOf()
                    : node.getTargetOf();

            // Process each association defining children ...
            for (Iterator<Association> pathsToChildren = childAssociationList.iterateAssociation(); pathsToChildren
                    .hasNext();) {
                Association child = pathsToChildren.next();
                String childNavText = getDirectionalLabel(lbscm, scheme, csvt, child, associationsNavigatedFwd);

                // Each association may have multiple children ...
                AssociatedConceptList branchItemList = child.getAssociatedConcepts();
                for (Iterator<AssociatedConcept> branchNodes = branchItemList
                        .iterateAssociatedConcept(); branchNodes.hasNext();) {
                    AssociatedConcept branchItemNode = branchNodes.next();
                    String branchItemCode = branchItemNode.getConceptCode();

                    // Add here if not in the list of excluded codes.
                    // This is also where we look to see if another level
                    // was indicated to be available.  If so, mark the
                    // entry with a '+' to indicate it can be expanded.
                    if (!codesToExclude.contains(branchItemCode)) {
                        TreeItem childItem = new TreeItem(branchItemCode, getCodeDescription(branchItemNode));
                        AssociationList grandchildBranch = associationsNavigatedFwd ? branchItemNode.getSourceOf()
                                : branchItemNode.getTargetOf();
                        if (grandchildBranch != null)
                            childItem.expandable = true;
                        ti.addChild(childNavText, childItem);
                    }
                }
            }
        }
    }

    /**
     * Prints the given tree item, recursing through all branches.
     * @param ti
     */

    protected void printTree(TreeItem ti, String focusCode, int depth) {
        StringBuffer indent = new StringBuffer();
        for (int i = 0; i < depth * 2; i++)
            indent.append("| ");

        StringBuffer codeAndText = new StringBuffer(indent).append(focusCode.equals(ti.code) ? ">>>>" : "")
                .append(ti.code).append(':')
                .append(ti.text.length() > 64 ? ti.text.substring(0, 62) + "..." : ti.text)
                .append(ti.expandable ? " [+]" : "");
        _logger.debug(codeAndText.toString());

        indent.append("| ");
        for (String association : ti.assocToChildMap.keySet()) {
            _logger.debug(indent.toString() + association);
            List<TreeItem> children = ti.assocToChildMap.get(association);
            Collections.sort(children);
            for (TreeItem childItem : children)
                printTree(childItem, focusCode, depth + 1);
        }
    }

    ///////////////////////////////////////////////////////
    // Helper Methods
    ///////////////////////////////////////////////////////

    /**
     * Returns the entity description for the given code.
     */
    protected String getCodeDescription(LexBIGService lbsvc, String scheme, CodingSchemeVersionOrTag csvt,
            String code) throws LBException {

        CodedNodeSet cns = lbsvc.getCodingSchemeConcepts(scheme, csvt);
        cns = cns.restrictToCodes(Constructors.createConceptReferenceList(code, scheme));
        ResolvedConceptReferenceList rcrl = cns.resolveToList(null, noopList_, null, 1);
        if (rcrl.getResolvedConceptReferenceCount() > 0) {
            EntityDescription desc = rcrl.getResolvedConceptReference(0).getEntityDescription();
            if (desc != null)
                return desc.getContent();
        }
        return "<Not assigned>";
    }

    /**
     * Returns the entity description for the given resolved concept reference.
     */
    protected String getCodeDescription(ResolvedConceptReference ref) throws LBException {
        EntityDescription desc = ref.getEntityDescription();
        if (desc != null)
            return desc.getContent();
        return "<Not assigned>";
    }

    public static boolean isBlank(String str) {
        if ((str == null) || str.matches("^\\s*$")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns the label to display for the given association and directional
     * indicator.
     */
    protected String getDirectionalLabel(LexBIGServiceConvenienceMethods lbscm, String scheme,
            CodingSchemeVersionOrTag csvt, Association assoc, boolean navigatedFwd) throws LBException {

        String assocLabel = navigatedFwd ? lbscm.getAssociationForwardName(assoc.getAssociationName(), scheme, csvt)
                : lbscm.getAssociationReverseName(assoc.getAssociationName(), scheme, csvt);
        //if (StringUtils.isBlank(assocLabel))
        if (isBlank(assocLabel))
            assocLabel = (navigatedFwd ? "" : "[Inverse]") + assoc.getAssociationName();
        return assocLabel;
    }

    /**
     * Resolves one or more paths from the hierarchy root to the given code
     * through a list of connected associations defined by the hierarchy.
     */
    protected AssociationList getPathsFromRoot(LexBIGService lbsvc, LexBIGServiceConvenienceMethods lbscm,
            String scheme, CodingSchemeVersionOrTag csvt, String hierarchyID, String focusCode,
            Map<String, EntityDescription> codesToDescriptions) throws LBException {

        // Get paths from the focus code to the root from the
        // convenience method.  All paths are resolved.  If only
        // one path is required, it would be possible to use
        // HierarchyPathResolveOption.ONE to reduce processing
        // and improve overall performance.
        AssociationList pathToRoot = lbscm.getHierarchyPathToRoot(scheme, csvt, null, focusCode, false,
                HierarchyPathResolveOption.ALL, null);

        // But for purposes of this example we need to display info
        // in order coming from root direction. Process the paths to root
        // recursively to reverse the order for processing ...
        AssociationList pathFromRoot = new AssociationList();
        for (int i = pathToRoot.getAssociationCount() - 1; i >= 0; i--)
            reverseAssoc(lbsvc, lbscm, scheme, csvt, pathToRoot.getAssociation(i), pathFromRoot,
                    codesToDescriptions);

        return pathFromRoot;
    }

    /**
     * Returns a description of the hierarchy defined by the given coding
     * scheme and matching the specified ID.
     */
    protected static SupportedHierarchy getSupportedHierarchy(LexBIGService lbsvc, String scheme,
            CodingSchemeVersionOrTag csvt, String hierarchyID) throws LBException {

        CodingScheme cs = lbsvc.resolveCodingScheme(scheme, csvt);
        if (cs == null) {
            throw new LBResourceUnavailableException("Coding scheme not found: " + scheme);
        }
        for (SupportedHierarchy h : cs.getMappings().getSupportedHierarchy())
            if (h.getLocalId().equals(hierarchyID))
                return h;
        throw new LBResourceUnavailableException("Hierarchy not defined: " + hierarchyID);
    }

    /**
     * Recursive call to reverse order of the given association list, adding
     * results to the given list. In context of this program we use this
     * technique to determine the path from root, starting from the path to root
     * provided by the standard convenience method.
     */
    protected AssociationList reverseAssoc(LexBIGService lbsvc, LexBIGServiceConvenienceMethods lbscm,
            String scheme, CodingSchemeVersionOrTag csvt, Association assoc, AssociationList addTo,
            Map<String, EntityDescription> codeToEntityDescriptionMap) throws LBException {

        ConceptReference acRef = assoc.getAssociationReference();
        AssociatedConcept acFromRef = new AssociatedConcept();
        acFromRef.setCodingScheme(acRef.getCodingScheme());
        acFromRef.setConceptCode(acRef.getConceptCode());
        AssociationList acSources = new AssociationList();
        acFromRef.setIsNavigable(Boolean.TRUE);
        acFromRef.setSourceOf(acSources);

        // Use cached description if available (should be cached
        // for all but original root) ...
        if (codeToEntityDescriptionMap.containsKey(acRef.getConceptCode()))
            acFromRef.setEntityDescription(codeToEntityDescriptionMap.get(acRef.getConceptCode()));
        // Otherwise retrieve on demand ...
        else
            acFromRef.setEntityDescription(Constructors
                    .createEntityDescription(getCodeDescription(lbsvc, scheme, csvt, acRef.getConceptCode())));

        AssociatedConceptList acl = assoc.getAssociatedConcepts();
        for (AssociatedConcept ac : acl.getAssociatedConcept()) {
            // Create reverse association (same non-directional name)
            Association rAssoc = new Association();
            rAssoc.setAssociationName(assoc.getAssociationName());

            // On reverse, old associated concept is new reference point.
            ConceptReference ref = new ConceptReference();
            ref.setCodingScheme(ac.getCodingScheme());
            ref.setConceptCode(ac.getConceptCode());
            rAssoc.setAssociationReference(ref);

            // And old reference is new associated concept.
            AssociatedConceptList rAcl = new AssociatedConceptList();
            rAcl.addAssociatedConcept(acFromRef);
            rAssoc.setAssociatedConcepts(rAcl);

            // Set reverse directional name, if available.
            String dirName = assoc.getDirectionalName();
            if (dirName != null)
                try {
                    rAssoc.setDirectionalName(lbscm.isForwardName(scheme, csvt, dirName)
                            ? lbscm.getAssociationReverseName(assoc.getAssociationName(), scheme, csvt)
                            : lbscm.getAssociationReverseName(assoc.getAssociationName(), scheme, csvt));
                } catch (LBException e) {
                }

            // Save code desc for future reference when setting up
            // concept references in recursive calls ...
            codeToEntityDescriptionMap.put(ac.getConceptCode(), ac.getEntityDescription());

            AssociationList sourceOf = ac.getSourceOf();
            if (sourceOf != null)
                for (Association sourceAssoc : sourceOf.getAssociation()) {
                    AssociationList pos = reverseAssoc(lbsvc, lbscm, scheme, csvt, sourceAssoc, addTo,
                            codeToEntityDescriptionMap);
                    pos.addAssociation(rAssoc);
                }
            else
                addTo.addAssociation(rAssoc);
        }
        return acSources;
    }

    ///////////////////////////////////////////////////////
    // Helper classes
    ///////////////////////////////////////////////////////

    /**
     * Inner class to hold tree items for printout.
     */
    public class TreeItem implements Comparable<TreeItem> {
        public String code = null;
        public String text = null;
        public boolean expandable = false;
        public Map<String, List<TreeItem>> assocToChildMap = new TreeMap<String, List<TreeItem>>();

        public boolean equals(Object o) {
            return o instanceof TreeItem && code.compareTo(((TreeItem) o).code) == 0;
        }

        public int compareTo(TreeItem ti) {
            String c1 = code;
            String c2 = ti.code;
            if (c1.startsWith("@"))
                return 1;
            if (c2.startsWith("@"))
                return -1;
            return c1.compareTo(c2);
        }

        public TreeItem(String code, String text) {
            super();
            this.code = code;
            this.text = text;
        }

        public void addAll(String assocText, List<TreeItem> children) {
            for (TreeItem item : children)
                addChild(assocText, item);
        }

        public void addChild(String assocText, TreeItem child) {
            List<TreeItem> children = assocToChildMap.get(assocText);
            if (children == null) {
                children = new ArrayList<TreeItem>();
                assocToChildMap.put(assocText, children);
            }
            int i;
            if ((i = children.indexOf(child)) >= 0) {
                TreeItem existingTreeItem = children.get(i);
                for (String assoc : child.assocToChildMap.keySet()) {
                    List<TreeItem> toAdd = child.assocToChildMap.get(assoc);
                    if (!toAdd.isEmpty()) {
                        existingTreeItem.addAll(assoc, toAdd);
                        existingTreeItem.expandable = false;
                    }
                }
            } else
                children.add(child);
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public Vector getHierarchyAssociationId(String scheme, String version) {

        Vector association_vec = new Vector();
        try {
            LexBIGService lbSvc = RemoteServerUtil.createLexBIGService();

            // Will handle secured ontologies later.
            CodingSchemeVersionOrTag versionOrTag = new CodingSchemeVersionOrTag();
            versionOrTag.setVersion(version);
            CodingScheme cs = lbSvc.resolveCodingScheme(scheme, versionOrTag);
            Mappings mappings = cs.getMappings();
            SupportedHierarchy[] hierarchies = mappings.getSupportedHierarchy();
            java.lang.String[] ids = hierarchies[0].getAssociationIds();

            for (int i = 0; i < ids.length; i++) {
                if (!association_vec.contains(ids[i])) {
                    association_vec.add(ids[i]);
                }
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return association_vec;
    }

    public HashMap getSubconcepts(String scheme, String version, String code) {
        //String assocName = "hasSubtype";
        String hierarchicalAssoName = "hasSubtype";
        Vector hierarchicalAssoName_vec = getHierarchyAssociationId(scheme, version);
        if (hierarchicalAssoName_vec != null && hierarchicalAssoName_vec.size() > 0) {
            hierarchicalAssoName = (String) hierarchicalAssoName_vec.elementAt(0);
        }
        return getAssociationTargets(scheme, version, code, hierarchicalAssoName);
    }

    public HashMap getAssociationTargets(String scheme, String version, String code, String assocName) {
        HashMap hmap = new HashMap();
        TreeItem ti = null;
        long ms = System.currentTimeMillis();
        Util.StopWatch stopWatch = new Util.StopWatch(); //DYEE

        Set<String> codesToExclude = Collections.EMPTY_SET;

        CodingSchemeVersionOrTag csvt = new CodingSchemeVersionOrTag();
        if (version != null)
            csvt.setVersion(version);
        ResolvedConceptReferenceList matches = null;
        Vector v = new Vector();
        try {
            //EVSApplicationService lbSvc = new RemoteServerUtil().createLexBIGService();
            LexBIGService lbSvc = RemoteServerUtil.createLexBIGService();
            LexBIGServiceConvenienceMethods lbscm = (LexBIGServiceConvenienceMethods) lbSvc
                    .getGenericExtension("LexBIGServiceConvenienceMethods");
            lbscm.setLexBIGService(lbSvc);

            String name = getCodeDescription(lbSvc, scheme, csvt, code);
            ti = new TreeItem(code, name);
            ti.expandable = false;

            CodedNodeGraph cng = lbSvc.getNodeGraph(scheme, csvt, null);
            ConceptReference focus = Constructors.createConceptReference(code, scheme);
            cng = cng.restrictToAssociations(Constructors.createNameAndValueList(assocName), null);
            boolean associationsNavigatedFwd = true;
            /*
                     ResolvedConceptReferenceList branch = cng.resolveAsList(focus, associationsNavigatedFwd,
                           !associationsNavigatedFwd, -1, 2, noopList_, null, null, null, -1, true);
            */
            ResolvedConceptReferenceList branch = cng.resolveAsList(focus, associationsNavigatedFwd,
                    !associationsNavigatedFwd, -1, 2, noopList_, null, null, null, -1, false);

            for (Iterator<ResolvedConceptReference> nodes = branch.iterateResolvedConceptReference(); nodes
                    .hasNext();) {
                ResolvedConceptReference node = nodes.next();
                AssociationList childAssociationList = associationsNavigatedFwd ? node.getSourceOf()
                        : node.getTargetOf();

                // Process each association defining children ...
                for (Iterator<Association> pathsToChildren = childAssociationList
                        .iterateAssociation(); pathsToChildren.hasNext();) {
                    Association child = pathsToChildren.next();
                    String childNavText = getDirectionalLabel(lbscm, scheme, csvt, child, associationsNavigatedFwd);

                    // Each association may have multiple children ...
                    AssociatedConceptList branchItemList = child.getAssociatedConcepts();
                    for (Iterator<AssociatedConcept> branchNodes = branchItemList
                            .iterateAssociatedConcept(); branchNodes.hasNext();) {
                        AssociatedConcept branchItemNode = branchNodes.next();
                        String branchItemCode = branchItemNode.getConceptCode();

                        // Add here if not in the list of excluded codes.
                        // This is also where we look to see if another level
                        // was indicated to be available.  If so, mark the
                        // entry with a '+' to indicate it can be expanded.
                        if (!codesToExclude.contains(branchItemCode)) {
                            TreeItem childItem = new TreeItem(branchItemCode, getCodeDescription(branchItemNode));
                            ti.expandable = true;
                            AssociationList grandchildBranch = associationsNavigatedFwd
                                    ? branchItemNode.getSourceOf()
                                    : branchItemNode.getTargetOf();
                            if (grandchildBranch != null)
                                childItem.expandable = true;
                            ti.addChild(childNavText, childItem);
                        }
                    }
                }
            }
            hmap.put(code, ti);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        _logger.debug(
                "Run time (milliseconds) getSubconcepts: " + (System.currentTimeMillis() - ms) + " to resolve ");
        _logger.debug("DYEE: getSubconcepts: " + stopWatch.getResult() + " to resolve ");
        return hmap;
    }

    //Focus code: C7387 C26709

    public static void main(String[] args) {
        String url = "http://lexevsapi-dev.nci.nih.gov/lexevsapi42";
        url = "http://lexevsapi.nci.nih.gov/lexevsapi42";

        String scheme = "NCI Thesaurus";
        String version = null;//"08.06d";
        String code = "C26709";

        TreeUtils test = new TreeUtils();
        HashMap hmap = test.getSubconcepts(scheme, version, code);
        test.printTree(hmap);

        code = "C2910";
        hmap = test.getSubconcepts(scheme, version, code);
        test.printTree(hmap);

    }
}

/*
ResolvedConceptReferenceList resolveAsList(ConceptReference graphFocus,
                                       boolean resolveForward,
                                       boolean resolveBackward,
                                       int resolveCodedEntryDepth,
                                       int resolveAssociationDepth,
                                       LocalNameList propertyNames,
                                       CodedNodeSet.PropertyType[] propertyTypes,
                                       SortOptionList sortOptions,
                                       LocalNameList filterOptions,
                                       int maxToReturn,
                                       boolean keepLastAssociationLevelUnresolved)
    
Graph
   Restricted Graph
   ResolvedConceptReferenceList by ResolveAsList
       ResolvedConceptReference (node)
            AssociationList
                  Association
                        AssociatedConcept
                                TreeItem
    
    
*/