/* Copyright (c) 2008 Google Inc.
*
* 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.google.gdata.data.apt;
import com.google.gdata.data.Kind;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.AnnotationProcessorFactory;
import com.sun.mirror.apt.Filer;
import com.sun.mirror.apt.Messager;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.ClassDeclaration;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.type.ClassType;
import com.sun.mirror.type.InterfaceType;
import com.sun.mirror.util.Types;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The DataAnnotationProcessFactory class supports auto-generation of
* metadata about GData Kind data model classes. Generation is handled
* by implementing the {@link AnnotationProcessor} interfaces defined for
* the Sun Annotation Processing Tool (APT).
* <p>
* The processing can be extended later for any other data-model related
* annotation usage.
*
*
*/
public class DataAnnotationProcessorFactory
implements AnnotationProcessorFactory {
/**
* The list of annotation types handled by this factory.
*/
private static List<String> supportedTypes =
Collections.unmodifiableList(
Arrays.asList("com.google.gdata.data.*"));
/**
* The DataAnnotationProcessor class provides annotation processing for the
* {@link Kind.Term} annotation. It generates schema-to-handler
* mapping files in META-INF/services which can be used to discover
* the {@link Kind.Adaptor} class for a given Kind at runtime.
*/
private static class DataAnnotationProcessor implements AnnotationProcessor {
private AnnotationProcessorEnvironment env;
private DataAnnotationProcessor(AnnotationProcessorEnvironment env) {
this.env = env;
}
/**
* Handles the processing and metadata generation associated with the
* {@link Kind.Term} annotation.
*/
private void handleKindTerms() {
Messager msg = env.getMessager();
Filer filer = env.getFiler();
AnnotationTypeDeclaration kindDecl = (AnnotationTypeDeclaration)
env.getTypeDeclaration(Kind.Term.class.getName());
if (kindDecl == null) {
msg.printError("Unable to find the Kind.Term annotation type");
return;
}
Types typeUtils = env.getTypeUtils();
TypeDeclaration intfDecl =
env.getTypeDeclaration(Kind.Adaptor.class.getName());
InterfaceType declaratorType =
(InterfaceType)typeUtils.getDeclaredType(intfDecl);
// Used to build a mapping from kind term values to adaptor class names.
Map <String, List<String>> adaptorMap =
new HashMap<String, List<String>>();
/*
* Phase 1: build an in-memory mapping from kind term values to
* the list of implementing adaptor class names.
*/
for (Declaration decl : env.getDeclarationsAnnotatedWith(kindDecl)) {
// Annotation is only valid on clasess
if (! (decl instanceof ClassDeclaration)) {
msg.printError(decl.getPosition(),
"@Kind.Term may only be used to annotate a class");
continue;
}
// The target class must implement Kind.Adaptor
ClassDeclaration classDecl = (ClassDeclaration)decl;
ClassType classType = (ClassType)typeUtils.getDeclaredType(classDecl);
if (!typeUtils.isAssignable(classType, declaratorType)) {
msg.printError(classDecl.getPosition(),
"Class annotated by @Kind.Term must implement Kind.Adaptor");
continue;
}
Kind.Term kindTerm = classDecl.getAnnotation(Kind.Term.class);
List<String> kindAdaptors = adaptorMap.get(kindTerm.value());
if (kindAdaptors == null) {
kindAdaptors = new ArrayList<String>();
adaptorMap.put(kindTerm.value(), kindAdaptors);
}
kindAdaptors.add(classDecl.toString());
}
/*
* Phase 2: write out a GData kind service mapping file for each
* term discovered in phase 1.
*/
for (String term : adaptorMap.keySet()) {
String kindService = Kind.getKindServiceName(term);
File servicePath = new File(kindService);
PrintWriter pw = null;
try {
pw = filer.createTextFile(Filer.Location.CLASS_TREE, "",
servicePath, null);
pw.println("# GData Kind Adaptors for " + term);
for (String adaptorClass : adaptorMap.get(term)) {
pw.println(adaptorClass);
}
} catch (IOException ioe) {
msg.printError("Unable to write kind metadata:" + servicePath);
ioe.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
}
msg.printNotice("Wrote kind metadata for " + term + " to "
+ servicePath);
}
}
public void process() {
handleKindTerms();
}
}
public Collection<String> supportedOptions() {
return Collections.emptyList();
}
public Collection<String> supportedAnnotationTypes() {
return supportedTypes;
}
public AnnotationProcessor getProcessorFor(
Set<AnnotationTypeDeclaration> atds,
AnnotationProcessorEnvironment env) {
return new DataAnnotationProcessor(env);
}
}