package sharpen.xobotos.api; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import com.thoughtworks.xstream.annotations.XStreamImplicit; import org.eclipse.jdt.core.dom.*; import sharpen.core.framework.BindingUtils; import sharpen.xobotos.config.LocationFilter.Match; import java.lang.reflect.Modifier; import java.util.List; @XStreamAlias(value="filter") public class Filter { @XStreamImplicit(itemFieldName="visibility") private List<Visibility> _visibility; @XStreamAlias("member-kind") private MemberKind _kind; @XStreamAsAttribute @XStreamAlias("regex") private boolean _regex; @XStreamImplicit(itemFieldName = "name") private List<String> _names; @XStreamAsAttribute @XStreamAlias("exclude") private boolean _exclude; public boolean isExcludeFilter() { return _exclude; } private boolean matchesVisibility(BodyDeclaration node) { if (_visibility == null) return true; for (final Visibility visibility : _visibility) { if (matchesVisibility(node, visibility)) return true; } return false; } private static boolean matchesVisibility(BodyDeclaration node, Visibility visibility) { if (visibility == Visibility.PUBLIC) return Modifier.isPublic(node.getModifiers()); else if (visibility == Visibility.PROTECTED) return Modifier.isProtected(node.getModifiers()); else if (visibility == Visibility.PRIVATE) return Modifier.isPrivate(node.getModifiers()); return false; } private boolean matchesMemberKind(ASTNode node) { if (_kind == null) return true; return matchesMemberKind(node, _kind); } public static boolean isDestructor(MethodDeclaration node) { return node.getName().toString().equals("finalize"); } private static boolean matchesMemberKind(ASTNode node, MemberKind kind) { switch (kind) { case CLASS: case INTERFACE: case ANONYMOUS_CLASS: if (node instanceof AnonymousClassDeclaration) return kind == MemberKind.ANONYMOUS_CLASS; if (!(node instanceof TypeDeclaration)) return false; TypeDeclaration tdecl = (TypeDeclaration) node; if (kind == MemberKind.CLASS) return !tdecl.isInterface(); else if (kind == MemberKind.INTERFACE) return tdecl.isInterface(); else return tdecl.resolveBinding().isAnonymous(); case ENUM: return node instanceof EnumDeclaration; case METHOD: case CONSTRUCTOR: case DESTRUCTOR: if (!(node instanceof MethodDeclaration)) return false; MethodDeclaration mdecl = (MethodDeclaration) node; if (kind == MemberKind.DESTRUCTOR) return isDestructor(mdecl); else if (kind == MemberKind.CONSTRUCTOR) return mdecl.isConstructor(); else return !isDestructor(mdecl) && !mdecl.isConstructor(); case STATIC_CONSTRUCTOR: if (!(node instanceof Initializer)) return false; Initializer initializer = (Initializer) node; return Modifier.isStatic(initializer.getModifiers()); case FIELD: return node instanceof FieldDeclaration; default: return false; } } public static String methodSignature(IMethodBinding binding) { StringBuffer buf = new StringBuffer(); buf.append(binding.getName()); buf.append('('); ITypeBinding[] parameterTypes = binding.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { if (i != 0) buf.append(","); buf.append(BindingUtils.qualifiedName(parameterTypes[i])); } buf.append(')'); return buf.toString(); } private boolean matchesName(BodyDeclaration node) { if (_names == null) return true; for (final String name : _names) { if (matchesName(node, name, _regex)) return true; } return false; } public static boolean matchesName(ASTNode node, String pattern, boolean regex) { if (node instanceof AbstractTypeDeclaration) { final AbstractTypeDeclaration tdecl = (AbstractTypeDeclaration) node; final ITypeBinding binding = tdecl.resolveBinding(); final String fullName = binding.getName(); if (regex) return pattern.matches(fullName); else return pattern.equals(fullName); } if (node instanceof MethodDeclaration) { final MethodDeclaration mdecl = (MethodDeclaration) node; final IMethodBinding binding = mdecl.resolveBinding(); final String fullSignature = methodSignature(binding); final String fullName = binding.getName(); if (regex) return fullSignature.matches(pattern); else if (pattern.indexOf('(') > 0) return pattern.equals(fullSignature); else return pattern.equals(fullName); } if (node instanceof FieldDeclaration) { final FieldDeclaration fdecl = (FieldDeclaration) node; if (fdecl.fragments().size() != 1) return false; final Object firstFragment = fdecl.fragments().get(0); final IVariableBinding binding = ((VariableDeclarationFragment) firstFragment).resolveBinding(); final String fullName = binding.getName(); if (regex) return pattern.matches(fullName); else return pattern.matches(fullName); } if (node instanceof VariableDeclarationFragment) { final VariableDeclarationFragment fragment = (VariableDeclarationFragment) node; final IVariableBinding binding = fragment.resolveBinding(); final String fullName = binding.getName(); if (regex) return pattern.matches(fullName); else return pattern.matches(fullName); } return false; } private boolean matchesInternal(ASTNode node) { if (!matchesMemberKind(node)) return false; if (!(node instanceof BodyDeclaration)) return false; BodyDeclaration body = (BodyDeclaration) node; if (!matchesVisibility(body)) return false; return matchesName(body); } public Match matches(ASTNode node) { if (!matchesInternal(node)) return Match.NO_MATCH; return _exclude ? Match.NEGATIVE : Match.POSITIVE; } }