package sk.stuba.fiit.perconik.core.java.dom;
import javax.annotation.Nullable;
import com.google.common.base.Predicate;
import org.eclipse.jdt.core.dom.ASTNode;
import sk.stuba.fiit.perconik.eclipse.jdt.core.dom.NodeType;
import sk.stuba.fiit.perconik.utilities.MoreStrings;
import sk.stuba.fiit.perconik.utilities.function.Numerate;
public final class NodeCounters {
private NodeCounters() {}
private enum NodeCounter implements Numerate<ASTNode> {
INSTANCE;
public int apply(@Nullable final ASTNode node) {
final AbstractCountingVisitor<ASTNode> visitor = new AbstractCountingVisitor<ASTNode>() {
@Override
public void preVisit(final ASTNode node) {
this.count ++;
}
};
return visitor.perform(node);
}
@Override
public String toString() {
return "nodes";
}
}
private enum LineCounter implements Numerate<ASTNode> {
INSTANCE;
public int apply(@Nullable final ASTNode node) {
if (node == null || !Nodes.hasSource(node)) {
return 0;
}
String source = Nodes.source(node, NodeRangeType.STANDARD);
return source != null ? source.split("\r?\n|\r").length : 0;
}
@Override
public String toString() {
return "lines(?)";
}
}
private enum CharacterCounter implements Numerate<ASTNode> {
INSTANCE;
public int apply(@Nullable final ASTNode node) {
return node != null ? node.getLength() : 0;
}
@Override
public String toString() {
return "characters";
}
}
private enum MemoryCounter implements Numerate<ASTNode> {
INSTANCE;
public int apply(@Nullable final ASTNode node) {
return node != null ? node.subtreeBytes() : 0;
}
@Override
public String toString() {
return "memory";
}
}
private static final <N extends ASTNode> Numerate<N> cast(final Numerate<?> numerate) {
// only for stateless internal singletons shared across all types
@SuppressWarnings("unchecked")
Numerate<N> result = (Numerate<N>) numerate;
return result;
}
public static <N extends ASTNode> Numerate<N> nodes() {
return cast(NodeCounter.INSTANCE);
}
public static <N extends ASTNode> Numerate<N> lines() {
return cast(LineCounter.INSTANCE);
}
public static <N extends ASTNode> Numerate<N> lines(final String source) {
return new Numerate<N>() {
public int apply(final N node) {
int index;
if (node == null || (index = node.getStartPosition()) == -1) {
return 0;
}
return MoreStrings.lines(source.substring(index, index + node.getLength())).size();
}
@Override
public String toString() {
return "lines(source)";
}
};
}
public static <N extends ASTNode> Numerate<N> characters() {
return cast(CharacterCounter.INSTANCE);
}
public static <N extends ASTNode> Numerate<N> memory() {
return cast(MemoryCounter.INSTANCE);
}
public static <N extends ASTNode> Numerate<N> usingFilter(final Predicate<ASTNode> filter) {
return NodeFilteringCounter.using(filter);
}
@SafeVarargs
public static <N extends ASTNode> Numerate<N> ofClass(final Class<? extends ASTNode> implementation, final Class<? extends ASTNode> ... rest) {
return usingFilter(NodeClassFilter.of(implementation, rest));
}
public static <N extends ASTNode> Numerate<N> ofClass(final Iterable<Class<? extends ASTNode>> implementations) {
return usingFilter(NodeClassFilter.of(implementations));
}
public static <N extends ASTNode> Numerate<N> ofType(final NodeType type, final NodeType ... rest) {
return usingFilter(NodeFilters.isMatching(type, rest));
}
public static <N extends ASTNode> Numerate<N> ofType(final Iterable<NodeType> types) {
return usingFilter(NodeFilters.isMatching(types));
}
}