/**
   * Does breadth first search and returns the Artifact corresponding to the shortest path (just
   * before the end)
   *
   * @param startArt
   * @param endArt
   * @return - Artifact corresponding to endArt-1
   */
  private Artifact breadthFirstSearch(
      Artifact startArt, Artifact endArt, int maxDepth, IProgressMonitor monitor) {
    /*
     * Pseudocode:
     * 1] before loop, if source!=target put in queue, else return null
     * 2] while queue !empty, i.e. the loop:
     * 3] pop from queue, and get all connected, if !=target queue
     * 4] else: return current, i.e. the step before the target
     */
    Set<URI> filteredPreds = getFilteredPredsForAutoBrowse();

    if (startArt.equals(endArt)) {
      logger.error("breadthFirstSearch should not be called with start==end", new Exception());
      return null;
    }

    // note: visible artifacts don't really matter for breadth-first search
    //  (and in reality for autobrowse)
    Set<Artifact> visitedArtifact = new HashSet<Artifact>(1000);

    List<Artifact> searchQueue = new ArrayList<Artifact>(100);
    searchQueue.add(startArt);

    // use null as a marker to indicate the completion of one level of depth
    searchQueue.add(null);

    int currDepth = 0;

    while (true) {
      Artifact searchNode = searchQueue.remove(0);
      if (searchNode == null) {
        currDepth++;
        if (currDepth >= maxDepth) {
          logger.info("Graph not connected in depth: " + maxDepth);
          return null;
        }
        if (searchQueue.isEmpty()) {
          logger.info("Graph not connected");
          return null;
        }
        monitor.worked(1);
        if (monitor.isCanceled()) return null;
        searchQueue.add(null);
        continue;
      }
      for (Artifact child : searchNode.queryConnectedArtifactsList(getRepo(), filteredPreds)) {
        if (child.equals(endArt)) return searchNode;
        if (visitedArtifact.contains(child)) continue;

        searchQueue.add(child);
        visitedArtifact.add(child);
      }
    }
  }
  /**
   * Ensures that all elements on a valid path (given by rel) are shown Note: src.rel.tgt &&
   * src!=tgt has to be true
   *
   * @param rel - join defining valid paths
   */
  public void showPathElements(JoinedRelType rel) {
    Artifact curArt = getArtifact().getArt();

    if (!isActive()) {
      log("not active");
      return;
    }

    logBeg("showPathElements");

    ReloController rc = getRootController();
    for (ArtifactEditPart visEP : rc.getVisibleNonDerivedArtifactEditParts()) {
      Artifact visArt = visEP.getArtifact().getArt();
      if (curArt.equals(visArt)) continue;
      // ObjectPair examiningPair = new ObjectPair(this, visEP);

      for (List<Artifact> resultSetVar : rel.getPaths(getRepo(), curArt, visArt)) {
        for (Artifact resultArt : resultSetVar) {
          rc.createOrFindArtifactEditPart(resultArt);
        }
      }

      // flip order of pair (everything else is identical
      for (List<Artifact> resultSetVar : rel.getPaths(getRepo(), visArt, curArt)) {
        for (Artifact resultArt : resultSetVar) {
          rc.createOrFindArtifactEditPart(resultArt);
        }
      }
    }
    logEnd();
  }
  protected void autoBrowse(
      Artifact startArt, Artifact endArt, int maxDepth, IProgressMonitor monitor) {
    logger.info("autoBrowsing: " + startArt + " // " + endArt);
    /*
      Pseudocode:
      1] let ptr=end
      2] ptrM1 = ptr-1
      3] get rel: ptr-?->ptrM1
      4] draw ptr-[rel]->ptrM1
      5] repeat till ptr=start
    */
    // draw from end-1
    Artifact ptr = endArt;
    while (!ptr.equals(startArt)) {
      logger.info("autoBrowsing - ptr: " + ptr);
      Artifact ptrM1 = breadthFirstSearch(startArt, ptr, maxDepth, monitor);
      if (ptrM1 == null) return;

      // draw links
      final Artifact currPtr = ptr;
      final Artifact currPtrM1 = ptrM1;
      PlatformUI.getWorkbench()
          .getDisplay()
          .asyncExec(
              new Runnable() {
                public void run() {
                  try {
                    URI rel;

                    // forward
                    rel =
                        getRepo()
                            .getStatement(currPtrM1.elementRes, (URI) null, currPtr.elementRes)
                            .getPredicate();
                    if (rel != null) logger.info(currPtrM1 + " --" + rel + "--> " + currPtr);
                    if (rel != null && !rel.equals(RSECore.contains))
                      getRootController().addRel(currPtrM1, rel, currPtr);

                    // reverse
                    rel =
                        getRepo()
                            .getStatement(currPtr.elementRes, (URI) null, currPtrM1.elementRes)
                            .getPredicate();
                    if (rel != null) logger.info(currPtr + " --" + rel + "--> " + currPtrM1);
                    if (rel != null && !rel.equals(RSECore.contains))
                      getRootController().addRel(currPtr, rel, currPtrM1);
                  } catch (Exception e) {
                    logger.error("Unexpected error while creating drawing figures", e);
                  }
                }
              });

      ptr = ptrM1;
    }
  }