/*
* Copyright (c) 2009-2010 Clark & Parsia, LLC. <http://www.clarkparsia.com>
*
* 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 com.clarkparsia.empire.util.apt;
import com.sun.mirror.apt.AnnotationProcessorFactory;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.AnnotationProcessors;
import com.sun.mirror.apt.RoundCompleteListener;
import com.sun.mirror.apt.RoundCompleteEvent;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.ClassDeclaration;
import com.sun.mirror.util.SimpleDeclarationVisitor;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.Arrays;
import java.util.Collections;
import java.util.Properties;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import com.clarkparsia.empire.annotation.RdfsClass;
import com.complexible.common.collect.Iterables2;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import javax.persistence.NamedQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
/**
* <p>Implementation of an APT-based annotation processor to pull out Annotation information
* relevant to Empire, such as the classes using the {@link RdfsClass} or {@link NamedQuery} annotations. This
* information is written in Java Properties format to a file called "empire.config" in the same directory APT
* is executed from. This file can then be put into the current dir of the application using Empire</p>
*
* @author Michael Grove
* @since 0.1
* @version 0.7
* @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html">Sun APT Docs</a>
*/
public final class EmpireAnnotationProcessorFactory implements AnnotationProcessorFactory {
/**
* A map of annotation class names to the fully qualified class names of classes which have the specific annotation.
*/
private Map<String, Collection<String>> mAnnotationClassMap;
/**
* Create a new EmpireAnnotationProcessorFactory
*/
public EmpireAnnotationProcessorFactory() {
mAnnotationClassMap = new HashMap<String, Collection<String>>();
}
/**
* @inheritDoc
*/
public Collection<String> supportedOptions() {
return Collections.emptyList();
}
/**
* @inheritDoc
*/
public Collection<String> supportedAnnotationTypes() {
return Arrays.asList(RdfsClass.class.getName(),
NamedQuery.class.getName(),
NamedQueries.class.getName(),
NamedNativeQuery.class.getName(),
NamedNativeQueries.class.getName());
}
/**
* @inheritDoc
*/
public AnnotationProcessor getProcessorFor(final Set<AnnotationTypeDeclaration> theDeclarations,
final AnnotationProcessorEnvironment theEnvironment) {
AnnotationProcessor aProcessor;
if (theDeclarations.isEmpty()) {
aProcessor = AnnotationProcessors.NO_OP;
}
else {
aProcessor = new SimpleAnnotationProcessor(theEnvironment);
}
return aProcessor;
}
/**
* An AnnotationProcessor which will collect all the classes with the given set of annotations and write them
* into a Properties file when the processing is done.
*/
private class SimpleAnnotationProcessor implements AnnotationProcessor {
private AnnotationProcessorEnvironment mEnv;
private SimpleAnnotationProcessor(final AnnotationProcessorEnvironment theEnv) {
mEnv = theEnv;
mEnv.addListener(new RoundCompleteListener() {
public void roundComplete(final RoundCompleteEvent theEvent) {
Properties aProps = new Properties();
for (String aClass : mAnnotationClassMap.keySet()) {
aProps.setProperty(aClass, Joiner.on(",").join(mAnnotationClassMap.get(aClass)));
}
// TODO: configure where this file is written (and what it's named) based on an APT option
OutputStream aStream = null;
try {
aStream = new FileOutputStream(new File("empire.apt.config"));
aProps.store(aStream, "Empire Config generated by APT");
}
catch (IOException e) {
System.err.println("There was a failure generating Empire config using APT");
e.printStackTrace();
}
finally {
if (aStream != null) {
try {
aStream.flush();
aStream.close();
}
catch (IOException e) {
// oh well.
}
}
}
}
});
}
/**
* @inheritDoc
*/
public void process() {
for (String aClass : supportedAnnotationTypes()) {
Collection<String> aCollection = new HashSet<String>();
// I think APT can do this in multiple passes, so if we've already collected some information, lets
// reuse it.
if (mAnnotationClassMap.containsKey(aClass)) {
aCollection = mAnnotationClassMap.get(aClass);
}
Iterables2.each(mEnv.getDeclarationsAnnotatedWith((AnnotationTypeDeclaration) mEnv.getTypeDeclaration(aClass)),
new Collector(aCollection));
mAnnotationClassMap.put(aClass, aCollection);
}
}
}
/**
* Executable for applying a Visitor to a Declaration in order to collect the class declaration information
*/
private class Collector implements Predicate<Declaration> {
/**
* The visitor that will be applied
*/
private CollectorVisitor mVisitor;
/**
* Create a new Collector
* @param theCollection the collector the visitor will append it's information to.
*/
private Collector(final Collection<String> theCollection) {
mVisitor = new CollectorVisitor(theCollection);
}
/**
* @inheritDoc
*/
public boolean apply(Declaration theDeclaration) {
theDeclaration.accept(mVisitor);
return true;
}
}
/**
* APT Visitor implementation that will inspect the class declarations with the given annotation and collect
* the list of classes with the annotation.
*/
private static class CollectorVisitor extends SimpleDeclarationVisitor {
/**
* The list of class names with the annotation
*/
private Collection<String> mCollection;
/**
* Create a new CollectorVisitor
* @param theCollection the collection to add to
*/
private CollectorVisitor(final Collection<String> theCollection) {
mCollection = theCollection;
}
/**
* @inheritDoc
*/
public void visitClassDeclaration(ClassDeclaration theDeclaration) {
mCollection.add(theDeclaration.getQualifiedName());
}
}
}