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 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.jsftemplating.annotation;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.AnnotationMirror;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.AnnotationTypeElementDeclaration;
import com.sun.mirror.declaration.AnnotationValue;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.MemberDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.Modifier;
import com.sun.mirror.declaration.ParameterDeclaration;
/**
* This is an AnnotationProcessor
for
* {@link Handler} annotations.
*
* @author Ken Paulsen ([email protected])
*/
public class HandlerAP implements AnnotationProcessor {
/**
* This is the constructor for the {@link Handler}
* AnnotationProcessor
. It expects the annotation to
* have a single value (called 'value', or have 'value' omitted) which
* specifies the identifier used to locate the class containing the
* annotation (the
* {@link com.sun.jsftemplating.component.factory.ComponentFactory}
* class).
*
* @param env The AnnotationProcessorEnvironment
.
* @param writer The PrintWriter
used for output.
*/
public HandlerAP(AnnotationProcessorEnvironment env, PrintWriter writer) {
_writer = writer;
_env = env;
}
/**
* This method will process the annotation. It produces a line using
* the PrintWriter
that contains the identifer and the
* class name which holds the annotation:
*
* [identifer]=[class name]
*/
public void process() {
// Temporary Variables
String key;
Object value;
String id;
List input;
List output;
// Get the supported annotation type (only 1)
AnnotationTypeDeclaration decl = (AnnotationTypeDeclaration)
_env.getTypeDeclaration(Handler.class.getName());
// Loop through the declarations that are annotated
for (Declaration dec : _env.getDeclarationsAnnotatedWith(decl)) {
// Loop through the annotations on the current declartion
for (AnnotationMirror mirror : dec.getAnnotationMirrors()) {
// Loop through the NVPs contained in the annotation
id = null;
input = null;
output = null;
for (Map.Entry entry : mirror.getElementValues().entrySet()) {
// At this point I'm processing a "Handler" annotation
// it may contain "id", "input", "output"
key = entry.getKey().getSimpleName();
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;
}
}
// Sanity Check
if (id == null) {
_env.getMessager().printError(
dec.getPosition(),
"'id' not specified for annotation of method: '"
+ ((MemberDeclaration) dec).getDeclaringType().
getQualifiedName()
+ "." + dec.getSimpleName() + "'.");
}
// Check for duplicate handler definitions
if (handlers.get(id) != null) {
_env.getMessager().printWarning(
dec.getPosition(),
"Handler with 'id' of '" + id + "' is declared more than once!'");
}
handlers.put(id, id);
// Record class / method names (and javadoc comment)
_writer.println(formatComment(dec.getDocComment()));
_writer.println(id + ".class="
+ ((MemberDeclaration) dec).getDeclaringType().
getQualifiedName());
_writer.println(id + ".method=" + dec.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 (!dec.getModifiers().contains(Modifier.PUBLIC)) {
_env.getMessager().printError(
dec.getPosition(),
"Annotated method: '"
+ ((MemberDeclaration) dec).getDeclaringType().
getQualifiedName()
+ "." + dec.getSimpleName()
+ "' should be declared public.");
}
// Make sure correct args are specified
Collection params =
((MethodDeclaration) dec).getParameters();
String pdec = params.iterator().next().getType().toString();
if ((params.size() != 1) || !pdec.equals(
"com.sun.jsftemplating.layout.descriptors.handler.HandlerContext")) {
_env.getMessager().printError(
dec.getPosition(),
"Annotated method: '"
+ ((MemberDeclaration) dec).getDeclaringType().
getQualifiedName()
+ "." + dec.getSimpleName()
+ "' must contain a single parameter of type 'com."
+ "sun.jsftemplating.layout.descriptors.handler."
+ "HandlerContext', instead type: '"
+ pdec + "' was found.");
}
// 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.
}
}
}
/**
* 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 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
StringBuffer buf = new StringBuffer("\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();
}
private PrintWriter _writer = null;
private AnnotationProcessorEnvironment _env = null;
private Map handlers = new HashMap();
}