/FileExtraction/Java_unseen/fergarrui_custom-bytecode-analyzer/src/main/java/net/nandgr/cba/callgraph/graph/InvocationGraphImpl.java
/*
 * Copyright (c) 2016-2017, Fernando Garcia
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.nandgr.cba.callgraph.graph;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import net.nandgr.cba.callgraph.model.Call;
import net.nandgr.cba.callgraph.model.MethodGraph;
import org.jgrapht.DirectedGraph;
import org.jgrapht.alg.CycleDetector;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class InvocationGraphImpl implements InvocationGraph<MethodGraph, Call> {

  private static final Logger logger = LoggerFactory.getLogger(InvocationGraphImpl.class);
  private final DirectedGraph<MethodGraph, DefaultEdge> directedGraph;

  public InvocationGraphImpl() {
    this.directedGraph = new DefaultDirectedGraph<>(DefaultEdge.class);
  }

  @Override
  public boolean contains(MethodGraph element) {
    return directedGraph.containsVertex(element);
  }

  @Override
  public void add(MethodGraph element, MethodGraph parent) {
    if (element.equals(parent)) {
      return;
    }
    directedGraph.addVertex(element);
    directedGraph.addVertex(parent);
    directedGraph.addEdge(parent, element);
    if (findCycles(element)) {
      directedGraph.removeEdge(parent, element);
    }
  }

  @Override
  public Collection<Call> pathsToParents(MethodGraph element) {
    Collection<Call> paths = new HashSet<>();
      logger.debug("Finding path to parents for: {} ", element);
    if (!directedGraph.containsVertex(element)) {
      logger.warn("Graph does not contain vertex: {} . Returning empty collection.", element);
      return paths;
    }
    Set<DefaultEdge> incomingEdges = directedGraph.incomingEdgesOf(element);
    if (incomingEdges.isEmpty()) {
      Call lonelyCall = new Call(element, null);
      paths.add(lonelyCall);
      return paths;
    }
    for (DefaultEdge incomingEdge : incomingEdges) {
      MethodGraph father = directedGraph.getEdgeSource(incomingEdge);
      logger.debug("Finding path to parents, element={}, father={} . About to find paths from father", element, father);
      Call call = new Call(father, element);
      paths.add(call);
      paths.addAll(pathsToParents(father));
    }
    return paths;
  }

  @Override
  public boolean findCycles(MethodGraph vertex) {
    CycleDetector<MethodGraph,DefaultEdge> cycleDetector = new CycleDetector(directedGraph);
    return cycleDetector.detectCyclesContainingVertex(vertex);
  }
}
