@Override public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) { System.out.println("Processing annotations"); try { final Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Plugin.class); if (elements.isEmpty()) { System.out.println("No elements to process"); return false; } collectPlugins(elements); writeCacheFile(elements.toArray(new Element[elements.size()])); System.out.println("Annotations processed"); return true; } catch (final IOException e) { e.printStackTrace(); error(e.getMessage()); return false; } catch (final Exception ex) { ex.printStackTrace(); error(ex.getMessage()); return false; } }
private void collectPlugins(final Iterable<? extends Element> elements) { final Elements elementUtils = processingEnv.getElementUtils(); final ElementVisitor<PluginEntry, Plugin> pluginVisitor = new PluginElementVisitor(elementUtils); final ElementVisitor<Collection<PluginEntry>, Plugin> pluginAliasesVisitor = new PluginAliasesElementVisitor( elementUtils); for (final Element element : elements) { final Plugin plugin = element.getAnnotation(Plugin.class); if (plugin == null) { continue; } final PluginEntry entry = element.accept(pluginVisitor, plugin); final Map<String, PluginEntry> category = pluginCache.getCategory(entry.getCategory()); category.put(entry.getKey(), entry); final Collection<PluginEntry> entries = element.accept(pluginAliasesVisitor, plugin); for (final PluginEntry pluginEntry : entries) { category.put(pluginEntry.getKey(), pluginEntry); } } }
@Override public Collection<PluginEntry> visitType(final TypeElement e, final Plugin plugin) { final PluginAliases aliases = e.getAnnotation(PluginAliases.class); if (aliases == null) { return DEFAULT_VALUE; } final Collection<PluginEntry> entries = new ArrayList<>(aliases.value().length); for (final String alias : aliases.value()) { final PluginEntry entry = new PluginEntry(); entry.setKey(alias.toLowerCase(Locale.US)); entry.setClassName(elements.getBinaryName(e).toString()); entry.setName(Plugin.EMPTY.equals(plugin.elementType()) ? alias : plugin.elementType()); entry.setPrintable(plugin.printObject()); entry.setDefer(plugin.deferChildren()); entry.setCategory(plugin.category()); entries.add(entry); } return entries; }
@Override public PluginEntry visitType(final TypeElement e, final Plugin plugin) { Objects.requireNonNull(plugin, "Plugin annotation is null."); final PluginEntry entry = new PluginEntry(); entry.setKey(plugin.name().toLowerCase(Locale.US)); entry.setClassName(elements.getBinaryName(e).toString()); entry.setName(Plugin.EMPTY.equals(plugin.elementType()) ? plugin.name() : plugin.elementType()); entry.setPrintable(plugin.printObject()); entry.setDefer(plugin.deferChildren()); entry.setCategory(plugin.category()); return entry; }
private void verifyFakePluginEntry(final String name, final PluginEntry fake) { assertNotNull("The plugin '" + name.toLowerCase() + "' was not found.", fake); assertEquals(FakePlugin.class.getName(), fake.getClassName()); assertEquals(name.toLowerCase(), fake.getKey()); assertEquals(Plugin.EMPTY, p.elementType()); assertEquals(name, fake.getName()); assertEquals(p.printObject(), fake.isPrintable()); assertEquals(p.deferChildren(), fake.isDefer()); }
@Test public void testNestedPlugin() throws Exception { final Plugin p = FakePlugin.Nested.class.getAnnotation(Plugin.class); final PluginEntry nested = pluginCache.getCategory(p.category()).get(p.name().toLowerCase()); assertNotNull(nested); assertEquals(p.name().toLowerCase(), nested.getKey()); assertEquals(FakePlugin.Nested.class.getName(), nested.getClassName()); assertEquals(p.name(), nested.getName()); assertEquals(Plugin.EMPTY, p.elementType()); assertEquals(p.printObject(), nested.isPrintable()); assertEquals(p.deferChildren(), nested.isDefer()); }
/** * @since 2.1 */ public Map<String, List<PluginType<?>>> loadFromPackage(final String pkg) { if (Strings.isBlank(pkg)) { // happens when splitting an empty string return Collections.emptyMap(); } Map<String, List<PluginType<?>>> existing = pluginsByCategoryByPackage.get(pkg); if (existing != null) { // already loaded this package return existing; } final long startTime = System.nanoTime(); final ResolverUtil resolver = new ResolverUtil(); final ClassLoader classLoader = Loader.getClassLoader(); if (classLoader != null) { resolver.setClassLoader(classLoader); } resolver.findInPackage(new PluginTest(), pkg); final Map<String, List<PluginType<?>>> newPluginsByCategory = new HashMap<>(); for (final Class<?> clazz : resolver.getClasses()) { final Plugin plugin = clazz.getAnnotation(Plugin.class); final String categoryLowerCase = plugin.category().toLowerCase(); List<PluginType<?>> list = newPluginsByCategory.get(categoryLowerCase); if (list == null) { newPluginsByCategory.put(categoryLowerCase, list = new ArrayList<>()); } final PluginEntry mainEntry = new PluginEntry(); final String mainElementName = plugin.elementType().equals( Plugin.EMPTY) ? plugin.name() : plugin.elementType(); mainEntry.setKey(plugin.name().toLowerCase()); mainEntry.setName(plugin.name()); mainEntry.setCategory(plugin.category()); mainEntry.setClassName(clazz.getName()); mainEntry.setPrintable(plugin.printObject()); mainEntry.setDefer(plugin.deferChildren()); final PluginType<?> mainType = new PluginType<>(mainEntry, clazz, mainElementName); list.add(mainType); final PluginAliases pluginAliases = clazz.getAnnotation(PluginAliases.class); if (pluginAliases != null) { for (final String alias : pluginAliases.value()) { final PluginEntry aliasEntry = new PluginEntry(); final String aliasElementName = plugin.elementType().equals( Plugin.EMPTY) ? alias.trim() : plugin.elementType(); aliasEntry.setKey(alias.trim().toLowerCase()); aliasEntry.setName(plugin.name()); aliasEntry.setCategory(plugin.category()); aliasEntry.setClassName(clazz.getName()); aliasEntry.setPrintable(plugin.printObject()); aliasEntry.setDefer(plugin.deferChildren()); final PluginType<?> aliasType = new PluginType<>(aliasEntry, clazz, aliasElementName); list.add(aliasType); } } } final long endTime = System.nanoTime(); final DecimalFormat numFormat = new DecimalFormat("#0.000000"); final double seconds = (endTime - startTime) * 1e-9; LOGGER.debug("Took {} seconds to load {} plugins from package {}", numFormat.format(seconds), resolver.getClasses().size(), pkg); // Note multiple threads could be calling this method concurrently. Both will do the work, // but only one will be allowed to store the result in the outer map. // Return the inner map produced by whichever thread won the race, so all callers will get the same result. existing = pluginsByCategoryByPackage.putIfAbsent(pkg, newPluginsByCategory); if (existing != null) { return existing; } return newPluginsByCategory; }
@Override public boolean matches(final Class<?> type) { return type != null && type.isAnnotationPresent(Plugin.class); }
@Override public String toString() { return "annotated with @" + Plugin.class.getSimpleName(); }
/** * Tests the presence of certain annotations and converter key in a given Converter class * * @param clazz PatterConverter class to test * @param key Converter key to match */ private void testPatternAnnotation(Class<? extends AccessLogPatternConverter> clazz, String key) { assertTrue(clazz.isAnnotationPresent(Plugin.class)); assertTrue(clazz.isAnnotationPresent(ConverterKeys.class)); assertEquals(key, clazz.getAnnotation(ConverterKeys.class).value()[0]); }