-
-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #342 from GerardPaligot/getting_started
- Loading branch information
Showing
5 changed files
with
235 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"/>' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
``` |