private boolean isDiseaseObject(Renderable r, Set<Long> diseaseIds) {
   if (r instanceof FlowLine) {
     FlowLine fl = (FlowLine) r;
     return isFlowLineRelatedToSet(fl, diseaseIds);
   }
   // Assume normal objects only: this is a very strong assumption
   if (r.getReactomeId() == null) return false;
   // A PE may be represented multiple times in a pathway diagram. Some of them
   // are linked to normal reactions, and some linked to disease reactions.
   // Have to check these cases.
   if (r instanceof HyperEdge) return diseaseIds.contains(r.getReactomeId());
   if (r instanceof Node) {
     Node node = (Node) r;
     List<HyperEdge> edges = node.getConnectedReactions();
     boolean isDisease = false;
     for (HyperEdge edge : edges) {
       if (edge instanceof FlowLine) {
         isDisease = isFlowLineRelatedToSet((FlowLine) edge, diseaseIds);
       } else if (edge.getReactomeId() != null && diseaseIds.contains(edge.getReactomeId())) {
         isDisease = true;
       }
       if (isDisease) return true;
     }
   }
   return false;
 }
 private boolean isNormalObject(Renderable r, Set<Long> normalIds) {
   if (r instanceof HyperEdge) {
     if (r instanceof FlowLine) return isFlowLineRelatedToSet((FlowLine) r, normalIds);
     // Most likely the following statements should not be reached. But place
     // here just in case.
     if (r.getReactomeId() == null) return true;
     if (normalIds.contains(r.getReactomeId())) return true;
   } else if (r instanceof Node) {
     if (r instanceof ProcessNode) return true; // Should be used only for normal pathway.
     if (r.getReactomeId() == null) return true;
     // Check if it is linked to another disease entities
     Node node = (Node) r;
     List<HyperEdge> edges = node.getConnectedReactions();
     boolean isNormal = false;
     for (HyperEdge edge : edges) {
       if (edge instanceof FlowLine) isNormal = isFlowLineRelatedToSet((FlowLine) edge, normalIds);
       if (isNormal) return true;
       if (edge.getReactomeId() != null && normalIds.contains(edge.getReactomeId())) return true;
       // Some special cases that use links
       if (!(edge instanceof EntitySetAndEntitySetLink)
           && !(edge instanceof EntitySetAndMemberLink)
           && edge.getReactomeId() == null) return true;
     }
   }
   return false;
 }
 /**
  * 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;
 }
 /**
  * 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);
   }
 }
 private void drawComponents(
     Graphics g, List<Renderable> comps, Rectangle clip, boolean drawEdgeFirst) {
   List<HyperEdge> edges = new ArrayList<HyperEdge>();
   for (Renderable obj : comps) {
     if (obj instanceof HyperEdge) edges.add((HyperEdge) obj);
   }
   if (drawEdgeFirst) {
     // Draw HyperEdges
     for (HyperEdge reaction : edges) {
       // Have to validate connect nodes first in case empty bounds
       List<Node> nodes = reaction.getConnectedNodes();
       for (Node node : nodes) node.validateBounds(g);
       reaction.validateConnectInfo();
       if (clip.intersects(reaction.getBounds())) reaction.render(g);
     }
   }
   // Draw complexes now
   drawComplexes(comps, clip, g);
   for (Renderable obj : comps) {
     if (obj instanceof RenderableCompartment
         || obj instanceof RenderableComplex
         || obj instanceof RenderablePathway) continue; // Escape it. It should be drawn earlier.
     if (obj instanceof Node) {
       Node node = (Node) obj;
       if (getHidePrivateNote() && (node instanceof Note) && ((Note) node).isPrivate()) continue;
       node.validateBounds(g);
       if (clip.intersects(node.getBounds())) node.render(g);
     }
   }
   if (!drawEdgeFirst) {
     // Draw HyperEdges
     for (HyperEdge reaction : edges) {
       reaction.validateConnectInfo();
       if (clip.intersects(reaction.getBounds())) reaction.render(g);
     }
   }
 }