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}