You are not logged in. Click here to log in.

Application Lifecycle Management

Search In Project

Search inClear

Tags:  not added yet

Annotation Processing Tool

By using source code Annotations within (modeling) source code, user-defined meta data that explain language constructs such variable/field declarations, a subroutine/function call, or a class in object-oriented languages.

The ngmf.ap tool is being developed to allow for a flexible source code adaptation and transformation. It has the following features:

  • Supports source files written in any programming language
  • Allows the handling of any annotation definition, there is no predifined annotation set
  • Allows the adaptation to any output transformation by adding custom handlers.

Some of the use cases are:

  • Generating Wiki documentation from Fortran component sources
  • Generating JNA Java proxy bindings from C sources
  • Generating Java OpenMI bindings from OMS components
  • Importing a component library into a data base and generate medata data records.
  • Generate CCA bindings from OMS components.

The ngmf.ap tool is written in Java.

Anntotation Basics

The Annotation syntax being used is following mostly the Java language annotation standard. However those annotations can be inserted in any source file. There are three main annotation flavors:

1. Tagging annotation

A tagging annotation does not have any argument, parenthesis are optional.

// @Execute
  int foo() {
  ...

2. Single value annotation

A single value annotation has a name and one argument in parenthesis. An argument could be a numerical literal or a String.
// @Description("A foo..")
  int foo() {
  ...

3. KVP Annotation

A key-value-pair annotation has an annotation name and a number of key/value arguments. This number is not limited. KVPs are comma separated and provide usually more detailed information about the annotation.

! F90 example
! @Author(name="Joe", email="j@foo.com")
  function foo()
  ...

Example

Lets introduce the tool by an example step by step.

Annotating a source file.

Lets suppose there is a file like the C (hamon.c) source below, and you want to create a JNA proxy 'interface' for it.
/* 
 * File:   testc.c
 * Author: od
 *
 * Created on August 11, 2008, 4:39 PM
 */

//@Description("Hamon ET")            
//@Library("ET")                       
//@Function
double hamon(
        //@Arg
        double daylen,
        //@Arg
        double temp,
        //@Arg
        int days)
{
    double Wt = 4.95 * exp(0.062 * temp) / 100.;
    double D2 = (daylen / 12.0) * (daylen / 12.0);
    double potET = 0.55 * days * D2 * Wt;
    if (potET <= 0.0) {
        potET = 0.0;
    }

    ...

There are a few things to know when annotating source files.

  • Annotations are usually one-liner, however they can span multiple lines (KVP Annotations)
  • Embed the annotation into a language comment
  • The line after an annotation should be the language construct to annotate or another annotation. In other words: annotations are always attached to a language level construct, they do not exist on their own, such as C pragmas.

Writing a new or using an existing Annotation handler

  • A annotation name should always be known to the Handler that is being used to process it.

If you want to write a new Handler for your set of Annotation, create a java class and implement the Handler interface provided in the ap package:

package ap;

import java.io.File;
import java.util.Map;

/**
 * Annotation Handler.
 *
 * Implement this interface to handle annotations. The processor will
 * call this handler once with start(), then handle(..) n-times for n annotations,
 * and finally done() when done with this file.
 *
 * @author od
 */
public interface Handler {

    /**
     * Called by the processor when starting processing annotations for a file.
     *
     * @param file the file that is processed.
     */
    void start(File file);

    /**
     * Handles a single annotation.
     *
     * @param annName        the name of the annotation
     * @param annValue       annotations KVPs in a hashmap. It is null if there
     *                       is no annotation argument (tagging annotation), 
     *                       annValue.get("value") will
     *                       return a single value argument.
     * @param nextSrcLine    the souce line that follows the annotations, if there
     *                       is none (e.g. another annotation follows), the 
     *                       value will be null.
     */
    void handle(String annName, Map<String, String> annValue, String nextSrcLine);

    /**
     * Done with file processing.
     * @throws Exception 
     */
    void done() throws Exception;
}

  • compile the created class add it to the classpath

You can also use an existing handler that is a part of the ap.handler package. For example The JNA handler ap.handler.JNA is part if the ngmf.ap.jar file.

public class JNA implements Handler {

    String libname;
    String javaFunction = "";
    String description = "";
    File original;

    @Override
    public void handle(String name, Map<String, String> kvp, String nextLine) {
        if (name.equals("Description")) {
            description = Processor.trimQuotes(kvp.get("value"));
        } else if (name.equals("Library")) {
            libname = Processor.trimQuotes(kvp.get("value"));
        } else if (name.equals("Function")) {
            javaFunction = javaFunction + "\n    " + nextLine.trim();
        } else if (name.equals("Arg")) {
            javaFunction = javaFunction + nextLine.trim();
            if (nextLine.trim().endsWith(")")) {
                javaFunction = javaFunction + ";";
            }
        }
    }

    @Override
    public void start(File file) {
        original = file;
    }

    @Override
    public void done() throws Exception {
        File out = Processor.newFileName(original, "", "", "java");
        PrintStream w = new PrintStream(out);

        System.out.println("Generating " + out);
        w.println("// Generated JNA proxy from '" + original.getPath() + "'");
        w.println("import com.sun.jna.Library;");
        w.println("import com.sun.jna.Native;");
        w.println();
        w.println("// " + description);
        w.println("public interface " + libname + " extends Library {");
        w.println("    // library mapping reference");
        w.println("    " + libname + " lib = (" + libname + ") Native.loadLibrary(\"" 
              + libname + "\",  " + libname + ".class);");
        w.print("    // DLL function(s)");
        w.println("    " + javaFunction);
        w.println("}");
        w.close();
    }
}

Processing with Ant

Lets suppose you'd like to process the file (hamon.c) and generate the JNA bindings for it.

Write an ant task, add the ap library to it with its builtin Ant task.

<?xml version="1.0" encoding="UTF-8"?>
<project name="changeme" default="all" basedir=".">
    <target name="all">
        <!-- introduce the task to ant -->
        <taskdef resource="ap/tasks.properties" classpath="dist/ngmf.ap.jar"/>
        <!-- process the file set (just hamon.c) using the JNA handler -->
        <ap handler="ap.handler.JNA">
            <fileset dir="." casesensitive="yes">
                <filename name="test/**/hamon*.c"/>
            </fileset>
        </ap>
    </target>
</project>

Running the Ant task above, a new file hamon.java will be create next to hamon.c with the following content:

// Generated JNA proxy from 'C:\od\projects\ngmf.ap\test\ap\hamon.c'
import com.sun.jna.Library;
import com.sun.jna.Native;

// Hamon ET
public interface ET extends Library {
    // library mapping reference
    ET lib = (ET) Native.loadLibrary("ET",  ET.class);
    // DLL function(s)    
    double hamon(double daylen,double temp,int days);
}

Limitations

  • Early stage, not tested much!!!!!