package sk.stuba.fiit.perconik.core.java.dom;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Nullable;
import com.google.common.base.Function;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import sk.stuba.fiit.perconik.eclipse.jdt.core.dom.NodeType;
import static com.google.common.base.Preconditions.checkNotNull;
public final class NodePathExtractor<N extends ASTNode> implements Function<N, Path> {
private static final LinkedList<ASTNode> empty = new LinkedList<>();
private final Function<ASTNode, String> transformer;
private NodePathExtractor(final Function<ASTNode, String> transformer) {
this.transformer = checkNotNull(transformer);
}
public static <N extends ASTNode> NodePathExtractor<N> using(final Function<ASTNode, String> strategy) {
return new NodePathExtractor<>(strategy);
}
@Override
public Path apply(@Nullable final ASTNode node) {
LinkedList<ASTNode> branch = branch(node);
if (branch.isEmpty()) {
return NodePaths.singleUnknownPath;
}
Iterator<ASTNode> iterator = branch.descendingIterator();
String first = this.transformer.apply(iterator.next());
String[] rest = new String[branch.size() - 1];
for (int i = 0; i < rest.length; i ++) {
ASTNode other = iterator.next();
if (other instanceof FieldDeclaration) {
rest[i] = fragments(((FieldDeclaration) other).fragments());
} else {
rest[i] = this.transformer.apply(other);
}
}
return Paths.get(first, rest);
}
private static LinkedList<ASTNode> branch(final ASTNode node) {
if (node == null) {
return empty;
}
LinkedList<ASTNode> branch = Nodes.downToRoot(node);
if (NodeType.valueOf(branch.getLast()) == NodeType.COMPILATION_UNIT) {
branch.removeLast();
}
return branch;
}
private String fragments(final List<VariableDeclarationFragment> fragments) {
Iterator<VariableDeclarationFragment> iterator = fragments.iterator();
StringBuilder builder = new StringBuilder();
if (iterator.hasNext()) {
builder.append(this.transformer.apply(iterator.next()));
while (iterator.hasNext()) {
builder.append(NodePaths.variableSeparator);
builder.append(this.transformer.apply(iterator.next()));
}
}
return builder.toString();
}
@Override
public boolean equals(@Nullable final Object o) {
if (o instanceof NodePathExtractor) {
NodePathExtractor<?> other = (NodePathExtractor<?>) o;
return this.transformer.equals(other.transformer);
}
return false;
}
@Override
public int hashCode() {
return this.transformer.hashCode();
}
@Override
public String toString() {
return "path(" + this.transformer + ")";
}
public Function<ASTNode, String> getTransformer() {
return this.transformer;
}
}