/**
  * Overlay a single disease reaction onto a normal reaction.
  *
  * @param normalReaction
  * @param diseaseReaction
  * @param overlaidObjects
  */
 private void overlayDiseaseReaction(HyperEdge normalReaction, GKInstance diseaseReaction)
     throws Exception {
   // Make a copy of the HyperEdge for future process that is related to Vertex and JSON generation
   HyperEdge reactionCopy = normalReaction.shallowCopy();
   reactionCopy.setReactomeId(diseaseReaction.getDBID());
   reactionCopy.setDisplayName(diseaseReaction.getDisplayName());
   reactionCopy.setLineColor(DefaultRenderConstants.DEFAULT_DISEASE_BACKGROUND);
   displayedObject.addComponent(reactionCopy);
   overlaidObjects.add(reactionCopy);
   // Want to handle inputs, outputs and catalysts since regulators can
   // be ignored
   List<Node> nodes = new ArrayList<Node>();
   nodes.addAll(normalReaction.getInputNodes());
   nodes.addAll(normalReaction.getOutputNodes());
   nodes.addAll(normalReaction.getHelperNodes());
   // List objects not listed in the disease reaction as crossed objects
   Set<GKInstance> participants = InstanceUtilities.getReactionParticipants(diseaseReaction);
   Set<Long> diseaseIds = new HashSet<Long>();
   for (GKInstance participant : participants) {
     diseaseIds.add(participant.getDBID());
     if (participant.getSchemClass().isValidAttribute(ReactomeJavaConstants.hasMember)) {
       List<GKInstance> list = participant.getAttributeValuesList(ReactomeJavaConstants.hasMember);
       if (list != null && list.size() > 0) {
         for (GKInstance inst : list) diseaseIds.add(inst.getDBID());
       }
     }
     if (participant.getSchemClass().isValidAttribute(ReactomeJavaConstants.hasCandidate)) {
       List<GKInstance> list =
           participant.getAttributeValuesList(ReactomeJavaConstants.hasCandidate);
       if (list != null && list.size() > 0) {
         for (GKInstance inst : list) diseaseIds.add(inst.getDBID());
       }
     }
   }
   Set<GKInstance> lofInstances = new HashSet<GKInstance>();
   Map<Node, GKInstance> normalToDiseaseEntity =
       mapMutatedToNormalNodes(diseaseReaction, normalReaction, nodes, lofInstances);
   for (Node node : nodes) {
     if (!diseaseIds.contains(node.getReactomeId())) {
       // Check if it should be mapped to a normal entity
       GKInstance diseaseEntity = normalToDiseaseEntity.get(node);
       if (diseaseEntity == null) crossedObjects.add(node); // Just crossed out
       else {
         Node diseaseNode =
             replaceNormalNode(node, diseaseEntity, contains(diseaseEntity, lofInstances));
         if (diseaseNode == null) continue; // Just in case
         // Re-link to diseaseNode
         ConnectInfo connectInfo = reactionCopy.getConnectInfo();
         List<?> widgets = connectInfo.getConnectWidgets();
         for (Object obj : widgets) {
           ConnectWidget widget = (ConnectWidget) obj;
           if (widget.getConnectedNode() == node) widget.replaceConnectedNode(diseaseNode);
         }
       }
     } else overlaidObjects.add(node);
   }
 }
 /**
  * Map mutated nodes in a disease reaction to displayed nodes in a normal reaction. The mapping is
  * based on sharing of referenceEntity. This may not be reliable!
  *
  * @param diseaseReaction
  * @param nodes
  * @throws InvalidAttributeException
  * @throws Exception @TODO: add a new attribute normalEntity in the PhysicalEntity class.
  */
 private Map<Node, GKInstance> mapMutatedToNormalNodes(
     GKInstance diseaseReaction,
     HyperEdge normalReaction,
     List<Node> nodes,
     Set<GKInstance> lofInstances)
     throws InvalidAttributeException, Exception {
   List<GKInstance> efs =
       diseaseReaction.getAttributeValuesList(ReactomeJavaConstants.entityFunctionalStatus);
   // Map mutated entities to normal entities via ReferenceGeneProduct
   Map<Node, GKInstance> normalToDiseaseEntity = new HashMap<Node, GKInstance>();
   if (efs == null) return normalToDiseaseEntity;
   for (GKInstance ef : efs) {
     GKInstance pe = (GKInstance) ef.getAttributeValue(ReactomeJavaConstants.physicalEntity);
     if (pe != null) {
       Set<GKInstance> refEntities = getReferenceEntity(pe);
       // want to find the matched node
       for (Node node : nodes) {
         if (node.getReactomeId() == null) continue;
         GKInstance nodeInst = adaptor.fetchInstance(node.getReactomeId());
         Set<GKInstance> nodeRefEntities = getReferenceEntity(nodeInst);
         nodeRefEntities.retainAll(refEntities);
         if (nodeRefEntities.size() > 0) {
           normalToDiseaseEntity.put(node, pe);
           if (isLOFEntity(ef)) {
             lofInstances.add(pe);
           }
           break;
         }
       }
     }
   }
   // TODO: May have to consider stoichiometries too!!!
   // In gain of functions, some inputs or outputs may not be used by normal reactions.
   // This method will do its best to map these reaction participants
   Collection<GKInstance> mapped = normalToDiseaseEntity.values();
   // First check inputs
   List<GKInstance> diseaseInputs =
       diseaseReaction.getAttributeValuesList(ReactomeJavaConstants.input);
   List<Node> normalNodes = normalReaction.getInputNodes();
   randomlyMapDiseaseNodesToNormalNodes(normalToDiseaseEntity, mapped, diseaseInputs, normalNodes);
   // Check outputs
   List<GKInstance> diseaseOutputs =
       diseaseReaction.getAttributeValuesList(ReactomeJavaConstants.output);
   normalNodes = normalReaction.getOutputNodes();
   randomlyMapDiseaseNodesToNormalNodes(
       normalToDiseaseEntity, mapped, diseaseOutputs, normalNodes);
   // Check helpers
   GKInstance ca =
       (GKInstance) diseaseReaction.getAttributeValue(ReactomeJavaConstants.catalystActivity);
   if (ca != null) {
     List<GKInstance> catalysts = ca.getAttributeValuesList(ReactomeJavaConstants.physicalEntity);
     normalNodes = normalReaction.getHelperNodes();
     randomlyMapDiseaseNodesToNormalNodes(normalToDiseaseEntity, mapped, catalysts, normalNodes);
   }
   return normalToDiseaseEntity;
 }