/* This file is part of GNUnet. Copyright (C) 2011, 2012 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gnunet.construct; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.StandardLocation; import java.io.IOException; import java.io.Writer; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Set; /** * Creates a resource file 'MsgMap.txt' in the package 'org.gnunet.construct' of the source tree. */ @SupportedAnnotationTypes("org.gnunet.construct.UnionCase") @SupportedSourceVersion(SourceVersion.RELEASE_6) public class MessageIdAnnotationProcessor extends AbstractProcessor { private final Table idToMember = HashBasedTable.create(); @Override public boolean process(Set typeElements, RoundEnvironment roundEnvironment) { if (roundEnvironment.errorRaised()) { return false; } Types types = processingEnv.getTypeUtils(); Elements elements = processingEnv.getElementUtils(); if (roundEnvironment.processingOver()) { Filer filer = processingEnv.getFiler(); FileObject outfile; try { outfile = filer.createResource(StandardLocation.SOURCE_OUTPUT, "org.gnunet.construct", "MsgMap.txt"); } catch (IOException e) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not create MsgMap.txt"); return false; } try { Writer w = outfile.openWriter(); for (Table.Cell cell : idToMember.cellSet()) { w.write(cell.getRowKey() + '|' + cell.getColumnKey() + '=' + cell.getValue() + '\n'); } DateFormat fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); w.write("# generated " + fmt.format(new Date()) + '\n'); w.close(); } catch (IOException e) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not write MsgMap.txt"); } processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "message map written to " + outfile.toUri()); } else { for (Element e : roundEnvironment.getElementsAnnotatedWith(UnionCase.class)) { UnionCase ann = e.getAnnotation(UnionCase.class); // get the uppermost parent class that implements MessageUnion. This is the union type. // processingEnv.getElementUtils(). //processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "element :" + e.toString()); List parents = processingEnv.getTypeUtils().directSupertypes(e.asType()); TypeMirror msg = elements.getTypeElement("org.gnunet.construct.MessageUnion").asType(); TypeMirror unionInterface = null; for (TypeMirror p : parents) { if (types.isSubtype(p, msg)) { unionInterface = p; break; } } if (unionInterface == null) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format( "class %s annotated with @UnionCase does not implement an interface inheriting MessageUnion", e.getSimpleName())); return false; } String unionName = getClassName(types.asElement(unionInterface)); if (idToMember.contains(unionName, ann.value())) { String existingName = idToMember.get(unionName, ann.value()); processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format( "duplicate message id %s for union '%s: '%s' and '%s'", ann.value(), unionName, getClassName(e), existingName)); return false; } idToMember.put(unionName, ann.value(), getClassName(e)); } } return false; } /** * Get the fully qualified class name, where packages are seperated with '.', and * inner classes are separated with '$' * * @param e the Element representing a class * @return the fully qualified class name */ private String getClassName(Element e) { assert e.getKind().isClass(); String name = e.getSimpleName().toString(); String pkg = processingEnv.getElementUtils().getPackageOf(e).getQualifiedName().toString() + '.'; String outer = ""; while (((e = e.getEnclosingElement()) != null) && e.getKind().isClass()) { outer = String.format("%s$%s", e.getSimpleName(), outer); } return pkg + outer + name; } }