summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis/TranslationIndex.java
diff options
context:
space:
mode:
authorGravatar Michael Smith2015-05-21 23:30:00 +0100
committerGravatar Michael Smith2015-05-21 23:30:00 +0100
commite3f452250e51b7271f3989c7dfd12e4422934942 (patch)
tree5aa482f9a6e21eb318a3e23e7d8274d77c73faf6 /src/cuchaz/enigma/analysis/TranslationIndex.java
downloadenigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.tar.gz
enigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.tar.xz
enigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.zip
Support Gradle alongside SSJB
This makes builds faster, simpler and better automated but still keeps Cuchaz happy. :)
Diffstat (limited to 'src/cuchaz/enigma/analysis/TranslationIndex.java')
-rw-r--r--src/cuchaz/enigma/analysis/TranslationIndex.java298
1 files changed, 298 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java
new file mode 100644
index 0000000..a491cfc
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/TranslationIndex.java
@@ -0,0 +1,298 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import java.io.IOException;
14import java.io.InputStream;
15import java.io.ObjectInputStream;
16import java.io.ObjectOutputStream;
17import java.io.OutputStream;
18import java.io.Serializable;
19import java.util.Collection;
20import java.util.HashMap;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24import java.util.zip.GZIPInputStream;
25import java.util.zip.GZIPOutputStream;
26
27import javassist.CtBehavior;
28import javassist.CtClass;
29import javassist.CtField;
30import javassist.bytecode.Descriptor;
31
32import com.google.common.collect.HashMultimap;
33import com.google.common.collect.Lists;
34import com.google.common.collect.Maps;
35import com.google.common.collect.Multimap;
36
37import cuchaz.enigma.mapping.ArgumentEntry;
38import cuchaz.enigma.mapping.BehaviorEntry;
39import cuchaz.enigma.mapping.ClassEntry;
40import cuchaz.enigma.mapping.Entry;
41import cuchaz.enigma.mapping.EntryFactory;
42import cuchaz.enigma.mapping.FieldEntry;
43import cuchaz.enigma.mapping.Translator;
44
45public class TranslationIndex implements Serializable {
46
47 private static final long serialVersionUID = 738687982126844179L;
48
49 private Map<ClassEntry,ClassEntry> m_superclasses;
50 private Multimap<ClassEntry,FieldEntry> m_fieldEntries;
51 private Multimap<ClassEntry,BehaviorEntry> m_behaviorEntries;
52 private Multimap<ClassEntry,ClassEntry> m_interfaces;
53
54 public TranslationIndex() {
55 m_superclasses = Maps.newHashMap();
56 m_fieldEntries = HashMultimap.create();
57 m_behaviorEntries = HashMultimap.create();
58 m_interfaces = HashMultimap.create();
59 }
60
61 public TranslationIndex(TranslationIndex other, Translator translator) {
62
63 // translate the superclasses
64 m_superclasses = Maps.newHashMap();
65 for (Map.Entry<ClassEntry,ClassEntry> mapEntry : other.m_superclasses.entrySet()) {
66 m_superclasses.put(
67 translator.translateEntry(mapEntry.getKey()),
68 translator.translateEntry(mapEntry.getValue())
69 );
70 }
71
72 // translate the interfaces
73 m_interfaces = HashMultimap.create();
74 for (Map.Entry<ClassEntry,ClassEntry> mapEntry : other.m_interfaces.entries()) {
75 m_interfaces.put(
76 translator.translateEntry(mapEntry.getKey()),
77 translator.translateEntry(mapEntry.getValue())
78 );
79 }
80
81 // translate the fields
82 m_fieldEntries = HashMultimap.create();
83 for (Map.Entry<ClassEntry,FieldEntry> mapEntry : other.m_fieldEntries.entries()) {
84 m_fieldEntries.put(
85 translator.translateEntry(mapEntry.getKey()),
86 translator.translateEntry(mapEntry.getValue())
87 );
88 }
89
90 m_behaviorEntries = HashMultimap.create();
91 for (Map.Entry<ClassEntry,BehaviorEntry> mapEntry : other.m_behaviorEntries.entries()) {
92 m_behaviorEntries.put(
93 translator.translateEntry(mapEntry.getKey()),
94 translator.translateEntry(mapEntry.getValue())
95 );
96 }
97 }
98
99 public void indexClass(CtClass c) {
100 indexClass(c, true);
101 }
102
103 public void indexClass(CtClass c, boolean indexMembers) {
104
105 ClassEntry classEntry = EntryFactory.getClassEntry(c);
106 if (isJre(classEntry)) {
107 return;
108 }
109
110 // add the superclass
111 ClassEntry superclassEntry = EntryFactory.getSuperclassEntry(c);
112 if (superclassEntry != null) {
113 m_superclasses.put(classEntry, superclassEntry);
114 }
115
116 // add the interfaces
117 for (String interfaceClassName : c.getClassFile().getInterfaces()) {
118 ClassEntry interfaceClassEntry = new ClassEntry(Descriptor.toJvmName(interfaceClassName));
119 if (!isJre(interfaceClassEntry)) {
120 m_interfaces.put(classEntry, interfaceClassEntry);
121 }
122 }
123
124 if (indexMembers) {
125 // add fields
126 for (CtField field : c.getDeclaredFields()) {
127 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
128 m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
129 }
130
131 // add behaviors
132 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
133 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior);
134 m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry);
135 }
136 }
137 }
138
139 public void renameClasses(Map<String,String> renames) {
140 EntryRenamer.renameClassesInMap(renames, m_superclasses);
141 EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries);
142 EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries);
143 }
144
145 public ClassEntry getSuperclass(ClassEntry classEntry) {
146 return m_superclasses.get(classEntry);
147 }
148
149 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
150 List<ClassEntry> ancestors = Lists.newArrayList();
151 while (classEntry != null) {
152 classEntry = getSuperclass(classEntry);
153 if (classEntry != null) {
154 ancestors.add(classEntry);
155 }
156 }
157 return ancestors;
158 }
159
160 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
161
162 // linear search is fast enough for now
163 List<ClassEntry> subclasses = Lists.newArrayList();
164 for (Map.Entry<ClassEntry,ClassEntry> entry : m_superclasses.entrySet()) {
165 ClassEntry subclass = entry.getKey();
166 ClassEntry superclass = entry.getValue();
167 if (classEntry.equals(superclass)) {
168 subclasses.add(subclass);
169 }
170 }
171 return subclasses;
172 }
173
174 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
175 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
176 out.add(subclassEntry);
177 getSubclassesRecursively(out, subclassEntry);
178 }
179 }
180
181 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
182 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
183 out.add(subclassEntry.getName());
184 getSubclassNamesRecursively(out, subclassEntry);
185 }
186 }
187
188 public Collection<Map.Entry<ClassEntry,ClassEntry>> getClassInterfaces() {
189 return m_interfaces.entries();
190 }
191
192 public Collection<ClassEntry> getInterfaces(ClassEntry classEntry) {
193 return m_interfaces.get(classEntry);
194 }
195
196 public boolean isInterface(ClassEntry classEntry) {
197 return m_interfaces.containsValue(classEntry);
198 }
199
200 public boolean entryExists(Entry entry) {
201 if (entry instanceof FieldEntry) {
202 return fieldExists((FieldEntry)entry);
203 } else if (entry instanceof BehaviorEntry) {
204 return behaviorExists((BehaviorEntry)entry);
205 } else if (entry instanceof ArgumentEntry) {
206 return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry());
207 }
208 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
209 }
210
211 public boolean fieldExists(FieldEntry fieldEntry) {
212 return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry);
213 }
214
215 public boolean behaviorExists(BehaviorEntry behaviorEntry) {
216 return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry);
217 }
218
219 public ClassEntry resolveEntryClass(Entry entry) {
220
221 if (entry instanceof ClassEntry) {
222 return (ClassEntry)entry;
223 }
224
225 ClassEntry superclassEntry = resolveSuperclass(entry);
226 if (superclassEntry != null) {
227 return superclassEntry;
228 }
229
230 ClassEntry interfaceEntry = resolveInterface(entry);
231 if (interfaceEntry != null) {
232 return interfaceEntry;
233 }
234
235 return null;
236 }
237
238 public ClassEntry resolveSuperclass(Entry entry) {
239
240 // this entry could refer to a method on a class where the method is not actually implemented
241 // travel up the inheritance tree to find the closest implementation
242 while (!entryExists(entry)) {
243
244 // is there a parent class?
245 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry());
246 if (superclassEntry == null) {
247 // this is probably a method from a class in a library
248 // we can't trace the implementation up any higher unless we index the library
249 return null;
250 }
251
252 // move up to the parent class
253 entry = entry.cloneToNewClass(superclassEntry);
254 }
255 return entry.getClassEntry();
256 }
257
258 public ClassEntry resolveInterface(Entry entry) {
259
260 // the interfaces for any class is a forest
261 // so let's look at all the trees
262 for (ClassEntry interfaceEntry : m_interfaces.get(entry.getClassEntry())) {
263 ClassEntry resolvedClassEntry = resolveSuperclass(entry.cloneToNewClass(interfaceEntry));
264 if (resolvedClassEntry != null) {
265 return resolvedClassEntry;
266 }
267 }
268 return null;
269 }
270
271 private boolean isJre(ClassEntry classEntry) {
272 String packageName = classEntry.getPackageName();
273 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
274 }
275
276 public void write(OutputStream out)
277 throws IOException {
278 GZIPOutputStream gzipout = new GZIPOutputStream(out);
279 ObjectOutputStream oout = new ObjectOutputStream(gzipout);
280 oout.writeObject(m_superclasses);
281 oout.writeObject(m_fieldEntries);
282 oout.writeObject(m_behaviorEntries);
283 gzipout.finish();
284 }
285
286 @SuppressWarnings("unchecked")
287 public void read(InputStream in)
288 throws IOException {
289 try {
290 ObjectInputStream oin = new ObjectInputStream(new GZIPInputStream(in));
291 m_superclasses = (HashMap<ClassEntry,ClassEntry>)oin.readObject();
292 m_fieldEntries = (HashMultimap<ClassEntry,FieldEntry>)oin.readObject();
293 m_behaviorEntries = (HashMultimap<ClassEntry,BehaviorEntry>)oin.readObject();
294 } catch (ClassNotFoundException ex) {
295 throw new Error(ex);
296 }
297 }
298}