bind pointcut

This commit is contained in:
peter
2011-05-25 18:45:07 +02:00
parent c8d7299328
commit 2c77401fc5
7 changed files with 131 additions and 21 deletions
@@ -90,7 +90,7 @@ public class CustomMembersGenerator implements GdslMembersHolderConsumer {
myDepot.addHolder(holder);
}
protected Object[] constructNewArgs(Object[] args) {
public Object[] constructNewArgs(Object[] args) {
final Object[] newArgs = new Object[args.length + 1];
//noinspection ManualArrayCopy
for (int i = 0; i < args.length; i++) {
@@ -1,22 +1,32 @@
package org.jetbrains.plugins.groovy.dsl;
import com.intellij.openapi.util.Key;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.dsl.toplevel.ClassContextFilter;
import java.util.*;
/**
* @author peter
*/
public abstract class DslPointcut<T> {
public abstract class DslPointcut<T,V> {
public static Key<Map<String, List>> BOUND = Key.create("gdsl.bound");
abstract boolean matches(T t, ProcessingContext context);
@Nullable
abstract List<V> matches(T src, ProcessingContext context);
abstract boolean operatesOn(Class c);
public static DslPointcut<GroovyClassDescriptor> currentType(final Object arg) {
return new DslPointcut<GroovyClassDescriptor>() {
public static DslPointcut<GroovyClassDescriptor, GdslType> currentType(final Object arg) {
return new DslPointcut<GroovyClassDescriptor, GdslType>() {
@Override
boolean matches(GroovyClassDescriptor groovyClassDescriptor, ProcessingContext context) {
return ClassContextFilter.subtypeOf((String)arg).isApplicable(groovyClassDescriptor, context);
List<GdslType> matches(GroovyClassDescriptor src, ProcessingContext context) {
if (ClassContextFilter.subtypeOf((String)arg).isApplicable(src, context)) {
return Arrays.asList(new GdslType(ClassContextFilter.findPsiType(src, context)));
}
return null;
}
@Override
@@ -26,11 +36,39 @@ public abstract class DslPointcut<T> {
};
}
public static <T> DslPointcut<T> unknownPointcut() {
return new DslPointcut<T>() {
public static DslPointcut bind(final Object arg) {
assert arg instanceof Map;
assert ((Map)arg).size() == 1;
final String name = (String)((Map)arg).keySet().iterator().next();
final DslPointcut pct = (DslPointcut)((Map)arg).values().iterator().next();
return new DslPointcut() {
@Override
boolean matches(T t, ProcessingContext context) {
return true;
List matches(Object src, ProcessingContext context) {
final List result = pct.matches(src, context);
if (result != null) {
Map<String, List> map = context.get(BOUND);
if (map == null) {
context.put(BOUND, map = new HashMap<String, List>());
}
map.put(name, result);
}
return result;
}
@Override
boolean operatesOn(Class c) {
return pct.operatesOn(c);
}
};
}
public static DslPointcut unknownPointcut() {
return new DslPointcut() {
@Override
List matches(Object src, ProcessingContext context) {
return Collections.emptyList();
}
@Override
@@ -0,0 +1,41 @@
package org.jetbrains.plugins.groovy.dsl;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiWildcardType;
/**
* @author peter
*/
public class GdslType {
private final PsiType myPsiType;
public GdslType(PsiType psiType) {
myPsiType = psiType;
}
public String getName() {
PsiType type = myPsiType;
if (type instanceof PsiWildcardType) {
type = ((PsiWildcardType)type).getBound();
}
if (type instanceof PsiClassType) {
final PsiClass resolve = ((PsiClassType)type).resolve();
if (resolve != null) {
return resolve.getName();
}
final String canonicalText = type.getCanonicalText();
final int i = canonicalText.indexOf('<');
if (i < 0) return canonicalText;
return canonicalText.substring(0, i);
}
if (type == null) {
return "";
}
return type.getCanonicalText();
}
}
@@ -6,6 +6,7 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PsiJavaPatterns
import com.intellij.util.ProcessingContext
import org.jetbrains.plugins.groovy.dsl.dsltop.GdslMembersProvider
import org.jetbrains.plugins.groovy.dsl.psi.PsiEnhancerCategory
import org.jetbrains.plugins.groovy.dsl.toplevel.CompositeContextFilter
import org.jetbrains.plugins.groovy.dsl.toplevel.Context
@@ -70,6 +71,7 @@ public class GroovyDslExecutor {
mc.contribute = contribute
mc.currentType = { arg -> DslPointcut.currentType(arg) }
mc.bind = { arg -> DslPointcut.bind(arg) }
oldStylePrimitives(mc)
@@ -112,11 +114,12 @@ public class GroovyDslExecutor {
enhancers << Pair.create(CompositeContextFilter.compose(cts, false), toDo)
}
def processVariants(GroovyClassDescriptor descriptor, consumer, ProcessingContext ctx) {
def processVariants(GroovyClassDescriptor descriptor, CustomMembersGenerator consumer, ProcessingContext ctx) {
for (pair in enhancers) {
if (pair.first.isApplicable(descriptor, ctx)) {
Closure f = pair.second.clone()
f.delegate = consumer
consumer.metaClass = contributionDelegateMetaClass(ctx, consumer)
f.resolveStrategy = Closure.DELEGATE_FIRST
use(cats) {
@@ -126,6 +129,29 @@ public class GroovyDslExecutor {
}
}
static final def memberProviders = GdslMembersProvider.EP_NAME.getExtensions()
private ExpandoMetaClass contributionDelegateMetaClass(ProcessingContext ctx, CustomMembersGenerator consumer) {
def mc = new ExpandoMetaClass(CustomMembersGenerator)
mc.methodMissing = { String name, Object args ->
final def newArgs = consumer.constructNewArgs(args)
// Get other DSL methods from extensions
for (d in memberProviders) {
final def variants = d.metaClass.respondsTo(d, name, newArgs)
if (variants.size() == 1) {
return d.invokeMethod(name, newArgs)
}
}
return null
}
def bound = ctx.get(DslPointcut.BOUND)
bound.each { name, value -> mc."$name" = value }
mc.initialize()
return mc
}
def String toString() {
return "${super.toString()}; file = $myFileName";
}
@@ -7,14 +7,14 @@ import org.jetbrains.plugins.groovy.dsl.toplevel.ContextFilter;
* @author peter
*/
public class PointcutContextFilter implements ContextFilter {
private final DslPointcut<GroovyClassDescriptor> myPointcut;
private final DslPointcut<GroovyClassDescriptor,?> myPointcut;
public PointcutContextFilter(DslPointcut<GroovyClassDescriptor> pointcut) {
public PointcutContextFilter(DslPointcut<GroovyClassDescriptor,?> pointcut) {
myPointcut = pointcut;
}
@Override
public boolean isApplicable(GroovyClassDescriptor descriptor, ProcessingContext ctx) {
return myPointcut.matches(descriptor, ctx);
return myPointcut.matches(descriptor, ctx) != null;
}
}
@@ -1,6 +1,5 @@
package org.jetbrains.plugins.groovy.dsl.toplevel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
@@ -11,7 +10,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiType;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.ConcurrentHashMap;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.dsl.GroovyClassDescriptor;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
@@ -29,18 +28,19 @@ public class ClassContextFilter implements ContextFilter {
public boolean isApplicable(GroovyClassDescriptor descriptor, ProcessingContext ctx) {
final PsiFile place = descriptor.getPlaceFile();
return myPattern.value(Pair.create(findPsiType(descriptor.getProject(), descriptor.getTypeText(), place, ctx), place));
return myPattern.value(Pair.create(findPsiType(descriptor, ctx), place));
}
@Nullable
private static PsiType findPsiType(Project project, String typeText, PsiFile place, ProcessingContext ctx) {
@NotNull
public static PsiType findPsiType(GroovyClassDescriptor descriptor, ProcessingContext ctx) {
String typeText = descriptor.getTypeText();
final String key = getClassKey(typeText);
final Object cached = ctx.get(key);
if (cached instanceof PsiType) {
return (PsiType)cached;
}
final PsiType found = JavaPsiFacade.getElementFactory(project).createTypeFromText(typeText, place);
final PsiType found = JavaPsiFacade.getElementFactory(descriptor.getProject()).createTypeFromText(typeText, descriptor.getPlaceFile());
ctx.put(key, found);
return found;
}
@@ -22,6 +22,11 @@ class DsldTest extends LightGroovyTestCase {
'println "".foo + [].<warning>foo</warning>'
}
public void testBind() {
checkHighlighting 'contribute(bind(types:currentType("java.lang.CharSequence"))) { property name:types[0].name[-3..-1] }',
'println "".ing + "".<warning>foo</warning>'
}
private def checkHighlighting(String dsl, String code) {
def file = myFixture.addFileToProject('a.gdsl', dsl)
GroovyDslFileIndex.activateUntilModification(file.virtualFile)