All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jsftemplating.annotation.HandlerAP Maven / Gradle / Ivy

/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://jsftemplating.dev.java.net/cddl1.html or
 * jsftemplating/cddl1.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at jsftemplating/cddl1.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006-2014 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.jsftemplating.annotation;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.SimpleElementVisitor6;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.StandardLocation;

/**
 * A JSR 169 compliant annotation processor for Handler annotation
 * This is required for JDK8+, since APT has been deprecated.
 * @author Romain Grecourt
 */
@SupportedAnnotationTypes(value = {
  "com.sun.jsftemplating.annotation.Handler",
  "com.sun.jsftemplating.annotation.HandlerInput",
  "com.sun.jsftemplating.annotation.HandlerOutput"})
@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedOptions(value = {
    "AnnotationVerifier.Annotations",
    "AnnotationVerifier.Baseclasses",
    "AnnotationVerifier.ClassAnnotation.Mappings"})
public class HandlerAP extends AbstractProcessor {

//  public static final String HANDLER_FILE = "Handler.map";
  public static final String HANDLER_FILE = "META-INF/jsftemplating/Handler.map";
  private PrintWriter writer = null;
  private boolean _setup = false;
  private Map handlers = new HashMap();

    private boolean setup() {
    if (_setup) {
      // Don't do setup more than once
      return true;
    }
    try {
      // Create factory mapping file
      writer = getMapWriter();
    } catch (IOException ex) {
      StringWriter buf = new StringWriter();
      ex.printStackTrace(new PrintWriter(buf));
      processingEnv.getMessager().printMessage(
          Diagnostic.Kind.ERROR,
          String.format("Unable to write %s file while processing @FormatDefinition annotation %s",
              HANDLER_FILE,
              buf.toString()));
      return false;
    }
    _setup = true;
    return _setup;
  }

  private PrintWriter getMapWriter() throws IOException {
    PrintWriter _writer = null;
    ClassLoader cl = this.getClass().getClassLoader();
    URL url;
    for (Enumeration urls = cl.getResources(HANDLER_FILE);
        urls.hasMoreElements() && (_writer == null);) {
      url = urls.nextElement();
      if ((url != null) && new File(url.getFile()).canRead()) {
        // Append to the existing file...
        _writer = new PrintWriter(new FileOutputStream(url.getFile(), true));
      }
    }
    if (_writer == null) {
      // File not found, create a new one...
      FileObject  fo = processingEnv.getFiler().createResource(
          StandardLocation.CLASS_OUTPUT,
          "",
          HANDLER_FILE);
      _writer = new PrintWriter(fo.openWriter());
      return _writer;
    }
    return _writer;
  }

  private static final class ElementVisitor extends SimpleElementVisitor6 {

    @Override
    public TypeElement visitType(TypeElement e, Void p) {
      return e;
    }
  }

  private static final class ExecutableElementVisitor extends SimpleElementVisitor6 {

    @Override
    public ExecutableElement visitExecutable(ExecutableElement e, Void p) {
      return e;
    }
  }

  private static TypeElement getTypeElement(Element elt) {
    return elt.accept(new ElementVisitor(), null);
  }

  private static TypeElement getDeclaringTypeElement(Element elt) {
    return getTypeElement(elt.getEnclosingElement());
  }

  private String getJavadocComments(Element elt) {
    return processingEnv.getElementUtils().getDocComment(elt);
  }

  /**
   * 

* This is a helper method that writes out property lines to represent * either a HandlerInput or a HandlerOutput. The type * that is passed in is expected to be either input or * output.

*/ private void writeIOProperties(String id, String type, List ioList) { int cnt = 0; for (AnnotationValue ioVal : ioList) { // Process each @HandlerInput annotation... for (Map.Entry prop : ((AnnotationMirror) ioVal.getValue()).getElementValues().entrySet()) { // Look at each "param": @Handlerput(param=) writer.println(id + "." + type + "[" + cnt + "]." + prop.getKey().getSimpleName() + "=" + convertClassName(prop.getValue().getValue().toString())); } cnt++; } } /** *

* This method attempts to convert the given clsName to * a valid class name. The issue is that arrays appear something like * "java.lang.String[]" where they should appear * "[Ljava.lang.String;".

*/ private String convertClassName(String str) { int idx = str.indexOf("[]"); if (idx == -1) { // For not only worry about Strings that contain array brackets return str; } // Count []'s int count = 0; while (idx != -1) { str = str.replaceFirst("\\[]", ""); idx = str.indexOf("[]"); count++; } // Generate new String String brackets = ""; for (idx = 0; idx < count; idx++) { brackets += "["; } // Return something of the format: [Ljava.lang.String; return brackets + "L" + str + ";"; } /** *

* This method strips off HTML tags, converts "<" and ">", * inserts '#' characters in front of each line, and ensures there * are no trailing returns.

*/ private String formatComment(String javadoc) { if (javadoc == null) { // No JavaDoc, return return ""; } // First trim off extra stuff int idx = javadoc.indexOf("@param"); if (idx > -1) { // Ignore @param stuff javadoc = javadoc.substring(0, idx); } javadoc = javadoc.trim(); // Now process the String StringBuilder buf = new StringBuilder("\n# "); int len = javadoc.length(); char ch; idx = 0; while (idx < len) { ch = javadoc.charAt(idx); switch (ch) { case '&': if ((idx + 3) < len) { if ((javadoc.charAt(idx + 2) == 't') && (javadoc.charAt(idx + 3) == ';')) { if (javadoc.charAt(idx + 1) == 'g') { buf.append('>'); idx += 3; } else if (javadoc.charAt(idx + 1) == 'l') { buf.append('<'); idx += 3; } } } break; case '<': idx++; while ((idx < len) && (javadoc.charAt(idx) != '>')) { idx++; } break; case '>': idx++; while ((idx < len) && (javadoc.charAt(idx) != '<')) { idx++; } break; case '\n': case '\r': if (((idx + 1) > len) && ((javadoc.charAt(idx + 1) == '\n') || (javadoc.charAt(idx + 1) == '\r'))) { idx++; } buf.append("\n# "); break; default: buf.append(ch); } idx++; } // Return the stripped javadoc return buf.toString(); } @Override public SourceVersion getSupportedSourceVersion() { if (SourceVersion.latest().compareTo(SourceVersion.RELEASE_6) > 0) { return SourceVersion.valueOf("RELEASE_7"); } else { return SourceVersion.RELEASE_6; } } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { String key; Object value; String id; List input; List output; setup(); for (Element decl : roundEnv.getElementsAnnotatedWith(Handler.class)) { for (AnnotationMirror an : decl.getAnnotationMirrors()) { // Loop through the NVPs contained in the annotation id = null; input = null; output = null; for (Map.Entry entry : an.getElementValues().entrySet()) { // At this point I'm processing a "Handler" annotation // it may contain "id", "input", "output" key = entry.getKey().getSimpleName().toString(); value = entry.getValue().getValue(); if (key.equals(Handler.ID)) { // Found 'id', save it id = value.toString(); } else if (key.equals(Handler.INPUT)) { // Found inputs input = (List) value; } else if (key.equals(Handler.OUTPUT)) { // Found outputs output = (List) value; } } TypeElement te = getTypeElement(decl); TypeElement teDecl = getDeclaringTypeElement(decl); // Sanity Check if (id == null) { processingEnv.getMessager().printMessage( Kind.ERROR, String.format("'id' is not specified for annotation of method: %s.%s", te.getQualifiedName().toString(), te.getSimpleName().toString()), decl); } // Check for duplicate handler definitions if (handlers.get(id) != null) { processingEnv.getMessager().printMessage(Kind.WARNING, String.format( "Handler with 'id' of '%s' is declared more than once!'", id), decl); } handlers.put(id, id); // Record class / method names (and javadoc comment) writer.println(formatComment(getJavadocComments(decl))); writer.println(String.format("%s.class=%s", id, teDecl.getQualifiedName())); writer.println(String.format("%s.method=%s",id,decl.getSimpleName())); // Now record inputs for this handler... if (input != null) { writeIOProperties(id, "input", input); } // Now record outputs for this handler... if (output != null) { writeIOProperties(id, "output", output); } // Method signature checks... // Make sure method is accessible (public) if (!decl.getModifiers().contains(Modifier.PUBLIC)) { processingEnv.getMessager().printMessage(Kind.ERROR, String.format("Annotated method: %s.%s should be declared public", teDecl.getQualifiedName().toString(), decl.getSimpleName().toString()), decl); } // Make sure correct args are specified ExecutableElement exe = decl.accept(new ExecutableElementVisitor(), null); List params = exe.getParameters(); String pdec = params.iterator().next().asType().toString(); if ((params.size() != 1) || !pdec.equals("com.sun.jsftemplating.layout.descriptors.handler.HandlerContext")) { processingEnv.getMessager().printMessage(Kind.ERROR, String.format("Annotated method: %s.%s must contain a single parameter of type 'com.sun.jsftemplating.layout.descriptors.handler.HandlerContext', instead type: %s was found", teDecl.getQualifiedName(), decl.getSimpleName(), pdec), decl); } // FIXME: Consider an alternate method declaration that annotates a pojo method // @Handler(id="foo") // public String method(String a, String b, String c) // annotates a handler "foo" with 3 inputs (a:String, b:String, c:String) and 1 output "result:String" // Will need a special way to invoke this. } } if(_setup){ writer.close(); } return roundEnv.processingOver(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy