blob: 4552defed13ec9f5ac47e7516fe775ce21f72732 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.jetbrains.plugins.groovy.dsl;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.dsl.toplevel.ClassContextFilter;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.ClassUtil;
import java.util.*;
/**
* @author peter
*/
@SuppressWarnings("UnusedDeclaration")
public abstract class DslPointcut<T,V> {
public static final DslPointcut UNKNOWN = new DslPointcut() {
@Override
List matches(Object src, ProcessingContext context) {
return Collections.emptyList();
}
@Override
boolean operatesOn(Class c) {
return true;
}
};
public static Key<Map<String, List>> BOUND = Key.create("gdsl.bound");
@Nullable
abstract List<V> matches(T src, ProcessingContext context);
abstract boolean operatesOn(Class c);
public DslPointcut<T, V> and(final DslPointcut<T, V> next) {
final DslPointcut<T, V> first = this;
return new DslPointcut<T, V>() {
@Override
List<V> matches(T src, ProcessingContext context) {
final List<V> vs1 = first.matches(src, context);
if (vs1 == null) return null;
final List<V> vs2 = next.matches(src, context);
if (vs2 == null) return null;
final List<V> result = new ArrayList<V>(vs1);
result.retainAll(new HashSet<V>(vs2));
return result;
}
@Override
boolean operatesOn(Class c) {
return first.operatesOn(c) && next.operatesOn(c);
}
};
}
public DslPointcut<T, V> or(final DslPointcut<T, V> next) {
final DslPointcut<T, V> first = this;
return new DslPointcut<T, V>() {
@Override
List<V> matches(T src, ProcessingContext context) {
final List<V> vs1 = first.matches(src, context);
final List<V> vs2 = next.matches(src, context);
if (vs1 == null && vs2 == null) return null;
final Set<V> result = new LinkedHashSet<V>();
if (vs1 != null) {
result.addAll(vs1);
}
if (vs2 != null) {
result.addAll(vs2);
}
return new ArrayList<V>(result);
}
@Override
boolean operatesOn(Class c) {
return first.operatesOn(c) && next.operatesOn(c);
}
};
}
public DslPointcut<T, V> bitwiseNegate() {
final DslPointcut<T, V> base = this;
return new DslPointcut<T, V>() {
@Override
List<V> matches(T src, ProcessingContext context) {
return base.matches(src, context) == null ? Collections.<V>emptyList() : null;
}
@Override
boolean operatesOn(Class c) {
return base.operatesOn(c);
}
};
}
public static DslPointcut<GdslType, GdslType> subType(final Object arg) {
return new DslPointcut<GdslType, GdslType>() {
@Override
List<GdslType> matches(GdslType src, ProcessingContext context) {
final PsiFile placeFile = context.get(GdslUtil.INITIAL_CONTEXT).getPlaceFile();
if (ClassContextFilter.isSubtype(src.psiType, placeFile, (String)arg)) {
return Arrays.asList(src);
}
return null;
}
@Override
boolean operatesOn(Class c) {
return GdslType.class == c;
}
};
}
public static DslPointcut<GroovyClassDescriptor, GdslType> currentType(final Object arg) {
final DslPointcut<GdslType,?> inner;
if (arg instanceof String) {
inner = subType(arg);
} else {
inner = (DslPointcut<GdslType, ?>)arg;
assert inner.operatesOn(GdslType.class) : "The argument to currentType should be a pointcut working with types, e.g. subType";
}
return new DslPointcut<GroovyClassDescriptor, GdslType>() {
@Override
List<GdslType> matches(GroovyClassDescriptor src, ProcessingContext context) {
final GdslType currentType = new GdslType(ClassUtil.findPsiType(src, context));
if (inner.matches(currentType, context) != null) {
return Arrays.asList(currentType);
}
return null;
}
@Override
boolean operatesOn(Class c) {
return GroovyClassDescriptor.class == c;
}
};
}
public static DslPointcut<GroovyClassDescriptor, GdslType> enclosingType(final Object arg) {
return new DslPointcut<GroovyClassDescriptor, GdslType>() {
@Override
List<GdslType> matches(GroovyClassDescriptor src, ProcessingContext context) {
List<GdslType> result = new ArrayList<GdslType>();
PsiElement place = src.getPlace();
while (true) {
final PsiClass cls = PsiTreeUtil.getContextOfType(place, PsiClass.class);
if (cls == null) {
break;
}
if (arg.equals(cls.getQualifiedName())) {
result.add(new GdslType(JavaPsiFacade.getElementFactory(cls.getProject()).createType(cls)));
}
place = cls;
}
return result.isEmpty() ? null : result;
}
@Override
boolean operatesOn(Class c) {
return GroovyClassDescriptor.class == c;
}
};
}
public static DslPointcut<Object, String> name(final Object arg) {
return new DslPointcut<Object, String>() {
@Override
List<String> matches(Object src, ProcessingContext context) {
if (src instanceof GdslType) {
return arg.equals(((GdslType)src).getName()) ? Arrays.asList((String)arg) : null;
}
if (src instanceof GdslMethod) {
return arg.equals(((GdslMethod)src).getName()) ? Arrays.asList((String)arg) : null;
}
return Collections.emptyList();
}
@Override
boolean operatesOn(Class c) {
return c == GdslType.class || c == GdslMethod.class;
}
};
}
public static DslPointcut<GroovyClassDescriptor, GdslMethod> enclosingMethod(final Object arg) {
final DslPointcut<? super GdslMethod,?> inner;
if (arg instanceof String) {
inner = name(arg);
} else {
inner = (DslPointcut<GdslMethod, ?>)arg;
assert inner.operatesOn(GdslMethod.class) : "The argument to enclosingMethod should be a pointcut working with methods, e.g. name";
}
return new DslPointcut<GroovyClassDescriptor, GdslMethod>() {
@Override
List<GdslMethod> matches(GroovyClassDescriptor src, ProcessingContext context) {
List<GdslMethod> result = new ArrayList<GdslMethod>();
PsiElement place = src.getPlace();
while (true) {
final PsiMethod method = PsiTreeUtil.getContextOfType(place, PsiMethod.class);
if (method == null) {
break;
}
final GdslMethod wrapper = new GdslMethod(method);
if (inner.matches(wrapper, context) != null) {
result.add(wrapper);
}
place = method;
}
return result.isEmpty() ? null : result;
}
@Override
boolean operatesOn(Class c) {
return GroovyClassDescriptor.class == c;
}
};
}
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
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);
}
};
}
}