summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/analysis/SourceIndex.java')
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndex.java185
1 files changed, 185 insertions, 0 deletions
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
new file mode 100644
index 0000000..a20fbb4
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java
@@ -0,0 +1,185 @@
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 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import com.google.common.collect.Multimap;
17
18import com.strobel.decompiler.languages.Region;
19import com.strobel.decompiler.languages.java.ast.AstNode;
20import com.strobel.decompiler.languages.java.ast.Identifier;
21
22import java.util.Collection;
23import java.util.List;
24import java.util.Map;
25import java.util.TreeMap;
26
27import cuchaz.enigma.mapping.Entry;
28
29public class SourceIndex {
30
31 private String m_source;
32 private TreeMap<Token, EntryReference<Entry, Entry>> m_tokenToReference;
33 private Multimap<EntryReference<Entry, Entry>, Token> m_referenceToTokens;
34 private Map<Entry, Token> m_declarationToToken;
35 private List<Integer> m_lineOffsets;
36 private boolean m_ignoreBadTokens;
37
38 public SourceIndex(String source) {
39 this(source, true);
40 }
41
42 public SourceIndex(String source, boolean ignoreBadTokens) {
43 m_source = source;
44 m_ignoreBadTokens = ignoreBadTokens;
45 m_tokenToReference = Maps.newTreeMap();
46 m_referenceToTokens = HashMultimap.create();
47 m_declarationToToken = Maps.newHashMap();
48 m_lineOffsets = Lists.newArrayList();
49
50 // count the lines
51 m_lineOffsets.add(0);
52 for (int i = 0; i < source.length(); i++) {
53 if (source.charAt(i) == '\n') {
54 m_lineOffsets.add(i + 1);
55 }
56 }
57 }
58
59 public String getSource() {
60 return m_source;
61 }
62
63 public Token getToken(AstNode node) {
64
65 // get the text of the node
66 String name = "";
67 if (node instanceof Identifier) {
68 name = ((Identifier) node).getName();
69 }
70
71 // get a token for this node's region
72 Region region = node.getRegion();
73 if (region.getBeginLine() == 0 || region.getEndLine() == 0) {
74 // DEBUG
75 System.err.println(String.format("WARNING: %s \"%s\" has invalid region: %s", node.getNodeType(), name, region));
76 return null;
77 }
78 Token token = new Token(
79 toPos(region.getBeginLine(), region.getBeginColumn()),
80 toPos(region.getEndLine(), region.getEndColumn()),
81 m_source
82 );
83 if (token.start == 0) {
84 // DEBUG
85 System.err.println(String.format("WARNING: %s \"%s\" has invalid start: %s", node.getNodeType(), name, region));
86 return null;
87 }
88
89 // DEBUG
90 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) );
91
92 // if the token has a $ in it, something's wrong. Ignore this token
93 if (name.lastIndexOf('$') >= 0 && m_ignoreBadTokens) {
94 // DEBUG
95 System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name));
96 return null;
97 }
98
99 return token;
100 }
101
102 public void addReference(AstNode node, Entry deobfEntry, Entry deobfContext) {
103 Token token = getToken(node);
104 if (token != null) {
105 EntryReference<Entry, Entry> deobfReference = new EntryReference<Entry, Entry>(deobfEntry, token.text, deobfContext);
106 m_tokenToReference.put(token, deobfReference);
107 m_referenceToTokens.put(deobfReference, token);
108 }
109 }
110
111 public void addDeclaration(AstNode node, Entry deobfEntry) {
112 Token token = getToken(node);
113 if (token != null) {
114 EntryReference<Entry, Entry> reference = new EntryReference<Entry, Entry>(deobfEntry, token.text);
115 m_tokenToReference.put(token, reference);
116 m_referenceToTokens.put(reference, token);
117 m_declarationToToken.put(deobfEntry, token);
118 }
119 }
120
121 public Token getReferenceToken(int pos) {
122 Token token = m_tokenToReference.floorKey(new Token(pos, pos, null));
123 if (token != null && token.contains(pos)) {
124 return token;
125 }
126 return null;
127 }
128
129 public Collection<Token> getReferenceTokens(EntryReference<Entry, Entry> deobfReference) {
130 return m_referenceToTokens.get(deobfReference);
131 }
132
133 public EntryReference<Entry, Entry> getDeobfReference(Token token) {
134 if (token == null) {
135 return null;
136 }
137 return m_tokenToReference.get(token);
138 }
139
140 public void replaceDeobfReference(Token token, EntryReference<Entry, Entry> newDeobfReference) {
141 EntryReference<Entry, Entry> oldDeobfReference = m_tokenToReference.get(token);
142 m_tokenToReference.put(token, newDeobfReference);
143 Collection<Token> tokens = m_referenceToTokens.get(oldDeobfReference);
144 m_referenceToTokens.removeAll(oldDeobfReference);
145 m_referenceToTokens.putAll(newDeobfReference, tokens);
146 }
147
148 public Iterable<Token> referenceTokens() {
149 return m_tokenToReference.keySet();
150 }
151
152 public Iterable<Token> declarationTokens() {
153 return m_declarationToToken.values();
154 }
155
156 public Iterable<Entry> declarations() {
157 return m_declarationToToken.keySet();
158 }
159
160 public Token getDeclarationToken(Entry deobfEntry) {
161 return m_declarationToToken.get(deobfEntry);
162 }
163
164 public int getLineNumber(int pos) {
165 // line number is 1-based
166 int line = 0;
167 for (Integer offset : m_lineOffsets) {
168 if (offset > pos) {
169 break;
170 }
171 line++;
172 }
173 return line;
174 }
175
176 public int getColumnNumber(int pos) {
177 // column number is 1-based
178 return pos - m_lineOffsets.get(getLineNumber(pos) - 1) + 1;
179 }
180
181 private int toPos(int line, int col) {
182 // line and col are 1-based
183 return m_lineOffsets.get(line - 1) + col - 1;
184 }
185}