Skip to content

Commit b25b9db

Browse files
committed
Android DrawAllocation. Closes #211
1 parent 66409be commit b25b9db

File tree

4 files changed

+364
-0
lines changed

4 files changed

+364
-0
lines changed

plugin/src/main/java/org/autorefactor/refactoring/rules/AllRefactoringRules.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public static List<RefactoringRule> getAllRefactoringRules() {
112112
new TestNGAssertRefactoring(),
113113
new ReplaceQualifiedNamesBySimpleNamesRefactoring(),
114114
new RemoveEmptyLinesRefactoring(),
115+
new AndroidDrawAllocationRefactoring(),
115116
new AndroidWakeLockRefactoring(),
116117
new AndroidViewHolderRefactoring(),
117118
new SwitchRefactoring());
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* AutoRefactor - Eclipse plugin to automatically refactor Java code bases.
3+
*
4+
* Copyright (C) 2016 Luis Cruz - Android Refactoring Rules
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program under LICENSE-GNUGPL. If not, see
18+
* <http://www.gnu.org/licenses/>.
19+
*
20+
*
21+
* All rights reserved. This program and the accompanying materials
22+
* are made available under the terms of the Eclipse Public License v1.0
23+
* which accompanies this distribution under LICENSE-ECLIPSE, and is
24+
* available at http://www.eclipse.org/legal/epl-v10.html
25+
*/
26+
package org.autorefactor.refactoring.rules;
27+
28+
import org.eclipse.jdt.core.dom.ASTNode;
29+
import org.eclipse.jdt.core.dom.ASTVisitor;
30+
import org.eclipse.jdt.core.dom.CastExpression;
31+
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
32+
import org.eclipse.jdt.core.dom.Expression;
33+
import org.eclipse.jdt.core.dom.ITypeBinding;
34+
import org.eclipse.jdt.core.dom.MethodInvocation;
35+
import org.eclipse.jdt.core.dom.SimpleName;
36+
import org.eclipse.jdt.core.dom.SimpleType;
37+
import org.eclipse.jdt.core.dom.Statement;
38+
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
39+
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
40+
import org.eclipse.jdt.core.dom.MethodDeclaration;
41+
42+
import static org.autorefactor.refactoring.ASTHelper.*;
43+
import static org.eclipse.jdt.core.dom.ASTNode.*;
44+
45+
import java.util.Arrays;
46+
import java.util.List;
47+
48+
import org.autorefactor.refactoring.ASTBuilder;
49+
import org.autorefactor.refactoring.Refactorings;
50+
51+
/** See {@link #getDescription()} method. */
52+
public class AndroidDrawAllocationRefactoring extends AbstractRefactoringRule {
53+
54+
@Override
55+
public String getDescription() {
56+
return "Optimization for Android applications to avoid the allocation of"
57+
+ " objects inside drawing routines. ";
58+
}
59+
60+
@Override
61+
public String getName() {
62+
return "Android DrawAllocation";
63+
}
64+
65+
@Override
66+
public boolean visit(MethodDeclaration node) {
67+
if (isMethod(node, "android.widget.TextView", "onDraw", "android.graphics.Canvas")) {
68+
node.accept(new OnDrawTransformer(this.ctx, node));
69+
}
70+
return VISIT_SUBTREE;
71+
}
72+
73+
static class OnDrawTransformer extends ASTVisitor {
74+
private RefactoringContext ctx;
75+
private MethodDeclaration onDrawDeclaration;
76+
77+
public OnDrawTransformer(RefactoringContext ctx, MethodDeclaration onDrawDeclaration) {
78+
this.ctx = ctx;
79+
this.onDrawDeclaration = onDrawDeclaration;
80+
}
81+
82+
public boolean isTypeBindingSubclassOf(ITypeBinding typeBinding, List<String> superClassStrings) {
83+
ITypeBinding superClass = typeBinding;
84+
while (superClass != null) {
85+
if (superClassStrings.contains(superClass.getErasure().getName())) {
86+
return true;
87+
}
88+
superClass = superClass.getSuperclass();
89+
}
90+
return false;
91+
}
92+
93+
@Override
94+
public boolean visit(VariableDeclarationFragment node) {
95+
Expression initializer = node.getInitializer();
96+
if (initializer != null) {
97+
if (initializer.getNodeType() == ASTNode.CAST_EXPRESSION) {
98+
initializer = ((CastExpression) initializer).getExpression();
99+
}
100+
if (initializer.getNodeType() == ASTNode.CLASS_INSTANCE_CREATION) {
101+
ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation) initializer;
102+
InitializerVisitor initializerVisitor = new InitializerVisitor();
103+
classInstanceCreation.accept(initializerVisitor);
104+
if (initializerVisitor.initializerCanBeExtracted) {
105+
Statement declarationStatement = getAncestor(node, VariableDeclarationStatement.class);
106+
if (declarationStatement != null) {
107+
final ASTBuilder b = this.ctx.getASTBuilder();
108+
final Refactorings r = this.ctx.getRefactorings();
109+
// Deal with collections
110+
if (isTypeBindingSubclassOf(node.getName().resolveTypeBinding(),
111+
Arrays.asList("AbstractCollection", "Collection", "List", "AbstractMap", "Map"))) {
112+
// It should only works with allocations of
113+
// empty collections.
114+
// Approximation: work only for 0 args
115+
if (classInstanceCreation.arguments().size() == 0) {
116+
// allocate object outside onDraw
117+
r.insertBefore(b.move(declarationStatement), onDrawDeclaration);
118+
// call collection.clear() in the end of onDraw
119+
ASTNode clearNode = b.toStmt(b.invoke(node.getName().getIdentifier(), "clear"));
120+
List<Statement> bodyStatements = statements(onDrawDeclaration.getBody());
121+
Statement lastStatement = bodyStatements.get(bodyStatements.size() - 1);
122+
if (ASTNode.RETURN_STATEMENT == lastStatement.getNodeType()) {
123+
r.insertBefore(clearNode, lastStatement);
124+
} else {
125+
r.insertAfter(clearNode, lastStatement);
126+
}
127+
return DO_NOT_VISIT_SUBTREE;
128+
}
129+
} else {
130+
r.insertBefore(b.move(declarationStatement), onDrawDeclaration);
131+
return DO_NOT_VISIT_SUBTREE;
132+
}
133+
}
134+
}
135+
}
136+
}
137+
return VISIT_SUBTREE;
138+
}
139+
}
140+
141+
// This visitor intends to make sure that our initializer does not use variables.
142+
// We assume that, if they are not present, initializer is constant.
143+
static class InitializerVisitor extends ASTVisitor {
144+
private boolean initializerCanBeExtracted = true;
145+
146+
@Override
147+
public boolean visit(MethodInvocation node) {
148+
initializerCanBeExtracted = false;
149+
return DO_NOT_VISIT_SUBTREE;
150+
}
151+
152+
// Skip SimpleNames inside SimpleType like the following
153+
// HashMap<Integer, Object>();
154+
//(this should keep initializerCanBeExtracted=true)
155+
@Override
156+
public boolean visit(SimpleType node) {
157+
return DO_NOT_VISIT_SUBTREE;
158+
}
159+
160+
@Override
161+
public boolean visit(SimpleName node) {
162+
initializerCanBeExtracted = false;
163+
return DO_NOT_VISIT_SUBTREE;
164+
}
165+
}
166+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* AutoRefactor - Eclipse plugin to automatically refactor Java code bases.
3+
*
4+
* Copyright (C) 2016 Luis Cruz - Android Refactoring
5+
* Copyright (C) 2016 Jean-Noël Rouvignac - code cleanups
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program under LICENSE-GNUGPL. If not, see
19+
* <http://www.gnu.org/licenses/>.
20+
*
21+
*
22+
* All rights reserved. This program and the accompanying materials
23+
* are made available under the terms of the Eclipse Public License v1.0
24+
* which accompanies this distribution under LICENSE-ECLIPSE, and is
25+
* available at http://www.eclipse.org/legal/epl-v10.html
26+
*/
27+
package org.autorefactor.refactoring.rules.samples_in;
28+
29+
import java.util.ArrayList;
30+
import java.util.HashMap;
31+
import java.util.List;
32+
import java.util.Map;
33+
import android.content.Context;
34+
import android.graphics.Rect;
35+
import android.util.AttributeSet;
36+
import android.widget.Button;
37+
38+
public class AndroidDrawAllocationSample extends Button {
39+
40+
private Rect cachedRect;
41+
42+
public AndroidDrawAllocationSample(Context context, AttributeSet attrs, int defStyle) {
43+
super(context, attrs, defStyle);
44+
}
45+
46+
@Override
47+
protected void onDraw(android.graphics.Canvas canvas) {
48+
super.onDraw(canvas);
49+
50+
// Various allocations:
51+
new String("foo");
52+
String s = new String("bar");
53+
54+
Integer i = new Integer(5);
55+
56+
// Cached object initialized lazily: should not complain about these
57+
if (cachedRect == null) {
58+
cachedRect = new Rect(0, 0, 100, 100);
59+
}
60+
if (cachedRect == null || cachedRect.width() != 50) {
61+
cachedRect = new Rect(0, 0, 50, 100);
62+
}
63+
64+
boolean b = Boolean.valueOf(true); // auto-boxing
65+
66+
Integer i2 = new Integer(i);
67+
Integer i3 = (Integer) new Integer(2);
68+
Map<Integer, Object> myOtherMap = new HashMap<Integer, Object>();
69+
70+
// Non-allocations
71+
super.animate();
72+
int x = 4 + '5';
73+
74+
// This will involve allocations, but we don't track
75+
// inter-procedural stuff here
76+
someOtherMethod();
77+
}
78+
79+
void someOtherMethod() {
80+
// Allocations are accepted here
81+
new String("foo");
82+
String s = new String("bar");
83+
boolean b = Boolean.valueOf(true);
84+
}
85+
86+
public class DrawAllocationSampleTwo extends Button {
87+
public DrawAllocationSampleTwo(Context context) {
88+
super(context);
89+
}
90+
@Override
91+
protected void onDraw(android.graphics.Canvas canvas) {
92+
super.onDraw(canvas);
93+
List<Integer> array = new ArrayList<Integer>();
94+
return;
95+
}
96+
}
97+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* AutoRefactor - Eclipse plugin to automatically refactor Java code bases.
3+
*
4+
* Copyright (C) 2016 Luis Cruz - Android Refactoring
5+
* Copyright (C) 2016 Jean-Noël Rouvignac - code cleanups
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program under LICENSE-GNUGPL. If not, see
19+
* <http://www.gnu.org/licenses/>.
20+
*
21+
*
22+
* All rights reserved. This program and the accompanying materials
23+
* are made available under the terms of the Eclipse Public License v1.0
24+
* which accompanies this distribution under LICENSE-ECLIPSE, and is
25+
* available at http://www.eclipse.org/legal/epl-v10.html
26+
*/
27+
package org.autorefactor.refactoring.rules.samples_out;
28+
29+
import java.util.ArrayList;
30+
import java.util.HashMap;
31+
import java.util.List;
32+
import java.util.Map;
33+
import android.content.Context;
34+
import android.graphics.Rect;
35+
import android.util.AttributeSet;
36+
import android.widget.Button;
37+
38+
public class AndroidDrawAllocationSample extends Button {
39+
40+
private Rect cachedRect;
41+
42+
public AndroidDrawAllocationSample(Context context, AttributeSet attrs, int defStyle) {
43+
super(context, attrs, defStyle);
44+
}
45+
46+
String s = new String("bar");
47+
48+
Integer i = new Integer(5);
49+
50+
Integer i3 = (Integer) new Integer(2);
51+
52+
Map<Integer, Object> myOtherMap = new HashMap<Integer, Object>();
53+
54+
@Override
55+
protected void onDraw(android.graphics.Canvas canvas) {
56+
super.onDraw(canvas);
57+
58+
// Various allocations:
59+
new String("foo");
60+
// Cached object initialized lazily: should not complain about these
61+
if (cachedRect == null) {
62+
cachedRect = new Rect(0, 0, 100, 100);
63+
}
64+
if (cachedRect == null || cachedRect.width() != 50) {
65+
cachedRect = new Rect(0, 0, 50, 100);
66+
}
67+
68+
boolean b = Boolean.valueOf(true); // auto-boxing
69+
70+
Integer i2 = new Integer(i);
71+
// Non-allocations
72+
super.animate();
73+
int x = 4 + '5';
74+
75+
// This will involve allocations, but we don't track
76+
// inter-procedural stuff here
77+
someOtherMethod();
78+
myOtherMap.clear();
79+
}
80+
81+
void someOtherMethod() {
82+
// Allocations are accepted here
83+
new String("foo");
84+
String s = new String("bar");
85+
boolean b = Boolean.valueOf(true);
86+
}
87+
88+
public class DrawAllocationSampleTwo extends Button {
89+
public DrawAllocationSampleTwo(Context context) {
90+
super(context);
91+
}
92+
List<Integer> array = new ArrayList<Integer>();
93+
@Override
94+
protected void onDraw(android.graphics.Canvas canvas) {
95+
super.onDraw(canvas);
96+
array.clear();
97+
return;
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)