Table of Contents

-

Practical Information

How to subscribe to Spoon's mailing list

Go here and fill the form.

back

How to access Spoon's CVS

Go here and follow the instructions.

back

Basics

How to install and use SpoonJDT (Eclipse Plugin)

SpoonJDT is installed like any other Eclipse plugin through the Update Manager. The Spoon Update Site is http://spoon.gforge.inria.fr/eclipse/. Once the plugin is installed, you can deploy Spoonlets that perform specific processing. For more details, go here.

back

How to use Spoon in standalone mode (without SpoonJDT)

You can download the Spoon standalone version (here) that includes the Eclipse JDT compiler and can be applied to any Java program using command-line style or Ant (see next two entries). For both uses, you need to specify a list of compiled processor types to apply to your program, and define the location of the source code to be processed. Optionally, if you use templates, you have to specify the location of your template source files.

back

How to use Spoon with Ant

First, get the Spoon standalone version.

You need to set the Spoon jar and the compiled processors in the classpath reference (see ant taskdef).

<!-- define spoon task -->
<taskdef name="spoon" classname="spoon.SpoonTask" 
   classpathref= "classpath"/>

<!-- process some files -->
<spoon classpathref= "classpath" verbose= "true">
    <sourceSet dir= "${src}" includes= "x/y/z/src/" />
    <templateset dir= "${src}" includes= "x/y/z/template/" />
    <processor type= "x.y.z.MyProcessor1" />
    <processor type= "x.y.z.MyProcessor2" />
    <processor type= "x.y.z.MyProcessor3" />
    ...
</spoon>

<!-- process some files with a spoonlet rather than a processors list -->
<spoon classpathref= "classpath" verbose= "true" spoonlet="myspoonlet.jar">
    <sourceSet dir= "${src}" includes= "x/y/z/src/" />
</spoon>

back

How to use Spoon with the Java launcher

First, get the Spoon standalone version.

You can process program with the Java launcher. For instance, with the following command:

java spoon.Launcher -i x/y/z/src/ -t x/y/z/template/ 
  -p x.y.z.MyProcessor1;x.y.z.MyProcessor2;x.y.z.MyProcessor3...

Usage: java <launcher name> [option(s)]
Options : 
  [-h|--help]
  [-v|--verbose]
        Output messages about what the compiler is doing
  [--vvv]
        Generate all debugging info
  [--compliance <compliance>]
        set java compliance level (1,2,3,4,5 or 6) (default: 5)
  [(-s|--spoonlet) <spoonlet>]
        List of spoonlet files to load
  [(-i|--input) <input>]
        List of path to sources files
  [(-p|--processors) <processors>]
        List of processor's qualified name to be used
  [(-t|--template) <template>]
        list of path to templates java files
  [(-o|--output) <output>]
        specify where to place generated java files (default: spooned)
  [--properties <properties>]
        Directory to search for spoon properties files
  [<class>]
        class to launch within the Spoon context (Main class)
  [arguments1 arguments2 ... argumentsN]
        parameters to be passed to the main method
  [--no]
        disable output printing
  [-c|--compile]
        compile generated sources
  [(-b|--build) <build>]
        specify where to place generated class files (default: spoonBuild)
  [-g|--gui]
        show spoon model after processing

back

How to use Spoon with Maven

A Maven plugin by David Bernard is on its way! It is avalaible in beta at http://alchim.sf.net/spoon-maven-plugin/.

back

How to write your own processor(s)

You need to get the standalone version Spoon jar (here) and add it to the build path of your Java project. Then you have to subclass the class spoon.processing.AbstractProcessor and implement the process method. This class is parameterized by the type of program element you want to process. These types are those of the Spoon's Java metamodel defined in the spoon.reflect.declaration package and spoon.reflect.code package. For example, to process all the Java program elements, you can write the following processor:

import spoon.processing.AbstractProcessor;
import spoon.reflect.declaration.CtElement;

public class MyProcessor extends AbstractProcessor<CtElement> {
  public void process(CtElement element) {
    // do your processing here
  }
}

In the process method, you can access the currenly processed element passed as a parameter. Spoon automatically scan all the elements of the target program so that you do not have to implement the scanning yourself. On contrary to APT or JSR 269, you can also modify the program while scanning it. As an example, the following processor reports warnings when it meet undocumented public methods:

import spoon.processing.AbstractProcessor;
import spoon.processing.Severity;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.ModifierKind;

public class MyProcessor extends AbstractProcessor<CtMethod> {
  public void process(CtMethod method) {
    if (method.getModifiers().contains(ModifierKind.PUBLIC)
        && method.getDocComment() == null) {
      getFactory().getEnvironment().report(
           Severity.WARNING, method,"undocumented public method");
    }
  }
}

Once compiled, you can apply your processor direclty with the Java launcher or Ant (here), or you can package it in a Spoonlet in order to deploy it in Eclipse (here).

back

How to deploy your processors in a Spoonlet for Eclipse

You can deploy a set of processors to be applied to an Eclipse Java project. To do so, you have to create a Spoonlet XML descriptor called 'spoon.xml'. This file should be placed in the jar file used to distribute your spoonlet (the path does not matter). This Spoonlet jar file can be deployed using the SpoonJDT plugin (see ). Note that if you are developping your processors in an Eclipse Java project, you do not need to package them in a jar file, since the SpoonJDT plugin can deploy a Spoonlet by referencing a 'spoon.xml' file within the current Eclipse's workspace.

The DTD of the 'spoon.xml' file is available here

Example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE spoon SYSTEM "http://spoon.gforge.inria.fr/pub/xml/spoonlet.dtd" >

<spoon>
  <processor name="A short description"
     class="x.y.z.MyProcessor" active="true"
     doc="A documentation on what the processor is doing.">
  </processor>
</spoon>

back

How to internationalize a Spoonlet

You can use a ResourceBundle named "spoonlet" in your code to internationalize your application. The processor attribute name and doc starting with '%' will be substitued by values found in your properties files with default locale.

More information about java internationalization here.

Example:

spoonlet_en.properties file:

Idiom = Class implements Cloneable
Idiom_doc = Class implements Cloneable but does not define or use clone method.

extract of spoon.xml file:

<processor active="true"
	name="%Idiom"
	class="spoon.vsuite.findbugs.am.Idiom"
	doc="%Idiom_doc"/>

Sample java code:

ResourceBundle messages = ResourceBundle.getBundle("spoonlet",Locale.getDefault());
System.out.println(messages.getString("Idiom"));

back

How to process annotations like with APT or JSR 269

Spoon is fully compatible with annotations and you can process any program element, including annotations. Even simpler, you can declare that you want to process a certain annotation type by subclassing the special kind of processor spoon.processing.AbstractAnnotationProcessor. For instance, to process the methods annotated with @SuppressWarnings:

import spoon.processing.AbstractAnnotationProcessor;
import spoon.reflect.declaration.CtMethod;

public class MyAnnotationProcessor extends 
    AbstractAnnotationProcessor<SuppressWarnings,CtMethod> {
  public void process(SuppressWarnings a,CtMethod method) {
    // do the processing
  }
}

back

Advanced

How to preserve the transformed source code formatting and one-line comments

There is a way to preserve comments and formatting of the existing code. However, it requires to use the code fragment API.

The idea of code fragments is that you indicate the changes you make in the code at the compilation unit level. For example to replace an expression e:

public void process(CtExpression e) {
  // gets the compilation unit
  CompilationUnit cu=e.getPosition().getCompilationUnit();
  // creates an initialize the code fragment
  SourceCodeFragment fragment = new SourceCodeFragment();
  // the fragment will start to be printed out at the original 
  // start position of the expression
  fragment.position = e.getPosition().getSourceStart();
  // here we replace the whole expression
  // note: to insert, just leave replacementLength to 0 (default)
  fragment.replacementLength = 
    e.getPosition().getSourceEnd() - e.getPosition().getSourceStart();
  // here put whatever code you want to replace the expression with...
  fragment.code="...";
  // now just add the code fragment to the compilation unit
  cu.addSourceCodeFragment(fragment); 
  // you can add as many code fragments as you wish
}

You then just need to start Spoon with the -f option (--fragments). In this mode, all the chages in the AST will ignored and the source code will be changed only when code fragments are found on the compilation units. Note that this feature is not supported (yet) by the Eclipse plugin (so you need to run Spoon in standalone).

How to implement and deploy configurable processors

To create a configurable processor, you must define properties in your processors that Spoon will fill automatically with some values found in XML files. In processors, you define properties by annotating a field with @spoon.processing.Property. The field can be a primitive value (including java.lang.String), a reference, or a collection/array of those. To set the default value for the property, you can affect a value in the field declaration.

Properties files in standalone mode

In standalone mode (no Eclipse plugin), the properties can be stored in XML files (one for each processor) - see the DTD here. Property files should be named with the fully-qualified class name of processor (with the xml extension). You can configure the location of files with option --properties location in command-line or <spoon properties="location"> with Ant.

Properties in Spoonlets

If you create a Spoonlet to package your processors, properties default values have to be defined in the 'spoon.xml' deployment descriptor. Note that Spoonlets can be used in standalone mode or with Eclipse or any other Spoonlet containers. Packaging Spoonlets for Eclipse is explained here.

Examples

This is a sample processor with properties:

package test;

import java.util.Arrays;
import spoon.processing.AbstractManualProcessor;
import spoon.processing.Property;

public class Sample extends AbstractManualProcessor {
  @Property
  String[] spooners = new String[] { "none" };

  @Property
  double a;

  public void process() {
    System.out.println(Arrays.asList(spooners));
    System.out.println(a);
  }
}

And its associated property file:

<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "http://spoon.gforge.inria.fr/pub/xml/properties.dtd">

<properties>
  <property name="spooners">
    <value>Yoda</value>
    <value>Padawan</value>
  </property>
  <property name="a" value="5.0" />
</properties>

If you run this processor with Spoon in standalone mode by specifying the location of the property file, you should get:

[Yoda, Padawan]
5.0
Done

When using Spoonlets, the Spoonlet deployment descriptor would look like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE spoon SYSTEM "http://spoon.gforge.inria.fr/pub/xml/spoonlet.dtd" >

<spoon>
  <processor name="Properties test"
     class="test.Sample" active="true"
     doc="Prints out the contents of the properties.">
    <property name="spooners">
      <value>Yoda</value>
      <value>Padawan</value>
    </property>
    <property name="a" value="5.0" />
  </processor>
</spoon>

back

How to implement program transformations with well-typed Templates

See the section Generative Programming with Spoon of the Tutorial.

back

How to prevent Annotation processors from consuming the annotations that they process

By default, whenever an Annotation Processor processes a CtElement it will consume (delete) the processed annotation from it. If you want the annotation to be kept, override the init() method from the AbstractAnnotationProcessor class, and call the protected method clearConsumedAnnotationTypes like so:

  @Override
  public void init() {
    super.init();
    clearConsumedAnnotationTypes();
  }

back

How to compare and create type references in a type-safe way

Use actual classes instead of strings.

CtTypeReference t=...
if(t.getActualClass()==int.class) { ... }
Factory f=...
t=f.Type().createReference(int.class);

back