TreeMaker Difficulties, Questions and Answers

Apache NetBeans Wiki Index

Note: These pages are being reviewed.

Question: How to rewrite Java/MDR modification code to new API?

Answer: Most of the actions can be replaced by the new method. There are several conceptual differences: The most important and visible is immutability of the new trees, so you cannot modify original model elements (in Java/MDR speech), you have to always create new tree (in Retouche speech). Here are couple of examples:

Question: I need to make textual replace of the tags from the template with some values in method body

Answer: Currently there is no direct way how to do that. We could consider to add method to obtain body as a text to TreeMaker class, but bear in mind that such a method does not modify source, so it is not correct place. For the time being, you can workaround it by following code:

(Code will not work, there are still many bugs.)

    ...
    TreeMaker make = workingCopy.getTreeMaker();
    ...
    MethodTree method = ...;
    BlockTree body = method.getBody();

    // get SourcePositions instance for your working copy and
    // fetch out start and end position.
    SourcePositions sp = workingCopy.getTrees().getSourcePositions();
    int start = (int) sp.getStartPosition(cut, body);
    int end = (int) sp.getEndPosition(cut, body);
    // get body text from source text
    String bodyText = workingCopy.getText().substring(start, end);
    MethodTree modified = make.Method(
        method.getModifiers(), // copy original values
        method.getName(),
        method.getReturnType(),
        method.getTypeParameters(),
        method.getParameters(),
        method.getThrows(),
        bodyText.replace("{0}", "-tag-replace-"), // replace body with the new text
        null // not applicable here
    );
    // rewrite the original modifiers with the new one:
    workingCopy.rewrite(method, modified);
    ...

Question: How do I create a constructor? Passing TypeKind.VOID as the return type to TreeMaker.Method() generates a method with a void return type.

Answer: by passing "<init>" as the method name and null as the return type. See also issue 88697.

Question: Create an Import and add it to a class

Retouche:

TreeMaker make;
CompilationUnitTree cut = ...;
CompilationUnitTree copy = make.addCompUnitImport(
    cut,
    make.Import(make.Identifier("java.io.IOException"), false)
);
workingCopy.rewrite(node, copy);
There is a management tool which allows to handle imports automatically. Use make.QualIdent to create expression tree - import will be handled for it, if it does not exist yet.

Question: Create that some imports are not already there

This is some kind of workaround. Users should use automatic import management (QualIdentTree) in the most cases. For those who request manual handling, use similar scenario in retouche:

CompilationUnitTree cut = ...;
List<ImportTree> imports = cut.getImports();
for (ImportTree dovoz : imports) [
   if ("whateverYouWant".equals(dovoz.getQualifiedIndetifier().toString())) {
       found = true; break;
   }
It is not recommended to make your own import management, instead of, use the automatic one. Use your own solution only if automatic one does not fit to your needs!

Question: Create a method

It is very similar to other creations. All the stuff is done in TreeMaker. Here is a short example: Retouche:

// create modifiers for parameters (no modifier present) and create annotations. (again empty list)
ModifiersTree parMods = make.Modifiers(Collections.<Modifier>emptySet(), Collections.<AnnotationTree>emptyList());
// make a variable trees - representing parameters
VariableTree par1 = make.Variable(parMods, "a", make.PrimitiveType(TypeKind.INT), null);
VariableTree par2 = make.Variable(parMods, "b", make.PrimitiveType(TypeKind.FLOAT), null);
List<VariableTree> parList = new ArrayList<VariableTree>(2);
parList.add(par1);
parList.add(par2);

// now, start the method creation
MethodTree newMethod = make.Method(
            make.Modifiers(Collections.singleton(Modifier.PUBLIC)), // modifiers and annotations
            "newlyCreatedMethod", // name
            make.PrimitiveType(TypeKind.VOID), // return type
            Collections.<TypeParameterTree>emptyList(), // type parameters for parameters
            parList, // parameters
            Collections.singletonList(make.Identifier("java.io.IOException")), // throws
            make.Block(Collections.EMPTY_LIST, false), // empty statement block
            null // default value - not applicable here, used by annotations
        );

Question: Add a method to a class

Consider you have created method and want to add it to a superclass:

Java/MDR:

Class clazz = ...;
Method method = ...;
clazz.getContents().add(method);

Retouche:

TreeMaker make = ...;
ClassTree clazz = ...;
MethodTree method = make.Method(...);
ClassTree copy = make.addClassMember(clazz, method);
workingCopy.rewrite(clazz, copy);

Question: Set a super class

Java/MDR:

Class clazz = ...;
clazz.setSuperClassName("motherClassName");

Retouche:

ClassTree clazz = ...;
ClassTree copy = clazz.setExtends(clazz, make.Identifier("MotherClassName");
workingCopy.rewrite(clazz, copy);

Question: Add a parameter to a constructor

Java/MDR:

Constructor construct = tgtClass.getConstructor(new ArrayList(), false);
Parameter param = pkg.getParameter().createParameter(
        "theRef", // NOI18N
        Collections.EMPTY_LIST, // annotations
        false, // is final
        getTypeRef(pkg, mbean.getWrappedClassName()), // typename
        0, // dimCount
        false);
construct.getParameters().add(param);

Retouche:

TreeMaker make = ...;
ClassTree clazz = ...;
MethodTree constr = ...;
VariableTree var = make.Variable(make.Modifiers(Collections.<Modifier>emptySet()), "theRef", make.Identifier("someType"), null);
MethodTree copy = make.addMethodParameter(constr, var);
workingCopy.rewrite(constr, copy);

Question: Add a field to a class

Java/MDR:

Class clazz = ...;
Field refField = pkg.getField().createField("theRef", Collections.EMPTY_LIST, Modifier.PRIVATE, null, ...);
clazz.getFeatures().add(0, refField);

Retouche:

TreeMaker make = ...;
ClassTree clazz = ...;
VariableTree var = make.Variable(make.Modifiers(Modifier.PUBLIC), "theRef", make.Identifier("someType", null);
ClassTree clazzCopy = make.insertClassMember(0, var);
workingCopy.rewrite(clazz, clazzCopy);
All fields, local variables and parameters is represented by VariableTree in Jsr199.

Question: Remove constructor’s 'throws' clause

Java/MDR:

Constructor construct = ...;
construct.getExceptionNames().clear();

Retouche:

TreeMaker make = ...;
MethodTree method = ...;
MethodTree modified = make.Method( // copy original values
    method.getModifiers(),
    method.getName(),
    method.getReturnType(),
    method.getTypeParameters(),
    method.getParameters(),
    Collections.<ExpressionTree>emptyList(), // use empty list instead of orig. value
    method.getBody(),
    null // not applicable here
);
workingCopy.rewrite(method, modified);

For exact 'throws' item removal, you can use methods make.removeMethodThrows(…​) in TreeMaker class.

Question: Clear body text

Java/MDR:

Method method = ...;
method.setBodyText("");

Retouche:

TreeMaker make = ...;
MethodTree method = ..;
BlockTree emptyBlock = make.Block(Collections.<StatementTree>emptyList(), false);
workingCopy.rewrite(method.getBody(), emptyBlock);

Question: Type object

Question: Access to the String representation of a method body

There is not any direct support for such a functionality. You have to obtain positions and then cut the string from the source. We consider about adding such a method somewhere to the API. (Currently no suitable places has been found.)

Java/MDR:

    String bodyText = method.getBodyText();

Retouche:

    ...
    TreeMaker make = workingCopy.getTreeMaker();
    ...
    MethodTree method = ...;
    BlockTree body = method.getBody();

    // get SourcePositions instance for your working copy and
    // fetch out start and end position.
    SourcePositions sp = workingCopy.getTrees().getSourcePositions();
    int start = (int) sp.getStartPosition(cut, body);
    int end = (int) sp.getEndPosition(cut, body);
    // get body text from source text
    String bodyText = workingCopy.getText().substring(start, end);

Question: Add a set of Exceptions to a method

Java/MDR:

method.getExceptionNames().addAll(exceptions);

Retouche:

MethodTree node = ...; // original MethodTree node to modify
MethodTree copy = make.addMethodThrows(node, make.Identifier("IOException"));
copy = make.addMethodThrows(copy, make.Identifier("FileNotFoundException"));
workingCopy.rewrite(node, copy);

Short comment about functionality is available below in next question.

Question: How to make the class implements an interface

Java/MDR:

JavaModelPackage pkg = (JavaModelPackage) tgtClass.refImmediatePackage();
tgtClass.getInterfaceNames().add(pkg.getMultipartId().createMultipartId(
            "MBeanRegistration", // NOI18N
            null,
            Collections.EMPTY_LIST));

Retouche:

workingCopy.toPhase(Phase.RESOLVED);
TreeMaker make = workingCopy.getTreeMaker();
ClassTree clazz = ...; // obtain class somewhere
ClassTree copy = class.addImplementsClause(make.Identifier("MBeanRegistration"));
workingCopy.rewrite(class, copy);

Java/MDR:

method.getExceptionNames().addAll(exceptions);

Retouche:

MethodTree method = ...;
List<ExpressionTree> listCopy = new ArrayList<ExpressionTree>(method.getThrows());
listCopy.add(exceptions);
MethodTree copy = make.Method(
       method.getModifiers(),
       method.getName(),
       method.getReturnType(),
       method.getTypeParameters(),
       method.getParameters(),
       listCopy,
       method.getBody(),
       null
);
workingCopy.rewrite(method, copy);

From the example above, it is obvious that this solution is not straightforward enough. You have also another chance how to do it:

Collection<IdentifierTree> exceptions = ...;
MethodTree method = ...;
MethodTree copy = make.addMethodThrows(method, make.Identifier("IOException"));
copy = make.addMethodThrows(copy, make.Identifier("FileNotFoundException"));
copy = make.addMethodThrows(copy, make.Identifier("IllegalArgumentException));
... etc.
workingCopy.rewrite(method, copy);

Bear in mind that in every next 'add' call, you have to use created copy instead of the original method MethodTree! (See the first parameter of addMethodThrows invocation.) This solution is shorter and simple to write, but it has minor performance impact when adding many items .It is up to user if he uses first or second solution, the result is the same. It is obvious that if you want to add just one item, you will use provided addMethodThrows method.

Users can be confused when add multiple 'extends' clause to interface. Extends clause in interface is represented by 'implements' clause in class.

Question: How do I create a field with an initial value if the initial value is not a primitive, for example something like the following?

MyClass field = Something.getMyClass();

Answer: Obviously you have to create appropriate tree, in the example above method invocation with member select tree inside.

Such a code might look like:

...
CompilationUnitTree cut = workingCopy.getCompilationUnit();
TreeMaker make = workingCopy.getTreeMaker();
ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
VariableTree var = make.Variable(make.Modifiers(
    Collections.<Modifier>emptySet(), Collections.<AnnotationTree>emptyList()),
    "myField",
    make.Identifier("MyClass"),
    make.MethodInvocation(
    Collections.<ExpressionTree>emptyList(),
    make.MemberSelect(
        make.Identifier("Something"), "getMyClass"),
        Collections.<ExpressionTree>emptyList()
    )
);
ClassTree copy = make.addClassMember(clazz, var);
workingCopy.rewrite(clazz, copy);
...

Question: How to access and update the Javadoc of an existing class.

Currently totally broken, there are issues reported and will be addressed in M7: (#89873, #90302, #92325) There are methods in CommentHandlerService, they will be perhaps reused.

If you are not familiar with trees enough, write sample source code, run user action task against the source code and dump the tree to some readable form. That allows you to learn how expressions are represented in tree.

Question: How to create a comment within an annotation?

It should be possible to add the comment using IdentifierTree (not a completely clean solution, though)

IdentifierTree commentTree = make.Identifier("/* ... */");
AnnotationTree newAnnotation = treeMaker.Annotation(
treeMaker.QualIdent("com.acme.InsertedAnnotation"),
Collections.singletonList(commentTree));

Taken from nbdev@netbeans.org / answered by jlahoda