package org.lrg.outcode.visitors;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.lrg.outcode.IHindsight;
import org.lrg.outcode.builder.RelTypes;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
public class FeatureEnvy {
private ArrayList<Node> featureEnvys = new ArrayList<Node>();
public static ArrayList<String> uniqueFiles = new ArrayList<String>();
private boolean addInUniqueFiles;
private final class ConsumerImplementation implements Consumer<Relationship> {
private final Counter c;
private ArrayList<Node> currentClassAndAncestors;
private ConsumerImplementation(ArrayList<Node> currentClassAndAncestors, Counter c) {
this.currentClassAndAncestors = currentClassAndAncestors;
this.c = c;
}
@Override
public void accept(Relationship t) {
Node endNode = t.getEndNode();
if (t.isType(RelTypes.CALLS) && !endNode.hasProperty("get"))
return;
if (t.getEndNode() != null) {
Node accessedClass = parentNode(endNode);
if (currentClassAndAncestors.contains(accessedClass))
c.currentClassAccesses++;
else if (c.accessedClasses.size() > 0 && !c.accessedClasses.contains(accessedClass))
c.currentClassAccesses++;// fake access so we filter
// this method, we only
// care for accesses
else if (c.accessedClasses.isEmpty()) {
c.foreignAccesses++;
c.accessedClasses.add(accessedClass);
} else
c.foreignAccesses++;
}
}
}
private class Counter {
Integer currentClassAccesses = 0;
List<Node> accessedClasses = new ArrayList<Node>();
protected Integer foreignAccesses = 0;
}
public FeatureEnvy(boolean addInUniqueFiles) {
this.addInUniqueFiles = addInUniqueFiles;
}
public void findFeatureEnvy(Node method) {
final Node currentClass = parentNode(method);
if (currentClass != null) {
ArrayList<Node> currentClassAndAncestors = computeCurrentClassAndAncestors(currentClass);
Iterable<Relationship> accesses = method.getRelationships(RelTypes.ACCESSES, Direction.OUTGOING);
final Counter c = new Counter();
accesses.forEach(new ConsumerImplementation(currentClassAndAncestors, c));
Iterable<Relationship> calls = method.getRelationships(RelTypes.CALLS, Direction.OUTGOING);
calls.forEach(new ConsumerImplementation(currentClassAndAncestors, c));
method.setProperty("currentClassAccesses", c.currentClassAccesses);
method.setProperty("foreignAccesses", c.foreignAccesses);
if (c.currentClassAccesses == 0 && c.foreignAccesses > 2) {
featureEnvys.add(method);
printFullName(method);
}
}
}
private ArrayList<Node> computeCurrentClassAndAncestors(Node startClas) {
ArrayList<Node> result = new ArrayList<Node>();
result.add(startClas);
Node currentClass = startClas;
while (currentClass != null && currentClass.getRelationships(RelTypes.EXTENDS, Direction.OUTGOING).iterator().hasNext()) {
Node endNode = currentClass.getRelationships(RelTypes.EXTENDS, Direction.OUTGOING).iterator().next().getEndNode();
if (endNode != null)
result.add(endNode);
currentClass = endNode;
}
return result;
}
private void printFullName(Node method) {
method.setProperty("fe", "true");
Node parentClass = parentNode(method);
if (parentClass != null) {
Node parentPackage = parentNode(parentNode(parentClass));
if (parentPackage != null) {
String fileName = parentPackage.getProperty(IHindsight.NAME) + "." + parentClass.getProperty(IHindsight.NAME);
if (!uniqueFiles.contains(fileName) && addInUniqueFiles)
uniqueFiles.add(fileName);
System.out.println(parentPackage.getProperty(IHindsight.NAME) + "." + parentClass.getProperty(IHindsight.NAME));
// System.out.println(method.getProperty("code"));
}
}
}
private Node parentNode(Node node) {
if (node != null) {
Iterable<Relationship> toParent = node.getRelationships(RelTypes.PARENT, Direction.OUTGOING);
if (toParent.iterator().hasNext()) {
Node parent = toParent.iterator().next().getEndNode();
return parent;
}
}
return null;
}
public void listFiles() {
System.out.println("total " + uniqueFiles.size());
}
}