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;
 }
 public void randomlyMapDiseaseNodesToNormalNodes(
     Map<Node, GKInstance> normalToDiseaseEntity,
     Collection<GKInstance> mapped,
     List<GKInstance> diseaseInputs,
     List<Node> normalNodes) {
   if (diseaseInputs != null && diseaseInputs.size() > 0) {
     normalNodes.removeAll(normalToDiseaseEntity.keySet());
     if (normalNodes.size() > 0) {
       for (GKInstance input : diseaseInputs) {
         // if (mapped.contains(input))
         if (contains(input, mapped)) continue; // it has been mapped already
         // Check if it has been used based on DB_ID
         Node matched = null;
         for (Node node : normalNodes) {
           if (node.getReactomeId().equals(input.getDBID())) {
             matched = node;
             break;
           }
         }
         if (matched == null)
           // Just pick up the first node
           matched = normalNodes.get(0);
         normalToDiseaseEntity.put(matched, input);
         normalNodes.remove(matched);
         if (normalNodes.size() == 0) break;
       }
     }
   }
 }
 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;
 }
 /**
  * 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;
 }
 /**
  * If a FlowLine connects two normal object, it should be a normal object. Otherwise, it should be
  * a disease object.
  *
  * @param flowLine
  * @param ids
  * @return
  */
 private boolean isFlowLineRelatedToSet(FlowLine flowLine, Set<Long> ids) {
   Node input = flowLine.getInputNode(0);
   Node output = flowLine.getOutputNode(0);
   if (input != null
       && (ids.contains(input.getReactomeId()) || input instanceof ProcessNode)
       && // ProcessNode should be used in normal pathway only
       output != null
       && (ids.contains(output.getReactomeId()) || output instanceof ProcessNode)) return true;
   //        if (output != null && normalIds.contains(output.getReactomeId()))
   //            return true;
   return false;
 }
 private void drawCrosses(Graphics g, List<Node> nodes, Rectangle clip) {
   Graphics2D g2 = (Graphics2D) g;
   Stroke oldStroke = g2.getStroke();
   Stroke stroke = new BasicStroke(DefaultRenderConstants.DEFAULT_RED_CROSS_WIDTH);
   g2.setStroke(stroke);
   Paint oldPaint = g2.getPaint();
   g2.setColor(Color.RED);
   for (Node node : nodes) {
     Rectangle r = node.getBounds();
     if (!clip.intersects(r)) continue;
     g2.drawLine(r.x, r.y, r.x + r.width, r.y + r.height);
     g2.drawLine(r.x, r.y + r.height, r.x + r.width, r.y);
   }
   g2.setStroke(oldStroke);
   g2.setPaint(oldPaint);
 }
 private Node replaceNormalNode(
     Node normalNode, GKInstance diseaseEntity, Boolean needDashedBorder) {
   Node diseaseNode = normalToDiseaseNode.get(normalNode);
   if (diseaseNode != null) return diseaseNode;
   try {
     // If a node exists already, it should use
     for (Renderable r : diseaseComps) {
       if (diseaseEntity.getDBID().equals(r.getReactomeId()) && r instanceof Node) {
         // This is rather arbitrary: if two nodes are very close,
         // use the existing one.
         int dx = Math.abs(r.getPosition().x - normalNode.getPosition().x);
         int dy = Math.abs(r.getPosition().y - normalNode.getPosition().y);
         if (dx < 10 && dy < 10) {
           // We don't need to create a new Node if it exists already
           normalToDiseaseNode.put(normalNode, (Node) r);
           overlaidObjects.add(r); // Add it to overlaid object to cover edges
           return (Node) r;
         }
       }
     }
     diseaseNode = normalNode.getClass().newInstance();
     RenderUtility.copyRenderInfo(normalNode, diseaseNode);
     // The following should NOT be called since NodeAttachment is
     // related to disease entity only.
     // TODO: Need to support this. Currently it is not supported!!! See example
     // in PI3/AKT cancer pathway.
     // diseaseNode.setNodeAttachmentsLocally(node.getNodeAttachments());
     diseaseNode.setDisplayName(diseaseEntity.getDisplayName());
     diseaseNode.setReactomeId(diseaseEntity.getDBID());
     diseaseNode.invalidateBounds();
     diseaseNode.setRenderer(normalNode.getRenderer());
     diseaseNode.setLineColor(DefaultRenderConstants.DEFAULT_DISEASE_BACKGROUND);
     diseaseNode.setNeedDashedBorder(needDashedBorder);
     RenderUtility.hideCompartmentInNodeName(diseaseNode);
     overlaidObjects.add(diseaseNode);
     displayedObject.addComponent(diseaseNode);
     normalToDiseaseNode.put(normalNode, diseaseNode);
     return diseaseNode;
   } catch (Exception e) {
     e.printStackTrace();
   }
   return null;
 }
 private void checkLossOfFunctionNodes() {
   try {
     lofNodes = new ArrayList<Node>();
     List<Renderable> components = displayedObject.getComponents();
     if (components == null || components.size() == 0) return;
     for (Renderable r : components) {
       if (r instanceof Node || r.getReactomeId() == null) continue;
       if (!diseaseIds.contains(r.getReactomeId())) continue;
       GKInstance inst = adaptor.fetchInstance(r.getReactomeId());
       if (!inst.getSchemClass().isa(ReactomeJavaConstants.ReactionlikeEvent)
           || !inst.getSchemClass().isValidAttribute(ReactomeJavaConstants.entityFunctionalStatus))
         continue;
       List<GKInstance> efs =
           inst.getAttributeValuesList(ReactomeJavaConstants.entityFunctionalStatus);
       Set<GKInstance> lofPEs = new HashSet<GKInstance>();
       for (GKInstance ef : efs) {
         GKInstance pe = (GKInstance) ef.getAttributeValue(ReactomeJavaConstants.physicalEntity);
         if (isLOFEntity(ef)) lofPEs.add(pe);
       }
       List<Node> nodes = ((HyperEdge) r).getConnectedNodes();
       for (Node node : nodes) {
         if (node.getReactomeId() == null) continue;
         GKInstance nodeInst = adaptor.fetchInstance(node.getReactomeId());
         Set<GKInstance> nodeRefEntities = getReferenceEntity(nodeInst);
         for (GKInstance lofPE : lofPEs) {
           Set<GKInstance> lofRefEntities = getReferenceEntity(lofPE);
           lofRefEntities.retainAll(nodeRefEntities);
           if (lofRefEntities.size() > 0) {
             // A LOF node
             lofNodes.add(node);
             break;
           }
         }
       }
     }
   } catch (Exception e) {
     e.printStackTrace();
   }
 }
 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);
     }
   }
 }
  @Override
  public void paint(Graphics g) {
    // Clear the editor
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    // In case g is provided by other graphics context (e.g during exporting)
    g2.setFont(getFont());
    g2.setBackground(background);
    Dimension size = getPreferredSize();
    g2.clearRect(0, 0, size.width, size.height);
    // TODO: Delete the follow two statements. For test only
    //        g2.setPaint(Color.yellow);
    //        g2.fillRect(0, 0, size.width, size.height);
    // Enable zooming
    g2.scale(scaleX, scaleY);

    List comps = displayedObject.getComponents();
    // Draw nodes first
    if (comps == null || comps.size() == 0) return;
    java.util.List edges = new ArrayList();
    Rectangle clip = g.getClipBounds();
    // Draw Compartments first
    List<RenderableCompartment> compartments = drawCompartments(g, comps, null, clip);
    drawComponents(g, normalComps, clip);
    if (isForNormal) {
      // Replace the original components with new set of Renderable objects for further processing
      RenderablePathway pathway = (RenderablePathway) displayedObject;
      List<Renderable> components = pathway.getComponents();
      components.clear();
      components.addAll(normalComps);
      components.addAll(compartments); // Don't forget them
      return;
    }
    // Draw a transparent background: light grey
    Color color = new Color(204, 204, 204, 175);
    g2.setPaint(color);
    //        Dimension size = getPreferredSize();
    // Preferred size has been scaled. Need to scale it back
    g2.fillRect(0, 0, (int) (size.width / scaleX + 1.0d), (int) (size.height / scaleY + 1.0d));
    // Draw disease related objects
    for (Renderable r : diseaseComps) {
      if (lofNodes.contains(r)) ((Node) r).setNeedDashedBorder(true);
    }
    drawComponents(g, diseaseComps, clip);
    if (overlaidObjects.size() > 0)
      drawComponents(
          g,
          overlaidObjects,
          clip,
          true); // Want to draw edge first so that no validation is needed.
    if (crossedObjects.size() > 0) drawCrosses(g, crossedObjects, clip);
    RenderablePathway pathway = (RenderablePathway) displayedObject;
    pathway.setBgComponents(new ArrayList<Renderable>(normalComps));
    List<Renderable> fgComps = new ArrayList<Renderable>();
    fgComps.addAll(diseaseComps);
    fgComps.addAll(overlaidObjects);
    // Don't want to place compartments and pathway
    for (Iterator<Renderable> it = fgComps.iterator(); it.hasNext(); ) {
      Renderable r = it.next();
      if (r instanceof RenderableCompartment || r instanceof RenderablePathway) it.remove();
    }
    pathway.setFgComponents(fgComps);
  }