/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * 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.jboss.errai.reflections; import static com.google.common.collect.Multimaps.newSetMultimap; import static com.google.common.collect.Multimaps.synchronizedSetMultimap; import java.lang.annotation.Inherited; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; import org.jboss.errai.reflections.scanners.ConvertersScanner; import org.jboss.errai.reflections.scanners.FieldAnnotationsScanner; import org.jboss.errai.reflections.scanners.MethodAnnotationsScanner; import org.jboss.errai.reflections.scanners.ResourcesScanner; import org.jboss.errai.reflections.scanners.Scanner; import org.jboss.errai.reflections.scanners.SubTypesScanner; import org.jboss.errai.reflections.scanners.TypeAnnotationsScanner; import org.jboss.errai.reflections.scanners.reg.ScannerRegistry; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Collections2; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; /** * stores metadata information in multimaps * <p>use the different query methods (getXXX) to query the metadata * <p>the query methods are string based, and does not cause the class loader to define the types * <p>use {@link org.jboss.errai.reflections.Reflections#getStore()} to access this store */ public class Store { private final LoadingCache<String, Multimap<String, String>> loadingCache; public Store(final Configuration configuration) { this(configuration.getExecutorService() != null); } protected Store(final boolean parallelExecutor) { // if (parallelExecutor) { final CacheLoader<String, Multimap<String, String>> from = CacheLoader.from(new Function<String, Multimap<String, String>>() { public Multimap<String, String> apply(final String indexName) { return synchronizedSetMultimap(newSetMultimap(new HashMap<String, Collection<String>>(), new Supplier<Set<String>>() { public Set<String> get() { return Sets.newHashSet(); } })); } }); loadingCache = CacheBuilder.newBuilder().build(from); } protected Store() { this(false); } /** * get the values of given keys stored for the given scanner class */ public Set<String> get(final Class<? extends Scanner> scannerClass, final String... keys) { final Set<String> result = Sets.newHashSet(); final Multimap<String, String> map = get(scannerClass); for (final String key : keys) { result.addAll(map.get(key)); } return result; } /** * return the multimap store of the given scanner. not immutable */ public Multimap<String, String> get(final Scanner scanner) { return get(scanner.getName()); } /** * return the multimap store of the given scanner class. not immutable */ public Multimap<String, String> get(final Class<? extends Scanner> scannerClass) { return get(ScannerRegistry.getRegistry().getName(scannerClass)); } /** * return the multimap store of the given scanner name. not immutable */ public Multimap<String, String> get(final String scannerName) { try { return loadingCache.get(scannerName); } catch (ExecutionException e) { throw new RuntimeException("error loading from cache", e); } } /** * return the store map. not immutable */ public Map<String, Multimap<String, String>> getStoreMap() { return loadingCache.asMap(); } /** * merges given store into this */ void merge(final Store outer) { final ConcurrentMap<String, Multimap<String, String>> storeMap = loadingCache.asMap(); final ConcurrentMap<String, Multimap<String, String>> outerStoreMap = outer.loadingCache.asMap(); for (final String indexName : outerStoreMap.keySet()) { Multimap<String, String> stringStringMultimap = storeMap.get(indexName); if (stringStringMultimap == null) { storeMap.put(indexName, stringStringMultimap = HashMultimap.create()); } stringStringMultimap.putAll(outer.get(indexName)); } } /** * return the keys count */ public Integer getKeysCount() { Integer keys = 0; for (final Multimap<String, String> multimap : loadingCache.asMap().values()) { keys += multimap.keySet().size(); } return keys; } /** * return the values count */ public Integer getValuesCount() { Integer values = 0; for (final Multimap<String, String> multimap : loadingCache.asMap().values()) { values += multimap.size(); } return values; } //query /** * get sub types of a given type */ public Set<String> getSubTypesOf(final String type) { final Set<String> result = new HashSet<String>(); final Set<String> subTypes = get(SubTypesScanner.class, type); result.addAll(subTypes); for (final String subType : subTypes) { result.addAll(getSubTypesOf(subType)); } return result; } /** * get types annotated with a given annotation, both classes and annotations * <p>{@link java.lang.annotation.Inherited} is honored * <p><i>Note that this (@Inherited) meta-annotation type has no effect if the annotated type is used for anything other than a class. * Also, this meta-annotation causes annotations to be inherited only from superclasses; annotations on implemented interfaces have no effect.</i> */ public Set<String> getTypesAnnotatedWith(final String annotation) { return getTypesAnnotatedWith(annotation, true); } /** * get types annotated with a given annotation, both classes and annotations * <p>{@link java.lang.annotation.Inherited} is honored according to given honorInherited * <p><i>Note that this (@Inherited) meta-annotation type has no effect if the annotated type is used for anything other than a class. * Also, this meta-annotation causes annotations to be inherited only from superclasses; annotations on implemented interfaces have no effect.</i> */ public Set<String> getTypesAnnotatedWith(final String annotation, final boolean honorInherited) { final Set<String> result = new HashSet<String>(); if (isAnnotation(annotation)) { final Set<String> types = get(TypeAnnotationsScanner.class, annotation); result.addAll(types); //directly annotated if (honorInherited && isInheritedAnnotation(annotation)) { //when honoring @Inherited, meta-annotation should only effect annotated super classes and it's sub types for (final String type : types) { if (isClass(type)) { result.addAll(getSubTypesOf(type)); } } } else if (!honorInherited) { //when not honoring @Inherited, meta annotation effects all subtypes, including annotations interfaces and classes for (final String type : types) { if (isAnnotation(type)) { result.addAll(getTypesAnnotatedWith(annotation, false)); } else if (hasSubTypes(type)) { result.addAll(getSubTypesOf(type)); } } } } return result; } /** * get method names annotated with a given annotation */ public Set<String> getMethodsAnnotatedWith(final String annotation) { return get(MethodAnnotationsScanner.class, annotation); } /** * get fields annotated with a given annotation */ public Set<String> getFieldsAnnotatedWith(final String annotation) { return get(FieldAnnotationsScanner.class, annotation); } /** * get 'converter' methods that could effectively convert from type 'from' to type 'to' */ public Set<String> getConverters(final String from, final String to) { return get(ConvertersScanner.class, ConvertersScanner.getConverterKey(from, to)); } /** * get resources relative paths where simple name (key) equals given name */ public Set<String> getResources(final String key) { return get(ResourcesScanner.class, key); } /** * get resources relative paths where simple name (key) matches given namePredicate */ public Set<String> getResources(final Predicate<String> namePredicate) { final Set<String> keys = get(ResourcesScanner.class).keySet(); final Collection<String> matches = Collections2.filter(keys, namePredicate); return get(ResourcesScanner.class, matches.toArray(new String[matches.size()])); } /** * get resources relative paths where simple name (key) matches given regular expression * <pre>Set<String> xmls = reflections.getResources(".*\\.xml");</pre> */ public Set<String> getResources(final Pattern pattern) { return getResources(new Predicate<String>() { public boolean apply(final String input) { return pattern.matcher(input).matches(); } }); } //support /** * is the given type name a class. <p>causes class loading */ public boolean isClass(final String type) { //todo create a string version of this return !isInterface(type); } /** * is the given type name an interface. <p>causes class loading */ public boolean isInterface(final String aClass) { //todo create a string version of this return ReflectionUtils.forName(aClass).isInterface(); } /** * is the given type is an annotation, based on the metadata stored by TypeAnnotationsScanner */ public boolean isAnnotation(final String typeAnnotatedWith) { return getTypeAnnotations().contains(typeAnnotatedWith); } /** * is the given annotation an inherited annotation, based on the metadata stored by TypeAnnotationsScanner */ public boolean isInheritedAnnotation(final String typeAnnotatedWith) { return get(TypeAnnotationsScanner.class).get(Inherited.class.getName()).contains(typeAnnotatedWith); } /** * does the given type has sub types, based on the metadata stored by SubTypesScanner */ public boolean hasSubTypes(final String typeAnnotatedWith) { return getSuperTypes().contains(typeAnnotatedWith); } /** * get all super types that have stored sub types, based on the metadata stored by SubTypesScanner */ public Multiset<String> getSuperTypes() { return get(SubTypesScanner.class).keys(); } /** * get all annotations, based on metadata stored by TypeAnnotationsScanner */ public Set<String> getTypeAnnotations() { return get(TypeAnnotationsScanner.class).keySet(); } }