TreeMaker Difficulties, Questions and Answers
- Question: How to rewrite Java/MDR modification code to new API?
- Question: I need to make textual replace of the tags from the template with some values in method body
- 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.
- Question: Create an Import and add it to a class
- Question: Create that some imports are not already there
- Question: Create a method
- Question: Add a method to a class
- Question: Set a super class
- Question: Add a parameter to a constructor
- Question: Add a field to a class
- Question: Remove constructor’s 'throws' clause
- Question: Clear body text
- Question: Type object
- Question: Access to the String representation of a method body
- Question: Contextualize the Java classes search
- Question: Add a set of Exceptions to a method
- Question: How to make the class implements an interface
- 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?
- Question: How to access and update the Javadoc of an existing class.
- Question: How to create a comment within an annotation?
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: 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