java-decompiler: renamer interface cleaned
[idea/community.git] / plugins / java-decompiler / engine / src / org / jetbrains / java / decompiler / modules / renamer / IdentifierConverter.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.java.decompiler.modules.renamer;
17
18 import org.jetbrains.java.decompiler.code.CodeConstants;
19 import org.jetbrains.java.decompiler.main.DecompilerContext;
20 import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
21 import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;
22 import org.jetbrains.java.decompiler.struct.StructClass;
23 import org.jetbrains.java.decompiler.struct.StructContext;
24 import org.jetbrains.java.decompiler.struct.StructField;
25 import org.jetbrains.java.decompiler.struct.StructMethod;
26 import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
27 import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
28 import org.jetbrains.java.decompiler.struct.gen.NewClassNameBuilder;
29 import org.jetbrains.java.decompiler.util.VBStyleCollection;
30
31 import java.io.IOException;
32 import java.util.*;
33
34 public class IdentifierConverter implements NewClassNameBuilder {
35
36   private StructContext context;
37   private IIdentifierRenamer helper;
38   private PoolInterceptor interceptor;
39   private List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>();
40   private List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>();
41   private Map<String, Map<String, String>> interfaceNameMaps = new HashMap<String, Map<String, String>>();
42
43   public void rename(StructContext context) {
44     try {
45       this.context = context;
46
47       String user_class = (String)DecompilerContext.getProperty(IFernflowerPreferences.USER_RENAMER_CLASS);
48       if (user_class != null) {
49         try {
50           helper = (IIdentifierRenamer)IdentifierConverter.class.getClassLoader().loadClass(user_class).newInstance();
51         }
52         catch (Exception ignored) { }
53       }
54
55       if (helper == null) {
56         helper = new ConverterHelper();
57       }
58
59       interceptor = new PoolInterceptor(helper);
60
61       buildInheritanceTree();
62
63       renameAllClasses();
64
65       renameInterfaces();
66
67       renameClasses();
68
69       DecompilerContext.setPoolInterceptor(interceptor);
70       context.reloadContext();
71     }
72     catch (IOException ex) {
73       throw new RuntimeException("Renaming failed!");
74     }
75   }
76
77   private void renameClasses() {
78     List<ClassWrapperNode> lstClasses = getReversePostOrderListIterative(rootClasses);
79     Map<String, Map<String, String>> classNameMaps = new HashMap<String, Map<String, String>>();
80
81     for (ClassWrapperNode node : lstClasses) {
82       StructClass cl = node.getClassStruct();
83       Map<String, String> names = new HashMap<String, String>();
84
85       // merge information on super class
86       if (cl.superClass != null) {
87         Map<String, String> mapClass = classNameMaps.get(cl.superClass.getString());
88         if (mapClass != null) {
89           names.putAll(mapClass);
90         }
91       }
92
93       // merge information on interfaces
94       for (String ifName : cl.getInterfaceNames()) {
95         Map<String, String> mapInt = interfaceNameMaps.get(ifName);
96         if (mapInt != null) {
97           names.putAll(mapInt);
98         }
99         else {
100           StructClass clintr = context.getClass(ifName);
101           if (clintr != null) {
102             names.putAll(processExternalInterface(clintr));
103           }
104         }
105       }
106
107       renameClassIdentifiers(cl, names);
108
109       if (!node.getSubclasses().isEmpty()) {
110         classNameMaps.put(cl.qualifiedName, names);
111       }
112     }
113   }
114
115   private Map<String, String> processExternalInterface(StructClass cl) {
116     Map<String, String> names = new HashMap<String, String>();
117
118     for (String ifName : cl.getInterfaceNames()) {
119       Map<String, String> mapInt = interfaceNameMaps.get(ifName);
120       if (mapInt != null) {
121         names.putAll(mapInt);
122       }
123       else {
124         StructClass clintr = context.getClass(ifName);
125         if (clintr != null) {
126           names.putAll(processExternalInterface(clintr));
127         }
128       }
129     }
130
131     renameClassIdentifiers(cl, names);
132
133     return names;
134   }
135
136   private void renameInterfaces() {
137     List<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces);
138     Map<String, Map<String, String>> interfaceNameMaps = new HashMap<String, Map<String, String>>();
139
140     // rename methods and fields
141     for (ClassWrapperNode node : lstInterfaces) {
142
143       StructClass cl = node.getClassStruct();
144       Map<String, String> names = new HashMap<String, String>();
145
146       // merge information on super interfaces
147       for (String ifName : cl.getInterfaceNames()) {
148         Map<String, String> mapInt = interfaceNameMaps.get(ifName);
149         if (mapInt != null) {
150           names.putAll(mapInt);
151         }
152       }
153
154       renameClassIdentifiers(cl, names);
155
156       interfaceNameMaps.put(cl.qualifiedName, names);
157     }
158
159     this.interfaceNameMaps = interfaceNameMaps;
160   }
161
162   private void renameAllClasses() {
163     // order not important
164     List<ClassWrapperNode> lstAllClasses = new ArrayList<ClassWrapperNode>(getReversePostOrderListIterative(rootInterfaces));
165     lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses));
166
167     // rename all interfaces and classes
168     for (ClassWrapperNode node : lstAllClasses) {
169       renameClass(node.getClassStruct());
170     }
171   }
172
173   private void renameClass(StructClass cl) {
174
175     if (!cl.isOwn()) {
176       return;
177     }
178
179     String classOldFullName = cl.qualifiedName;
180
181     // TODO: rename packages
182     String clSimpleName = ConverterHelper.getSimpleClassName(classOldFullName);
183     if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, clSimpleName, null, null)) {
184       String classNewFullName;
185
186       do {
187         String classname = helper.getNextClassName(classOldFullName, ConverterHelper.getSimpleClassName(classOldFullName));
188         classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, classname);
189       }
190       while (context.getClasses().containsKey(classNewFullName));
191
192       interceptor.addName(classOldFullName, classNewFullName);
193     }
194   }
195
196   private void renameClassIdentifiers(StructClass cl, Map<String, String> names) {
197     // all classes are already renamed
198     String classOldFullName = cl.qualifiedName;
199     String classNewFullName = interceptor.getName(classOldFullName);
200
201     if (classNewFullName == null) {
202       classNewFullName = classOldFullName;
203     }
204
205     // methods
206     HashSet<String> setMethodNames = new HashSet<String>();
207     for (StructMethod md : cl.getMethods()) {
208       setMethodNames.add(md.getName());
209     }
210
211     VBStyleCollection<StructMethod, String> methods = cl.getMethods();
212     for (int i = 0; i < methods.size(); i++) {
213
214       StructMethod mt = methods.get(i);
215       String key = methods.getKey(i);
216
217       boolean isPrivate = mt.hasModifier(CodeConstants.ACC_PRIVATE);
218
219       String name = mt.getName();
220       if (!cl.isOwn() || mt.hasModifier(CodeConstants.ACC_NATIVE)) {
221         // external and native methods must not be renamed
222         if (!isPrivate) {
223           names.put(key, name);
224         }
225       }
226       else if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) {
227         if (isPrivate || !names.containsKey(key)) {
228           do {
229             name = helper.getNextMethodName(classOldFullName, name, mt.getDescriptor());
230           }
231           while (setMethodNames.contains(name));
232
233           if (!isPrivate) {
234             names.put(key, name);
235           }
236         }
237         else {
238           name = names.get(key);
239         }
240
241         interceptor.addName(classOldFullName + " " + mt.getName() + " " + mt.getDescriptor(),
242                             classNewFullName + " " + name + " " + buildNewDescriptor(false, mt.getDescriptor()));
243       }
244     }
245
246     // external fields are not being renamed
247     if (!cl.isOwn()) {
248       return;
249     }
250
251     // fields
252     // FIXME: should overloaded fields become the same name?
253     HashSet<String> setFieldNames = new HashSet<String>();
254     for (StructField fd : cl.getFields()) {
255       setFieldNames.add(fd.getName());
256     }
257
258     for (StructField fd : cl.getFields()) {
259       if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) {
260         String newName;
261         do {
262           newName = helper.getNextFieldName(classOldFullName, fd.getName(), fd.getDescriptor());
263         }
264         while (setFieldNames.contains(newName));
265
266         interceptor.addName(classOldFullName + " " + fd.getName() + " " + fd.getDescriptor(),
267                             classNewFullName + " " + newName + " " + buildNewDescriptor(true, fd.getDescriptor()));
268       }
269     }
270   }
271
272   @Override
273   public String buildNewClassname(String className) {
274     return interceptor.getName(className);
275   }
276
277   private String buildNewDescriptor(boolean isField, String descriptor) {
278     String newDescriptor;
279     if (isField) {
280       newDescriptor = FieldDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this);
281     }
282     else {
283       newDescriptor = MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this);
284     }
285     return newDescriptor != null ? newDescriptor : descriptor;
286   }
287
288   private static List<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) {
289     List<ClassWrapperNode> res = new ArrayList<ClassWrapperNode>();
290
291     LinkedList<ClassWrapperNode> stackNode = new LinkedList<ClassWrapperNode>();
292     LinkedList<Integer> stackIndex = new LinkedList<Integer>();
293
294     Set<ClassWrapperNode> setVisited = new HashSet<ClassWrapperNode>();
295
296     for (ClassWrapperNode root : roots) {
297       stackNode.add(root);
298       stackIndex.add(0);
299     }
300
301     while (!stackNode.isEmpty()) {
302       ClassWrapperNode node = stackNode.getLast();
303       int index = stackIndex.removeLast();
304
305       setVisited.add(node);
306
307       List<ClassWrapperNode> lstSubs = node.getSubclasses();
308
309       for (; index < lstSubs.size(); index++) {
310         ClassWrapperNode sub = lstSubs.get(index);
311         if (!setVisited.contains(sub)) {
312           stackIndex.add(index + 1);
313           stackNode.add(sub);
314           stackIndex.add(0);
315           break;
316         }
317       }
318
319       if (index == lstSubs.size()) {
320         res.add(0, node);
321         stackNode.removeLast();
322       }
323     }
324
325     return res;
326   }
327
328   private void buildInheritanceTree() {
329     Map<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>();
330     Map<String, StructClass> classes = context.getClasses();
331
332     List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>();
333     List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>();
334
335     for (StructClass cl : classes.values()) {
336       if (!cl.isOwn()) {
337         continue;
338       }
339
340       LinkedList<StructClass> stack = new LinkedList<StructClass>();
341       LinkedList<ClassWrapperNode> stackSubNodes = new LinkedList<ClassWrapperNode>();
342
343       stack.add(cl);
344       stackSubNodes.add(null);
345
346       while (!stack.isEmpty()) {
347         StructClass clStr = stack.removeFirst();
348         ClassWrapperNode child = stackSubNodes.removeFirst();
349
350         ClassWrapperNode node = nodes.get(clStr.qualifiedName);
351         boolean isNewNode = (node == null);
352
353         if (isNewNode) {
354           nodes.put(clStr.qualifiedName, node = new ClassWrapperNode(clStr));
355         }
356
357         //noinspection ConstantConditions
358         if (child != null) {
359           node.addSubclass(child);
360         }
361
362         if (!isNewNode) {
363           break;
364         }
365         else {
366           boolean isInterface = clStr.hasModifier(CodeConstants.ACC_INTERFACE);
367           boolean found_parent = false;
368
369           if (isInterface) {
370             for (String ifName : clStr.getInterfaceNames()) {
371               StructClass clParent = classes.get(ifName);
372               if (clParent != null) {
373                 stack.add(clParent);
374                 stackSubNodes.add(node);
375                 found_parent = true;
376               }
377             }
378           }
379           else if (clStr.superClass != null) { // null iff java/lang/Object
380             StructClass clParent = classes.get(clStr.superClass.getString());
381             if (clParent != null) {
382               stack.add(clParent);
383               stackSubNodes.add(node);
384               found_parent = true;
385             }
386           }
387
388           if (!found_parent) { // no super class or interface
389             (isInterface ? rootInterfaces : rootClasses).add(node);
390           }
391         }
392       }
393     }
394
395     this.rootClasses = rootClasses;
396     this.rootInterfaces = rootInterfaces;
397   }
398 }