Original source looks like:
package org.netbeans.test.codegen;
public class Tutorial1 {
}
At the end, we want to see source like:
package org.netbeans.test.codegen;
import java.io.Externalizable;
public class Tutorial1 implements Externalizable {
}
In short, this can be done with this code:
public void run(WorkingCopy workingCopy) throws IOException {
workingCopy.toPhase(Phase.RESOLVED); // is it neccessary?
CompilationUnitTree cut = workingCopy.getCompilationUnit();
TreeMaker make = workingCopy.getTreeMaker();
for (Tree typeDecl : cut.getTypeDecls()) {
if (Tree.Kind.CLASS == typeDecl.getKind()) {
ClassTree clazz = (ClassTree) typeDecl;
ExpressionTree implementsClause = make.Identifier("Externalizable");
implementsClause = make.Identifier("java.io.Externalizable");
TypeElement element = workingCopy.getElements().getTypeElement("java.io.Externalizable");
implementsClause = make.QualIdent(element);
ClassTree modifiedClazz = make.addClassImplementsClause(clazz, implementsClause);
workingCopy.rewrite(clazz, modifiedClazz);
} // end if
} // end for
} // end run
Here is steps description:
public class Tutorial1 implements Externalizable {
}
public class Tutorial1 implements java.io.Externalizable {
}
import java.io.Externalizable;
public class Tutorial1 implements Externalizable {
}
public void writeExternal(final ObjectOutput arg0) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
We have to prepare all elements belonging to method. First of all, prepare modifiers for the method. We use TreeMaker instance make again.
public void run(WorkingCopy workingCopy) throws IOException {
...
// create method modifier: public and no annotation
ModifiersTree methodModifiers = make.Modifiers(
Collections.<Modifier>singleton(Modifier.PUBLIC),
Collections.<AnnotationTree>emptyList()
);
...
}
Next step is preparing method parameter arg0 of type ObjectOutput and modifier final:
public void run(WorkingCopy workingCopy) throws IOException {
...
// create parameter:
// final ObjectOutput arg0
VariableTree parameter = make.Variable(
make.Modifiers(
Collections.<Modifier>singleton(Modifier.FINAL),
Collections.<AnnotationTree>emptyList()
),
"arg0", // name
make.Identifier("Object"), // parameter type
null // initializer - does not make sense in parameters.
);
...
}
Method throws exception, prepare exception identifier IOException. It is the same when we prepared interface for implements clause.
public void run(WorkingCopy workingCopy) throws IOException {
...
// prepare simple name to throws clause:
// 'throws IOException' and its import will be added (if it is not available yet)
TypeElement element = workingCopy.getElements().getTypeElement("java.io.IOException");
ExpressionTree throwsClause = make.QualIdent(element);
...
}
We have everything, what we need for method creation. Make method:
public void run(WorkingCopy workingCopy) throws IOException {
...
// create method.
MethodTree newMethod = make.Method(
methodModifiers, // public
"writeExternal", // writeExternal
make.PrimitiveType(TypeKind.VOID), // return type "void"
Collections.<TypeParameterTree>emptyList(), // type parameters - none
Collections.<VariableTree>singletonList(parameter), // final ObjectOutput arg0
Collections.<ExpressionTree>singletonList(throwsClause), // throws
"{ throw new UnsupportedOperationException(\"Not supported yet.\") }", // body text
null // default value - not applicable here, used by annotations
);
...
}
In the example above, we used the most often used factory method for source code method creation. It contains string for its body. You can add it as plain syntax correct text and engine will do imports and formatting stuff for you. There is also second method, which allows to add the body as a block:
public void run(WorkingCopy workingCopy) throws IOException {
...
// create method.
MethodTree newMethod = make.Method(
methodModifiers, // public
"writeExternal", // writeExternal
make.PrimitiveType(TypeKind.VOID), // return type "void"
Collections.<TypeParameterTree>emptyList(), // type parameters - none
Collections.<VariableTree>singletonList(parameter), // final ObjectOutput arg0
Collections.<ExpressionTree>singletonList(throwsClause), // throws
make.Block(Collections.<StatementTree>emptyList(), false), // empty statement block
null // default value - not applicable here, used by annotations
);
...
}
Example creates method with empty body.
At the end, do not forget to add it to type declaration and register change on working copy:
// and in the same way as interface was added to implements clause,
// add feature to the class as its member:
ClassTree modifiedClazz = make.addClassMember(clazz, newMethod);
workingCopy.rewrite(clazz, modifiedClazz);
...
}
Do you want to see it in a practice? Open the java/source project, go to unit test packages, then org.netbeans.api.java.source.gen package, open file TutorialTest.java and run it in IDE. You can experiment with it little bit.