package sk.stuba.fiit.perconik.core.java.dom; import java.util.List; import javax.annotation.Nullable; import com.google.common.base.Predicate; import org.eclipse.jdt.core.dom.ASTNode; import sk.stuba.fiit.perconik.utilities.function.ListCollector; import static com.google.common.base.Preconditions.checkNotNull; public abstract class NodeFilteringCollector<N extends ASTNode, R extends ASTNode> implements ListCollector<N, R> { NodeFilteringCollector() {} public static <N extends R, R extends ASTNode> NodeFilteringCollector<N, R> using(final Predicate<N> filter) { return new Generic<>(filter); } public static <N extends ASTNode, R extends ASTNode> NodeFilteringCollector<N, R> using(final NodeClassFilter<N, R> filter) { return new Type<>(filter); } private static final class Generic<N extends R, R extends ASTNode> extends NodeFilteringCollector<N, R> { final Predicate<N> filter; Generic(final Predicate<N> filter) { this.filter = checkNotNull(filter); } @Override public List<R> apply(@Nullable final N node) { return new Processor().perform(node); } private class Processor extends AbstractCollectingVisitor<N, R> { Processor() {} @Override public final void preVisit(final ASTNode node) { // this cast is unsafe, therefore the try block @SuppressWarnings("unchecked") N casted = (N) node; try { if (Generic.this.filter.apply(casted)) { this.result.add(casted); } } catch (ClassCastException e) {} } } @Override public Predicate<N> getFilter() { return this.filter; } } private static final class Type<N extends ASTNode, R extends ASTNode> extends NodeFilteringCollector<N, R> { final NodeClassFilter<N, R> filter; Type(final NodeClassFilter<N, R> filter) { this.filter = checkNotNull(filter); } @Override public List<R> apply(@Nullable final N node) { return new Processor().perform(node); } private final class Processor extends AbstractCollectingVisitor<N, R> { Processor() {} @Override public void preVisit(final ASTNode node) { for (Class<? extends R> type: Type.this.filter.getNodeClasses()) { if (type.isInstance(node)) { boolean accepted = false; // this cast is unsafe, therefore the try block @SuppressWarnings("unchecked") N casted = (N) node; try { accepted = Type.this.filter.apply(casted); } catch (ClassCastException e) { continue; } if (accepted) { this.result.add(type.cast(node)); } return; } } } } @Override public Predicate<N> getFilter() { return this.filter; } } @Override public final boolean equals(@Nullable final Object o) { if (o instanceof NodeFilteringCollector) { NodeFilteringCollector<?, ?> other = (NodeFilteringCollector<?, ?>) o; return this.getFilter().equals(other.getFilter()); } return false; } @Override public final int hashCode() { return this.getFilter().hashCode(); } @Override public final String toString() { return "collector(" + this.getFilter() + ")"; } public abstract Predicate<N> getFilter(); }