Skip to content

Commit

Permalink
Merge pull request #342 from GerardPaligot/getting_started
Browse files Browse the repository at this point in the history
  • Loading branch information
monperrus committed Oct 6, 2015
2 parents 5a20172 + c614bde commit 4259de5
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 45 deletions.
15 changes: 15 additions & 0 deletions doc/_jekyll/_data/alerts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
tip: '<div class="alert alert-success" role="alert"><i class="fa fa-check-square-o"></i> <b>Tip: </b>'
note: '<div class="alert alert-info" role="alert"><i class="fa fa-info-circle"></i> <b>Note: </b>'
important: '<div class="alert alert-warning" role="alert"><i class="fa fa-warning"></i> <b>Important: </b>'
warning: '<div class="alert alert-danger" role="alert"><i class="fa fa-exclamation-circle"></i> <b>Warning: </b>'
end: '</div>'

callout_danger: '<div class="bs-callout bs-callout-danger">'
callout_default: '<div class="bs-callout bs-callout-default">'
callout_primary: '<div class="bs-callout bs-callout-primary">'
callout_success: '<div class="bs-callout bs-callout-success">'
callout_info: '<div class="bs-callout bs-callout-info">'
callout_warning: '<div class="bs-callout bs-callout-warning">'

hr_faded: '<hr class="faded"/>'
hr_shaded: '<hr class="shaded"/>'
14 changes: 14 additions & 0 deletions doc/_jekyll/_data/sidebar_doc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,17 @@ entries:
platform: all
product: all
version: all

- title: First analysis processor
url: /first_processor.html
audience: writers, designers
platform: all
product: all
version: all

- title: First transformation processor
url: /first_transformation.html
audience: writers, designers
platform: all
product: all
version: all
50 changes: 5 additions & 45 deletions doc/_jekyll/_includes/custom/doc/doc_homepage.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{% include linkrefs.html %}

## Overview
## Spoon

Spoon is an open-source library that enables you to transform (see below) and analyze Java source code (see example) . Spoon provides a complete and fine-grained Java metamodel where any program element (classes, methods, fields, statements, expressions...) can be accessed both for reading and modification. Spoon takes as input source code and produces transformed source code ready to be compiled.

For documentation, there is the [Spoon technical report](https://hal.inria.fr/hal-01078532) and slides [here](http://www.monperrus.net/martin/lecture-slides-source-code-analysis-and-transformation.pdf).
- If you use Spoon for industrial purposes, please consider funding Spoon through a research contract with Inria (contact Martin Monperrus for this).

If you use Spoon for academic purposes, please cite: Renaud Pawlak, Martin Monperrus, Nicolas Petitprez, Carlos Noguera, Lionel Seinturier. "Spoon v2: Large Scale Source Code Analysis and Transformation for Java". Technical Report hal-01078532, Inria. 2014.
- If you use Spoon for academic purposes, please cite: Renaud Pawlak, Martin Monperrus, Nicolas Petitprez, Carlos Noguera, Lionel Seinturier. Spoon: A Library for Implementing Analyses and Transformations of Java Source Code”. In Software: Practice and Experience, Wiley-Blackwell, 2015. Doi: 10.1002/spe.2346.

```latex
@article{pawlak:hal-01169705,
Expand All @@ -20,10 +20,6 @@ If you use Spoon for academic purposes, please cite: Renaud Pawlak, Martin Monpe
}
```

If you use Spoon for industrial purposes, please consider funding Spoon through a research contract with Inria (contact [Martin Monperrus](http://www.monperrus.net/martin/) for this).

Do you want to improve this site? pull requests on <https://github.com/INRIA/spoon/tree/website> are welcome!

## News

Star Spoon on Github: [https://github.com/INRIA/spoon](https://github.com/INRIA/spoon) :-)
Expand All @@ -41,46 +37,10 @@ Star Spoon on Github: [https://github.com/INRIA/spoon](https://github.com/INRIA/
- September 30, 2013: Spoon 1.6 is released.
- April 12, 2012: Spoon 1.5 is released.

## Using Spoon

To display the AST of a program contained in sourceFolder:

```bash
$ java -jar spoon-core-{{site.spoon_release}}-jar-with-dependencies.jar -i sourceFolder -g
```


To detect empty catch blocks, the following Spoon code has to be written:

```java
/**
* Reports warnings when empty catch blocks are found.
*/
public class CatchProcessor extends AbstractProcessor<CtCatch> {

public List<CtCatch> emptyCatchs = new ArrayList<CtCatch>();

public void process(CtCatch element) {
if (element.getBody().getStatements().size() == 0) {
emptyCatchs.add(element);
getFactory().getEnvironment().report(this, Severity.WARNING,
element, "empty catch clause");
}
}

}
```

and launched as follows (the main class is spoon.Launcher):

```bash
$ java -cp your-bin-folder:spoon-core-{{site.spoon_release}}-jar-with-dependencies.jar spoon.Launcher -i sourceFolder -p CatchProcessor
```

Spoon processes all source files of sourceFolder and writes to the resulting code to the folder "spooned".

## Download

### Jar file

[v{{site.spoon_release}} JAR](https://gforge.inria.fr/frs/?group_id=73) - [Javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs)

The source code to the Spoon and this website is [available on GitHub](https://github.com/INRIA/spoon).
Expand Down
84 changes: 84 additions & 0 deletions doc/first_processor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: First analysis processor
tags: [getting-started]
keywords: start, begin, hello world, processor, spoon
last_updated: October 1, 2015
---

## Creation of the processor

In Spoon, a processor is a combination of query and analysis code.
With this concept, developer can analyse all elements of a type given
and inspect each element of this type one per one.

For a first processor, we'll analyze all catch blocks of a `try {...} catch {...}`
element to know how many empty catch blocks we have in a project. This kind of empty
catch can be considered bad practice. That could be a great information to know how
many and where are these catches in a project to fill them with some code,
e.g. throws a runtime exception or logs the exception.

To implement this analysis, create a new Java class which extends `AbstractProcessor` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/processing/AbstractProcessor.html)).
This super class takes a generic type parameter to know what type you want inspect in a AST.
For this tutorial, we inspect a catch, a `CtCatch` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/code/CtCatch.html)).

{{site.data.alerts.note}}
You can view the complete meta model of Spoon at <a href="http://spoon.gforge.inria.fr/diagrams.html">this page</a>.
It is a simple way to know what you can inspect with processors.
{{site.data.alerts.end}}

When the class `AbstractProcessor` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/processing/AbstractProcessor.html))
is extended, implement the method `void process(E element)`where `E` is a generic type for
any elements of the AST (all classes in the Spoon meta model which extends `CtElement` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/declaration/CtElement.html))). It is in this method that you can access all information you want of the the current `CtCatch` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/code/CtCatch.html)).
A first implementation of the empty catch processor is:

```java
package fr.inria.gforge.spoon.processors;

import org.apache.log4j.Level;
import spoon.processing.AbstractProcessor;
import spoon.reflect.code.CtCatch;

/**
* Reports warnings when empty catch blocks are found.
*/
public class CatchProcessor extends AbstractProcessor<CtCatch> {
public void process(CtCatch element) {
if (element.getBody().getStatements().size() == 0) {
getFactory().getEnvironment().report(this, Level.WARN, element, "empty catch clause");
}
}
}
```

What do we do in the body of the process method?
We get the body of the `CtCatch` element (body is an instance of `CtBlock`).
On this block, we get all statements and if there isn't statement, it means the block catch is empty!

Yes, it's that easy! Spoon's AST is designed to be comprehensible by Java developers.
This point is one of the most important point in the philosophy of Spoon,
all concepts are designed to be instinctive for Java developers.

## Apply the processor

In this "Getting Started", we'll see how we can apply the processor in command line.

First, compile your processor. You can use javac in command line to generate the `.class` file
or Maven to generate the `.jar` file with all of your processors and their dependencies.

You have a processor compiled, you'll apply it on our project. If you are in a Maven or
Gradle project, there are a plugin for these technologies ([here](https://github.com/SpoonLabs/spoon-maven-plugin)
for Maven and [here](https://github.com/SpoonLabs/spoon-gradle-plugin) for Gradle).

Second, you must download the latest jar file of Spoon. This archive is available at this [link](https://gforge.inria.fr/frs/download.php/latestzip/86/Spoon-latest.zip).

Execute the archive of Spoon:

```bash
$ java -classpath /path/to/binary/of/your/processor.jar:spoon-core-{{site.spoon_release}}-jar-with-dependencies.jar spoon.Launcher -i /path/to/src/of/your/project -p fr.inria.gforge.spoon.processors.CatchProcessor
```

{{site.data.alerts.important}}
1. Specify all dependencies in your classpath. If your processor has dependencies, don't forget to package your processor.jar with all dependencies!
2. Specify spoon in the classpath because we use spoon concepts in our processor.
3. Specify your processors in fully qualified name.
{{site.data.alerts.end}}
117 changes: 117 additions & 0 deletions doc/first_transformation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
title: First transformation processor
tags: [getting-started]
keywords: start, begin, hello world, processor, spoon, factory, setter
last_updated: October 1, 2015
---


## Goal


We'll make a first transformation that adds a field to a class
and initializes it in the constructor of the current class.


## Factories and setters

With `Factory` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/factory/Factory.html)),
you can get and create all elements of the meta model. For example, if you want
to create a class with the name "Tacos", use the factory to create an empty class
and fill information on the created element to set its name.

```java
CtClass newClass = factory.Core().createClass();
newClass.setSimpleName("Tacos");
```

First, create a new field. To do that, create the type referenced by our field.
This type is a `java.util.List` which have a `java.util.Date` as generic type.

```java
final CtTypeReference<Date> dateRef = getFactory().Code().createCtTypeReference(Date.class);
final CtTypeReference<List<Date>> listRef = getFactory().Code().createCtTypeReference(List.class);
listRef.addActualTypeArgument(dateRef);
```

`dateRef`, a `CtTypeReference` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/reference/CtTypeReference.html)),
is created by our factory from `Date.class` given by Java. We also created `listRef` which
is created by our factory from `List.class` and we add our `dateRef` as actual type argument
which represents the generic type of the list.

Now, create the field. A field has a name, a type and private.

```java
final CtField<List<Date>> listOfDates = getFactory().Core().<List<Date>>createField();
listOfDates.setSimpleName("dates");
listOfDates.setType(listRef);
listOfDates.addModifier(ModifierKind.PRIVATE);
```

We have created a field named "dates", with a private visibility and typed by our previous type reference,
`listRef`, which is `java.util.List<java.util.Date>`.

Second, create the constructor. Before the creation of a `CtConstructor` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/declaration/CtConstructor.html)),
create all objects necessary for this constructor and set them in the target constructor.
The constructor has a parameter typed by the same type of the field previously created
and has a body to assign the parameter to the field.

```java
final CtCodeSnippetStatement statementInConstructor = getFactory().Code().createCodeSnippetStatement("this.dates = dates");

final CtBlock<?> ctBlockOfConstructor = getFactory().Code().createCtBlock(statementInConstructor);

final CtParameter<List<Date>> parameter = getFactory().Core().<List<Date>>createParameter();
parameter.setType(listRef);
parameter.setSimpleName("dates");

final CtConstructor constructor = getFactory().Core().createConstructor();
constructor.setBody(ctBlockOfConstructor);
constructor.setParameters(Collections.<CtParameter<?>>singletonList(parameter));
constructor.addModifier(ModifierKind.PUBLIC);
```

*Wow! Wait ... What is `CtCodeSnippetStatement`?*

You can convert any string in a `CtStatement` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/code/CtStatement.html))
with `createCodeSnippetStatement(String statement)` or in `CtExpression` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/code/CtExpression.html))
with `createCodeSnippetExpression(String expression)`. In our case, we convert `this.dates = dates`
in a `CtAssignement` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/code/CtAssignment.html))
with an assignment and an assigned elements.

With this last example, you have created a statement that you have put in a block.
You have created a parameter typed by the same type as the field and
you have put all these objects in the constructor.

Finally, apply all transformations in your processor:

```java
public class ClassProcessor extends AbstractProcessor<CtClass<?>> {
@Override
public void process(CtClass<?> ctClass) {
// Creates field.
final CtTypeReference<Date> dateRef = getFactory().Code().createCtTypeReference(Date.class);
final CtTypeReference<List<Date>> listRef = getFactory().Code().createCtTypeReference(List.class);
listRef.addActualTypeArgument(dateRef);
final CtField<List<Date>> listOfDates = getFactory().Core().<List<Date>>createField();
listOfDates.<CtField>setType(listRef);
listOfDates.<CtField>addModifier(ModifierKind.PRIVATE);
listOfDates.setSimpleName("dates");

// Creates constructor.
final CtCodeSnippetStatement statementInConstructor = getFactory().Code().createCodeSnippetStatement("this.dates = dates");
final CtBlock<?> ctBlockOfConstructor = getFactory().Code().createCtBlock(statementInConstructor);
final CtParameter<List<Date>> parameter = getFactory().Core().<List<Date>>createParameter();
parameter.<CtParameter>setType(listRef);
parameter.setSimpleName("dates");
final CtConstructor constructor = getFactory().Core().createConstructor();
constructor.setBody(ctBlockOfConstructor);
constructor.setParameters(Collections.<CtParameter<?>>singletonList(parameter));
constructor.addModifier(ModifierKind.PUBLIC);

// Apply transformation.
ctClass.addField(listOfDates);
ctClass.addConstructor(constructor);
}
}
```

0 comments on commit 4259de5

Please # to comment.