This commit is contained in:
@@ -144,7 +144,8 @@ THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
Jadx-gui components
|
Jadx-gui components
|
||||||
===================
|
===================
|
||||||
|
|
||||||
RSyntaxTextArea library licensed under modified BSD license:
|
RSyntaxTextArea library (https://github.com/bobbylight/RSyntaxTextArea)
|
||||||
|
licensed under modified BSD license:
|
||||||
|
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
Copyright (c) 2012, Robert Futrell
|
Copyright (c) 2012, Robert Futrell
|
||||||
@@ -174,8 +175,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
Concurrent Trees (https://code.google.com/p/concurrent-trees/)
|
||||||
|
licenced under Apache License 2.0:
|
||||||
|
|
||||||
|
*******************************************************************************
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*******************************************************************************
|
||||||
|
|
||||||
|
|
||||||
Icons copied from several places:
|
Icons copied from several places:
|
||||||
- Eclipse Project (JDT UI) - licensed under EPL v1.0 (http://www.eclipse.org/legal/epl-v10.html)
|
- Eclipse Project (JDT UI) - licensed under EPL v1.0 (http://www.eclipse.org/legal/epl-v10.html)
|
||||||
- famfamfam silk icon set (http://www.famfamfam.com/lab/icons/silk/) - licensed under Creative Commons Attribution 2.5 License (http://creativecommons.org/licenses/by/2.5/)
|
- famfamfam silk icon set (http://www.famfamfam.com/lab/icons/silk/) - licensed
|
||||||
|
under Creative Commons Attribution 2.5 License (http://creativecommons.org/licenses/by/2.5/)
|
||||||
|
|
||||||
JFontChooser Component - http://sourceforge.jp/projects/jfontchooser/
|
JFontChooser Component - http://sourceforge.jp/projects/jfontchooser/
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import jadx.core.dex.nodes.MethodNode;
|
|||||||
|
|
||||||
public class FieldInitAttr implements IAttribute {
|
public class FieldInitAttr implements IAttribute {
|
||||||
|
|
||||||
public static FieldInitAttr NULL_VALUE = constValue(null);
|
public static final FieldInitAttr NULL_VALUE = constValue(null);
|
||||||
|
|
||||||
public enum InitType {
|
public enum InitType {
|
||||||
CONST,
|
CONST,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ mainClassName = 'jadx.gui.JadxGUI'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile(project(":jadx-core"))
|
compile(project(":jadx-core"))
|
||||||
compile(project(":jadx-cli"))
|
compile(project(":jadx-cli"))
|
||||||
compile 'com.fifesoft:rsyntaxtextarea:2.5.6'
|
compile 'com.fifesoft:rsyntaxtextarea:2.5.7'
|
||||||
compile 'com.google.code.gson:gson:2.3.1'
|
compile 'com.google.code.gson:gson:2.3.1'
|
||||||
compile files('libs/jfontchooser-1.0.5.jar')
|
compile files('libs/jfontchooser-1.0.5.jar')
|
||||||
compile 'com.googlecode.concurrent-trees:concurrent-trees:2.4.0'
|
compile 'com.googlecode.concurrent-trees:concurrent-trees:2.4.0'
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package jadx.gui.jobs;
|
||||||
|
|
||||||
|
import jadx.gui.JadxWrapper;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.FutureTask;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public abstract class BackgroundJob {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(DecompileJob.class);
|
||||||
|
|
||||||
|
protected final JadxWrapper wrapper;
|
||||||
|
private final ThreadPoolExecutor executor;
|
||||||
|
private Future<Boolean> future;
|
||||||
|
|
||||||
|
public BackgroundJob(JadxWrapper wrapper, int threadsCount) {
|
||||||
|
this.wrapper = wrapper;
|
||||||
|
this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Future<Boolean> process() {
|
||||||
|
if (future != null) {
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
ExecutorService shutdownExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
FutureTask<Boolean> task = new ShutdownTask();
|
||||||
|
shutdownExecutor.execute(task);
|
||||||
|
shutdownExecutor.shutdown();
|
||||||
|
future = task;
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ShutdownTask extends FutureTask<Boolean> {
|
||||||
|
public ShutdownTask() {
|
||||||
|
super(new Callable<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean call() throws Exception {
|
||||||
|
runJob();
|
||||||
|
executor.shutdown();
|
||||||
|
return executor.awaitTermination(5, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||||
|
executor.shutdownNow();
|
||||||
|
return super.cancel(mayInterruptIfRunning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void runJob();
|
||||||
|
|
||||||
|
public abstract String getInfoString();
|
||||||
|
|
||||||
|
protected void addTask(Runnable runnable) {
|
||||||
|
executor.execute(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processAndWait() {
|
||||||
|
try {
|
||||||
|
process().get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("BackgroundJob.processAndWait failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isComplete() {
|
||||||
|
try {
|
||||||
|
return future != null && future.isDone();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("BackgroundJob.isComplete failed", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProgress() {
|
||||||
|
return (int) (executor.getCompletedTaskCount() * 100 / (double) executor.getTaskCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package jadx.gui.jobs;
|
||||||
|
|
||||||
|
import jadx.gui.ui.ProgressPanel;
|
||||||
|
import jadx.gui.utils.CacheObject;
|
||||||
|
import jadx.gui.utils.TextSearchIndex;
|
||||||
|
import jadx.gui.utils.Utils;
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class BackgroundWorker extends SwingWorker<Void, Void> {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(BackgroundWorker.class);
|
||||||
|
|
||||||
|
private final CacheObject cache;
|
||||||
|
private final ProgressPanel progressPane;
|
||||||
|
|
||||||
|
public BackgroundWorker(CacheObject cacheObject, ProgressPanel progressPane) {
|
||||||
|
this.cache = cacheObject;
|
||||||
|
this.progressPane = progressPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exec() {
|
||||||
|
if (isDone()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressPane.setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addPropertyChangeListener(progressPane);
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
if (isDone()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG.debug("Canceling background jobs ...");
|
||||||
|
cancel(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground() throws Exception {
|
||||||
|
try {
|
||||||
|
System.gc();
|
||||||
|
LOG.debug("Memory usage: Before decompile: {}", Utils.memoryInfo());
|
||||||
|
runJob(cache.getDecompileJob());
|
||||||
|
|
||||||
|
LOG.debug("Memory usage: Before index: {}", Utils.memoryInfo());
|
||||||
|
runJob(cache.getIndexJob());
|
||||||
|
LOG.debug("Memory usage: After index: {}", Utils.memoryInfo());
|
||||||
|
|
||||||
|
System.gc();
|
||||||
|
LOG.debug("Memory usage: After gc: {}", Utils.memoryInfo());
|
||||||
|
|
||||||
|
TextSearchIndex searchIndex = cache.getTextIndex();
|
||||||
|
if (cache.getIndexJob().isUseFastSearch()
|
||||||
|
&& searchIndex != null
|
||||||
|
&& searchIndex.getSkippedCount() > 0) {
|
||||||
|
LOG.warn("Indexing of some classes skipped, count: {}, low memory: {}",
|
||||||
|
searchIndex.getSkippedCount(), Utils.memoryInfo());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Exception in background worker", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runJob(BackgroundJob job) {
|
||||||
|
if (isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progressPane.changeLabel(this, job.getInfoString());
|
||||||
|
Future<Boolean> future = job.process();
|
||||||
|
while (!future.isDone()) {
|
||||||
|
try {
|
||||||
|
setProgress(job.getProgress());
|
||||||
|
if (isCancelled()) {
|
||||||
|
future.cancel(false);
|
||||||
|
}
|
||||||
|
Thread.sleep(500);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Background worker error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
progressPane.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package jadx.gui.jobs;
|
||||||
|
|
||||||
|
import jadx.api.JavaClass;
|
||||||
|
import jadx.gui.JadxWrapper;
|
||||||
|
|
||||||
|
public class DecompileJob extends BackgroundJob {
|
||||||
|
|
||||||
|
public DecompileJob(JadxWrapper wrapper, int threadsCount) {
|
||||||
|
super(wrapper, threadsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void runJob() {
|
||||||
|
for (final JavaClass cls : wrapper.getClasses()) {
|
||||||
|
addTask(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
cls.decompile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getInfoString() {
|
||||||
|
return "Decompiling: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package jadx.gui.jobs;
|
||||||
|
|
||||||
|
import jadx.api.JavaClass;
|
||||||
|
import jadx.core.codegen.CodeWriter;
|
||||||
|
import jadx.gui.JadxWrapper;
|
||||||
|
import jadx.gui.settings.JadxSettings;
|
||||||
|
import jadx.gui.utils.CacheObject;
|
||||||
|
import jadx.gui.utils.CodeLinesInfo;
|
||||||
|
import jadx.gui.utils.CodeUsageInfo;
|
||||||
|
import jadx.gui.utils.TextSearchIndex;
|
||||||
|
import jadx.gui.utils.Utils;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class IndexJob extends BackgroundJob {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(IndexJob.class);
|
||||||
|
private final CacheObject cache;
|
||||||
|
private final boolean useFastSearch;
|
||||||
|
|
||||||
|
public IndexJob(JadxWrapper wrapper, JadxSettings settings, CacheObject cache) {
|
||||||
|
super(wrapper, settings.getThreadsCount());
|
||||||
|
this.useFastSearch = settings.isUseFastSearch();
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void runJob() {
|
||||||
|
final TextSearchIndex index = new TextSearchIndex();
|
||||||
|
final CodeUsageInfo usageInfo = new CodeUsageInfo();
|
||||||
|
cache.setTextIndex(index);
|
||||||
|
cache.setUsageInfo(usageInfo);
|
||||||
|
for (final JavaClass cls : wrapper.getClasses()) {
|
||||||
|
addTask(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
index.indexNames(cls);
|
||||||
|
|
||||||
|
CodeLinesInfo linesInfo = new CodeLinesInfo(cls);
|
||||||
|
String[] lines = splitIntoLines(cls);
|
||||||
|
|
||||||
|
usageInfo.processClass(cls, linesInfo, lines);
|
||||||
|
if (useFastSearch && Utils.isFreeMemoryAvailable()) {
|
||||||
|
index.indexCode(cls, linesInfo, lines);
|
||||||
|
} else {
|
||||||
|
index.classCodeIndexSkipped(cls);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Index error in class: {}", cls.getFullName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected String[] splitIntoLines(JavaClass cls) {
|
||||||
|
String[] lines = cls.getCode().split(CodeWriter.NL);
|
||||||
|
int count = lines.length;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
lines[i] = lines[i].trim();
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getInfoString() {
|
||||||
|
return "Indexing: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseFastSearch() {
|
||||||
|
return useFastSearch;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package jadx.gui.settings;
|
|||||||
|
|
||||||
import jadx.cli.JadxCLIArgs;
|
import jadx.cli.JadxCLIArgs;
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -10,12 +9,14 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||||
|
|
||||||
public class JadxSettings extends JadxCLIArgs {
|
public class JadxSettings extends JadxCLIArgs {
|
||||||
|
|
||||||
private static final String USER_HOME = System.getProperty("user.home");
|
private static final String USER_HOME = System.getProperty("user.home");
|
||||||
private static final int RECENT_FILES_COUNT = 15;
|
private static final int RECENT_FILES_COUNT = 15;
|
||||||
|
|
||||||
private static final Font DEFAULT_FONT = new JLabel().getFont();
|
private static final Font DEFAULT_FONT = new RSyntaxTextArea().getFont();
|
||||||
|
|
||||||
static final Set<String> SKIP_FIELDS = new HashSet<String>(Arrays.asList(
|
static final Set<String> SKIP_FIELDS = new HashSet<String>(Arrays.asList(
|
||||||
"files", "input", "outputDir", "verbose", "printHelp"
|
"files", "input", "outputDir", "verbose", "printHelp"
|
||||||
@@ -27,6 +28,7 @@ public class JadxSettings extends JadxCLIArgs {
|
|||||||
private boolean checkForUpdates = true;
|
private boolean checkForUpdates = true;
|
||||||
private List<String> recentFiles = new ArrayList<String>();
|
private List<String> recentFiles = new ArrayList<String>();
|
||||||
private String fontStr = "";
|
private String fontStr = "";
|
||||||
|
private boolean useFastSearch = false;
|
||||||
|
|
||||||
public void sync() {
|
public void sync() {
|
||||||
JadxSettingsAdapter.store(this);
|
JadxSettingsAdapter.store(this);
|
||||||
@@ -73,10 +75,8 @@ public class JadxSettings extends JadxCLIArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addRecentFile(String filePath) {
|
public void addRecentFile(String filePath) {
|
||||||
if (recentFiles.contains(filePath)) {
|
recentFiles.remove(filePath);
|
||||||
return;
|
recentFiles.add(0, filePath);
|
||||||
}
|
|
||||||
recentFiles.add(filePath);
|
|
||||||
int count = recentFiles.size();
|
int count = recentFiles.size();
|
||||||
if (count > RECENT_FILES_COUNT) {
|
if (count > RECENT_FILES_COUNT) {
|
||||||
recentFiles.subList(0, count - RECENT_FILES_COUNT).clear();
|
recentFiles.subList(0, count - RECENT_FILES_COUNT).clear();
|
||||||
@@ -136,6 +136,14 @@ public class JadxSettings extends JadxCLIArgs {
|
|||||||
this.deobfuscationUseSourceNameAsAlias = useSourceNameAsAlias;
|
this.deobfuscationUseSourceNameAsAlias = useSourceNameAsAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUseFastSearch() {
|
||||||
|
return useFastSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseFastSearch(boolean useFastSearch) {
|
||||||
|
this.useFastSearch = useFastSearch;
|
||||||
|
}
|
||||||
|
|
||||||
public Font getFont() {
|
public Font getFont() {
|
||||||
if (fontStr.isEmpty()) {
|
if (fontStr.isEmpty()) {
|
||||||
return DEFAULT_FONT;
|
return DEFAULT_FONT;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package jadx.gui.settings;
|
|||||||
|
|
||||||
import jadx.gui.JadxGUI;
|
import jadx.gui.JadxGUI;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
@@ -25,7 +26,9 @@ public class JadxSettingsAdapter {
|
|||||||
private static ExclusionStrategy EXCLUDE_FIELDS = new ExclusionStrategy() {
|
private static ExclusionStrategy EXCLUDE_FIELDS = new ExclusionStrategy() {
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldSkipField(FieldAttributes f) {
|
public boolean shouldSkipField(FieldAttributes f) {
|
||||||
return JadxSettings.SKIP_FIELDS.contains(f.getName());
|
return JadxSettings.SKIP_FIELDS.contains(f.getName())
|
||||||
|
|| f.hasModifier(Modifier.PUBLIC)
|
||||||
|
|| f.hasModifier(Modifier.TRANSIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -239,6 +239,14 @@ public class JadxSettingsWindow extends JDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
JCheckBox fastSearch = new JCheckBox();
|
||||||
|
fastSearch.setSelected(settings.isUseFastSearch());
|
||||||
|
fastSearch.addItemListener(new ItemListener() {
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
settings.setUseFastSearch(e.getStateChange() == ItemEvent.SELECTED);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
SettingsGroup other = new SettingsGroup(NLS.str("preferences.other"));
|
SettingsGroup other = new SettingsGroup(NLS.str("preferences.other"));
|
||||||
other.addRow(NLS.str("preferences.check_for_updates"), update);
|
other.addRow(NLS.str("preferences.check_for_updates"), update);
|
||||||
other.addRow(NLS.str("preferences.threads"), threadsCount);
|
other.addRow(NLS.str("preferences.threads"), threadsCount);
|
||||||
@@ -248,6 +256,7 @@ public class JadxSettingsWindow extends JDialog {
|
|||||||
other.addRow(NLS.str("preferences.cfg"), cfg);
|
other.addRow(NLS.str("preferences.cfg"), cfg);
|
||||||
other.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
|
other.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
|
||||||
other.addRow(NLS.str("preferences.font"), fontBtn);
|
other.addRow(NLS.str("preferences.font"), fontBtn);
|
||||||
|
other.addRow(NLS.str("preferences.fast_search"), fastSearch);
|
||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,50 @@
|
|||||||
package jadx.gui.treemodel;
|
package jadx.gui.treemodel;
|
||||||
|
|
||||||
import jadx.api.JavaClass;
|
import jadx.api.JavaNode;
|
||||||
import jadx.gui.utils.Utils;
|
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
|
|
||||||
public class CodeNode extends JClass {
|
public class CodeNode extends JNode {
|
||||||
|
|
||||||
private static final ImageIcon ICON = Utils.openIcon("file_obj");
|
private static final long serialVersionUID = 1658650786734966545L;
|
||||||
|
|
||||||
|
private final JNode jNode;
|
||||||
|
private final JClass jParent;
|
||||||
private final String line;
|
private final String line;
|
||||||
private final int lineNum;
|
private final int lineNum;
|
||||||
|
|
||||||
public CodeNode(JavaClass javaClass, int lineNum, String line) {
|
public CodeNode(JavaNode javaNode, int lineNum, String line) {
|
||||||
super(javaClass, (JClass) makeFrom(javaClass.getDeclaringClass()));
|
this.jNode = makeFrom(javaNode);
|
||||||
|
this.jParent = jNode.getJParent();
|
||||||
this.line = line;
|
this.line = line;
|
||||||
this.lineNum = lineNum;
|
this.lineNum = lineNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return ICON;
|
return jNode.getIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaNode getJavaNode() {
|
||||||
|
return jNode.getJavaNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JClass getJParent() {
|
||||||
|
return getRootClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JClass getRootClass() {
|
||||||
|
JClass parent = jParent;
|
||||||
|
if (parent != null) {
|
||||||
|
return parent.getRootClass();
|
||||||
|
}
|
||||||
|
if (jNode instanceof JClass) {
|
||||||
|
return (JClass) jNode;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -29,18 +52,23 @@ public class CodeNode extends JClass {
|
|||||||
return lineNum;
|
return lineNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String makeDescString() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDescString() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String makeString() {
|
public String makeString() {
|
||||||
return getCls().getFullName() + ":" + lineNum + " " + line;
|
return jNode.makeLongString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String makeLongString() {
|
public String makeLongString() {
|
||||||
return makeString();
|
return makeString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return makeString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package jadx.gui.treemodel;
|
|||||||
import jadx.api.JavaClass;
|
import jadx.api.JavaClass;
|
||||||
import jadx.api.JavaField;
|
import jadx.api.JavaField;
|
||||||
import jadx.api.JavaMethod;
|
import jadx.api.JavaMethod;
|
||||||
|
import jadx.api.JavaNode;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.Utils;
|
import jadx.gui.utils.Utils;
|
||||||
@@ -103,6 +104,11 @@ public class JClass extends JNode {
|
|||||||
return ICON_CLASS_DEFAULT;
|
return ICON_CLASS_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaNode getJavaNode() {
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JClass getJParent() {
|
public JClass getJParent() {
|
||||||
return jParent;
|
return jParent;
|
||||||
@@ -116,6 +122,11 @@ public class JClass extends JNode {
|
|||||||
return jParent.getRootClass();
|
return jParent.getRootClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return cls.getName();
|
||||||
|
}
|
||||||
|
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return cls.getFullName();
|
return cls.getFullName();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package jadx.gui.treemodel;
|
package jadx.gui.treemodel;
|
||||||
|
|
||||||
import jadx.api.JavaField;
|
import jadx.api.JavaField;
|
||||||
|
import jadx.api.JavaNode;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.gui.utils.OverlayIcon;
|
import jadx.gui.utils.OverlayIcon;
|
||||||
import jadx.gui.utils.Utils;
|
import jadx.gui.utils.Utils;
|
||||||
@@ -27,6 +28,11 @@ public class JField extends JNode {
|
|||||||
this.jParent = jClass;
|
this.jParent = jClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaNode getJavaNode() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JClass getJParent() {
|
public JClass getJParent() {
|
||||||
return jParent;
|
return jParent;
|
||||||
@@ -64,4 +70,14 @@ public class JField extends JNode {
|
|||||||
public String makeLongString() {
|
public String makeLongString() {
|
||||||
return Utils.typeFormat(field.getFullName(), field.getType());
|
return Utils.typeFormat(field.getFullName(), field.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return field.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return this == o || o instanceof JField && field.equals(((JField) o).field);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package jadx.gui.treemodel;
|
package jadx.gui.treemodel;
|
||||||
|
|
||||||
import jadx.api.JavaMethod;
|
import jadx.api.JavaMethod;
|
||||||
|
import jadx.api.JavaNode;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.gui.utils.OverlayIcon;
|
import jadx.gui.utils.OverlayIcon;
|
||||||
@@ -29,11 +30,20 @@ public class JMethod extends JNode {
|
|||||||
this.jParent = jClass;
|
this.jParent = jClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaNode getJavaNode() {
|
||||||
|
return mth;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JClass getJParent() {
|
public JClass getJParent() {
|
||||||
return jParent;
|
return jParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArgType getReturnType() {
|
||||||
|
return mth.getReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JClass getRootClass() {
|
public JClass getRootClass() {
|
||||||
return jParent.getRootClass();
|
return jParent.getRootClass();
|
||||||
@@ -57,7 +67,7 @@ public class JMethod extends JNode {
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String makeBaseString() {
|
String makeBaseString() {
|
||||||
if (mth.isClassInit()) {
|
if (mth.isClassInit()) {
|
||||||
return "{...}";
|
return "{...}";
|
||||||
}
|
}
|
||||||
@@ -80,12 +90,22 @@ public class JMethod extends JNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String makeString() {
|
public String makeString() {
|
||||||
return Utils.typeFormat(makeBaseString(), mth.getReturnType());
|
return Utils.typeFormat(makeBaseString(), getReturnType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String makeLongString() {
|
public String makeLongString() {
|
||||||
String name = mth.getDeclaringClass().getFullName() + "." + makeBaseString();
|
String name = mth.getDeclaringClass().getFullName() + "." + makeBaseString();
|
||||||
return Utils.typeFormat(name, mth.getReturnType());
|
return Utils.typeFormat(name, getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return mth.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return this == o || o instanceof JMethod && mth.equals(((JMethod) o).mth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,20 +13,20 @@ import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
|||||||
|
|
||||||
public abstract class JNode extends DefaultMutableTreeNode {
|
public abstract class JNode extends DefaultMutableTreeNode {
|
||||||
public static JNode makeFrom(JavaNode node) {
|
public static JNode makeFrom(JavaNode node) {
|
||||||
|
if (node == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (node instanceof JavaClass) {
|
if (node instanceof JavaClass) {
|
||||||
JClass p = (JClass) makeFrom(node.getDeclaringClass());
|
JClass p = (JClass) makeFrom(node.getDeclaringClass());
|
||||||
return new JClass((JavaClass) node, p);
|
return new JClass((JavaClass) node, p);
|
||||||
}
|
}
|
||||||
if (node instanceof JavaMethod) {
|
if (node instanceof JavaMethod) {
|
||||||
JavaMethod mth = (JavaMethod) node;
|
JavaMethod mth = (JavaMethod) node;
|
||||||
return new JMethod(mth, new JClass(mth.getDeclaringClass()));
|
return new JMethod(mth, (JClass) makeFrom(mth.getDeclaringClass()));
|
||||||
}
|
}
|
||||||
if (node instanceof JavaField) {
|
if (node instanceof JavaField) {
|
||||||
JavaField fld = (JavaField) node;
|
JavaField fld = (JavaField) node;
|
||||||
return new JField(fld, new JClass(fld.getDeclaringClass()));
|
return new JField(fld, (JClass) makeFrom(fld.getDeclaringClass()));
|
||||||
}
|
|
||||||
if (node == null) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
throw new JadxRuntimeException("Unknown type for JavaNode: " + node.getClass());
|
throw new JadxRuntimeException("Unknown type for JavaNode: " + node.getClass());
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,10 @@ public abstract class JNode extends DefaultMutableTreeNode {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JavaNode getJavaNode() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public String getContent() {
|
public String getContent() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -58,8 +62,24 @@ public abstract class JNode extends DefaultMutableTreeNode {
|
|||||||
|
|
||||||
public abstract Icon getIcon();
|
public abstract Icon getIcon();
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
JavaNode javaNode = getJavaNode();
|
||||||
|
if (javaNode == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return javaNode.getName();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract String makeString();
|
public abstract String makeString();
|
||||||
|
|
||||||
|
public String makeDescString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDescString() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public String makeLongString() {
|
public String makeLongString() {
|
||||||
return makeString();
|
return makeString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import javax.swing.ImageIcon;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class JPackage extends JNode implements Comparable<JPackage> {
|
public class JPackage extends JNode implements Comparable<JPackage> {
|
||||||
private static final long serialVersionUID = -4120718634156839804L;
|
private static final long serialVersionUID = -4120718634156839804L;
|
||||||
|
|
||||||
@@ -45,6 +47,7 @@ public class JPackage extends JNode implements Comparable<JPackage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@@ -77,7 +80,7 @@ public class JPackage extends JNode implements Comparable<JPackage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(JPackage o) {
|
public int compareTo(@NotNull JPackage o) {
|
||||||
return name.compareTo(o.name);
|
return name.compareTo(o.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class JResource extends JNode implements Comparable<JResource> {
|
|||||||
private static final ImageIcon JAVA_ICON = Utils.openIcon("java_ovr");
|
private static final ImageIcon JAVA_ICON = Utils.openIcon("java_ovr");
|
||||||
private static final ImageIcon ERROR_ICON = Utils.openIcon("error_co");
|
private static final ImageIcon ERROR_ICON = Utils.openIcon("error_co");
|
||||||
|
|
||||||
public static enum JResType {
|
public enum JResType {
|
||||||
ROOT,
|
ROOT,
|
||||||
DIR,
|
DIR,
|
||||||
FILE
|
FILE
|
||||||
|
|||||||
@@ -0,0 +1,394 @@
|
|||||||
|
package jadx.gui.ui;
|
||||||
|
|
||||||
|
import jadx.gui.jobs.BackgroundJob;
|
||||||
|
import jadx.gui.jobs.DecompileJob;
|
||||||
|
import jadx.gui.treemodel.JNode;
|
||||||
|
import jadx.gui.treemodel.TextNode;
|
||||||
|
import jadx.gui.utils.CacheObject;
|
||||||
|
import jadx.gui.utils.NLS;
|
||||||
|
import jadx.gui.utils.Position;
|
||||||
|
import jadx.gui.utils.TextSearchIndex;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.Box;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.ListSelectionModel;
|
||||||
|
import javax.swing.ScrollPaneConstants;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import javax.swing.UIDefaults;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
|
import javax.swing.table.TableColumnModel;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Cursor;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.KeyAdapter;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||||
|
import org.fife.ui.rtextarea.SearchContext;
|
||||||
|
import org.fife.ui.rtextarea.SearchEngine;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public abstract class CommonSearchDialog extends JDialog {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(CommonSearchDialog.class);
|
||||||
|
private static final long serialVersionUID = 8939332306115370276L;
|
||||||
|
|
||||||
|
public static final int MAX_RESULTS_COUNT = 1000;
|
||||||
|
|
||||||
|
protected final TabbedPane tabbedPane;
|
||||||
|
protected final CacheObject cache;
|
||||||
|
protected final MainWindow mainWindow;
|
||||||
|
protected final Font codeFont;
|
||||||
|
|
||||||
|
protected ResultsModel resultsModel;
|
||||||
|
protected ResultsTable resultsTable;
|
||||||
|
protected JLabel warnLabel;
|
||||||
|
protected ProgressPanel progressPane;
|
||||||
|
|
||||||
|
protected String highlightText;
|
||||||
|
|
||||||
|
public CommonSearchDialog(MainWindow mainWindow) {
|
||||||
|
super(mainWindow);
|
||||||
|
this.mainWindow = mainWindow;
|
||||||
|
this.tabbedPane = mainWindow.getTabbedPane();
|
||||||
|
this.cache = mainWindow.getCacheObject();
|
||||||
|
this.codeFont = mainWindow.getSettings().getFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prepare() {
|
||||||
|
if (cache.getIndexJob().isComplete()) {
|
||||||
|
loadFinishedCommon();
|
||||||
|
loadFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LoadTask task = new LoadTask();
|
||||||
|
task.addPropertyChangeListener(progressPane);
|
||||||
|
task.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void openSelectedItem() {
|
||||||
|
int selectedId = resultsTable.getSelectedRow();
|
||||||
|
if (selectedId == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JNode node = (JNode) resultsModel.getValueAt(selectedId, 0);
|
||||||
|
tabbedPane.codeJump(new Position(node.getRootClass(), node.getLine()));
|
||||||
|
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initCommon() {
|
||||||
|
KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
||||||
|
getRootPane().registerKeyboardAction(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
}, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected JPanel initButtonsPanel() {
|
||||||
|
progressPane = new ProgressPanel(mainWindow, false);
|
||||||
|
|
||||||
|
JButton cancelButton = new JButton(NLS.str("search_dialog.cancel"));
|
||||||
|
cancelButton.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
JButton openBtn = new JButton(NLS.str("search_dialog.open"));
|
||||||
|
openBtn.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
|
openSelectedItem();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getRootPane().setDefaultButton(openBtn);
|
||||||
|
|
||||||
|
JPanel buttonPane = new JPanel();
|
||||||
|
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||||
|
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||||
|
buttonPane.add(progressPane);
|
||||||
|
buttonPane.add(Box.createRigidArea(new Dimension(5, 0)));
|
||||||
|
buttonPane.add(Box.createHorizontalGlue());
|
||||||
|
buttonPane.add(openBtn);
|
||||||
|
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||||
|
buttonPane.add(cancelButton);
|
||||||
|
return buttonPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JPanel initResultsTable() {
|
||||||
|
resultsModel = new ResultsModel();
|
||||||
|
resultsTable = new ResultsTable(resultsModel);
|
||||||
|
resultsTable.setShowHorizontalLines(false);
|
||||||
|
// resultsTable.setAutoCreateColumnsFromModel(true);
|
||||||
|
resultsTable.setDragEnabled(false);
|
||||||
|
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
resultsTable.setBackground(ContentArea.BACKGROUND);
|
||||||
|
resultsTable.setColumnSelectionAllowed(false);
|
||||||
|
resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||||
|
resultsTable.setAutoscrolls(false);
|
||||||
|
resultsTable.setDefaultRenderer(Object.class, new ResultsTableCellRenderer());
|
||||||
|
resultsTable.addMouseListener(new MouseAdapter() {
|
||||||
|
public void mouseClicked(MouseEvent evt) {
|
||||||
|
if (evt.getClickCount() == 2) {
|
||||||
|
openSelectedItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resultsTable.addKeyListener(new KeyAdapter() {
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||||
|
openSelectedItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
warnLabel = new JLabel();
|
||||||
|
warnLabel.setForeground(Color.RED);
|
||||||
|
warnLabel.setVisible(false);
|
||||||
|
|
||||||
|
JPanel resultsPanel = new JPanel();
|
||||||
|
resultsPanel.setLayout(new BoxLayout(resultsPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
resultsPanel.add(warnLabel);
|
||||||
|
resultsPanel.add(new JScrollPane(resultsTable,
|
||||||
|
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
|
||||||
|
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
|
||||||
|
resultsPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||||
|
return resultsPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class ResultsTable extends JTable {
|
||||||
|
private static final long serialVersionUID = 3901184054736618969L;
|
||||||
|
|
||||||
|
public ResultsTable(ResultsModel resultsModel) {
|
||||||
|
super(resultsModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateTable() {
|
||||||
|
ResultsModel model = (ResultsModel) getModel();
|
||||||
|
TableColumnModel columnModel = getColumnModel();
|
||||||
|
|
||||||
|
int width = getParent().getWidth();
|
||||||
|
int firstColMaxWidth = (int) (width * 0.5);
|
||||||
|
int rowCount = getRowCount();
|
||||||
|
int columnCount = getColumnCount();
|
||||||
|
if (!model.isAddDescColumn()) {
|
||||||
|
firstColMaxWidth = width;
|
||||||
|
}
|
||||||
|
Component codeComp = null;
|
||||||
|
for (int col = 0; col < columnCount; col++) {
|
||||||
|
int colWidth = 50;
|
||||||
|
for (int row = 0; row < rowCount; row++) {
|
||||||
|
TableCellRenderer renderer = getCellRenderer(row, col);
|
||||||
|
Component comp = prepareRenderer(renderer, row, col);
|
||||||
|
if (comp == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
colWidth = Math.max(comp.getPreferredSize().width, colWidth);
|
||||||
|
if (codeComp == null && col == 1) {
|
||||||
|
codeComp = comp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
colWidth += 10;
|
||||||
|
if (col == 0) {
|
||||||
|
colWidth = Math.min(colWidth, firstColMaxWidth);
|
||||||
|
} else {
|
||||||
|
colWidth = Math.max(colWidth, width - columnModel.getColumn(0).getPreferredWidth());
|
||||||
|
}
|
||||||
|
TableColumn column = columnModel.getColumn(col);
|
||||||
|
column.setPreferredWidth(colWidth);
|
||||||
|
}
|
||||||
|
if (codeComp != null) {
|
||||||
|
setRowHeight(codeComp.getPreferredSize().height + 4);
|
||||||
|
}
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class ResultsModel extends AbstractTableModel {
|
||||||
|
private static final long serialVersionUID = -7821286846923903208L;
|
||||||
|
private static final String[] COLUMN_NAMES = {"Node", "Code"};
|
||||||
|
|
||||||
|
private final List<JNode> rows = new ArrayList<JNode>();
|
||||||
|
private boolean addDescColumn;
|
||||||
|
|
||||||
|
protected void addAll(Iterable<? extends JNode> nodes) {
|
||||||
|
for (JNode node : nodes) {
|
||||||
|
int size = getRowCount();
|
||||||
|
if (size >= MAX_RESULTS_COUNT) {
|
||||||
|
if (size == MAX_RESULTS_COUNT) {
|
||||||
|
add(new TextNode("Search results truncated (limit: " + MAX_RESULTS_COUNT + ")"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(JNode node) {
|
||||||
|
if (node.hasDescString()) {
|
||||||
|
addDescColumn = true;
|
||||||
|
}
|
||||||
|
rows.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
addDescColumn = false;
|
||||||
|
rows.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAddDescColumn() {
|
||||||
|
return addDescColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return rows.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int index) {
|
||||||
|
return COLUMN_NAMES[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
|
return rows.get(rowIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ResultsTableCellRenderer implements TableCellRenderer {
|
||||||
|
private final Color selectedBackground;
|
||||||
|
private final Color selectedForeground;
|
||||||
|
|
||||||
|
ResultsTableCellRenderer() {
|
||||||
|
UIDefaults defaults = UIManager.getDefaults();
|
||||||
|
selectedBackground = defaults.getColor("List.selectionBackground");
|
||||||
|
selectedForeground = defaults.getColor("List.selectionForeground");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object obj, boolean isSelected,
|
||||||
|
boolean hasFocus, int row, int column) {
|
||||||
|
if (!(obj instanceof JNode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JNode node = (JNode) obj;
|
||||||
|
|
||||||
|
if (column == 0) {
|
||||||
|
JLabel label = new JLabel();
|
||||||
|
label.setOpaque(true);
|
||||||
|
if (isSelected) {
|
||||||
|
label.setBackground(selectedBackground);
|
||||||
|
label.setForeground(selectedForeground);
|
||||||
|
} else {
|
||||||
|
label.setBackground(ContentArea.BACKGROUND);
|
||||||
|
}
|
||||||
|
label.setIcon(node.getIcon());
|
||||||
|
label.setText(node.makeLongString() + " ");
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
if (node.hasDescString()) {
|
||||||
|
RSyntaxTextArea textArea = new RSyntaxTextArea();
|
||||||
|
textArea.setFont(codeFont);
|
||||||
|
textArea.setEditable(false);
|
||||||
|
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
|
||||||
|
textArea.setBackground(isSelected ? selectedBackground : ContentArea.BACKGROUND);
|
||||||
|
textArea.setText(" " + node.makeDescString());
|
||||||
|
textArea.setRows(1);
|
||||||
|
textArea.setColumns(textArea.getText().length());
|
||||||
|
if (highlightText != null) {
|
||||||
|
SearchEngine.markAll(textArea, new SearchContext(highlightText));
|
||||||
|
}
|
||||||
|
return textArea;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LoadTask extends SwingWorker<Void, Void> {
|
||||||
|
public LoadTask() {
|
||||||
|
loadStartCommon();
|
||||||
|
loadStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void doInBackground() {
|
||||||
|
try {
|
||||||
|
mainWindow.getBackgroundWorker().exec();
|
||||||
|
|
||||||
|
DecompileJob decompileJob = cache.getDecompileJob();
|
||||||
|
progressPane.changeLabel(this, decompileJob.getInfoString());
|
||||||
|
decompileJob.processAndWait();
|
||||||
|
|
||||||
|
BackgroundJob indexJob = cache.getIndexJob();
|
||||||
|
progressPane.changeLabel(this, indexJob.getInfoString());
|
||||||
|
indexJob.processAndWait();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Waiting background tasks failed", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void done() {
|
||||||
|
loadFinishedCommon();
|
||||||
|
loadFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void loadStartCommon() {
|
||||||
|
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
progressPane.setIndeterminate(true);
|
||||||
|
progressPane.setVisible(true);
|
||||||
|
resultsTable.setEnabled(false);
|
||||||
|
warnLabel.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFinishedCommon() {
|
||||||
|
setCursor(null);
|
||||||
|
resultsTable.setEnabled(true);
|
||||||
|
progressPane.setVisible(false);
|
||||||
|
|
||||||
|
TextSearchIndex textIndex = cache.getTextIndex();
|
||||||
|
if (textIndex == null) {
|
||||||
|
warnLabel.setText("Index not initialized, search will be disabled!");
|
||||||
|
warnLabel.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void loadFinished();
|
||||||
|
|
||||||
|
protected abstract void loadStart();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
package jadx.gui.ui;
|
package jadx.gui.ui;
|
||||||
|
|
||||||
import jadx.api.CodePosition;
|
import jadx.api.CodePosition;
|
||||||
|
import jadx.api.JavaNode;
|
||||||
import jadx.gui.settings.JadxSettings;
|
import jadx.gui.settings.JadxSettings;
|
||||||
import jadx.gui.treemodel.JClass;
|
import jadx.gui.treemodel.JClass;
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
import jadx.gui.utils.Position;
|
import jadx.gui.utils.Position;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.event.HyperlinkEvent;
|
import javax.swing.event.HyperlinkEvent;
|
||||||
import javax.swing.event.HyperlinkListener;
|
import javax.swing.event.HyperlinkListener;
|
||||||
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
import javax.swing.event.PopupMenuListener;
|
||||||
import javax.swing.text.BadLocationException;
|
import javax.swing.text.BadLocationException;
|
||||||
import javax.swing.text.Caret;
|
import javax.swing.text.Caret;
|
||||||
import javax.swing.text.DefaultCaret;
|
import javax.swing.text.DefaultCaret;
|
||||||
@@ -17,6 +23,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
|
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
|
||||||
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
|
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
|
||||||
@@ -63,11 +70,23 @@ class ContentArea extends RSyntaxTextArea {
|
|||||||
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator((JClass) node);
|
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator((JClass) node);
|
||||||
setLinkGenerator(codeLinkProcessor);
|
setLinkGenerator(codeLinkProcessor);
|
||||||
addHyperlinkListener(codeLinkProcessor);
|
addHyperlinkListener(codeLinkProcessor);
|
||||||
|
addMenuItems(this, (JClass) node);
|
||||||
}
|
}
|
||||||
|
|
||||||
setText(node.getContent());
|
setText(node.getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addMenuItems(ContentArea contentArea, JClass jCls) {
|
||||||
|
Action findUsage = new FindUsageAction(contentArea, jCls);
|
||||||
|
// TODO: hotkey works only when popup menu is shown
|
||||||
|
// findUsage.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_F7, KeyEvent.ALT_DOWN_MASK));
|
||||||
|
|
||||||
|
JPopupMenu popup = getPopupMenu();
|
||||||
|
popup.addSeparator();
|
||||||
|
popup.add(findUsage);
|
||||||
|
popup.addPopupMenuListener((PopupMenuListener) findUsage);
|
||||||
|
}
|
||||||
|
|
||||||
public void loadSettings() {
|
public void loadSettings() {
|
||||||
JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
|
JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
|
||||||
setFont(settings.getFont());
|
setFont(settings.getFont());
|
||||||
@@ -83,7 +102,7 @@ class ContentArea extends RSyntaxTextArea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (node instanceof JClass) {
|
if (node instanceof JClass) {
|
||||||
Position pos = getPosition((JClass) node, this, token.getOffset());
|
Position pos = getDefPosition((JClass) node, this, token.getOffset());
|
||||||
if (pos != null) {
|
if (pos != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -100,21 +119,30 @@ class ContentArea extends RSyntaxTextArea {
|
|||||||
return super.getForegroundForToken(t);
|
return super.getForegroundForToken(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Position getPosition(JClass jCls, RSyntaxTextArea textArea, int offset) {
|
static Position getDefPosition(JClass jCls, RSyntaxTextArea textArea, int offset) {
|
||||||
|
JavaNode node = getJavaNodeAtOffset(jCls, textArea, offset);
|
||||||
|
if (node == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CodePosition pos = jCls.getCls().getDefinitionPosition(node);
|
||||||
|
if (pos == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Position(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JavaNode getJavaNodeAtOffset(JClass jCls, RSyntaxTextArea textArea, int offset) {
|
||||||
try {
|
try {
|
||||||
int line = textArea.getLineOfOffset(offset);
|
int line = textArea.getLineOfOffset(offset);
|
||||||
int lineOffset = offset - textArea.getLineStartOffset(line);
|
int lineOffset = offset - textArea.getLineStartOffset(line);
|
||||||
CodePosition pos = jCls.getCls().getDefinitionPosition(line + 1, lineOffset + 1);
|
return jCls.getCls().getJavaNodeAtPosition(line + 1, lineOffset + 1);
|
||||||
if (pos != null && pos.isSet()) {
|
|
||||||
return new Position(pos);
|
|
||||||
}
|
|
||||||
} catch (BadLocationException e) {
|
} catch (BadLocationException e) {
|
||||||
LOG.error("Can't get line by offset", e);
|
LOG.error("Can't get java node by offset", e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Position getCurrentPosition() {
|
public Position getCurrentPosition() {
|
||||||
return new Position(node, getCaretLineNumber() + 1);
|
return new Position(node, getCaretLineNumber() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +194,52 @@ class ContentArea extends RSyntaxTextArea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class FindUsageAction extends AbstractAction implements PopupMenuListener {
|
||||||
|
private static final long serialVersionUID = 4692546569977976384L;
|
||||||
|
|
||||||
|
private final ContentArea contentArea;
|
||||||
|
private final JClass jCls;
|
||||||
|
|
||||||
|
private JavaNode node;
|
||||||
|
|
||||||
|
public FindUsageAction(ContentArea contentArea, JClass jCls) {
|
||||||
|
super("Find Usage");
|
||||||
|
this.contentArea = contentArea;
|
||||||
|
this.jCls = jCls;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (node == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MainWindow mainWindow = contentPanel.getTabbedPane().getMainWindow();
|
||||||
|
UsageDialog usageDialog = new UsageDialog(mainWindow, JNode.makeFrom(node));
|
||||||
|
usageDialog.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
|
||||||
|
node = null;
|
||||||
|
Point pos = contentArea.getMousePosition();
|
||||||
|
if (pos != null) {
|
||||||
|
Token token = contentArea.viewToToken(pos);
|
||||||
|
if (token != null) {
|
||||||
|
node = getJavaNodeAtOffset(jCls, contentArea, token.getOffset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setEnabled(node != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuCanceled(PopupMenuEvent e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class CodeLinkGenerator implements LinkGenerator, HyperlinkListener {
|
private class CodeLinkGenerator implements LinkGenerator, HyperlinkListener {
|
||||||
private final JClass jCls;
|
private final JClass jCls;
|
||||||
|
|
||||||
@@ -181,7 +255,7 @@ class ContentArea extends RSyntaxTextArea {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final int sourceOffset = token.getOffset();
|
final int sourceOffset = token.getOffset();
|
||||||
final Position defPos = getPosition(jCls, textArea, sourceOffset);
|
final Position defPos = getDefPosition(jCls, textArea, sourceOffset);
|
||||||
if (defPos == null) {
|
if (defPos == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -207,12 +281,7 @@ class ContentArea extends RSyntaxTextArea {
|
|||||||
public void hyperlinkUpdate(HyperlinkEvent e) {
|
public void hyperlinkUpdate(HyperlinkEvent e) {
|
||||||
Object obj = e.getSource();
|
Object obj = e.getSource();
|
||||||
if (obj instanceof Position) {
|
if (obj instanceof Position) {
|
||||||
Position pos = (Position) obj;
|
contentPanel.getTabbedPane().codeJump((Position) obj);
|
||||||
LOG.debug("Code jump to: {}", pos);
|
|
||||||
TabbedPane tabbedPane = contentPanel.getTabbedPane();
|
|
||||||
tabbedPane.getJumpManager().addPosition(getCurrentPosition());
|
|
||||||
tabbedPane.getJumpManager().addPosition(pos);
|
|
||||||
tabbedPane.showCode(pos);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ public class MainDropTarget implements DropTargetListener {
|
|||||||
private final MainWindow mainWindow;
|
private final MainWindow mainWindow;
|
||||||
|
|
||||||
public MainDropTarget(MainWindow mainWindow) {
|
public MainDropTarget(MainWindow mainWindow) {
|
||||||
super();
|
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +49,7 @@ public class MainDropTarget implements DropTargetListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public void drop(DropTargetDropEvent dtde) {
|
public void drop(DropTargetDropEvent dtde) {
|
||||||
if (!dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
|
if (!dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
|
||||||
dtde.rejectDrop();
|
dtde.rejectDrop();
|
||||||
@@ -57,7 +57,6 @@ public class MainDropTarget implements DropTargetListener {
|
|||||||
}
|
}
|
||||||
dtde.acceptDrop(dtde.getDropAction());
|
dtde.acceptDrop(dtde.getDropAction());
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Transferable transferable = dtde.getTransferable();
|
Transferable transferable = dtde.getTransferable();
|
||||||
List<File> transferData = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);
|
List<File> transferData = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);
|
||||||
if (transferData != null && transferData.size() > 0) {
|
if (transferData != null && transferData.size() > 0) {
|
||||||
@@ -65,7 +64,6 @@ public class MainDropTarget implements DropTargetListener {
|
|||||||
// load first file
|
// load first file
|
||||||
mainWindow.openFile(transferData.get(0));
|
mainWindow.openFile(transferData.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("File drop operation failed", e);
|
LOG.error("File drop operation failed", e);
|
||||||
}
|
}
|
||||||
@@ -73,7 +71,5 @@ public class MainDropTarget implements DropTargetListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dragExit(DropTargetEvent dte) {
|
public void dragExit(DropTargetEvent dte) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package jadx.gui.ui;
|
package jadx.gui.ui;
|
||||||
|
|
||||||
import jadx.gui.JadxWrapper;
|
import jadx.gui.JadxWrapper;
|
||||||
|
import jadx.gui.jobs.BackgroundWorker;
|
||||||
|
import jadx.gui.jobs.DecompileJob;
|
||||||
|
import jadx.gui.jobs.IndexJob;
|
||||||
import jadx.gui.settings.JadxSettings;
|
import jadx.gui.settings.JadxSettings;
|
||||||
import jadx.gui.settings.JadxSettingsWindow;
|
import jadx.gui.settings.JadxSettingsWindow;
|
||||||
import jadx.gui.treemodel.JClass;
|
import jadx.gui.treemodel.JClass;
|
||||||
@@ -48,7 +51,6 @@ import javax.swing.tree.ExpandVetoException;
|
|||||||
import javax.swing.tree.TreeNode;
|
import javax.swing.tree.TreeNode;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
import javax.swing.tree.TreeSelectionModel;
|
import javax.swing.tree.TreeSelectionModel;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.DisplayMode;
|
import java.awt.DisplayMode;
|
||||||
@@ -66,6 +68,8 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -111,7 +115,9 @@ public class MainWindow extends JFrame {
|
|||||||
private JToggleButton deobfToggleBtn;
|
private JToggleButton deobfToggleBtn;
|
||||||
private boolean isFlattenPackage;
|
private boolean isFlattenPackage;
|
||||||
private Link updateLink;
|
private Link updateLink;
|
||||||
|
private ProgressPanel progressPane;
|
||||||
|
private BackgroundWorker backgroundWorker;
|
||||||
|
|
||||||
private DropTarget dropTarget;
|
private DropTarget dropTarget;
|
||||||
|
|
||||||
public MainWindow(JadxSettings settings) {
|
public MainWindow(JadxSettings settings) {
|
||||||
@@ -119,6 +125,7 @@ public class MainWindow extends JFrame {
|
|||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.cacheObject = new CacheObject();
|
this.cacheObject = new CacheObject();
|
||||||
|
|
||||||
|
resetCache();
|
||||||
initUI();
|
initUI();
|
||||||
initMenuAndToolbar();
|
initMenuAndToolbar();
|
||||||
checkForUpdate();
|
checkForUpdate();
|
||||||
@@ -175,18 +182,45 @@ public class MainWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void openFile(File file) {
|
public void openFile(File file) {
|
||||||
cacheObject.reset();
|
tabbedPane.closeAllTabs();
|
||||||
|
resetCache();
|
||||||
wrapper.openFile(file);
|
wrapper.openFile(file);
|
||||||
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
|
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
|
||||||
settings.addRecentFile(file.getAbsolutePath());
|
settings.addRecentFile(file.getAbsolutePath());
|
||||||
initTree();
|
initTree();
|
||||||
setTitle(DEFAULT_TITLE + " - " + file.getName());
|
setTitle(DEFAULT_TITLE + " - " + file.getName());
|
||||||
|
runBackgroundJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void resetCache() {
|
||||||
|
cacheObject.reset();
|
||||||
|
int threadsCount = settings.getThreadsCount();
|
||||||
|
cacheObject.setDecompileJob(new DecompileJob(wrapper, threadsCount));
|
||||||
|
cacheObject.setIndexJob(new IndexJob(wrapper, settings, cacheObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void runBackgroundJobs() {
|
||||||
|
cancelBackgroundJobs();
|
||||||
|
backgroundWorker = new BackgroundWorker(cacheObject, progressPane);
|
||||||
|
new Timer().schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
backgroundWorker.exec();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void cancelBackgroundJobs() {
|
||||||
|
if (backgroundWorker != null) {
|
||||||
|
backgroundWorker.stop();
|
||||||
|
backgroundWorker = new BackgroundWorker(cacheObject, progressPane);
|
||||||
|
resetCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reOpenFile() {
|
public void reOpenFile() {
|
||||||
File openedFile = wrapper.getOpenFile();
|
File openedFile = wrapper.getOpenFile();
|
||||||
if (openedFile != null) {
|
if (openedFile != null) {
|
||||||
tabbedPane.closeAllTabs();
|
|
||||||
openFile(openedFile);
|
openFile(openedFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,14 +281,14 @@ public class MainWindow extends JFrame {
|
|||||||
if (obj instanceof JResource) {
|
if (obj instanceof JResource) {
|
||||||
JResource res = (JResource) obj;
|
JResource res = (JResource) obj;
|
||||||
if (res.getContent() != null) {
|
if (res.getContent() != null) {
|
||||||
tabbedPane.showCode(new Position(res, res.getLine()));
|
tabbedPane.codeJump(new Position(res, res.getLine()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (obj instanceof JNode) {
|
if (obj instanceof JNode) {
|
||||||
JNode node = (JNode) obj;
|
JNode node = (JNode) obj;
|
||||||
JClass cls = node.getRootClass();
|
JClass cls = node.getRootClass();
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
tabbedPane.showCode(new Position(cls, node.getLine()));
|
tabbedPane.codeJump(new Position(cls, node.getLine()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -539,14 +573,18 @@ public class MainWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
JScrollPane treeScrollPane = new JScrollPane(tree);
|
progressPane = new ProgressPanel(this, true);
|
||||||
splitPane.setLeftComponent(treeScrollPane);
|
|
||||||
|
JPanel leftPane = new JPanel(new BorderLayout());
|
||||||
|
leftPane.add(new JScrollPane(tree), BorderLayout.CENTER);
|
||||||
|
leftPane.add(progressPane, BorderLayout.PAGE_END);
|
||||||
|
splitPane.setLeftComponent(leftPane);
|
||||||
|
|
||||||
tabbedPane = new TabbedPane(this);
|
tabbedPane = new TabbedPane(this);
|
||||||
splitPane.setRightComponent(tabbedPane);
|
splitPane.setRightComponent(tabbedPane);
|
||||||
|
|
||||||
dropTarget = new DropTarget(this, DnDConstants.ACTION_COPY, new MainDropTarget(this));
|
dropTarget = new DropTarget(this, DnDConstants.ACTION_COPY, new MainDropTarget(this));
|
||||||
|
|
||||||
setContentPane(mainPanel);
|
setContentPane(mainPanel);
|
||||||
setTitle(DEFAULT_TITLE);
|
setTitle(DEFAULT_TITLE);
|
||||||
}
|
}
|
||||||
@@ -565,6 +603,12 @@ public class MainWindow extends JFrame {
|
|||||||
tabbedPane.loadSettings();
|
tabbedPane.loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
cancelBackgroundJobs();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
public JadxWrapper getWrapper() {
|
public JadxWrapper getWrapper() {
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
@@ -581,6 +625,10 @@ public class MainWindow extends JFrame {
|
|||||||
return cacheObject;
|
return cacheObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BackgroundWorker getBackgroundWorker() {
|
||||||
|
return backgroundWorker;
|
||||||
|
}
|
||||||
|
|
||||||
private class RecentFilesMenuListener implements MenuListener {
|
private class RecentFilesMenuListener implements MenuListener {
|
||||||
private final JMenu recentFiles;
|
private final JMenu recentFiles;
|
||||||
|
|
||||||
@@ -619,4 +667,5 @@ public class MainWindow extends JFrame {
|
|||||||
public void menuCanceled(MenuEvent e) {
|
public void menuCanceled(MenuEvent e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package jadx.gui.ui;
|
||||||
|
|
||||||
|
import jadx.gui.utils.Utils;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JProgressBar;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
|
||||||
|
public class ProgressPanel extends JPanel implements PropertyChangeListener {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -3238438119672015733L;
|
||||||
|
|
||||||
|
private static final Icon ICON_CANCEL = Utils.openIcon("cross");
|
||||||
|
|
||||||
|
private final JProgressBar progressBar;
|
||||||
|
private final JLabel progressLabel;
|
||||||
|
|
||||||
|
public ProgressPanel(final MainWindow mainWindow, boolean showCancelButton) {
|
||||||
|
progressLabel = new JLabel();
|
||||||
|
progressBar = new JProgressBar(0, 100);
|
||||||
|
progressBar.setIndeterminate(true);
|
||||||
|
progressBar.setStringPainted(false);
|
||||||
|
progressLabel.setLabelFor(progressBar);
|
||||||
|
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
||||||
|
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||||
|
setVisible(false);
|
||||||
|
add(progressLabel);
|
||||||
|
add(progressBar);
|
||||||
|
|
||||||
|
if (showCancelButton) {
|
||||||
|
JButton cancelButton = new JButton(ICON_CANCEL);
|
||||||
|
cancelButton.setPreferredSize(new Dimension(ICON_CANCEL.getIconWidth(), ICON_CANCEL.getIconHeight()));
|
||||||
|
cancelButton.setToolTipText("Cancel background jobs");
|
||||||
|
cancelButton.setBorderPainted(false);
|
||||||
|
cancelButton.setFocusPainted(false);
|
||||||
|
cancelButton.setContentAreaFilled(false);
|
||||||
|
cancelButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
mainWindow.cancelBackgroundJobs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(cancelButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
if ("progress".equals(evt.getPropertyName())) {
|
||||||
|
int progress = (Integer) evt.getNewValue();
|
||||||
|
progressBar.setIndeterminate(false);
|
||||||
|
progressBar.setValue(progress);
|
||||||
|
progressBar.setString(progress + "%");
|
||||||
|
progressBar.setStringPainted(true);
|
||||||
|
} else if ("label".equals(evt.getPropertyName())) {
|
||||||
|
setLabel((String) evt.getNewValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabel(String label) {
|
||||||
|
progressLabel.setText(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndeterminate(boolean newValue) {
|
||||||
|
progressBar.setIndeterminate(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeLabel(SwingWorker<?, ?> task, String label) {
|
||||||
|
task.firePropertyChange("label", null, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,53 +1,28 @@
|
|||||||
package jadx.gui.ui;
|
package jadx.gui.ui;
|
||||||
|
|
||||||
import jadx.api.JavaClass;
|
|
||||||
import jadx.gui.JadxWrapper;
|
|
||||||
import jadx.gui.treemodel.JNode;
|
|
||||||
import jadx.gui.treemodel.TextNode;
|
|
||||||
import jadx.gui.utils.CacheObject;
|
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
import jadx.gui.utils.Position;
|
|
||||||
import jadx.gui.utils.TextSearchIndex;
|
import jadx.gui.utils.TextSearchIndex;
|
||||||
import jadx.gui.utils.TextStandardActions;
|
import jadx.gui.utils.TextStandardActions;
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.DefaultListModel;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JCheckBox;
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JList;
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JProgressBar;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.KeyStroke;
|
|
||||||
import javax.swing.ListCellRenderer;
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.SwingWorker;
|
|
||||||
import javax.swing.UIDefaults;
|
|
||||||
import javax.swing.UIManager;
|
|
||||||
import javax.swing.WindowConstants;
|
import javax.swing.WindowConstants;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
import javax.swing.event.DocumentListener;
|
import javax.swing.event.DocumentListener;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.FlowLayout;
|
import java.awt.FlowLayout;
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.ItemEvent;
|
import java.awt.event.ItemEvent;
|
||||||
import java.awt.event.ItemListener;
|
import java.awt.event.ItemListener;
|
||||||
import java.awt.event.KeyAdapter;
|
import java.awt.event.KeyAdapter;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@@ -56,12 +31,11 @@ import java.util.Set;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class SearchDialog extends JDialog {
|
public class SearchDialog extends CommonSearchDialog {
|
||||||
|
|
||||||
private static final long serialVersionUID = -5105405456969134105L;
|
private static final long serialVersionUID = -5105405456969134105L;
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SearchDialog.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SearchDialog.class);
|
||||||
private static final int MAX_RESULTS_COUNT = 500;
|
|
||||||
|
|
||||||
enum SearchOptions {
|
enum SearchOptions {
|
||||||
CLASS,
|
CLASS,
|
||||||
@@ -72,67 +46,43 @@ public class SearchDialog extends JDialog {
|
|||||||
|
|
||||||
private Set<SearchOptions> options = EnumSet.allOf(SearchOptions.class);
|
private Set<SearchOptions> options = EnumSet.allOf(SearchOptions.class);
|
||||||
|
|
||||||
private final TabbedPane tabbedPane;
|
|
||||||
private final JadxWrapper wrapper;
|
|
||||||
private final CacheObject cache;
|
|
||||||
|
|
||||||
private JTextField searchField;
|
private JTextField searchField;
|
||||||
private ResultsModel resultsModel;
|
|
||||||
private JList resultsList;
|
|
||||||
private JProgressBar busyBar;
|
|
||||||
|
|
||||||
public SearchDialog(MainWindow mainWindow, Set<SearchOptions> options) {
|
public SearchDialog(MainWindow mainWindow, Set<SearchOptions> options) {
|
||||||
super(mainWindow);
|
super(mainWindow);
|
||||||
this.tabbedPane = mainWindow.getTabbedPane();
|
|
||||||
this.wrapper = mainWindow.getWrapper();
|
|
||||||
this.cache = mainWindow.getCacheObject();
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
initUI();
|
initUI();
|
||||||
addWindowListener(new WindowAdapter() {
|
addWindowListener(new WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void windowActivated(WindowEvent e) {
|
public void windowOpened(WindowEvent e) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
prepare();
|
openInit();
|
||||||
searchField.requestFocus();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void prepare() {
|
protected void openInit() {
|
||||||
TextSearchIndex index = cache.getTextIndex();
|
prepare();
|
||||||
if (index != null) {
|
String lastSearch = cache.getLastSearch();
|
||||||
return;
|
if (lastSearch != null) {
|
||||||
|
searchField.setText(lastSearch);
|
||||||
|
searchField.selectAll();
|
||||||
}
|
}
|
||||||
LoadTask task = new LoadTask();
|
searchField.requestFocus();
|
||||||
task.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadData() {
|
|
||||||
TextSearchIndex index = cache.getTextIndex();
|
|
||||||
if (index != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
index = new TextSearchIndex();
|
|
||||||
for (JavaClass cls : wrapper.getClasses()) {
|
|
||||||
index.indexNames(cls);
|
|
||||||
}
|
|
||||||
for (JavaClass cls : wrapper.getClasses()) {
|
|
||||||
index.indexCode(cls);
|
|
||||||
}
|
|
||||||
cache.setTextIndex(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void performSearch() {
|
private synchronized void performSearch() {
|
||||||
resultsModel.removeAllElements();
|
resultsModel.clear();
|
||||||
String text = searchField.getText();
|
String text = searchField.getText();
|
||||||
if (text == null || text.isEmpty() || options.isEmpty()) {
|
if (text == null || text.isEmpty() || options.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
cache.setLastSearch(text);
|
||||||
TextSearchIndex index = cache.getTextIndex();
|
TextSearchIndex index = cache.getTextIndex();
|
||||||
if (index == null) {
|
if (index == null) {
|
||||||
return;
|
return;
|
||||||
@@ -149,92 +99,9 @@ public class SearchDialog extends JDialog {
|
|||||||
if (options.contains(SearchOptions.CODE)) {
|
if (options.contains(SearchOptions.CODE)) {
|
||||||
resultsModel.addAll(index.searchCode(text));
|
resultsModel.addAll(index.searchCode(text));
|
||||||
}
|
}
|
||||||
LOG.info("Search returned {} results", resultsModel.size());
|
highlightText = text;
|
||||||
|
resultsTable.updateTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openSelectedItem() {
|
|
||||||
int selectedId = resultsList.getSelectedIndex();
|
|
||||||
if (selectedId == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
JNode node = (JNode) resultsModel.get(selectedId);
|
|
||||||
tabbedPane.showCode(new Position(node.getRootClass(), node.getLine()));
|
|
||||||
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LoadTask extends SwingWorker<Void, Void> {
|
|
||||||
public LoadTask() {
|
|
||||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
|
||||||
busyBar.setVisible(true);
|
|
||||||
searchField.setEnabled(false);
|
|
||||||
resultsList.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void doInBackground() {
|
|
||||||
loadData();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void done() {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
setCursor(null);
|
|
||||||
searchField.setEnabled(true);
|
|
||||||
resultsList.setEnabled(true);
|
|
||||||
busyBar.setVisible(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ResultsModel extends DefaultListModel {
|
|
||||||
private static final long serialVersionUID = -7821286846923903208L;
|
|
||||||
|
|
||||||
private void addAll(Iterable<? extends JNode> nodes) {
|
|
||||||
for (JNode node : nodes) {
|
|
||||||
if (size() >= MAX_RESULTS_COUNT) {
|
|
||||||
if (size() == MAX_RESULTS_COUNT) {
|
|
||||||
addElement(new TextNode("Search results truncated (limit: " + MAX_RESULTS_COUNT + ")"));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addElement(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ResultsCellRenderer implements ListCellRenderer {
|
|
||||||
private final Color selectedBackground;
|
|
||||||
private final Color selectedForeground;
|
|
||||||
|
|
||||||
ResultsCellRenderer() {
|
|
||||||
UIDefaults defaults = UIManager.getDefaults();
|
|
||||||
selectedBackground = defaults.getColor("List.selectionBackground");
|
|
||||||
selectedForeground = defaults.getColor("List.selectionForeground");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getListCellRendererComponent(JList list,
|
|
||||||
Object obj, int index, boolean isSelected, boolean cellHasFocus) {
|
|
||||||
if (!(obj instanceof JNode)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
JNode value = (JNode) obj;
|
|
||||||
JLabel label = new JLabel();
|
|
||||||
label.setOpaque(true);
|
|
||||||
label.setIcon(value.getIcon());
|
|
||||||
label.setText(value.makeLongString());
|
|
||||||
if (isSelected) {
|
|
||||||
label.setBackground(selectedBackground);
|
|
||||||
label.setForeground(selectedForeground);
|
|
||||||
}
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SearchFieldListener implements DocumentListener {
|
private class SearchFieldListener implements DocumentListener {
|
||||||
|
|
||||||
public void changedUpdate(DocumentEvent e) {
|
public void changedUpdate(DocumentEvent e) {
|
||||||
@@ -263,17 +130,6 @@ public class SearchDialog extends JDialog {
|
|||||||
JCheckBox fldChBox = makeOptionsCheckBox(NLS.str("search_dialog.field"), SearchOptions.FIELD);
|
JCheckBox fldChBox = makeOptionsCheckBox(NLS.str("search_dialog.field"), SearchOptions.FIELD);
|
||||||
JCheckBox codeChBox = makeOptionsCheckBox(NLS.str("search_dialog.code"), SearchOptions.CODE);
|
JCheckBox codeChBox = makeOptionsCheckBox(NLS.str("search_dialog.code"), SearchOptions.CODE);
|
||||||
|
|
||||||
resultsModel = new ResultsModel();
|
|
||||||
resultsList = new JList(resultsModel);
|
|
||||||
resultsList.setCellRenderer(new ResultsCellRenderer());
|
|
||||||
resultsList.addMouseListener(new MouseAdapter() {
|
|
||||||
public void mouseClicked(MouseEvent evt) {
|
|
||||||
if (evt.getClickCount() == 2) {
|
|
||||||
openSelectedItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
JPanel searchOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
JPanel searchOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
searchOptions.setBorder(BorderFactory.createTitledBorder(NLS.str("search_dialog.search_in")));
|
searchOptions.setBorder(BorderFactory.createTitledBorder(NLS.str("search_dialog.search_in")));
|
||||||
searchOptions.add(clsChBox);
|
searchOptions.add(clsChBox);
|
||||||
@@ -292,68 +148,30 @@ public class SearchDialog extends JDialog {
|
|||||||
searchPane.add(searchOptions);
|
searchPane.add(searchOptions);
|
||||||
searchPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
searchPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
JPanel listPane = new JPanel();
|
initCommon();
|
||||||
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
|
JPanel resultsPanel = initResultsTable();
|
||||||
listPane.add(new JScrollPane(resultsList));
|
JPanel buttonPane = initButtonsPanel();
|
||||||
listPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
|
||||||
|
|
||||||
busyBar = new JProgressBar();
|
|
||||||
busyBar.setIndeterminate(true);
|
|
||||||
busyBar.setVisible(false);
|
|
||||||
|
|
||||||
//Create and initialize the buttons.
|
|
||||||
JButton cancelButton = new JButton(NLS.str("search_dialog.cancel"));
|
|
||||||
cancelButton.addActionListener(new ActionListener() {
|
|
||||||
public void actionPerformed(ActionEvent event) {
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
JButton openBtn = new JButton(NLS.str("search_dialog.open"));
|
|
||||||
openBtn.addActionListener(new ActionListener() {
|
|
||||||
public void actionPerformed(ActionEvent event) {
|
|
||||||
openSelectedItem();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
JPanel buttonPane = new JPanel();
|
|
||||||
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
|
||||||
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
|
||||||
buttonPane.add(busyBar);
|
|
||||||
buttonPane.add(Box.createRigidArea(new Dimension(5, 0)));
|
|
||||||
buttonPane.add(Box.createHorizontalGlue());
|
|
||||||
buttonPane.add(openBtn);
|
|
||||||
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
|
|
||||||
buttonPane.add(cancelButton);
|
|
||||||
|
|
||||||
Container contentPane = getContentPane();
|
Container contentPane = getContentPane();
|
||||||
contentPane.add(searchPane, BorderLayout.PAGE_START);
|
contentPane.add(searchPane, BorderLayout.PAGE_START);
|
||||||
contentPane.add(listPane, BorderLayout.CENTER);
|
contentPane.add(resultsPanel, BorderLayout.CENTER);
|
||||||
contentPane.add(buttonPane, BorderLayout.PAGE_END);
|
contentPane.add(buttonPane, BorderLayout.PAGE_END);
|
||||||
getRootPane().setDefaultButton(openBtn);
|
|
||||||
|
|
||||||
searchField.addKeyListener(new KeyAdapter() {
|
searchField.addKeyListener(new KeyAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void keyReleased(KeyEvent e) {
|
public void keyReleased(KeyEvent e) {
|
||||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||||
resultsList.requestFocus();
|
if (resultsModel.getRowCount() != 0) {
|
||||||
if (!resultsModel.isEmpty()) {
|
resultsTable.setRowSelectionInterval(0, 0);
|
||||||
resultsList.setSelectedIndex(0);
|
|
||||||
}
|
}
|
||||||
|
resultsTable.requestFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
|
||||||
getRootPane().registerKeyboardAction(new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
}, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
|
|
||||||
|
|
||||||
setTitle(NLS.str("menu.text_search"));
|
setTitle(NLS.str("menu.text_search"));
|
||||||
pack();
|
pack();
|
||||||
setSize(700, 500);
|
setSize(800, 500);
|
||||||
setLocationRelativeTo(null);
|
setLocationRelativeTo(null);
|
||||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||||
setModalityType(ModalityType.MODELESS);
|
setModalityType(ModalityType.MODELESS);
|
||||||
@@ -375,4 +193,14 @@ public class SearchDialog extends JDialog {
|
|||||||
});
|
});
|
||||||
return chBox;
|
return chBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadFinished() {
|
||||||
|
searchField.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadStart() {
|
||||||
|
searchField.setEnabled(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import javax.swing.JPopupMenu;
|
|||||||
import javax.swing.JTabbedPane;
|
import javax.swing.JTabbedPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.plaf.basic.BasicButtonUI;
|
import javax.swing.plaf.basic.BasicButtonUI;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.FlowLayout;
|
import java.awt.FlowLayout;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
@@ -29,8 +30,13 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
class TabbedPane extends JTabbedPane {
|
class TabbedPane extends JTabbedPane {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(TabbedPane.class);
|
||||||
private static final long serialVersionUID = -8833600618794570904L;
|
private static final long serialVersionUID = -8833600618794570904L;
|
||||||
|
|
||||||
private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross");
|
private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross");
|
||||||
@@ -65,19 +71,46 @@ class TabbedPane extends JTabbedPane {
|
|||||||
return mainWindow;
|
return mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
void showCode(final Position pos) {
|
private void showCode(final Position pos) {
|
||||||
final ContentPanel contentPanel = getCodePanel(pos.getNode());
|
final ContentPanel contentPanel = getCodePanel(pos.getNode());
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
setSelectedComponent(contentPanel);
|
setSelectedComponent(contentPanel);
|
||||||
ContentArea contentArea = contentPanel.getContentArea();
|
ContentArea contentArea = contentPanel.getContentArea();
|
||||||
contentArea.scrollToLine(pos.getLine());
|
int line = pos.getLine();
|
||||||
|
if (line < 0) {
|
||||||
|
try {
|
||||||
|
line = 1 + contentArea.getLineOfOffset(-line);
|
||||||
|
} catch (BadLocationException e) {
|
||||||
|
LOG.error("Can't get line for: {}", pos, e);
|
||||||
|
line = pos.getNode().getLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contentArea.scrollToLine(line);
|
||||||
contentArea.requestFocus();
|
contentArea.requestFocus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void codeJump(Position pos) {
|
||||||
|
Position curPos = getCurrentPosition();
|
||||||
|
if (curPos != null) {
|
||||||
|
jumps.addPosition(curPos);
|
||||||
|
jumps.addPosition(pos);
|
||||||
|
}
|
||||||
|
showCode(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Position getCurrentPosition() {
|
||||||
|
ContentPanel selectedCodePanel = getSelectedCodePanel();
|
||||||
|
if (selectedCodePanel == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return selectedCodePanel.getContentArea().getCurrentPosition();
|
||||||
|
}
|
||||||
|
|
||||||
public void navBack() {
|
public void navBack() {
|
||||||
Position pos = jumps.getPrev();
|
Position pos = jumps.getPrev();
|
||||||
if (pos != null) {
|
if (pos != null) {
|
||||||
@@ -116,6 +149,7 @@ class TabbedPane extends JTabbedPane {
|
|||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
ContentPanel getSelectedCodePanel() {
|
ContentPanel getSelectedCodePanel() {
|
||||||
return (ContentPanel) getSelectedComponent();
|
return (ContentPanel) getSelectedComponent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package jadx.gui.ui;
|
||||||
|
|
||||||
|
import jadx.gui.treemodel.JNode;
|
||||||
|
import jadx.gui.utils.CodeUsageInfo;
|
||||||
|
import jadx.gui.utils.NLS;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.WindowConstants;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class UsageDialog extends CommonSearchDialog {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -5105405789969134105L;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(UsageDialog.class);
|
||||||
|
|
||||||
|
private final JNode node;
|
||||||
|
|
||||||
|
public UsageDialog(MainWindow mainWindow, JNode node) {
|
||||||
|
super(mainWindow);
|
||||||
|
this.node = node;
|
||||||
|
|
||||||
|
initUI();
|
||||||
|
addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowOpened(WindowEvent e) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
openInit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void openInit() {
|
||||||
|
prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadFinished() {
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadStart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void performSearch() {
|
||||||
|
resultsModel.clear();
|
||||||
|
|
||||||
|
CodeUsageInfo usageInfo = cache.getUsageInfo();
|
||||||
|
if (usageInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resultsModel.addAll(usageInfo.getUsageList(node));
|
||||||
|
// TODO: highlight only needed node usage
|
||||||
|
highlightText = null;
|
||||||
|
resultsTable.updateTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initUI() {
|
||||||
|
JLabel lbl = new JLabel(NLS.str("usage_dialog.label"));
|
||||||
|
JLabel nodeLabel = new JLabel(this.node.makeLongString(), this.node.getIcon(), SwingConstants.LEFT);
|
||||||
|
lbl.setLabelFor(nodeLabel);
|
||||||
|
|
||||||
|
JPanel searchPane = new JPanel();
|
||||||
|
searchPane.setLayout(new FlowLayout(FlowLayout.LEFT));
|
||||||
|
searchPane.add(lbl);
|
||||||
|
searchPane.add(nodeLabel);
|
||||||
|
searchPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
initCommon();
|
||||||
|
JPanel resultsPanel = initResultsTable();
|
||||||
|
JPanel buttonPane = initButtonsPanel();
|
||||||
|
|
||||||
|
Container contentPane = getContentPane();
|
||||||
|
contentPane.add(searchPane, BorderLayout.PAGE_START);
|
||||||
|
contentPane.add(resultsPanel, BorderLayout.CENTER);
|
||||||
|
contentPane.add(buttonPane, BorderLayout.PAGE_END);
|
||||||
|
|
||||||
|
setTitle(NLS.str("usage_dialog.title"));
|
||||||
|
pack();
|
||||||
|
setSize(800, 500);
|
||||||
|
setLocationRelativeTo(null);
|
||||||
|
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||||
|
setModalityType(ModalityType.MODELESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,31 @@
|
|||||||
package jadx.gui.utils;
|
package jadx.gui.utils;
|
||||||
|
|
||||||
|
import jadx.gui.jobs.DecompileJob;
|
||||||
|
import jadx.gui.jobs.IndexJob;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class CacheObject {
|
public class CacheObject {
|
||||||
@Nullable
|
|
||||||
|
private DecompileJob decompileJob;
|
||||||
|
private IndexJob indexJob;
|
||||||
|
|
||||||
private TextSearchIndex textIndex;
|
private TextSearchIndex textIndex;
|
||||||
|
private CodeUsageInfo usageInfo;
|
||||||
|
private String lastSearch;
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
textIndex = null;
|
textIndex = null;
|
||||||
|
lastSearch = null;
|
||||||
|
usageInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecompileJob getDecompileJob() {
|
||||||
|
return decompileJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecompileJob(DecompileJob decompileJob) {
|
||||||
|
this.decompileJob = decompileJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -15,7 +33,33 @@ public class CacheObject {
|
|||||||
return textIndex;
|
return textIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTextIndex(@Nullable TextSearchIndex textIndex) {
|
public void setTextIndex(TextSearchIndex textIndex) {
|
||||||
this.textIndex = textIndex;
|
this.textIndex = textIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getLastSearch() {
|
||||||
|
return lastSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastSearch(String lastSearch) {
|
||||||
|
this.lastSearch = lastSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public CodeUsageInfo getUsageInfo() {
|
||||||
|
return usageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsageInfo(@Nullable CodeUsageInfo usageInfo) {
|
||||||
|
this.usageInfo = usageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexJob getIndexJob() {
|
||||||
|
return indexJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndexJob(IndexJob indexJob) {
|
||||||
|
this.indexJob = indexJob;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package jadx.gui.utils;
|
||||||
|
|
||||||
|
import jadx.api.JavaClass;
|
||||||
|
import jadx.api.JavaMethod;
|
||||||
|
import jadx.api.JavaNode;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NavigableMap;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
public class CodeLinesInfo {
|
||||||
|
private NavigableMap<Integer, JavaNode> map = new TreeMap<Integer, JavaNode>();
|
||||||
|
|
||||||
|
public CodeLinesInfo(JavaClass cls) {
|
||||||
|
addClass(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addClass(JavaClass cls) {
|
||||||
|
map.put(cls.getDecompiledLine(), cls);
|
||||||
|
for (JavaClass innerCls : cls.getInnerClasses()) {
|
||||||
|
map.put(innerCls.getDecompiledLine(), innerCls);
|
||||||
|
addClass(innerCls);
|
||||||
|
}
|
||||||
|
for (JavaMethod mth : cls.getMethods()) {
|
||||||
|
map.put(mth.getDecompiledLine(), mth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JavaNode getJavaNodeByLine(int line) {
|
||||||
|
Map.Entry<Integer, JavaNode> entry = map.floorEntry(line);
|
||||||
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package jadx.gui.utils;
|
||||||
|
|
||||||
|
import jadx.api.CodePosition;
|
||||||
|
import jadx.api.JavaClass;
|
||||||
|
import jadx.api.JavaNode;
|
||||||
|
import jadx.gui.treemodel.CodeNode;
|
||||||
|
import jadx.gui.treemodel.JNode;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CodeUsageInfo {
|
||||||
|
|
||||||
|
public static class UsageInfo {
|
||||||
|
private final List<CodeNode> usageList = new ArrayList<CodeNode>();
|
||||||
|
|
||||||
|
public List<CodeNode> getUsageList() {
|
||||||
|
return usageList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<JNode, UsageInfo> usageMap = new HashMap<JNode, UsageInfo>();
|
||||||
|
|
||||||
|
public void processClass(JavaClass javaClass, CodeLinesInfo linesInfo, String[] lines) {
|
||||||
|
Map<CodePosition, JavaNode> usage = javaClass.getUsageMap();
|
||||||
|
for (Map.Entry<CodePosition, JavaNode> entry : usage.entrySet()) {
|
||||||
|
CodePosition codePosition = entry.getKey();
|
||||||
|
JavaNode javaNode = entry.getValue();
|
||||||
|
addUsage(JNode.makeFrom(javaNode), javaClass, linesInfo, codePosition, lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addUsage(JNode jNode, JavaClass javaClass,
|
||||||
|
CodeLinesInfo linesInfo, CodePosition codePosition, String[] lines) {
|
||||||
|
UsageInfo usageInfo = usageMap.get(jNode);
|
||||||
|
if (usageInfo == null) {
|
||||||
|
usageInfo = new UsageInfo();
|
||||||
|
usageMap.put(jNode, usageInfo);
|
||||||
|
}
|
||||||
|
int line = codePosition.getLine();
|
||||||
|
JavaNode javaNodeByLine = linesInfo.getJavaNodeByLine(line);
|
||||||
|
String codeLine = lines[line - 1].trim();
|
||||||
|
CodeNode codeNode = new CodeNode(javaNodeByLine == null ? javaClass : javaNodeByLine, line, codeLine);
|
||||||
|
usageInfo.getUsageList().add(codeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CodeNode> getUsageList(JNode node) {
|
||||||
|
UsageInfo usageInfo = usageMap.get(node);
|
||||||
|
if (usageInfo == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return usageInfo.getUsageList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ public class LogCollector extends CyclicBufferAppender<ILoggingEvent> {
|
|||||||
|
|
||||||
public LogCollector() {
|
public LogCollector() {
|
||||||
setName("LogCollector");
|
setName("LogCollector");
|
||||||
setMaxSize(50000);
|
setMaxSize(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package jadx.gui.utils;
|
||||||
|
|
||||||
|
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
|
||||||
|
import com.googlecode.concurrenttrees.suffix.ConcurrentSuffixTree;
|
||||||
|
|
||||||
|
public class SuffixTree<V> {
|
||||||
|
|
||||||
|
private final ConcurrentSuffixTree<V> tree;
|
||||||
|
|
||||||
|
public SuffixTree() {
|
||||||
|
this.tree = new ConcurrentSuffixTree<V>(new DefaultCharArrayNodeFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String str, V value) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tree.putIfAbsent(str, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<V> getValuesForKeysContaining(String str) {
|
||||||
|
return tree.getValuesForKeysContaining(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return tree.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,19 +3,18 @@ package jadx.gui.utils;
|
|||||||
import jadx.api.JavaClass;
|
import jadx.api.JavaClass;
|
||||||
import jadx.api.JavaField;
|
import jadx.api.JavaField;
|
||||||
import jadx.api.JavaMethod;
|
import jadx.api.JavaMethod;
|
||||||
|
import jadx.api.JavaNode;
|
||||||
|
import jadx.core.codegen.CodeWriter;
|
||||||
import jadx.gui.treemodel.CodeNode;
|
import jadx.gui.treemodel.CodeNode;
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
|
import jadx.gui.ui.CommonSearchDialog;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.util.ArrayList;
|
||||||
import java.io.StringReader;
|
import java.util.List;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
|
|
||||||
import com.googlecode.concurrenttrees.suffix.ConcurrentSuffixTree;
|
|
||||||
import com.googlecode.concurrenttrees.suffix.SuffixTree;
|
|
||||||
|
|
||||||
public class TextSearchIndex {
|
public class TextSearchIndex {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(TextSearchIndex.class);
|
private static final Logger LOG = LoggerFactory.getLogger(TextSearchIndex.class);
|
||||||
@@ -25,15 +24,16 @@ public class TextSearchIndex {
|
|||||||
private SuffixTree<JNode> fldNamesTree;
|
private SuffixTree<JNode> fldNamesTree;
|
||||||
private SuffixTree<CodeNode> codeTree;
|
private SuffixTree<CodeNode> codeTree;
|
||||||
|
|
||||||
|
private List<JavaClass> skippedClasses = new ArrayList<JavaClass>();
|
||||||
|
|
||||||
public TextSearchIndex() {
|
public TextSearchIndex() {
|
||||||
clsNamesTree = new ConcurrentSuffixTree<JNode>(new DefaultCharArrayNodeFactory());
|
clsNamesTree = new SuffixTree<JNode>();
|
||||||
mthNamesTree = new ConcurrentSuffixTree<JNode>(new DefaultCharArrayNodeFactory());
|
mthNamesTree = new SuffixTree<JNode>();
|
||||||
fldNamesTree = new ConcurrentSuffixTree<JNode>(new DefaultCharArrayNodeFactory());
|
fldNamesTree = new SuffixTree<JNode>();
|
||||||
codeTree = new ConcurrentSuffixTree<CodeNode>(new DefaultCharArrayNodeFactory());
|
codeTree = new SuffixTree<CodeNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void indexNames(JavaClass cls) {
|
public void indexNames(JavaClass cls) {
|
||||||
cls.decompile();
|
|
||||||
clsNamesTree.put(cls.getFullName(), JNode.makeFrom(cls));
|
clsNamesTree.put(cls.getFullName(), JNode.makeFrom(cls));
|
||||||
for (JavaMethod mth : cls.getMethods()) {
|
for (JavaMethod mth : cls.getMethods()) {
|
||||||
mthNamesTree.put(mth.getFullName(), JNode.makeFrom(mth));
|
mthNamesTree.put(mth.getFullName(), JNode.makeFrom(mth));
|
||||||
@@ -46,18 +46,15 @@ public class TextSearchIndex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void indexCode(JavaClass cls) {
|
public void indexCode(JavaClass cls, CodeLinesInfo linesInfo, String[] lines) {
|
||||||
try {
|
try {
|
||||||
String code = cls.getCode();
|
int count = lines.length;
|
||||||
BufferedReader bufReader = new BufferedReader(new StringReader(code));
|
for (int i = 0; i < count; i++) {
|
||||||
String line;
|
String line = lines[i];
|
||||||
int lineNum = 0;
|
|
||||||
while ((line = bufReader.readLine()) != null) {
|
|
||||||
lineNum++;
|
|
||||||
line = line.trim();
|
|
||||||
if (!line.isEmpty()) {
|
if (!line.isEmpty()) {
|
||||||
CodeNode node = new CodeNode(cls, lineNum, line);
|
int lineNum = i + 1;
|
||||||
codeTree.put(line, node);
|
JavaNode node = linesInfo.getJavaNodeByLine(lineNum);
|
||||||
|
codeTree.put(line, new CodeNode(node == null ? cls : node, lineNum, line));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -78,6 +75,59 @@ public class TextSearchIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<CodeNode> searchCode(String text) {
|
public Iterable<CodeNode> searchCode(String text) {
|
||||||
return codeTree.getValuesForKeysContaining(text);
|
Iterable<CodeNode> items;
|
||||||
|
if (codeTree.size() > 0) {
|
||||||
|
items = codeTree.getValuesForKeysContaining(text);
|
||||||
|
if (skippedClasses.isEmpty()) {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items = null;
|
||||||
|
}
|
||||||
|
List<CodeNode> list = new ArrayList<CodeNode>();
|
||||||
|
if (items != null) {
|
||||||
|
for (CodeNode item : items) {
|
||||||
|
list.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addSkippedClasses(list, text);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSkippedClasses(List<CodeNode> list, String text) {
|
||||||
|
for (JavaClass javaClass : skippedClasses) {
|
||||||
|
String code = javaClass.getCode();
|
||||||
|
int pos = 0;
|
||||||
|
while (pos != -1) {
|
||||||
|
pos = searchNext(list, text, javaClass, code, pos);
|
||||||
|
}
|
||||||
|
if (list.size() > CommonSearchDialog.MAX_RESULTS_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int searchNext(List<CodeNode> list, String text, JavaNode javaClass, String code, int startPos) {
|
||||||
|
int pos = code.indexOf(text, startPos);
|
||||||
|
if (pos == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int lineStart = 1 + code.lastIndexOf(CodeWriter.NL, pos);
|
||||||
|
int lineEnd = code.indexOf(CodeWriter.NL, pos + text.length());
|
||||||
|
String line = code.substring(lineStart, lineEnd == -1 ? code.length() : lineEnd);
|
||||||
|
list.add(new CodeNode(javaClass, -pos, line.trim()));
|
||||||
|
return lineEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void classCodeIndexSkipped(JavaClass cls) {
|
||||||
|
this.skippedClasses.add(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JavaClass> getSkippedClasses() {
|
||||||
|
return skippedClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSkippedCount() {
|
||||||
|
return skippedClasses.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,4 +84,30 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
return overIcon;
|
return overIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isFreeMemoryAvailable() {
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
long maxMemory = runtime.maxMemory();
|
||||||
|
long totalFree = runtime.freeMemory() + maxMemory - runtime.totalMemory();
|
||||||
|
return totalFree > maxMemory * 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String memoryInfo() {
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
long maxMemory = runtime.maxMemory();
|
||||||
|
long allocatedMemory = runtime.totalMemory();
|
||||||
|
long freeMemory = runtime.freeMemory();
|
||||||
|
|
||||||
|
sb.append("free: ").append(format(freeMemory));
|
||||||
|
sb.append(", allocated: ").append(format(allocatedMemory));
|
||||||
|
sb.append(", max: ").append(format(maxMemory));
|
||||||
|
sb.append(", total free: ").append(format(freeMemory + maxMemory - allocatedMemory));
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(long mem) {
|
||||||
|
return Long.toString((long) (mem / 1024. / 1024.)) + "MB";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ search_dialog.method=Method
|
|||||||
search_dialog.field=Field
|
search_dialog.field=Field
|
||||||
search_dialog.code=Code
|
search_dialog.code=Code
|
||||||
|
|
||||||
|
usage_dialog.title=Usage search
|
||||||
|
usage_dialog.label=Usage for:
|
||||||
|
|
||||||
preferences.title=Preferences
|
preferences.title=Preferences
|
||||||
preferences.deobfuscation=Deobfuscation
|
preferences.deobfuscation=Deobfuscation
|
||||||
preferences.other=Other
|
preferences.other=Other
|
||||||
@@ -58,6 +61,7 @@ preferences.threads=Processing threads count
|
|||||||
preferences.cfg=Generate methods CFG graphs (in 'dot' format)
|
preferences.cfg=Generate methods CFG graphs (in 'dot' format)
|
||||||
preferences.raw_cfg=Generate RAW CFG graphs
|
preferences.raw_cfg=Generate RAW CFG graphs
|
||||||
preferences.font=Editor font
|
preferences.font=Editor font
|
||||||
|
preferences.fast_search=Fast search (uses more memory)
|
||||||
preferences.select_font=Select
|
preferences.select_font=Select
|
||||||
preferences.deobfuscation_on=Enable deobfuscation
|
preferences.deobfuscation_on=Enable deobfuscation
|
||||||
preferences.deobfuscation_force=Force rewrite deobfuscation map file
|
preferences.deobfuscation_force=Force rewrite deobfuscation map file
|
||||||
|
|||||||
Reference in New Issue
Block a user