From d6b2a223a7973941e5e4fb45c8ceec4885891496 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Mar 2015 12:53:11 -0400 Subject: starting on field matching gui --- src/cuchaz/enigma/ConvertMain.java | 136 +++++++++++++++---- src/cuchaz/enigma/convert/ClassMatches.java | 153 +++++++++++++++++++++ src/cuchaz/enigma/convert/FieldMatches.java | 35 +++++ src/cuchaz/enigma/convert/MappingsConverter.java | 8 +- src/cuchaz/enigma/convert/Matches.java | 153 --------------------- src/cuchaz/enigma/convert/MatchesReader.java | 8 +- src/cuchaz/enigma/convert/MatchesWriter.java | 6 +- src/cuchaz/enigma/gui/ClassMatchingGui.java | 145 ++++++-------------- src/cuchaz/enigma/gui/CodeReader.java | 164 +++++++++++++++++++++++ src/cuchaz/enigma/gui/FieldMatchingGui.java | 131 ++++++++++++++++++ src/cuchaz/enigma/gui/Gui.java | 2 +- src/cuchaz/enigma/gui/GuiTricks.java | 58 -------- 12 files changed, 645 insertions(+), 354 deletions(-) create mode 100644 src/cuchaz/enigma/convert/ClassMatches.java create mode 100644 src/cuchaz/enigma/convert/FieldMatches.java delete mode 100644 src/cuchaz/enigma/convert/Matches.java create mode 100644 src/cuchaz/enigma/gui/CodeReader.java create mode 100644 src/cuchaz/enigma/gui/FieldMatchingGui.java diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 2afd9ca9..624eb40a 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -6,14 +6,16 @@ import java.io.FileWriter; import java.io.IOException; import java.util.jar.JarFile; +import cuchaz.enigma.convert.ClassMatches; +import cuchaz.enigma.convert.FieldMatches; import cuchaz.enigma.convert.MappingsConverter; -import cuchaz.enigma.convert.Matches; import cuchaz.enigma.convert.MatchesReader; import cuchaz.enigma.convert.MatchesWriter; import cuchaz.enigma.gui.ClassMatchingGui; -import cuchaz.enigma.gui.ClassMatchingGui.SaveListener; +import cuchaz.enigma.gui.FieldMatchingGui; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsChecker; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; @@ -30,11 +32,13 @@ public class ConvertMain { File inMappingsFile = new File("../Enigma Mappings/1.8.mappings"); File outMappingsFile = new File("../Enigma Mappings/1.8.3.mappings"); Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); - File matchingFile = new File(inMappingsFile.getName() + ".matching"); + File classMatchingFile = new File(inMappingsFile.getName() + ".class.matching"); + File fieldMatchingFile = new File(inMappingsFile.getName() + ".field.matching"); - //computeMatches(matchingFile, sourceJar, destJar, mappings); - editMatches(matchingFile, sourceJar, destJar, mappings); - //convertMappings(outMappingsFile, sourceJar, destJar, mappings, matchingFile); + //computeMatches(classMatchingFile, sourceJar, destJar, mappings); + //editClasssMatches(classMatchingFile, sourceJar, destJar, mappings); + //convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchingFile); + editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchingFile, fieldMatchingFile); /* TODO // write out the converted mappings @@ -45,28 +49,25 @@ public class ConvertMain { */ } - private static void computeMatches(File matchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + private static void computeMatches(File classMatchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { - Matches matches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); - MatchesWriter.write(matches, matchingFile); - System.out.println("Wrote:\n\t" + matchingFile.getAbsolutePath()); + ClassMatches classMatches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); + MatchesWriter.writeClasses(classMatches, classMatchingFile); + System.out.println("Wrote:\n\t" + classMatchingFile.getAbsolutePath()); } - private static void editMatches(final File matchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + private static void editClasssMatches(final File classMatchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { System.out.println("Reading matches..."); - Matches matches = MatchesReader.read(matchingFile); - System.out.println("Indexing source jar..."); - Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); - sourceDeobfuscator.setMappings(mappings); - System.out.println("Indexing dest jar..."); - Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(mappings); System.out.println("Starting GUI..."); - new ClassMatchingGui(matches, sourceDeobfuscator, destDeobfuscator).setSaveListener(new SaveListener() { + new ClassMatchingGui(classMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(new ClassMatchingGui.SaveListener() { @Override - public void save(Matches matches) { + public void save(ClassMatches matches) { try { - MatchesWriter.write(matches, matchingFile); + MatchesWriter.writeClasses(matches, classMatchingFile); } catch (IOException ex) { throw new Error(ex); } @@ -74,21 +75,100 @@ public class ConvertMain { }); } - private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File matchingFile) + private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchingFile) throws IOException { System.out.println("Reading matches..."); - Matches matches = MatchesReader.read(matchingFile); - System.out.println("Indexing source jar..."); - Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); - sourceDeobfuscator.setMappings(mappings); - System.out.println("Indexing dest jar..."); - Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(mappings); - Mappings newMappings = MappingsConverter.newMappings(matches, mappings, sourceDeobfuscator, destDeobfuscator); + Mappings newMappings = MappingsConverter.newMappings(classMatches, mappings, deobfuscators.source, deobfuscators.source); try (FileWriter out = new FileWriter(outMappingsFile)) { new MappingsWriter().write(out, newMappings); } System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); } + + private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchingFile, final File fieldMatchingFile) + throws IOException, MappingParseException { + + System.out.println("Reading matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + FieldMatches fieldMatches; + if (fieldMatchingFile.exists() /* TEMP */ && false) { + // TODO + //fieldMatches = MatchesReader.readFields(fieldMatchingFile); + } else { + fieldMatches = new FieldMatches(); + } + + // prep deobfuscators + Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); + deobfuscators.source.setMappings(sourceMappings); + Mappings destMappings = new MappingsReader().read(new FileReader(destMappingsFile)); + MappingsChecker checker = new MappingsChecker(deobfuscators.dest.getJarIndex()); + checker.dropBrokenMappings(destMappings); + deobfuscators.dest.setMappings(destMappings); + + new FieldMatchingGui(classMatches, fieldMatches, checker.getDroppedFieldMappings(), deobfuscators.source, deobfuscators.dest).setSaveListener(new FieldMatchingGui.SaveListener() { + @Override + public void save(FieldMatches matches) { + /* TODO + try { + MatchesWriter.writeFields(matches, fieldMatchingFile); + } catch (IOException ex) { + throw new Error(ex); + } + */ + } + }); + } + + private static class Deobfuscators { + + public Deobfuscator source; + public Deobfuscator dest; + + public Deobfuscators(JarFile sourceJar, JarFile destJar) { + System.out.println("Indexing source jar..."); + IndexerThread sourceIndexer = new IndexerThread(sourceJar); + sourceIndexer.start(); + System.out.println("Indexing dest jar..."); + IndexerThread destIndexer = new IndexerThread(destJar); + destIndexer.start(); + sourceIndexer.joinOrBail(); + destIndexer.joinOrBail(); + source = sourceIndexer.deobfuscator; + dest = destIndexer.deobfuscator; + } + } + + private static class IndexerThread extends Thread { + + private JarFile m_jarFile; + public Deobfuscator deobfuscator; + + public IndexerThread(JarFile jarFile) { + m_jarFile = jarFile; + deobfuscator = null; + } + + public void joinOrBail() { + try { + join(); + } catch (InterruptedException ex) { + throw new Error(ex); + } + } + + @Override + public void run() { + try { + deobfuscator = new Deobfuscator(m_jarFile); + } catch (IOException ex) { + throw new Error(ex); + } + } + } } diff --git a/src/cuchaz/enigma/convert/ClassMatches.java b/src/cuchaz/enigma/convert/ClassMatches.java new file mode 100644 index 00000000..f8b2afdc --- /dev/null +++ b/src/cuchaz/enigma/convert/ClassMatches.java @@ -0,0 +1,153 @@ +package cuchaz.enigma.convert; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class ClassMatches implements Iterable { + + Collection m_matches; + Map m_matchesBySource; + Map m_matchesByDest; + BiMap m_uniqueMatches; + Map m_ambiguousMatchesBySource; + Map m_ambiguousMatchesByDest; + Set m_unmatchedSourceClasses; + Set m_unmatchedDestClasses; + + public ClassMatches() { + this(new ArrayList()); + } + + public ClassMatches(Collection matches) { + m_matches = matches; + m_matchesBySource = Maps.newHashMap(); + m_matchesByDest = Maps.newHashMap(); + m_uniqueMatches = HashBiMap.create(); + m_ambiguousMatchesBySource = Maps.newHashMap(); + m_ambiguousMatchesByDest = Maps.newHashMap(); + m_unmatchedSourceClasses = Sets.newHashSet(); + m_unmatchedDestClasses = Sets.newHashSet(); + + for (ClassMatch match : matches) { + indexMatch(match); + } + } + + public void add(ClassMatch match) { + m_matches.add(match); + indexMatch(match); + } + + public void remove(ClassMatch match) { + for (ClassEntry sourceClass : match.sourceClasses) { + m_matchesBySource.remove(sourceClass); + m_uniqueMatches.remove(sourceClass); + m_ambiguousMatchesBySource.remove(sourceClass); + m_unmatchedSourceClasses.remove(sourceClass); + } + for (ClassEntry destClass : match.destClasses) { + m_matchesByDest.remove(destClass); + m_uniqueMatches.inverse().remove(destClass); + m_ambiguousMatchesByDest.remove(destClass); + m_unmatchedDestClasses.remove(destClass); + } + m_matches.remove(match); + } + + public int size() { + return m_matches.size(); + } + + @Override + public Iterator iterator() { + return m_matches.iterator(); + } + + private void indexMatch(ClassMatch match) { + if (!match.isMatched()) { + // unmatched + m_unmatchedSourceClasses.addAll(match.sourceClasses); + m_unmatchedDestClasses.addAll(match.destClasses); + } else { + if (match.isAmbiguous()) { + // ambiguously matched + for (ClassEntry entry : match.sourceClasses) { + m_ambiguousMatchesBySource.put(entry, match); + } + for (ClassEntry entry : match.destClasses) { + m_ambiguousMatchesByDest.put(entry, match); + } + } else { + // uniquely matched + m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); + } + } + for (ClassEntry entry : match.sourceClasses) { + m_matchesBySource.put(entry, match); + } + for (ClassEntry entry : match.destClasses) { + m_matchesByDest.put(entry, match); + } + } + + public BiMap getUniqueMatches() { + return m_uniqueMatches; + } + + public Set getUnmatchedSourceClasses() { + return m_unmatchedSourceClasses; + } + + public Set getUnmatchedDestClasses() { + return m_unmatchedDestClasses; + } + + public Set getAmbiguouslyMatchedSourceClasses() { + return m_ambiguousMatchesBySource.keySet(); + } + + public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { + return m_ambiguousMatchesBySource.get(sourceClass); + } + + public ClassMatch getMatchBySource(ClassEntry sourceClass) { + return m_matchesBySource.get(sourceClass); + } + + public ClassMatch getMatchByDest(ClassEntry destClass) { + return m_matchesByDest.get(destClass); + } + + public void removeSource(ClassEntry sourceClass) { + ClassMatch match = m_matchesBySource.get(sourceClass); + if (match != null) { + remove(match); + match.sourceClasses.remove(sourceClass); + if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { + add(match); + } + } + } + + public void removeDest(ClassEntry destClass) { + ClassMatch match = m_matchesByDest.get(destClass); + if (match != null) { + remove(match); + match.destClasses.remove(destClass); + if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { + add(match); + } + } + } +} diff --git a/src/cuchaz/enigma/convert/FieldMatches.java b/src/cuchaz/enigma/convert/FieldMatches.java new file mode 100644 index 00000000..f78a8f55 --- /dev/null +++ b/src/cuchaz/enigma/convert/FieldMatches.java @@ -0,0 +1,35 @@ +package cuchaz.enigma.convert; + +import java.util.Collection; +import java.util.Set; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Sets; + +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; + + +public class FieldMatches { + + private BiMap m_matches; + private Set m_unmatchedSourceFields; + + public FieldMatches() { + m_matches = HashBiMap.create(); + m_unmatchedSourceFields = Sets.newHashSet(); + } + + public void addUnmatchedSourceFields(Set fieldEntries) { + m_unmatchedSourceFields.addAll(fieldEntries); + } + + public Collection getSourceClassesWithUnmatchedFields() { + Set classEntries = Sets.newHashSet(); + for (FieldEntry fieldEntry : m_unmatchedSourceFields) { + classEntries.add(fieldEntry.getClassEntry()); + } + return classEntries; + } +} diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index 5883878c..667ee9de 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -37,7 +37,7 @@ import cuchaz.enigma.mapping.MethodMapping; public class MappingsConverter { - public static Matches computeMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { + public static ClassMatches computeMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { // index jars System.out.println("Indexing source jar..."); @@ -49,7 +49,7 @@ public class MappingsConverter { // compute the matching ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); - return new Matches(matching.matches()); + return new ClassMatches(matching.matches()); } public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap knownMatches) { @@ -115,7 +115,7 @@ public class MappingsConverter { return lastMatching; } - public static Mappings newMappings(Matches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { // sort the unique matches by size of inner class chain Multimap> matchesByDestChainSize = HashMultimap.create(); @@ -172,7 +172,7 @@ public class MappingsConverter { return newMappings; } - private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping mapping, final Matches matches, boolean useSimpleName) { + private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping mapping, final ClassMatches matches, boolean useSimpleName) { ClassNameReplacer replacer = new ClassNameReplacer() { @Override diff --git a/src/cuchaz/enigma/convert/Matches.java b/src/cuchaz/enigma/convert/Matches.java deleted file mode 100644 index 19bb155f..00000000 --- a/src/cuchaz/enigma/convert/Matches.java +++ /dev/null @@ -1,153 +0,0 @@ -package cuchaz.enigma.convert; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import cuchaz.enigma.mapping.ClassEntry; - - -public class Matches implements Iterable { - - Collection m_matches; - Map m_matchesBySource; - Map m_matchesByDest; - BiMap m_uniqueMatches; - Map m_ambiguousMatchesBySource; - Map m_ambiguousMatchesByDest; - Set m_unmatchedSourceClasses; - Set m_unmatchedDestClasses; - - public Matches() { - this(new ArrayList()); - } - - public Matches(Collection matches) { - m_matches = matches; - m_matchesBySource = Maps.newHashMap(); - m_matchesByDest = Maps.newHashMap(); - m_uniqueMatches = HashBiMap.create(); - m_ambiguousMatchesBySource = Maps.newHashMap(); - m_ambiguousMatchesByDest = Maps.newHashMap(); - m_unmatchedSourceClasses = Sets.newHashSet(); - m_unmatchedDestClasses = Sets.newHashSet(); - - for (ClassMatch match : matches) { - indexMatch(match); - } - } - - public void add(ClassMatch match) { - m_matches.add(match); - indexMatch(match); - } - - public void remove(ClassMatch match) { - for (ClassEntry sourceClass : match.sourceClasses) { - m_matchesBySource.remove(sourceClass); - m_uniqueMatches.remove(sourceClass); - m_ambiguousMatchesBySource.remove(sourceClass); - m_unmatchedSourceClasses.remove(sourceClass); - } - for (ClassEntry destClass : match.destClasses) { - m_matchesByDest.remove(destClass); - m_uniqueMatches.inverse().remove(destClass); - m_ambiguousMatchesByDest.remove(destClass); - m_unmatchedDestClasses.remove(destClass); - } - m_matches.remove(match); - } - - public int size() { - return m_matches.size(); - } - - @Override - public Iterator iterator() { - return m_matches.iterator(); - } - - private void indexMatch(ClassMatch match) { - if (!match.isMatched()) { - // unmatched - m_unmatchedSourceClasses.addAll(match.sourceClasses); - m_unmatchedDestClasses.addAll(match.destClasses); - } else { - if (match.isAmbiguous()) { - // ambiguously matched - for (ClassEntry entry : match.sourceClasses) { - m_ambiguousMatchesBySource.put(entry, match); - } - for (ClassEntry entry : match.destClasses) { - m_ambiguousMatchesByDest.put(entry, match); - } - } else { - // uniquely matched - m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); - } - } - for (ClassEntry entry : match.sourceClasses) { - m_matchesBySource.put(entry, match); - } - for (ClassEntry entry : match.destClasses) { - m_matchesByDest.put(entry, match); - } - } - - public BiMap getUniqueMatches() { - return m_uniqueMatches; - } - - public Set getUnmatchedSourceClasses() { - return m_unmatchedSourceClasses; - } - - public Set getUnmatchedDestClasses() { - return m_unmatchedDestClasses; - } - - public Set getAmbiguouslyMatchedSourceClasses() { - return m_ambiguousMatchesBySource.keySet(); - } - - public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { - return m_ambiguousMatchesBySource.get(sourceClass); - } - - public ClassMatch getMatchBySource(ClassEntry sourceClass) { - return m_matchesBySource.get(sourceClass); - } - - public ClassMatch getMatchByDest(ClassEntry destClass) { - return m_matchesByDest.get(destClass); - } - - public void removeSource(ClassEntry sourceClass) { - ClassMatch match = m_matchesBySource.get(sourceClass); - if (match != null) { - remove(match); - match.sourceClasses.remove(sourceClass); - if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { - add(match); - } - } - } - - public void removeDest(ClassEntry destClass) { - ClassMatch match = m_matchesByDest.get(destClass); - if (match != null) { - remove(match); - match.destClasses.remove(destClass); - if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { - add(match); - } - } - } -} diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java index 808f8d0a..b43535cb 100644 --- a/src/cuchaz/enigma/convert/MatchesReader.java +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -14,19 +14,19 @@ import cuchaz.enigma.mapping.ClassEntry; public class MatchesReader { - public static Matches read(File file) + public static ClassMatches readClasses(File file) throws IOException { try (BufferedReader in = new BufferedReader(new FileReader(file))) { - Matches matches = new Matches(); + ClassMatches matches = new ClassMatches(); String line = null; while ((line = in.readLine()) != null) { - matches.add(readMatch(line)); + matches.add(readClassMatch(line)); } return matches; } } - private static ClassMatch readMatch(String line) + private static ClassMatch readClassMatch(String line) throws IOException { String[] sides = line.split(":", 2); return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java index 49ffb6d7..6658e2a3 100644 --- a/src/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/cuchaz/enigma/convert/MatchesWriter.java @@ -9,16 +9,16 @@ import cuchaz.enigma.mapping.ClassEntry; public class MatchesWriter { - public static void write(Matches matches, File file) + public static void writeClasses(ClassMatches matches, File file) throws IOException { try (FileWriter out = new FileWriter(file)) { for (ClassMatch match : matches) { - writeMatch(out, match); + writeClassMatch(out, match); } } } - private static void writeMatch(FileWriter out, ClassMatch match) + private static void writeClassMatch(FileWriter out, ClassMatch match) throws IOException { writeClasses(out, match.sourceClasses); out.write(":"); diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java index ff7cda99..b6744515 100644 --- a/src/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java @@ -15,7 +15,6 @@ import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; -import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; @@ -28,22 +27,18 @@ import javax.swing.WindowConstants; import com.beust.jcommander.internal.Lists; import com.beust.jcommander.internal.Maps; import com.google.common.collect.BiMap; -import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; -import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.analysis.Token; import cuchaz.enigma.convert.ClassIdentifier; import cuchaz.enigma.convert.ClassIdentity; import cuchaz.enigma.convert.ClassMatch; +import cuchaz.enigma.convert.ClassMatches; import cuchaz.enigma.convert.ClassMatching; import cuchaz.enigma.convert.ClassNamer; import cuchaz.enigma.convert.MappingsConverter; -import cuchaz.enigma.convert.Matches; import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsChecker; import de.sciss.syntaxpane.DefaultSyntaxKit; @@ -55,21 +50,21 @@ public class ClassMatchingGui { Matched { @Override - public Collection getSourceClasses(Matches matches) { + public Collection getSourceClasses(ClassMatches matches) { return matches.getUniqueMatches().keySet(); } }, Unmatched { @Override - public Collection getSourceClasses(Matches matches) { + public Collection getSourceClasses(ClassMatches matches) { return matches.getUnmatchedSourceClasses(); } }, Ambiguous { @Override - public Collection getSourceClasses(Matches matches) { + public Collection getSourceClasses(ClassMatches matches) { return matches.getAmbiguouslyMatchedSourceClasses(); } }; @@ -82,7 +77,7 @@ public class ClassMatchingGui { return button; } - public abstract Collection getSourceClasses(Matches matches); + public abstract Collection getSourceClasses(ClassMatches matches); public static SourceType getDefault() { return values()[0]; @@ -90,23 +85,22 @@ public class ClassMatchingGui { } public static interface SaveListener { - public void save(Matches matches); + public void save(ClassMatches matches); } // controls private JFrame m_frame; private ClassSelector m_sourceClasses; private ClassSelector m_destClasses; - private JEditorPane m_sourceReader; - private JEditorPane m_destReader; + private CodeReader m_sourceReader; + private CodeReader m_destReader; private JLabel m_sourceClassLabel; private JLabel m_destClassLabel; private JButton m_matchButton; private Map m_sourceTypeButtons; private JCheckBox m_advanceCheck; - private SelectionHighlightPainter m_selectionHighlightPainter; - private Matches m_matches; + private ClassMatches m_classMatches; private Deobfuscator m_sourceDeobfuscator; private Deobfuscator m_destDeobfuscator; private ClassEntry m_sourceClass; @@ -114,14 +108,14 @@ public class ClassMatchingGui { private SourceType m_sourceType; private SaveListener m_saveListener; - public ClassMatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - m_matches = matches; + m_classMatches = matches; m_sourceDeobfuscator = sourceDeobfuscator; m_destDeobfuscator = destDeobfuscator; // init frame - m_frame = new JFrame(Constants.Name); + m_frame = new JFrame(Constants.Name + " - Class Matcher"); final Container pane = m_frame.getContentPane(); pane.setLayout(new BorderLayout()); @@ -188,8 +182,8 @@ public class ClassMatchingGui { // init source panels DefaultSyntaxKit.initKit(); - m_sourceReader = makeReader(); - m_destReader = makeReader(); + m_sourceReader = new CodeReader(); + m_destReader = new CodeReader(); // init all the splits JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); @@ -238,8 +232,6 @@ public class ClassMatchingGui { m_frame.setVisible(true); m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - m_selectionHighlightPainter = new SelectionHighlightPainter(); - // init state updateDestMappings(); setSourceType(SourceType.getDefault()); @@ -247,19 +239,6 @@ public class ClassMatchingGui { m_saveListener = null; } - private JEditorPane makeReader() { - - JEditorPane reader = new JEditorPane(); - reader.setEditable(false); - reader.setContentType("text/java"); - - // turn off token highlighting (it's wrong most of the time anyway...) - DefaultSyntaxKit kit = (DefaultSyntaxKit)reader.getEditorKit(); - kit.toggleComponent(reader, "de.sciss.syntaxpane.components.TokenMarker"); - - return reader; - } - public void setSaveListener(SaveListener val) { m_saveListener = val; } @@ -267,7 +246,7 @@ public class ClassMatchingGui { private void updateDestMappings() { Mappings newMappings = MappingsConverter.newMappings( - m_matches, + m_classMatches, m_sourceDeobfuscator.getMappings(), m_sourceDeobfuscator, m_destDeobfuscator @@ -294,13 +273,13 @@ public class ClassMatchingGui { // show the source classes m_sourceType = val; - m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_matches), m_sourceDeobfuscator)); + m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_classMatches), m_sourceDeobfuscator)); // update counts for (SourceType sourceType : SourceType.values()) { m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)", sourceType.name(), - sourceType.getSourceClasses(m_matches).size() + sourceType.getSourceClasses(m_classMatches).size() )); } } @@ -345,7 +324,7 @@ public class ClassMatchingGui { if (m_sourceClass != null) { // show the dest class(es) - ClassMatch match = m_matches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); + ClassMatch match = m_classMatches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); assert(match != null); if (match.destClasses.isEmpty()) { @@ -376,7 +355,12 @@ public class ClassMatchingGui { } setDestClass(null); - decompileClass(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); + m_sourceReader.decompileClass(m_sourceClass, m_sourceDeobfuscator, new Runnable() { + @Override + public void run() { + m_sourceReader.navigateToClassDeclaration(m_sourceClass); + } + }); updateMatchButton(); } @@ -386,7 +370,7 @@ public class ClassMatchingGui { ClassEntry obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); // set up identifiers - ClassNamer namer = new ClassNamer(m_matches.getUniqueMatches()); + ClassNamer namer = new ClassNamer(m_classMatches.getUniqueMatches()); ClassIdentifier sourceIdentifier = new ClassIdentifier( m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), namer.getSourceNamer(), true @@ -401,7 +385,7 @@ public class ClassMatchingGui { // rank all the unmatched dest classes against the source class ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); List scoredDestClasses = Lists.newArrayList(); - for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { + for (ClassEntry unmatchedDestClass : m_classMatches.getUnmatchedDestClasses()) { ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); @@ -420,59 +404,14 @@ public class ClassMatchingGui { m_destClass = classEntry; m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); - decompileClass(m_destClass, m_destDeobfuscator, m_destReader); - - updateMatchButton(); - } - - protected void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { - - if (classEntry == null) { - reader.setText(null); - return; - } - - reader.setText("(decompiling...)"); - - // run in a separate thread to keep ui responsive - new Thread() { + m_destReader.decompileClass(m_destClass, m_destDeobfuscator, new Runnable() { @Override public void run() { - - // get the outermost class - ClassEntry outermostClassEntry = classEntry; - while (outermostClassEntry.isInnerClass()) { - outermostClassEntry = outermostClassEntry.getOuterClassEntry(); - } - - // decompile it - CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); - String source = deobfuscator.getSource(sourceTree); - reader.setText(source); - SourceIndex sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); - - // navigate to the class declaration - Token token = sourceIndex.getDeclarationToken(classEntry); - if (token == null) { - // couldn't find the class declaration token, might be an anonymous class - // look for any declaration in that class instead - for (Entry entry : sourceIndex.declarations()) { - if (entry.getClassEntry().equals(classEntry)) { - token = sourceIndex.getDeclarationToken(entry); - break; - } - } - } - - if (token != null) { - GuiTricks.navigateToToken(reader, token, m_selectionHighlightPainter); - } else { - // couldn't find anything =( - System.out.println("Unable to find declaration in source for " + classEntry); - } - + m_destReader.navigateToClassDeclaration(m_destClass); } - }.start(); + }); + + updateMatchButton(); } private void updateMatchButton() { @@ -480,7 +419,7 @@ public class ClassMatchingGui { ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); - BiMap uniqueMatches = m_matches.getUniqueMatches(); + BiMap uniqueMatches = m_classMatches.getUniqueMatches(); boolean twoSelected = m_sourceClass != null && m_destClass != null; boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest); @@ -529,11 +468,11 @@ public class ClassMatchingGui { ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass); // remove the classes from their match - m_matches.removeSource(obfSource); - m_matches.removeDest(obfDest); + m_classMatches.removeSource(obfSource); + m_classMatches.removeDest(obfDest); // add them as matched classes - m_matches.add(new ClassMatch(obfSource, obfDest)); + m_classMatches.add(new ClassMatch(obfSource, obfDest)); ClassEntry nextClass = null; if (m_advanceCheck.isSelected()) { @@ -554,8 +493,8 @@ public class ClassMatchingGui { ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass); // remove the source to break the match, then add the source back as unmatched - m_matches.removeSource(obfSource); - m_matches.add(new ClassMatch(obfSource, null)); + m_classMatches.removeSource(obfSource); + m_classMatches.add(new ClassMatch(obfSource, null)); save(); updateMatches(); @@ -577,7 +516,7 @@ public class ClassMatchingGui { private void save() { if (m_saveListener != null) { - m_saveListener.save(m_matches); + m_saveListener.save(m_classMatches); } } @@ -589,15 +528,15 @@ public class ClassMatchingGui { ClassMatching matching = MappingsConverter.computeMatching( m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), - m_matches.getUniqueMatches() + m_classMatches.getUniqueMatches() ); - Matches newMatches = new Matches(matching.matches()); + ClassMatches newMatches = new ClassMatches(matching.matches()); System.out.println(String.format("Automatch found %d new matches", - newMatches.getUniqueMatches().size() - m_matches.getUniqueMatches().size() + newMatches.getUniqueMatches().size() - m_classMatches.getUniqueMatches().size() )); // update the current matches - m_matches = newMatches; + m_classMatches = newMatches; save(); updateMatches(); } diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java new file mode 100644 index 00000000..05feb59e --- /dev/null +++ b/src/cuchaz/enigma/gui/CodeReader.java @@ -0,0 +1,164 @@ +package cuchaz.enigma.gui; + +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JEditorPane; +import javax.swing.SwingUtilities; +import javax.swing.Timer; +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter.HighlightPainter; + +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import de.sciss.syntaxpane.DefaultSyntaxKit; + + +public class CodeReader extends JEditorPane { + + private static final long serialVersionUID = 3673180950485748810L; + + private static final Object m_lock = new Object(); + + private SelectionHighlightPainter m_highlightPainter; + private SourceIndex m_sourceIndex; + + public CodeReader() { + + setEditable(false); + setContentType("text/java"); + + // turn off token highlighting (it's wrong most of the time anyway...) + DefaultSyntaxKit kit = (DefaultSyntaxKit)getEditorKit(); + kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); + + m_highlightPainter = new SelectionHighlightPainter(); + m_sourceIndex = null; + } + + public void setCode(String code) { + // sadly, the java lexer is not thread safe, so we have to serialize all these calls + synchronized (m_lock) { + setText(code); + } + } + + public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { + decompileClass(classEntry, deobfuscator, null); + } + + public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Runnable callback) { + + if (classEntry == null) { + setCode(null); + return; + } + + setCode("(decompiling...)"); + + // run decompilation in a separate thread to keep ui responsive + new Thread() { + @Override + public void run() { + + // get the outermost class + ClassEntry outermostClassEntry = classEntry; + while (outermostClassEntry.isInnerClass()) { + outermostClassEntry = outermostClassEntry.getOuterClassEntry(); + } + + // decompile it + CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); + String source = deobfuscator.getSource(sourceTree); + setCode(source); + m_sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); + + if (callback != null) { + callback.run(); + } + } + }.start(); + } + + public void navigateToClassDeclaration(ClassEntry classEntry) { + + // navigate to the class declaration + Token token = m_sourceIndex.getDeclarationToken(classEntry); + if (token == null) { + // couldn't find the class declaration token, might be an anonymous class + // look for any declaration in that class instead + for (Entry entry : m_sourceIndex.declarations()) { + if (entry.getClassEntry().equals(classEntry)) { + token = m_sourceIndex.getDeclarationToken(entry); + break; + } + } + } + + if (token != null) { + navigateToToken(token); + } else { + // couldn't find anything =( + System.out.println("Unable to find declaration in source for " + classEntry); + } + } + + public void navigateToToken(final Token token) { + navigateToToken(this, token, m_highlightPainter); + } + + // HACKHACK: someday we can update the main GUI to use this code reader + public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { + + // set the caret position to the token + editor.setCaretPosition(token.start); + editor.grabFocus(); + + try { + // make sure the token is visible in the scroll window + Rectangle start = editor.modelToView(token.start); + Rectangle end = editor.modelToView(token.end); + final Rectangle show = start.union(end); + show.grow(start.width * 10, start.height * 6); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + editor.scrollRectToVisible(show); + } + }); + } catch (BadLocationException ex) { + throw new Error(ex); + } + + // highlight the token momentarily + final Timer timer = new Timer(200, new ActionListener() { + private int m_counter = 0; + private Object m_highlight = null; + + @Override + public void actionPerformed(ActionEvent event) { + if (m_counter % 2 == 0) { + try { + m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); + } catch (BadLocationException ex) { + // don't care + } + } else if (m_highlight != null) { + editor.getHighlighter().removeHighlight(m_highlight); + } + + if (m_counter++ > 6) { + Timer timer = (Timer)event.getSource(); + timer.stop(); + } + } + }); + timer.start(); + } +} diff --git a/src/cuchaz/enigma/gui/FieldMatchingGui.java b/src/cuchaz/enigma/gui/FieldMatchingGui.java new file mode 100644 index 00000000..de9ba142 --- /dev/null +++ b/src/cuchaz/enigma/gui/FieldMatchingGui.java @@ -0,0 +1,131 @@ +package cuchaz.enigma.gui; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.util.Map; + +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.WindowConstants; + +import cuchaz.enigma.Constants; +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.convert.ClassMatches; +import cuchaz.enigma.convert.FieldMatches; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; +import de.sciss.syntaxpane.DefaultSyntaxKit; + + +public class FieldMatchingGui { + + public static interface SaveListener { + public void save(FieldMatches matches); + } + + // controls + private JFrame m_frame; + private ClassSelector m_sourceClasses; + private CodeReader m_sourceReader; + private CodeReader m_destReader; + + private ClassMatches m_classMatches; + private FieldMatches m_fieldMatches; + private Map m_droppedFieldMappings; + private Deobfuscator m_sourceDeobfuscator; + private Deobfuscator m_destDeobfuscator; + private SaveListener m_saveListener; + + public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Map droppedFieldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + + m_classMatches = classMatches; + m_fieldMatches = fieldMatches; + m_droppedFieldMappings = droppedFieldMappings; + m_sourceDeobfuscator = sourceDeobfuscator; + m_destDeobfuscator = destDeobfuscator; + + // init frame + m_frame = new JFrame(Constants.Name + " - Field Matcher"); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + // init classes side + JPanel classesPanel = new JPanel(); + classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS)); + classesPanel.setPreferredSize(new Dimension(200, 0)); + pane.add(classesPanel, BorderLayout.WEST); + classesPanel.add(new JLabel("Classes")); + + m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_sourceClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setSourceClass(classEntry); + } + }); + JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); + classesPanel.add(sourceScroller); + + // init fields side + JPanel fieldsPanel = new JPanel(); + fieldsPanel.setLayout(new BoxLayout(fieldsPanel, BoxLayout.PAGE_AXIS)); + fieldsPanel.setPreferredSize(new Dimension(200, 0)); + pane.add(fieldsPanel, BorderLayout.WEST); + fieldsPanel.add(new JLabel("Destination Fields")); + + // init readers + DefaultSyntaxKit.initKit(); + m_sourceReader = new CodeReader(); + m_destReader = new CodeReader(); + + // init all the splits + JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, new JScrollPane(m_sourceReader)); + splitLeft.setResizeWeight(0); // let the right side take all the slack + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), fieldsPanel); + splitRight.setResizeWeight(1); // let the left side take all the slack + JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); + splitCenter.setResizeWeight(0.5); // resize 50:50 + pane.add(splitCenter, BorderLayout.CENTER); + splitCenter.resetToPreferredSizes(); + + // init bottom panel + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new FlowLayout()); + + // show the frame + pane.doLayout(); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + // init state + m_saveListener = null; + m_fieldMatches.addUnmatchedSourceFields(droppedFieldMappings.keySet()); + m_sourceClasses.setClasses(m_fieldMatches.getSourceClassesWithUnmatchedFields()); + } + + public void setSaveListener(SaveListener val) { + m_saveListener = val; + } + + protected void setSourceClass(ClassEntry obfSourceClass) { + + // get the matched dest class + final ClassEntry obfDestClass = m_classMatches.getUniqueMatches().get(obfSourceClass); + if (obfDestClass == null) { + throw new Error("No matching dest class for source class: " + obfSourceClass); + } + + m_sourceReader.decompileClass(obfSourceClass, m_sourceDeobfuscator); + m_destReader.decompileClass(obfDestClass, m_destDeobfuscator); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 38a6ee9a..ea05d255 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -737,7 +737,7 @@ public class Gui { if (token == null) { throw new IllegalArgumentException("Token cannot be null!"); } - GuiTricks.navigateToToken(m_editor, token, m_selectionHighlightPainter); + CodeReader.navigateToToken(m_editor, token, m_selectionHighlightPainter); redraw(); } diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index 5a3a01de..df9e2215 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java @@ -11,21 +11,11 @@ package cuchaz.enigma.gui; import java.awt.Font; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import javax.swing.JComponent; -import javax.swing.JEditorPane; import javax.swing.JLabel; -import javax.swing.SwingUtilities; -import javax.swing.Timer; import javax.swing.ToolTipManager; -import javax.swing.text.BadLocationException; -import javax.swing.text.Highlighter.HighlightPainter; - -import cuchaz.enigma.analysis.Token; public class GuiTricks { @@ -43,52 +33,4 @@ public class GuiTricks { manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); manager.setInitialDelay(oldDelay); } - - public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { - - // set the caret position to the token - editor.setCaretPosition(token.start); - editor.grabFocus(); - - try { - // make sure the token is visible in the scroll window - Rectangle start = editor.modelToView(token.start); - Rectangle end = editor.modelToView(token.end); - final Rectangle show = start.union(end); - show.grow(start.width * 10, start.height * 6); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - editor.scrollRectToVisible(show); - } - }); - } catch (BadLocationException ex) { - throw new Error(ex); - } - - // highlight the token momentarily - final Timer timer = new Timer(200, new ActionListener() { - private int m_counter = 0; - private Object m_highlight = null; - - @Override - public void actionPerformed(ActionEvent event) { - if (m_counter % 2 == 0) { - try { - m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); - } catch (BadLocationException ex) { - // don't care - } - } else if (m_highlight != null) { - editor.getHighlighter().removeHighlight(m_highlight); - } - - if (m_counter++ > 6) { - Timer timer = (Timer)event.getSource(); - timer.stop(); - } - } - }); - timer.start(); - } } -- cgit v1.2.3