-
Notifications
You must be signed in to change notification settings - Fork 6
/
ConvertingVisitor.xtend
191 lines (171 loc) · 5.9 KB
/
ConvertingVisitor.xtend
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package org.eclipse.xtend.java2xtend
import com.google.common.base.Optional
import java.beans.Introspector
import java.util.List
import org.eclipse.jdt.core.dom.ASTNode
import org.eclipse.jdt.core.dom.ASTVisitor
import org.eclipse.jdt.core.dom.Block
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor
import org.eclipse.jdt.core.dom.CustomInfixExpression
import org.eclipse.jdt.core.dom.EnhancedForStatement
import org.eclipse.jdt.core.dom.Expression
import org.eclipse.jdt.core.dom.ForStatement
import org.eclipse.jdt.core.dom.InfixExpression
import org.eclipse.jdt.core.dom.MethodInvocation
import org.eclipse.jdt.core.dom.NameWrapper
import org.eclipse.jdt.core.dom.Statement
import org.eclipse.jdt.core.dom.TypeLiteral
import static org.eclipse.jdt.core.dom.ASTNode.*
import static extension java.lang.Character.*
import static extension org.eclipse.xtend.java2xtend.ConvertingVisitor.*
import org.eclipse.jdt.core.dom.PrefixExpression
class ConvertingVisitor extends ASTVisitor {
override visit(EnhancedForStatement node) {
val ast = node.AST
node.parameter.type = ast.newSimpleType(new NameWrapper(ast, ''))
true
}
override visit(TypeLiteral qname) {
val methodCall = qname.AST.newMethodInvocation
methodCall.name = qname.AST.newSimpleName("typeof")
methodCall.arguments.add(qname.AST.newSimpleName(qname.type.toString))
replaceNode(qname, methodCall)
false
}
private def toFieldAccess(MethodInvocation node, String newName) {
if (node.expression == null) {
new NameWrapper(node.AST, newName)
} else {
node.AST.newFieldAccess() => [ f |
f.expression = node.expression.copy
f.name = new NameWrapper(node.AST, newName)
]
}
}
override visit(MethodInvocation node) {
if (node.expression?.toString == "System.out") {
if (node.name.toString.startsWith("print")) {
node.expression.delete
return true
}
}
if (node.name.identifier == 'equals' && node.arguments.size === 1) {
var ASTNode replace = node;
var operator = '=='
if(#[node.parent].filter(typeof(PrefixExpression)).exists[it.operator == PrefixExpression$Operator::NOT]) {
replace = node.parent;
operator = '!='
}
val newInfix = new CustomInfixExpression(node.AST, operator)
newInfix.leftOperand = node.expression.copy
newInfix.rightOperand = (node.arguments.head as Expression).copy
replaceNode(replace, newInfix)
return true
}
val getterPrefixes = #['is','get','has']
val name = node.name;
val identifier = name.identifier
if (node.arguments.empty) {
val newIdentifier = Optional::fromNullable(getterPrefixes.filter [
identifier.startsWith(it)
&& identifier.length > it.length
&& identifier.charAt(it.length).upperCase
].map[
Introspector::decapitalize(identifier.substring(it.length))
].head)
val newName = newIdentifier.or(identifier)
val newNode = toFieldAccess(node, newName)
replaceNode(node, newNode)
return true
}else if(node.arguments.size == 1 && identifier.startsWith("set")) {
val newName = Introspector::decapitalize(identifier.substring("set".length))
val newNode = node.AST.newAssignment => [a|
a.leftHandSide = toFieldAccess(node, newName)
a.rightHandSide = (node.arguments.head as Expression).copy
]
replaceNode(node, newNode)
}
true
}
override visit(ForStatement node) {
val xfor = XtendFor::create(node)
if (xfor != null)
return super.visit(node)
//need to convert to while loop
val block = node.AST.newBlock
node.initializers
.map[it as Expression]
.map[node.AST.newExpressionStatement(it.copy)]
.forEach[
it.accept(this)
block.statements.add(it)
]
val whileStmt = node.AST.newWhileStatement
whileStmt.expression = node.expression.copy
whileStmt.body = node.body.copy as Block => [
statements.addAll(node.updaters.map[it as Expression].map[node.AST.newExpressionStatement(it.copy)])
]
block.statements.add(whileStmt)
replaceNode(node, block)
true
}
override visit(InfixExpression exp) {
switch exp.operator {
case InfixExpression$Operator::EQUALS:
replaceOp(exp, '===')
case InfixExpression$Operator::NOT_EQUALS:
replaceOp(exp, '!==')
case InfixExpression$Operator::AND:
replaceOpWithMethod(exp, 'bitwiseAnd')
case InfixExpression$Operator::OR:
replaceOpWithMethod(exp, 'bitwiseOr')
case InfixExpression$Operator::XOR:
replaceOpWithMethod(exp, 'bitwiseXor')
}
true
}
private def replaceOp(InfixExpression exp, String op) {
val newInfix = new CustomInfixExpression(exp.AST, op)
newInfix.leftOperand = exp.leftOperand.copy
newInfix.rightOperand = exp.rightOperand.copy
replaceNode(exp, newInfix)
}
private def replaceOpWithMethod(InfixExpression exp, String name) {
val newNode = exp.AST.newMethodInvocation => [m|
m.expression = exp.leftOperand.copy
m.name.identifier = name
m.arguments.add(exp.rightOperand.copy)
]
replaceNode(exp, newNode)
}
def static copy(Expression exp) {
copySubtree(exp.AST, exp) as Expression
}
def static copy(Statement exp) {
copySubtree(exp.AST, exp) as Statement
}
private def replaceNode(ASTNode node, ASTNode exp) {
val parent = node.parent
val location = node.locationInParent
try{
if (location instanceof ChildListPropertyDescriptor) {
// There's a convention in the AST classes:
// For a ChildListPropertyDescriptor.id string value there's a
// corresponding no-arg method for retrieving the list eg. MethodInvocation.arguments().
val method = parent.class.getMethod(location.id)
val list = method.invoke(parent) as List<Object>
val index = list.indexOf(node)
if(index >= 0){
list.set(index, exp);
}else{
throw new IllegalArgumentException(node +" not found in "+list+" ("+index+")")
}
} else {
parent.setStructuralProperty(location, exp)
}
exp.accept(this)
}catch(Exception ex){
throw new RuntimeException("Failed to replace node: "+node+" with "+exp+" in "+parent, ex)
}
}
}