001/* 002 * Copyright 2014 Atteo. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.atteo.classindex; 018 019import java.lang.annotation.Annotation; 020import java.lang.annotation.Retention; 021import java.lang.annotation.RetentionPolicy; 022import java.lang.reflect.Constructor; 023import java.lang.reflect.Modifier; 024import java.util.ArrayList; 025import java.util.List; 026 027import org.atteo.classindex.ClassFilter.UnionBuilder; 028 029public final class ClassFilter { 030 /** 031 * Class from. 032 */ 033 public interface Predicate { 034 /** 035 * Returns true if the class should be included in the result. 036 */ 037 boolean matches(Class<?> klass); 038 } 039 040 public interface FilterBuilder extends Predicate { 041 /** 042 * Filters given classes. 043 */ 044 <T> Iterable<Class<? extends T>> from(Iterable<Class<? extends T>> classes); 045 } 046 047 public interface UnionBuilder extends FilterBuilder { 048 /** 049 * Satisfies given predicate. 050 * @param predicate predicate to satisfy 051 * @return 052 */ 053 UnionBuilder satisfying(Predicate predicate); 054 055 /** 056 * Returns top level classes. 057 */ 058 UnionBuilder topLevel(); 059 060 /** 061 * Returns top level or static nested classes. 062 */ 063 UnionBuilder topLevelOrStaticNested(); 064 065 /** 066 * Returns classes nested (directly or indirectly) in given class. 067 */ 068 UnionBuilder enclosedIn(final Class<?> enclosing); 069 070 /** 071 * Returns classes nested directly in given class. 072 */ 073 UnionBuilder enclosedDirectlyIn(final Class<?> enclosing); 074 075 /** 076 * Returns classes annotated with given annotation. 077 * <p> 078 * As opposed to {@link ClassIndex#getAnnotated(Class)} this method only works if annotation 079 * is itself annotation with {@link Retention} set to {@link RetentionPolicy#RUNTIME}. 080 * </p> 081 * @throws IllegalStateException if annotation retention policy is not set equal to 082 * {@link RetentionPolicy#RUNTIME}. 083 */ 084 UnionBuilder annotatedWith(final Class<? extends Annotation> annotation); 085 086 /** 087 * Returns classes marked with given modifiers. 088 * @param modifiers modifiers to expect, see {@link Modifier} 089 */ 090 UnionBuilder withModifiers(int modifiers); 091 092 /** 093 * Returns classes not marked with given modifiers. 094 * @param modifiers modifiers to expect, see {@link Modifier} 095 */ 096 UnionBuilder withoutModifiers(int modifiers); 097 098 /** 099 * Returns classes which have public default constructor. 100 * <p> 101 * Default constructor is a constructor without any parameters. 102 * Note that (non-static) inner classes never have the default constructor 103 * (see: <a href="http://thecodersbreakfast.net/index.php?post/2011/09/26/Inner-classes-and-the-myth-of-the-default-constructor">Inner classes and the myth of the default constructor</a>) 104 * </p> 105 */ 106 UnionBuilder withPublicDefaultConstructor(); 107 } 108 109 private ClassFilter() { 110 } 111 112 private static abstract class CommonFilterBuilder implements FilterBuilder { 113 @Override 114 public <T> Iterable<Class<? extends T>> from(Iterable<Class<? extends T>> classes) { 115 List<Class<? extends T>> result = new ArrayList<>(); 116 for (Class<? extends T> klass : classes) { 117 if (matches(klass)) { 118 result.add(klass); 119 } 120 } 121 122 return result; 123 } 124 } 125 126 private static class Builder extends CommonFilterBuilder implements UnionBuilder { 127 private final List<Predicate> predicates = new ArrayList<>(); 128 129 @Override 130 public Builder satisfying(Predicate predicate) { 131 predicates.add(predicate); 132 return this; 133 } 134 135 @Override 136 public Builder topLevel() { 137 return satisfying(new Predicate() { 138 @Override 139 public boolean matches(Class<?> klass) { 140 return klass.getEnclosingClass() == null; 141 } 142 }); 143 } 144 145 @Override 146 public UnionBuilder topLevelOrStaticNested() { 147 return satisfying(new Predicate() { 148 @Override 149 public boolean matches(Class<?> klass) { 150 return klass.getEnclosingClass() == null || (klass.getModifiers() & Modifier.STATIC) != 0; 151 } 152 }); 153 } 154 155 @Override 156 public Builder enclosedIn(final Class<?> enclosing) { 157 return satisfying(new Predicate() { 158 @Override 159 public boolean matches(Class<?> klass) { 160 while (true) { 161 klass = klass.getEnclosingClass(); 162 if (klass == null) { 163 return false; 164 } 165 if (enclosing.equals(klass)) { 166 return true; 167 } 168 } 169 } 170 }); 171 } 172 173 @Override 174 public UnionBuilder enclosedDirectlyIn(final Class<?> enclosing) { 175 return satisfying(new Predicate() { 176 @Override 177 public boolean matches(Class<?> klass) { 178 klass = klass.getEnclosingClass(); 179 if (klass == null) { 180 return false; 181 } 182 return klass == enclosing; 183 } 184 }); 185 } 186 187 @Override 188 public Builder annotatedWith(final Class<? extends Annotation> annotation) { 189 Retention retention = annotation.getAnnotation(Retention.class); 190 if (retention == null || retention.value() != RetentionPolicy.RUNTIME) { 191 throw new IllegalStateException("Cannot filter annotated with annotation without retention policy" 192 + " set to RUNTIME: " + annotation.getName()); 193 } 194 return satisfying(new Predicate() { 195 @Override 196 public boolean matches(Class<?> klass) { 197 return klass.isAnnotationPresent(annotation); 198 } 199 }); 200 } 201 202 @Override 203 public UnionBuilder withModifiers(final int modifiers) { 204 return satisfying(new Predicate() { 205 @Override 206 public boolean matches(Class<?> klass) { 207 return (klass.getModifiers() & modifiers) != 0; 208 } 209 }); 210 } 211 212 @Override 213 public UnionBuilder withoutModifiers(final int modifiers) { 214 return satisfying(new Predicate() { 215 @Override 216 public boolean matches(Class<?> klass) { 217 return (klass.getModifiers() & modifiers) == 0; 218 } 219 }); 220 } 221 222 @Override 223 public UnionBuilder withPublicDefaultConstructor() { 224 return satisfying(new Predicate() { 225 @Override 226 public boolean matches(Class<?> klass) { 227 try { 228 Constructor<?> constructor = klass.getConstructor(); 229 return (constructor.getModifiers() & Modifier.PUBLIC) != 0; 230 } catch (NoSuchMethodException | SecurityException e) { 231 return false; 232 } 233 } 234 }); 235 } 236 237 @Override 238 public boolean matches(Class<?> klass) { 239 for (Predicate predicate : predicates) { 240 if (!predicate.matches(klass)) { 241 return false; 242 } 243 } 244 return true; 245 } 246 } 247 248 /** 249 * Returns a builder for a filter which satisfies all selected predicates. 250 */ 251 public static UnionBuilder only() { 252 return new Builder(); 253 } 254 255 /** 256 * Returns a filter which satisfies any of the selected predicates. 257 * @param alternatives alternative predicates 258 * @return filter which satisfies any of the provided predicates 259 */ 260 public static FilterBuilder any(final Predicate... alternatives) { 261 return new CommonFilterBuilder() { 262 @Override 263 public boolean matches(Class<?> klass) { 264 for (Predicate alternative : alternatives) { 265 if (alternative.matches(klass)) { 266 return true; 267 } 268 } 269 return false; 270 } 271 }; 272 } 273}