Skip to content

Commit

Permalink
Add pointcut for filtering by constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Jan 10, 2019
1 parent 38e8ae1 commit 11a6815
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,36 @@ final class PointcutEvaluationTests extends GroovyEclipseTestSuite {
doTestOfLastMatch('2', 'currentType(methods("intValue"))', 'java.lang.Integer')
}

@Test
void testCurrentTypeHasConstructor1() {
doTestOfLastMatch('2', 'currentType(hasConstructor("int"))', 'java.lang.Integer')
}

@Test
void testCurrentTypeHasConstructor2() {
doTestOfLastMatch('2', 'currentType(hasConstructor("String"))', 'java.lang.Integer')
}

@Test
void testCurrentTypeHasConstructor3() {
doTestOfLastMatch('""', 'currentType(hasConstructor("char[],int,int"))', 'java.lang.String')
}

@Test
void testCurrentTypeHasConstructor4() {
doTestOfLastMatch('2', 'currentType(hasConstructor("Unknown"))', null)
}

@Test
void testCurrentTypeHasConstructor5() {
doTestOfLastMatch('2', 'currentType(hasConstructor(isPublic()))', 'java.lang.Integer')
}

@Test
void testCurrentTypeHasConstructor6() {
doTestOfLastMatch('2', 'currentType(hasConstructor(isPrivate()))', null)
}

@Test
void testCurrentTypeFieldsAndMethods1() {
doTestOfLastMatch('2', 'currentType(fields("value") & methods("intValue"))', 'java.lang.Integer')
Expand Down Expand Up @@ -641,9 +671,9 @@ final class PointcutEvaluationTests extends GroovyEclipseTestSuite {
void testNestedCalls1() {
doTestOfLastBindingSet(
'bar {\n' +
' foo {\n' +
' XXX\n' +
' }\n' +
' foo {\n' +
' XXX\n' +
' }\n' +
'}', 'bind( x: enclosingCall()) & bind(y: currentIdentifier("XXX"))',
new BindingResult('x', 'foo(), bar()'),
new BindingResult('y', 'Var: XXX'))
Expand All @@ -653,9 +683,9 @@ final class PointcutEvaluationTests extends GroovyEclipseTestSuite {
void testNestedCalls2() {
doTestOfLastBindingSet(
'foo {\n' +
' bar {\n' +
' XXX\n' +
' }\n' +
' bar {\n' +
' XXX\n' +
' }\n' +
'}', 'bind( x: enclosingCall()) & bind(y: currentIdentifier("XXX"))',
new BindingResult('x', 'bar(), foo()'),
new BindingResult('y', 'Var: XXX'))
Expand All @@ -665,9 +695,9 @@ final class PointcutEvaluationTests extends GroovyEclipseTestSuite {
void testNestedCalls3() {
doTestOfLastBindingSet(
'foo {\n' +
' foo {\n' +
' XXX\n' +
' }\n' +
' foo {\n' +
' XXX\n' +
' }\n' +
'}', 'bind( x: enclosingCall()) & bind(y: currentIdentifier("XXX"))',
new BindingResult('x', 'foo(), foo()'),
new BindingResult('y', 'Var: XXX'))
Expand All @@ -677,9 +707,9 @@ final class PointcutEvaluationTests extends GroovyEclipseTestSuite {
void testNestedCallNames1() {
doTestOfLastBindingSet(
'bar {\n' +
' foo {\n' +
' XXX\n' +
' }\n' +
' foo {\n' +
' XXX\n' +
' }\n' +
'}', 'bind( x: enclosingCallName()) & bind(y: currentIdentifier("XXX"))',
new BindingResult('x', 'foo, bar'),
new BindingResult('y', 'Var: XXX'))
Expand All @@ -689,9 +719,9 @@ final class PointcutEvaluationTests extends GroovyEclipseTestSuite {
void testNestedCallNames2() {
doTestOfLastBindingSet(
'foo {\n' +
' bar {\n' +
' XXX\n' +
' }\n' +
' bar {\n' +
' XXX\n' +
' }\n' +
'}', 'bind( x: enclosingCallName()) & bind(y: currentIdentifier("XXX"))',
new BindingResult('x', 'bar, foo'),
new BindingResult('y', 'Var: XXX'))
Expand All @@ -701,9 +731,9 @@ final class PointcutEvaluationTests extends GroovyEclipseTestSuite {
void testNestedCallsName3() {
doTestOfLastBindingSet(
'foo {\n' +
' foo {\n' +
' XXX\n' +
' }\n' +
' foo {\n' +
' XXX\n' +
' }\n' +
'}',
'bind( x: enclosingCallName()) & bind(y: currentIdentifier("XXX"))',

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2017 the original author or authors.
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -36,7 +36,7 @@
*/
public abstract class FilteringPointcut<T> extends AbstractPointcut {

private final Class<T> filterBy;
protected final Class<T> filterBy;

public FilteringPointcut(IStorage containerIdentifier, String pointcutName, Class<T> filterBy) {
super(containerIdentifier, pointcutName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.dsl.pointcuts.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.function.Function;

import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.eclipse.dsl.pointcuts.GroovyDSLDContext;
import org.eclipse.core.resources.IStorage;
import org.eclipse.jdt.groovy.core.util.GroovyUtils;

public abstract class FindASTPointcut<T extends ASTNode> extends FilteringPointcut<T> {

private final Function<ClassNode, ? extends Collection<T>> exploder;
private final Function<T, String> stringify;

public FindASTPointcut(IStorage containerIdentifier, String pointcutName, Class<T> filterBy,
Function<ClassNode, ? extends Collection<T>> exploder, Function<T, String> stringify) {
super(containerIdentifier, pointcutName, filterBy);
this.exploder = Objects.requireNonNull(exploder);
this.stringify = Objects.requireNonNull(stringify);
}

@Override
protected final Collection<T> explodeObject(Object object) {
Collection<T> result = new ArrayList<>();
explodeObject(object, result);
return result;
}

protected void explodeObject(Object object, Collection<T> result) {
if (filterBy.isInstance(object)) {
@SuppressWarnings("unchecked")
T node = (T) object;
result.add(node);
} else if (object instanceof ClassNode) {
result.addAll(exploder.apply(
GroovyUtils.getWrapperTypeIfPrimitive((ClassNode) object)));
} else if (object instanceof Collection) {
((Collection<?>) object).forEach(item -> explodeObject(item, result));
}
}

@Override
protected T filterObject(T result, GroovyDSLDContext context, String firstArgAsString) {
if (firstArgAsString == null || firstArgAsString.equals(stringify.apply(result))) {
return result;
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.dsl.pointcuts.impl;

import java.util.List;
import java.util.stream.Collectors;

import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.eclipse.core.resources.IStorage;
import org.eclipse.jdt.groovy.core.util.GroovyUtils;

/**
* Matches if the input has a constructor with the supplied characteristics --
* either a signature (comma-separated simple type names) or another pointcut
* like {@code hasAnnotation}.
*/
public class FindCtorPointcut extends FindASTPointcut<ConstructorNode> {

public FindCtorPointcut(IStorage containerIdentifier, String pointcutName) {
super(containerIdentifier, pointcutName, ConstructorNode.class, ClassNode::getDeclaredConstructors, ctor -> {
List<ClassNode> paramTypes = GroovyUtils.getParameterTypes(ctor.getParameters());
return paramTypes.stream().map(FindCtorPointcut::getSimpleTypeName)
.map(name -> name.substring(name.lastIndexOf('$') + 1))
.collect(Collectors.joining(","));
});
}

private static String getSimpleTypeName(ClassNode type) {
int dims = 0;
while (type.isArray()) {
dims += 1;
type = type.getComponentType();
}

String name = type.getNameWithoutPackage();
while (dims > 0) {
name += "[]";
dims -= 1;
}
return name;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2018 the original author or authors.
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,60 +15,17 @@
*/
package org.codehaus.groovy.eclipse.dsl.pointcuts.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.eclipse.dsl.pointcuts.GroovyDSLDContext;
import org.eclipse.core.resources.IStorage;
import org.eclipse.jdt.groovy.core.util.GroovyUtils;

/**
* the match returns true if the pattern passed in has a field with the
* supplied characteristics (either a name, or another pointcut such as hasAnnotation).
* Matches if the input has a field with the supplied characteristics -- either
* a name or another pointcut like {@code hasAnnotation}.
*/
public class FindFieldPointcut extends FilteringPointcut<FieldNode> {
public class FindFieldPointcut extends FindASTPointcut<FieldNode> {

public FindFieldPointcut(IStorage containerIdentifier, String pointcutName) {
super(containerIdentifier, pointcutName, FieldNode.class);
}

/**
* Converts toMatch to a collection of field nodes. Might be null or empty list
* In either of these cases, this is considered a non-match
* @param toMatch the object to explode
*/
@Override
protected Collection<FieldNode> explodeObject(Object toMatch) {
if (toMatch instanceof Collection) {
Collection<FieldNode> fields = new ArrayList<>();
for (Object obj : (Collection<?>) toMatch) {
if (obj instanceof FieldNode) {
fields.add((FieldNode) obj);
} else if (obj instanceof ClassNode) {
fields.addAll(((ClassNode) obj).getFields());
}
}
return fields;
} else if (toMatch instanceof FieldNode) {
return Collections.singleton((FieldNode) toMatch);
} else if (toMatch instanceof ClassNode) {
return new ArrayList<>(GroovyUtils.getWrapperTypeIfPrimitive((ClassNode) toMatch).getFields());
}
return null;
}

/**
* This gets called if the pointcut argument is a String argument
*/
@Override
protected FieldNode filterObject(FieldNode result, GroovyDSLDContext context, String firstArgAsString) {
if (firstArgAsString == null || result.getName().equals(firstArgAsString)) {
return result;
} else {
return null;
}
super(containerIdentifier, pointcutName, FieldNode.class, ClassNode::getFields, FieldNode::getName);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2017 the original author or authors.
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,56 +15,17 @@
*/
package org.codehaus.groovy.eclipse.dsl.pointcuts.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.eclipse.dsl.pointcuts.GroovyDSLDContext;
import org.eclipse.core.resources.IStorage;

/**
* the match returns true if the pattern passed in has a method with the
* supplied characteristics (either a name, or another pointcut such as hasAnnotation).
* Matches if the input has a method with the supplied characteristics -- either
* a name or another pointcut like {@code hasAnnotation}.
*/
public class FindMethodPointcut extends FilteringPointcut<MethodNode> {
public class FindMethodPointcut extends FindASTPointcut<MethodNode> {

public FindMethodPointcut(IStorage containerIdentifier, String pointcutName) {
super(containerIdentifier, pointcutName, MethodNode.class);
}

/**
* Converts toMatch to a collection of method nodes. Might be null or empty list
* In either of these cases, this is considered a non-match
* @param toMatch the object to explode
*/
@Override
protected Collection<MethodNode> explodeObject(Object toMatch) {
if (toMatch instanceof Collection) {
Collection<MethodNode> methods = new ArrayList<>();
for (Object obj : (Collection<?>) toMatch) {
if (obj instanceof MethodNode) {
methods.add((MethodNode) obj);
} else if (obj instanceof ClassNode) {
methods.addAll(((ClassNode) obj).getMethods());
}
}
return methods;
} else if (toMatch instanceof MethodNode) {
return Collections.singleton((MethodNode) toMatch);
} else if (toMatch instanceof ClassNode) {
return new ArrayList<>(((ClassNode) toMatch).getMethods());
}
return null;
}

@Override
protected MethodNode filterObject(MethodNode result, GroovyDSLDContext context, String firstArgAsString) {
if (firstArgAsString == null || result.getName().equals(firstArgAsString)) {
return result;
} else {
return null;
}
super(containerIdentifier, pointcutName, MethodNode.class, ClassNode::getMethods, MethodNode::getName);
}
}
Loading

0 comments on commit 11a6815

Please sign in to comment.