Skip to content

Commit

Permalink
SafeInvoker
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Dec 2, 2016
1 parent a6fb067 commit cb4deb3
Show file tree
Hide file tree
Showing 11 changed files with 522 additions and 85 deletions.
33 changes: 33 additions & 0 deletions src/main/java/spoon/reflect/visitor/ElementPipe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (C) 2006-2016 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* 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 CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.reflect.visitor;

import spoon.reflect.declaration.CtElement;

/**
* Allows functional processing of elements.
* The client algorithm can call {@link #accept(CtElement)}
* with some input element and ElementPipe implementation will call
* output {@link ElementConsumer} zero, one or more times
* with input element or any other computed element.
*
* The {@link ElementPipe} can be used in QueryBuilder on place of {@link Filter}
*
* @param <T> type of elements produced by this ElementPipe
*/
public interface ElementPipe<I extends CtElement, O extends CtElement> extends ElementConsumer<I>, ElementSupplier<O> {
}
34 changes: 34 additions & 0 deletions src/main/java/spoon/reflect/visitor/ElementSupplier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (C) 2006-2016 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* 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 CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.reflect.visitor;

import spoon.reflect.declaration.CtElement;

/**
* Allows functional processing of elements.
* For example the QueryBuilder can accept implementation of this inteface
* and use it produce elements for next level of filtering
*
* @param <T> type of elements produced by this ElementPipe
*/
public interface ElementSupplier<T extends CtElement> {
/**
* Is usually called once to initialize output ElementConsumer.
* @param output
*/
void setOutput(ElementConsumer<T> output);
}
96 changes: 96 additions & 0 deletions src/main/java/spoon/reflect/visitor/FilterPipe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package spoon.reflect.visitor;

import spoon.reflect.declaration.CtElement;
import spoon.reflect.visitor.chain.ElementPipeImpl;
import spoon.reflect.visitor.filter.AbstractFilter;
import spoon.support.util.RtHelper;
import spoon.support.util.SafeInvoker;

/**
*
* @param <T> type of element produced by this pipe
*/
public class FilterPipe<T extends CtElement> extends ElementPipeImpl<T> {

private Filter<? super CtElement> filter;
private SafeInvoker<Filter<? super CtElement>> invoke_matches = new SafeInvoker<>(CtElement.class, "matches");
private SafeInvoker<Filter<? super CtElement>> invoke_setFilterInput = new SafeInvoker<>(CtElement.class, "setFilterInput");
/*
* Type which is accepted by the Filter#matches()
*/
private Class<? super CtElement> filterType;

public FilterPipe() {
}

public FilterPipe(Filter<? super CtElement> filter) {
setFilter(filter);
}

@Override
public void accept(CtElement element) {
if (element == null) {
return;
}
//first check if filter matches
boolean matches = true;
if (filter != null) {
try {
if ((filterType == null || filterType.isAssignableFrom(element.getClass()))) {
matches = filter.matches(element);
}
} catch (ClassCastException e) {
// expected, some elements are not of type T
// Still need to protect from CCE, if users extend Filter (instead of AbstractFilter) directly,
// but with concrete type parameter
matches = false;
}
}
if (matches) {
//it matches. Send the element to output
fireElement(element);
}
}

public Filter<? super CtElement> getFilter() {
return filter;
}

@SuppressWarnings("unchecked")
public void setFilter(Filter<? super CtElement> filter) {
this.filter = filter;
if (filter != null) {
if (filter instanceof AbstractFilter) {
filterType = ((AbstractFilter<? super CtElement>) filter).getType();
} else {
Class<?>[] params = RtHelper.getMethodParameterTypes(filter.getClass(), "matches", 1);
filterType = (Class<? super CtElement>) params[0];
}
} else {
filterType = null;
}
}

public Class<? super CtElement> getFilterType() {
return filterType;
}

public void runOnScanner(CtElement inputElement, QueryVisitor<T> p_queryVisitor) {
if (filter != null) {
if (filter instanceof ChainableFilter && inputElement != null) {
try {
((ChainableFilter<CtElement, CtElement>) filter).setFilterInput(inputElement);
} catch (ClassCastException e) {
//the filter does not accepts this as input. Ignore this not matching element
return;
}
}
if (filter instanceof ScopeAwareFilter) {
queryVisitor.scan(((ScopeAwareFilter) filter).getSearchScope());
} else {
queryVisitor.scan(inputElement);
}
}
scan(element);
}
}
11 changes: 9 additions & 2 deletions src/main/java/spoon/reflect/visitor/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package spoon.reflect.visitor;

import java.util.ArrayList;
import java.util.List;

import spoon.reflect.declaration.CtElement;
Expand Down Expand Up @@ -76,9 +77,15 @@ public static <E extends CtElement> List<E> getElements(Factory factory,
*/
public static <E extends CtElement> List<E> getElements(
CtElement rootElement, Filter<E> filter) {
QueryVisitor<E> visitor = new QueryVisitor<>(filter);
List<E> list = new ArrayList<>();
QueryVisitor<E> visitor = new QueryVisitor<>(filter, new ElementConsumer<E>() {
@Override
public void accept(E element) {
list.add(element);
}
});
visitor.scan(rootElement);
return visitor.getResult();
return list;
}

/**
Expand Down
60 changes: 33 additions & 27 deletions src/main/java/spoon/reflect/visitor/QueryBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
public class QueryBuilder<T extends CtElement> {

private ArrayList<CtElement> inputs = new ArrayList<>();
private List<Filter<?>> chainItems = new ArrayList<>();
private List<ElementConsumer<? super CtElement>> chainItems = new ArrayList<>();

public QueryBuilder() {
}
Expand All @@ -75,14 +75,34 @@ public QueryBuilder<T> onInput(CtElement... input) {
*/
@SuppressWarnings("unchecked")
public <U extends CtElement> QueryBuilder<U> then(Filter<U> filter) {
chainItems.add(filter);
chainItems.add(new QueryVisitor<>(filter));
return (QueryBuilder<U>) this;
}

/**
* @param pipe which is called for each processed element
* and it's output is registered to call next level of the chain
* @return this instance, so it can be used for next query building.
*/
public <U extends CtElement> QueryBuilder<U> then(ElementPipe<U> pipe) {
chainItems.add(pipe);
return (QueryBuilder<U>) this;
}

/**
* @param consumer which is called for each processed element.
* If the consumer does not implements {@link ElementSupplier}, then it is terminal consumer of this chain
* @return this instance, so it can be used for next query building.
*/
public <U extends CtElement> QueryBuilder<U> then(ElementConsumer<? super CtElement> consumer) {
chainItems.add(consumer);
return (QueryBuilder<U>) this;
}

/**
* @return ElementConsumer which contains whole chain of built filters and which finally will call outputConsumer
*/
public ElementConsumer<CtElement> build(ElementConsumer<T> outputConsumer) {
public ElementConsumer<? super CtElement> build(ElementConsumer<T> outputConsumer) {
return build(outputConsumer, 0);
}

Expand All @@ -92,31 +112,17 @@ public ElementConsumer<CtElement> build(ElementConsumer<T> outputConsumer) {
* @return the ElementConsumer of the level level.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected ElementConsumer<CtElement> build(ElementConsumer<T> outputConsumer, int level) {
protected ElementConsumer<? super CtElement> build(ElementConsumer<T> outputConsumer, int level) {
if (chainItems.size() == level) {
//there is no filter for this level. return outputConsumer
return (ElementConsumer<CtElement>) outputConsumer;
//there is no chain item for this level. return outputConsumer
return (ElementConsumer<? super CtElement>) outputConsumer;
}
final Filter filter = (Filter) chainItems.get(level);
final QueryVisitor queryVisitor = new QueryVisitor(filter, build(outputConsumer, level + 1));
return new ElementConsumer<CtElement>() {
@Override
public void accept(CtElement inputElement) {
if (filter instanceof ChainableFilter && inputElement != null) {
try {
((ChainableFilter<CtElement, CtElement>) filter).setFilterInput(inputElement);
} catch (ClassCastException e) {
//the filter does not accepts this as input. Ignore this not matching element
return;
}
}
if (filter instanceof ScopeAwareFilter) {
queryVisitor.scan(((ScopeAwareFilter) filter).getSearchScope());
} else {
queryVisitor.scan(inputElement);
}
}
};
final ElementConsumer<? super CtElement> consumer = chainItems.get(level);
if (consumer instanceof ElementSupplier) {
//the chain item will produce elements for next chain item. Register it's input as output of this level.
((ElementSupplier) consumer).setOutput(build(outputConsumer, level + 1));
}
return consumer;
}

/**
Expand All @@ -139,7 +145,7 @@ public void accept(T element) {
* @param consumer
*/
public void forEach(ElementConsumer<T> consumer) {
ElementConsumer<CtElement> myConsumer = build(consumer);
ElementConsumer<? super CtElement> myConsumer = build(consumer);
if (inputs.isEmpty()) {
//there is no input element defined
if (chainItems.isEmpty()) {
Expand Down
Loading

0 comments on commit cb4deb3

Please sign in to comment.