Skip to content

Commit 951e781

Browse files
committed
Android DrawAllocation. Closes #211
1 parent 5065d09 commit 951e781

File tree

4 files changed

+354
-0
lines changed

4 files changed

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

0 commit comments

Comments
 (0)