From dc2c70c32ea47d60754c42859835478c3e554326 Mon Sep 17 00:00:00 2001 From: psytester Date: Mon, 3 Jun 2019 11:03:43 +0200 Subject: [PATCH 01/26] continue search on remaining files after error In case of IllegalStateException with "Invalid BootstrapMethods attribute entry: 2 additional arguments required for method java/lang/invoke/StringConcatFactory.makeConcatWithConstants, but only 1 specified." this is a known issue of Procyon <= 0.5.35 and the available fix it not yet released, refer to https://bitbucket.org/mstrobel/procyon/issues/336/ The search does not stop anymore, simply a hint is given in found class list for that error. In case of other IllegalStateException or general Exceptions hold on the search in remaining files, too. For example, my openJDK 11 based WAR file has other error cases in Procyon too. java.lang.IllegalArgumentException: Argument 'value' must be in the range [1, 18], but value was: 19. java.lang.IllegalArgumentException: Argument 'value' must be in the range [1, 18], but value was: 20. both at com.strobel.core.VerifyArgument.inRange(VerifyArgument.java:347) --- src/us/deathmarine/luyten/FindAllBox.java | 47 +++++++++++++++-------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/us/deathmarine/luyten/FindAllBox.java b/src/us/deathmarine/luyten/FindAllBox.java index 92bb1e3c..9dc95537 100644 --- a/src/us/deathmarine/luyten/FindAllBox.java +++ b/src/us/deathmarine/luyten/FindAllBox.java @@ -218,23 +218,38 @@ public void run() { if (entry.getName().endsWith(".class")) { synchronized (settings) { String internalName = StringUtilities.removeRight(entry.getName(), ".class"); - TypeReference type = Model.metadataSystem.lookupType(internalName); - TypeDefinition resolvedType = null; - if (type == null || ((resolvedType = type.resolve()) == null)) { - throw new Exception("Unable to resolve type."); + TypeReference type = null; + try { + type = Model.metadataSystem.lookupType(internalName); + TypeDefinition resolvedType = null; + if (type != null && ((resolvedType = type.resolve()) != null)) { + StringWriter stringwriter = new StringWriter(); + DecompilationOptions decompilationOptions; + decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput.setUnicodeOutputEnabled( + decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, + decompilationOptions); + if (search(stringwriter.toString())) + addClassName(entry.getName()); + } + } catch (IllegalStateException ise) { + if (ise.getMessage().toString().contains("Invalid BootstrapMethods attribute entry: 2 additional arguments required for method java/lang/invoke/StringConcatFactory.makeConcatWithConstants, but only 1 specified.")) { + // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to https://bitbucket.org/mstrobel/procyon/issues/336/ + // Searching in a WAR or JAR file could pop-up a lot of error dialogs for a lot of class files, we simply say nothing here + addClassName(entry.getName() + " (search failed due to known Exception in Procyon <= 0.5.35. Opening file will fail too)"); + } else { + // all other IllegalStateException cases + addClassName(entry.getName() + " (search failed due to Exception. Opening file will fail too)"); + Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), ise); + } + } catch (Exception e) { + addClassName(entry.getName() + " (search failed due to Exception. Opening file will fail too)"); + Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), e); } - StringWriter stringwriter = new StringWriter(); - DecompilationOptions decompilationOptions; - decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput.setUnicodeOutputEnabled( - decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, - decompilationOptions); - if (search(stringwriter.toString())) - addClassName(entry.getName()); } } else { From 0ac78dce50c9b442fbb176a58eef4b829dee36e7 Mon Sep 17 00:00:00 2001 From: renxuelei Date: Thu, 27 Aug 2020 13:58:54 +0800 Subject: [PATCH 02/26] solve TreeNode to DefaultMutableTreeNode type conversion error for openjdk 11.0.8. --- src/us/deathmarine/luyten/Model.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index 2d0b802f..72dec7eb 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -655,9 +655,9 @@ public DefaultMutableTreeNode loadNodesByUserObj(DefaultMutableTreeNode node, Li @SuppressWarnings("unchecked") public DefaultMutableTreeNode getChild(DefaultMutableTreeNode node, TreeNodeUserObject name) { - Enumeration entry = node.children(); + Enumeration entry = node.children(); while (entry.hasMoreElements()) { - DefaultMutableTreeNode nods = entry.nextElement(); + DefaultMutableTreeNode nods = (DefaultMutableTreeNode) entry.nextElement(); if (((TreeNodeUserObject) nods.getUserObject()).getOriginalName().equals(name.getOriginalName())) { return nods; } From 560d2a7e95c2d27706df871f17bd5f56289c7a1b Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 8 Oct 2021 22:44:26 +0200 Subject: [PATCH 03/26] Ignore IntelliJ files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index d5656400..ad307054 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ /target /lib /resources + +# IntelliJ +.idea/ +*.iml From 7188afd160736a3a516f0a502f486ecf27724357 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 8 Oct 2021 22:44:37 +0200 Subject: [PATCH 04/26] Update RSTA --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 25b2f9f3..158f348c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.fifesoft rsyntaxtextarea - 3.0.2 + 3.1.3 com.apple From 5f63ab95af50ef620a2f9de07d5a66c010bccfc1 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 8 Oct 2021 22:51:50 +0200 Subject: [PATCH 05/26] General cleanup --- src/us/deathmarine/luyten/ConfigSaver.java | 6 +- .../luyten/DecompilerLinkProvider.java | 26 +++--- src/us/deathmarine/luyten/DirPreferences.java | 4 +- src/us/deathmarine/luyten/DropListener.java | 14 +-- src/us/deathmarine/luyten/FileDialog.java | 8 +- src/us/deathmarine/luyten/FileSaver.java | 32 +++---- src/us/deathmarine/luyten/FindAllBox.java | 27 ++---- src/us/deathmarine/luyten/FindBox.java | 4 +- src/us/deathmarine/luyten/JFontChooser.java | 35 ++++---- src/us/deathmarine/luyten/JarEntryFilter.java | 6 +- src/us/deathmarine/luyten/LinkProvider.java | 14 +-- src/us/deathmarine/luyten/Luyten.java | 3 +- .../deathmarine/luyten/LuytenTypeLoader.java | 6 +- src/us/deathmarine/luyten/MainMenuBar.java | 24 +++--- src/us/deathmarine/luyten/MainWindow.java | 6 +- src/us/deathmarine/luyten/Model.java | 86 +++++++++---------- src/us/deathmarine/luyten/OpenFile.java | 37 +++----- src/us/deathmarine/luyten/RecentFiles.java | 7 +- src/us/deathmarine/luyten/Selection.java | 7 +- .../luyten/TooLargeFileException.java | 2 +- src/us/deathmarine/luyten/TreeUtil.java | 6 +- src/us/deathmarine/luyten/WindowPosition.java | 7 +- 22 files changed, 166 insertions(+), 201 deletions(-) diff --git a/src/us/deathmarine/luyten/ConfigSaver.java b/src/us/deathmarine/luyten/ConfigSaver.java index 6d537589..77b41e75 100644 --- a/src/us/deathmarine/luyten/ConfigSaver.java +++ b/src/us/deathmarine/luyten/ConfigSaver.java @@ -33,7 +33,7 @@ public class ConfigSaver { private WindowPosition findWindowPosition; private LuytenPreferences luytenPreferences; - private static ConfigSaver theLoadedInstance; + private static volatile ConfigSaver theLoadedInstance; /** * Do not instantiate, get the loaded instance @@ -121,7 +121,7 @@ private LuytenPreferences loadLuytenPreferences(Preferences prefs) throws Except field.set(newLuytenPrefs, prefs.get(prefId, defaultStr)); } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { - Boolean defaultBool = (Boolean) (defaultVal == null ? new Boolean(false) : defaultVal); + Boolean defaultBool = (Boolean) (defaultVal == null ? Boolean.FALSE : defaultVal); field.setBoolean(newLuytenPrefs, prefs.getBoolean(prefId, defaultBool)); } else if (field.getType() == Integer.class || field.getType() == int.class) { @@ -177,7 +177,7 @@ private void saveLuytenPreferences(Preferences prefs) throws Exception { prefs.put(prefId, (String) (value == null ? "" : value)); } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { - prefs.putBoolean(prefId, (Boolean) (value == null ? new Boolean(false) : value)); + prefs.putBoolean(prefId, (Boolean) (value == null ? Boolean.FALSE : value)); } else if (field.getType() == Integer.class || field.getType() == int.class) { prefs.putInt(prefId, (Integer) (value == null ? new Integer(0) : value)); diff --git a/src/us/deathmarine/luyten/DecompilerLinkProvider.java b/src/us/deathmarine/luyten/DecompilerLinkProvider.java index b63d9ad6..1c707d32 100644 --- a/src/us/deathmarine/luyten/DecompilerLinkProvider.java +++ b/src/us/deathmarine/luyten/DecompilerLinkProvider.java @@ -48,7 +48,7 @@ public void writeDefinition(String text, Object definition, boolean isLocal) { if (uniqueStr != null) { // fix link's underline length: _java.util.HashSet_ // -> _HashSet_ - text = text.replaceAll("[^\\.]*\\.", ""); + text = text.replaceAll("[^.]*\\.", ""); int from = stringwriter.getBuffer().length() - text.length(); int to = stringwriter.getBuffer().length(); definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); @@ -66,7 +66,7 @@ public void writeReference(String text, Object reference, boolean isLocal) { if (text != null && reference != null) { String uniqueStr = createUniqueStrForReference(reference); if (uniqueStr != null) { - text = text.replaceAll("[^\\.]*\\.", ""); + text = text.replaceAll("[^.]*\\.", ""); int from = stringwriter.getBuffer().length() - text.length(); int to = stringwriter.getBuffer().length(); if (reference instanceof FieldReference) { @@ -138,7 +138,7 @@ private String getPathAndTypeStr(TypeReference typeRef) { } private TypeReference getMostOuterTypeRef(TypeReference typeRef) { - int maxDecraringDepth = typeRef.getFullName().split("(\\.|\\$)").length; + int maxDecraringDepth = typeRef.getFullName().split("([.$])").length; for (int i = 0; i < maxDecraringDepth; i++) { TypeReference declaringTypeRef = typeRef.getDeclaringType(); if (declaringTypeRef == null) { @@ -161,10 +161,10 @@ private TypeReference getMostOuterTypeRefBySlowLookuping(TypeReference typeRef) if (packageName == null) return typeRef; String[] nameParts = name.split("\\$"); - String newName = ""; + StringBuilder newName = new StringBuilder(); String sep = ""; for (int i = 0; i < nameParts.length - 1; i++) { - newName = newName + sep + nameParts[i]; + newName.append(sep).append(nameParts[i]); sep = "$"; String newInternalName = packageName.replaceAll("\\.", "/") + "/" + newName; TypeReference newTypeRef = metadataSystem.lookupType(newInternalName); @@ -229,13 +229,9 @@ public boolean isLinkNavigable(String uniqueStr) { // check linked field/method exists if (uniqueStr.startsWith("method")) { - if (findMethodInType(typeDef, uniqueStr) == null) { - return false; - } + return findMethodInType(typeDef, uniqueStr) != null; } else if (uniqueStr.startsWith("field")) { - if (findFieldInType(typeDef, uniqueStr) == null) { - return false; - } + return findFieldInType(typeDef, uniqueStr) != null; } return true; } @@ -251,7 +247,7 @@ private MethodDefinition findMethodInType(TypeDefinition typeDef, String uniqueS List declaredMethods = typeDef.getDeclaredMethods(); if (declaredMethods == null) return null; - boolean isFound = false; + boolean isFound; for (MethodDefinition declaredMethod : declaredMethods) { isFound = (declaredMethod != null && methodName.equals(declaredMethod.getName())); isFound = (isFound && methodErasedSignature.equals(declaredMethod.getErasedSignature())); @@ -276,7 +272,7 @@ private FieldDefinition findFieldInType(TypeDefinition typeDef, String uniqueStr List declaredFields = typeDef.getDeclaredFields(); if (declaredFields == null) return null; - boolean isFound = false; + boolean isFound; for (FieldDefinition declaredField : declaredFields) { isFound = (declaredField != null && fieldName.equals(declaredField.getName())); if (isFound) { @@ -335,7 +331,7 @@ public String getLinkDescription(String uniqueStr) { String declaringTypeName = declaringTypeDef.getName(); if (declaringTypeName != null) { constructorName = StringUtilities.removeLeft(constructorName, declaringTypeName); - constructorName = constructorName.replaceAll("^(\\.|\\$)", ""); + constructorName = constructorName.replaceAll("^([.$])", ""); } } } @@ -366,7 +362,7 @@ public String getLinkDescription(String uniqueStr) { } private String erasePackageInfoFromDesc(String desc) { - String limiters = "\\(\\)\\<\\>\\[\\]\\?\\s,"; + String limiters = "\\(\\)<>\\[]\\?\\s,"; desc = desc.replaceAll("(?<=[^" + limiters + "]*)([^" + limiters + "]*)\\.", ""); return desc; } diff --git a/src/us/deathmarine/luyten/DirPreferences.java b/src/us/deathmarine/luyten/DirPreferences.java index ea5df808..30ff250a 100644 --- a/src/us/deathmarine/luyten/DirPreferences.java +++ b/src/us/deathmarine/luyten/DirPreferences.java @@ -4,7 +4,7 @@ import java.io.File; class DirPreferences { - private LuytenPreferences luytenPrefs; + private final LuytenPreferences luytenPrefs; public DirPreferences(LuytenPreferences luytenPrefs) { this.luytenPrefs = luytenPrefs; @@ -59,4 +59,4 @@ void saveSaveDialogDir(JFileChooser fc) { Luyten.showExceptionDialog("Exception!", e); } } -} \ No newline at end of file +} diff --git a/src/us/deathmarine/luyten/DropListener.java b/src/us/deathmarine/luyten/DropListener.java index ff32a01d..ffd45ecf 100644 --- a/src/us/deathmarine/luyten/DropListener.java +++ b/src/us/deathmarine/luyten/DropListener.java @@ -18,7 +18,7 @@ * Drag-Drop (only MainWindow should be called from here) */ public class DropListener implements DropTargetListener { - private MainWindow mainWindow; + private final MainWindow mainWindow; public DropListener(MainWindow mainWindow) { this.mainWindow = mainWindow; @@ -51,16 +51,16 @@ public void drop(DropTargetDropEvent event) { } else { DataFlavor[] flavors = transferable.getTransferDataFlavors(); boolean handled = false; - for (int zz = 0; zz < flavors.length; zz++) { - if (flavors[zz].isRepresentationClassReader()) { + for (DataFlavor flavor : flavors) { + if (flavor.isRepresentationClassReader()) { try { - Reader reader = flavors[zz].getReaderForText(transferable); + Reader reader = flavor.getReaderForText(transferable); BufferedReader br = new BufferedReader(reader); - List list = new ArrayList(); - String line = null; + List list = new ArrayList<>(); + String line; while ((line = br.readLine()) != null) { try { - if (new String("" + (char) 0).equals(line)) + if (("" + (char) 0).equals(line)) continue; File file = new File(new URI(line)); list.add(file); diff --git a/src/us/deathmarine/luyten/FileDialog.java b/src/us/deathmarine/luyten/FileDialog.java index b573ac87..cf11a4a2 100644 --- a/src/us/deathmarine/luyten/FileDialog.java +++ b/src/us/deathmarine/luyten/FileDialog.java @@ -10,8 +10,8 @@ */ public class FileDialog { private final DirPreferences dirPreferences; - private ConfigSaver configSaver; - private Component parent; + private final ConfigSaver configSaver; + private final Component parent; private JFileChooser fcOpen; private JFileChooser fcSave; private JFileChooser fcSaveAll; @@ -33,7 +33,7 @@ public void run() { } catch (Exception e) { Luyten.showExceptionDialog("Exception!", e); } - }; + } }.start(); } @@ -112,7 +112,7 @@ private JFileChooser createFileChooser(String... fileFilters) { return fc; } - public class FileChooserFileFilter extends FileFilter { + public static class FileChooserFileFilter extends FileFilter { String objType; public FileChooserFileFilter(String string) { diff --git a/src/us/deathmarine/luyten/FileSaver.java b/src/us/deathmarine/luyten/FileSaver.java index e6ee49b0..3233d49f 100644 --- a/src/us/deathmarine/luyten/FileSaver.java +++ b/src/us/deathmarine/luyten/FileSaver.java @@ -13,6 +13,7 @@ import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.HashSet; import java.util.List; @@ -44,8 +45,8 @@ */ public class FileSaver { - private JProgressBar bar; - private JLabel label; + private final JProgressBar bar; + private final JLabel label; private boolean cancel; private boolean extracting; @@ -77,9 +78,10 @@ public void run() { boolean isUnicodeEnabled = settings.isUnicodeOutputEnabled(); long time = System.currentTimeMillis(); try (FileOutputStream fos = new FileOutputStream(file); - OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, "UTF-8") + OutputStreamWriter writer = isUnicodeEnabled + ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) : new OutputStreamWriter(fos); - BufferedWriter bw = new BufferedWriter(writer);) { + BufferedWriter bw = new BufferedWriter(writer)) { label.setText("Extracting: " + file.getName()); bar.setVisible(true); bw.write(text); @@ -137,10 +139,10 @@ private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { try (JarFile jfile = new JarFile(inFile); FileOutputStream dest = new FileOutputStream(outFile); BufferedOutputStream buffDest = new BufferedOutputStream(dest); - ZipOutputStream out = new ZipOutputStream(buffDest);) { + ZipOutputStream out = new ZipOutputStream(buffDest)) { bar.setMinimum(0); bar.setMaximum(jfile.size()); - byte data[] = new byte[1024]; + byte[] data = new byte[1024]; DecompilerSettings settings = cloneSettings(); LuytenTypeLoader typeLoader = new LuytenTypeLoader(); MetadataSystem metadataSystem = new MetadataSystem(typeLoader); @@ -151,7 +153,7 @@ private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { decompilationOptions.setSettings(settings); decompilationOptions.setFullDecompilation(true); - List mass = null; + List mass; JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); LuytenPreferences luytenPrefs = ConfigSaver.getLoadedInstance().getLuytenPreferences(); if (luytenPrefs.isFilterOutInnerClassEntries()) { @@ -161,7 +163,7 @@ private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { } Enumeration ent = jfile.entries(); - Set history = new HashSet(); + Set history = new HashSet<>(); int tick = 0; while (ent.hasMoreElements() && !cancel) { bar.setValue(++tick); @@ -181,11 +183,11 @@ private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); String internalName = StringUtilities.removeRight(entry.getName(), ".class"); TypeReference type = metadataSystem.lookupType(internalName); - TypeDefinition resolvedType = null; + TypeDefinition resolvedType; if ((type == null) || ((resolvedType = type.resolve()) == null)) { throw new Exception("Unable to resolve type."); } - Writer writer = isUnicodeEnabled ? new OutputStreamWriter(out, "UTF-8") + Writer writer = isUnicodeEnabled ? new OutputStreamWriter(out, StandardCharsets.UTF_8) : new OutputStreamWriter(out); PlainTextOutput plainTextOutput = new PlainTextOutput(writer); plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); @@ -242,7 +244,7 @@ private void doSaveClassDecompiled(File inFile, File outFile) throws Exception { decompilationOptions.setFullDecompilation(true); boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); - TypeDefinition resolvedType = null; + TypeDefinition resolvedType; if (type == null || ((resolvedType = type.resolve()) == null)) { throw new Exception("Unable to resolve type."); } @@ -254,19 +256,19 @@ private void doSaveClassDecompiled(File inFile, File outFile) throws Exception { System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); try (FileOutputStream fos = new FileOutputStream(outFile); - OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, "UTF-8") + OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) : new OutputStreamWriter(fos); - BufferedWriter bw = new BufferedWriter(writer);) { + BufferedWriter bw = new BufferedWriter(writer)) { bw.write(decompiledSource); bw.flush(); } } private void doSaveUnknownFile(File inFile, File outFile) throws Exception { - try (FileInputStream in = new FileInputStream(inFile); FileOutputStream out = new FileOutputStream(outFile);) { + try (FileInputStream in = new FileInputStream(inFile); FileOutputStream out = new FileOutputStream(outFile)) { System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); - byte data[] = new byte[1024]; + byte[] data = new byte[1024]; int count; while ((count = in.read(data, 0, 1024)) != -1) { out.write(data, 0, count); diff --git a/src/us/deathmarine/luyten/FindAllBox.java b/src/us/deathmarine/luyten/FindAllBox.java index 7be55812..01661846 100644 --- a/src/us/deathmarine/luyten/FindAllBox.java +++ b/src/us/deathmarine/luyten/FindAllBox.java @@ -7,10 +7,6 @@ import com.strobel.decompiler.DecompilerSettings; import com.strobel.decompiler.PlainTextOutput; -import us.deathmarine.luyten.ConfigSaver; -import us.deathmarine.luyten.MainWindow; -import us.deathmarine.luyten.Model; - import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; @@ -21,7 +17,6 @@ import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.File; -import java.io.IOException; import java.io.InputStreamReader; import java.io.StringWriter; import java.util.Collections; @@ -59,13 +54,13 @@ public class FindAllBox extends JDialog { private JProgressBar progressBar; boolean locked; - private JLabel statusLabel = new JLabel(""); + private final JLabel statusLabel = new JLabel(""); - private DefaultListModel classesList = new DefaultListModel(); + private final DefaultListModel classesList = new DefaultListModel<>(); private Thread tmp_thread; - private MainWindow mainWindow; + private final MainWindow mainWindow; public FindAllBox(final MainWindow mainWindow) { this.setDefaultCloseOperation(HIDE_ON_CLOSE); @@ -86,7 +81,7 @@ public FindAllBox(final MainWindow mainWindow) { this.getRootPane().setDefaultButton(findButton); - list = new JList(classesList); + list = new JList<>(classesList); list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); list.setLayoutOrientation(JList.VERTICAL_WRAP); list.setVisibleRowCount(-1); @@ -96,7 +91,7 @@ public void mouseClicked(MouseEvent evt) { JList list = (JList) evt.getSource(); if (evt.getClickCount() == 2) { int index = list.locationToIndex(evt.getPoint()); - String entryName = (String) list.getModel().getElementAt(index); + String entryName = list.getModel().getElementAt(index); String[] array = entryName.split("/"); if (entryName.toLowerCase().endsWith(".class")) { String internalName = StringUtilities.removeRight(entryName, ".class"); @@ -115,8 +110,6 @@ public void mouseClicked(MouseEvent evt) { jfile.getInputStream(jfile.getEntry(entryName)), array[array.length - 1], entryName); jfile.close(); - } catch (IOException e) { - e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } @@ -221,7 +214,7 @@ public void run() { if (entry.getName().endsWith(".class")) { synchronized (settings) { String internalName = StringUtilities.removeRight(entry.getName(), ".class"); - TypeReference type = null; + TypeReference type; try { type = Model.metadataSystem.lookupType(internalName); TypeDefinition resolvedType = null; @@ -240,7 +233,7 @@ public void run() { addClassName(entry.getName()); } } catch (IllegalStateException ise) { - if (ise.getMessage().toString().contains("Invalid BootstrapMethods attribute entry: 2 additional arguments required for method java/lang/invoke/StringConcatFactory.makeConcatWithConstants, but only 1 specified.")) { + if (ise.getMessage().contains("Invalid BootstrapMethods attribute entry: 2 additional arguments required for method java/lang/invoke/StringConcatFactory.makeConcatWithConstants, but only 1 specified.")) { // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to https://bitbucket.org/mstrobel/procyon/issues/336/ // Searching in a WAR or JAR file could pop-up a lot of error dialogs for a lot of class files, we simply say nothing here addClassName(entry.getName() + " (search failed due to known Exception in Procyon <= 0.5.35. Opening file will fail too)"); @@ -260,7 +253,7 @@ public void run() { long nonprintableCharactersCount = 0; try (InputStreamReader inputStreamReader = new InputStreamReader( jfile.getInputStream(entry)); - BufferedReader reader = new BufferedReader(inputStreamReader);) { + BufferedReader reader = new BufferedReader(inputStreamReader)) { String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); @@ -310,9 +303,7 @@ private boolean search(String bulk) { a = a.toLowerCase(); b = b.toLowerCase(); } - if (b.contains(a)) - return true; - return false; + return b.contains(a); } private void setHideOnEscapeButton() { diff --git a/src/us/deathmarine/luyten/FindBox.java b/src/us/deathmarine/luyten/FindBox.java index 0c14256a..d06b92a1 100644 --- a/src/us/deathmarine/luyten/FindBox.java +++ b/src/us/deathmarine/luyten/FindBox.java @@ -32,9 +32,9 @@ public class FindBox extends JDialog { JCheckBox wholew; JCheckBox reverse; JCheckBox wrap; - private JButton findButton; + private final JButton findButton; JTextField textField; - private MainWindow mainWindow; + private final MainWindow mainWindow; public void showFindBox() { this.setVisible(true); diff --git a/src/us/deathmarine/luyten/JFontChooser.java b/src/us/deathmarine/luyten/JFontChooser.java index 1f57bed7..1db91060 100644 --- a/src/us/deathmarine/luyten/JFontChooser.java +++ b/src/us/deathmarine/luyten/JFontChooser.java @@ -260,8 +260,7 @@ public JList getFontSizeList() { * @see #setSelectedFontFamily **/ public String getSelectedFontFamily() { - String fontName = (String) getFontFamilyList().getSelectedValue(); - return fontName; + return (String) getFontFamilyList().getSelectedValue(); } /** @@ -289,7 +288,7 @@ public int getSelectedFontStyle() { * @see #setSelectedFontSize **/ public int getSelectedFontSize() { - int fontSize = 1; + int fontSize; String fontSizeString = getFontSizeTextField().getText(); while (true) { try { @@ -313,8 +312,7 @@ public int getSelectedFontSize() { * @see java.awt.Font **/ public Font getSelectedFont() { - Font font = new Font(getSelectedFontFamily(), getSelectedFontStyle(), getSelectedFontSize()); - return font; + return new Font(getSelectedFontFamily(), getSelectedFontStyle(), getSelectedFontSize()); } /** @@ -323,12 +321,12 @@ public Font getSelectedFont() { * @param name * the family name of the selected font. * - * @see getSelectedFontFamily + * @see #getSelectedFontFamily **/ public void setSelectedFontFamily(String name) { String[] names = getFontFamilies(); for (int i = 0; i < names.length; i++) { - if (names[i].toLowerCase().equals(name.toLowerCase())) { + if (names[i].equalsIgnoreCase(name)) { getFontFamilyList().setSelectedIndex(i); break; } @@ -420,20 +418,19 @@ public void windowClosing(WindowEvent e) { dialog.setVisible(true); dialog.dispose(); - dialog = null; return dialogResultValue; } protected class ListSelectionHandler implements ListSelectionListener { - private JTextComponent textComponent; + private final JTextComponent textComponent; ListSelectionHandler(JTextComponent textComponent) { this.textComponent = textComponent; } public void valueChanged(ListSelectionEvent e) { - if (e.getValueIsAdjusting() == false) { + if (!e.getValueIsAdjusting()) { JList list = (JList) e.getSource(); String selectedValue = (String) list.getSelectedValue(); @@ -450,7 +447,7 @@ public void valueChanged(ListSelectionEvent e) { } protected class TextFieldFocusHandlerForTextSelection extends FocusAdapter { - private JTextComponent textComponent; + private final JTextComponent textComponent; public TextFieldFocusHandlerForTextSelection(JTextComponent textComponent) { this.textComponent = textComponent; @@ -466,15 +463,15 @@ public void focusLost(FocusEvent e) { } } - protected class TextFieldKeyHandlerForListSelectionUpDown extends KeyAdapter { - private JList targetList; + protected static class TextFieldKeyHandlerForListSelectionUpDown extends KeyAdapter { + private final JList targetList; public TextFieldKeyHandlerForListSelectionUpDown(JList list) { this.targetList = list; } public void keyPressed(KeyEvent e) { - int i = targetList.getSelectedIndex(); + int i; switch (e.getKeyCode()) { case KeyEvent.VK_UP: i = targetList.getSelectedIndex() - 1; @@ -497,7 +494,7 @@ public void keyPressed(KeyEvent e) { } } - protected class ListSearchTextFieldDocumentHandler implements DocumentListener { + protected static class ListSearchTextFieldDocumentHandler implements DocumentListener { JList targetList; public ListSearchTextFieldDocumentHandler(JList targetList) { @@ -542,7 +539,7 @@ private void update(DocumentEvent event) { } public class ListSelector implements Runnable { - private int index; + private final int index; public ListSelector(int index) { this.index = index; @@ -560,7 +557,7 @@ protected class DialogOKAction extends AbstractAction { */ private static final long serialVersionUID = 1618273732543947323L; protected static final String ACTION_NAME = "OK"; - private JDialog dialog; + private final JDialog dialog; protected DialogOKAction(JDialog dialog) { this.dialog = dialog; @@ -581,7 +578,7 @@ protected class DialogCancelAction extends AbstractAction { */ private static final long serialVersionUID = -4941763616565382601L; protected static final String ACTION_NAME = "Cancel"; - private JDialog dialog; + private final JDialog dialog; protected DialogCancelAction(JDialog dialog) { this.dialog = dialog; @@ -769,4 +766,4 @@ protected String[] getFontStyleNames() { } return fontStyleNames; } -} \ No newline at end of file +} diff --git a/src/us/deathmarine/luyten/JarEntryFilter.java b/src/us/deathmarine/luyten/JarEntryFilter.java index fc8a292a..90135df9 100644 --- a/src/us/deathmarine/luyten/JarEntryFilter.java +++ b/src/us/deathmarine/luyten/JarEntryFilter.java @@ -34,15 +34,15 @@ public List getAllEntriesFromJar() { public List getEntriesWithoutInnerClasses() { List mass = new ArrayList<>(); Enumeration entries = jfile.entries(); - Set possibleInnerClasses = new HashSet(); - Set baseClasses = new HashSet(); + Set possibleInnerClasses = new HashSet<>(); + Set baseClasses = new HashSet<>(); while (entries.hasMoreElements()) { JarEntry e = entries.nextElement(); if (!e.isDirectory()) { String entryName = e.getName(); - if (entryName != null && entryName.trim().length() > 0) { + if (entryName.trim().length() > 0) { entryName = entryName.trim(); if (!entryName.endsWith(".class")) { diff --git a/src/us/deathmarine/luyten/LinkProvider.java b/src/us/deathmarine/luyten/LinkProvider.java index e6911e9f..e156a3a0 100644 --- a/src/us/deathmarine/luyten/LinkProvider.java +++ b/src/us/deathmarine/luyten/LinkProvider.java @@ -5,18 +5,18 @@ public interface LinkProvider { - public void generateContent(); + void generateContent(); - public String getTextContent(); + String getTextContent(); - public void processLinks(); + void processLinks(); - public Map getDefinitionToSelectionMap(); + Map getDefinitionToSelectionMap(); - public Map> getReferenceToSelectionsMap(); + Map> getReferenceToSelectionsMap(); - public boolean isLinkNavigable(String uniqueStr); + boolean isLinkNavigable(String uniqueStr); - public String getLinkDescription(String uniqueStr); + String getLinkDescription(String uniqueStr); } diff --git a/src/us/deathmarine/luyten/Luyten.java b/src/us/deathmarine/luyten/Luyten.java index 6c5bd7b9..51c09761 100644 --- a/src/us/deathmarine/luyten/Luyten.java +++ b/src/us/deathmarine/luyten/Luyten.java @@ -11,7 +11,6 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.URI; -import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import java.util.List; import java.util.ArrayList; @@ -48,7 +47,7 @@ public void run() { if (lockSocket != null) { lockSocket.close(); } - } catch (IOException e) { + } catch (IOException ignored) { } } })); diff --git a/src/us/deathmarine/luyten/LuytenTypeLoader.java b/src/us/deathmarine/luyten/LuytenTypeLoader.java index 1198ac01..35e0569d 100644 --- a/src/us/deathmarine/luyten/LuytenTypeLoader.java +++ b/src/us/deathmarine/luyten/LuytenTypeLoader.java @@ -11,11 +11,11 @@ public final class LuytenTypeLoader implements ITypeLoader { private final List _typeLoaders; public LuytenTypeLoader() { - _typeLoaders = new ArrayList(); + _typeLoaders = new ArrayList<>(); _typeLoaders.add(new InputTypeLoader()); } - public final List getTypeLoaders() { + public List getTypeLoaders() { return _typeLoaders; } @@ -31,4 +31,4 @@ public boolean tryLoadType(final String internalName, final Buffer buffer) { return false; } -} \ No newline at end of file +} diff --git a/src/us/deathmarine/luyten/MainMenuBar.java b/src/us/deathmarine/luyten/MainMenuBar.java index bd108c70..6c364d7d 100644 --- a/src/us/deathmarine/luyten/MainMenuBar.java +++ b/src/us/deathmarine/luyten/MainMenuBar.java @@ -45,7 +45,7 @@ public class MainMenuBar extends JMenuBar { private static final long serialVersionUID = -7949855817172562075L; private final MainWindow mainWindow; - private final Map languageLookup = new HashMap(); + private final Map languageLookup = new HashMap<>(); private JMenu recentFiles; private JMenuItem clearRecentFiles; @@ -68,8 +68,8 @@ public class MainMenuBar extends JMenuBar { private JCheckBoxMenuItem filterOutInnerClassEntries; private JCheckBoxMenuItem singleClickOpenEnabled; private JCheckBoxMenuItem exitByEscEnabled; - private DecompilerSettings settings; - private LuytenPreferences luytenPrefs; + private final DecompilerSettings settings; + private final LuytenPreferences luytenPrefs; public MainMenuBar(MainWindow mainWnd) { this.mainWindow = mainWnd; @@ -113,7 +113,7 @@ public void run() { buildOperationMenu(operationMenu); refreshMenuPopup(operationMenu); - buildSettingsMenu(settingsMenu, configSaver); + buildSettingsMenu(settingsMenu); refreshMenuPopup(settingsMenu); buildHelpMenu(helpMenu); @@ -198,7 +198,7 @@ public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) { JTabbedPane house = mainWindow.getSelectedModel().house; - if (e.getModifiers() != 2 || house.getTabCount() == 0) + if (e.getModifiers() != InputEvent.CTRL_MASK || house.getTabCount() == 0) mainWindow.onCloseFileMenu(); else { mainWindow.getSelectedModel().closeOpenTab(house.getSelectedIndex()); @@ -251,7 +251,7 @@ public void actionPerformed(ActionEvent e) { // automatically if (!Boolean.getBoolean("apple.laf.useScreenMenuBar")) { menuItem = new JMenuItem("Exit"); - menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, ActionEvent.ALT_MASK)); + menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK)); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -421,7 +421,7 @@ public void actionPerformed(ActionEvent e) { operationMenu.add(exitByEscEnabled); } - private void buildSettingsMenu(JMenu settingsMenu, ConfigSaver configSaver) { + private void buildSettingsMenu(JMenu settingsMenu) { settingsMenu.removeAll(); ActionListener settingsChanged = new ActionListener() { @Override @@ -573,14 +573,14 @@ public void actionPerformed(ActionEvent event) { link.addMouseListener(new LinkListener(procyon, link)); pane.add(link); pane.add(new JLabel("Version: " + Procyon.version())); - pane.add(new JLabel("(c) 2018 Mike Strobel")); + pane.add(new JLabel("(c) 2021 Mike Strobel")); String rsyntax = "https://github.com/bobbylight/RSyntaxTextArea"; link = new JLabel("" + rsyntax + ""); link.setCursor(new Cursor(Cursor.HAND_CURSOR)); link.addMouseListener(new LinkListener(rsyntax, link)); pane.add(link); - pane.add(new JLabel("Version: 3.0.2")); - pane.add(new JLabel("(c) 2019 Robert Futrell")); + pane.add(new JLabel("Version: 3.1.3")); + pane.add(new JLabel("(c) 2021 Robert Futrell")); pane.add(new JLabel(" ")); JOptionPane.showMessageDialog(null, pane); } @@ -631,7 +631,7 @@ private void populateSettingsFromSettingsMenu() { private class ThemeAction extends AbstractAction { private static final long serialVersionUID = -6618680171943723199L; - private String xml; + private final String xml; public ThemeAction(String name, String xml) { putValue(NAME, name); @@ -645,7 +645,7 @@ public void actionPerformed(ActionEvent e) { } } - private class LinkListener extends MouseAdapter { + private static class LinkListener extends MouseAdapter { String link; JLabel label; diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java index ab89c04f..d1ec54e2 100644 --- a/src/us/deathmarine/luyten/MainWindow.java +++ b/src/us/deathmarine/luyten/MainWindow.java @@ -51,7 +51,7 @@ public MainWindow(File fileFromCommandLine) { windowPosition = configSaver.getMainWindowPosition(); luytenPrefs = configSaver.getLuytenPreferences(); - jarModels = new HashMap(); + jarModels = new HashMap<>(); mainMenuBar = new MainMenuBar(this); this.setJMenuBar(mainMenuBar); @@ -297,9 +297,9 @@ public void onListLoadedClasses() { bar.setVisible(true); bar.setIndeterminate(true); while (myCL != null) { - sb.append("ClassLoader: " + myCL + "\n"); + sb.append("ClassLoader: ").append(myCL).append("\n"); for (Iterator iter = list(myCL); iter.hasNext();) { - sb.append("\t" + iter.next() + "\n"); + sb.append("\t").append(iter.next()).append("\n"); } myCL = myCL.getParent(); } diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index 9245994c..bfd4b2dc 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -1,6 +1,5 @@ package us.deathmarine.luyten; -import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; @@ -77,21 +76,21 @@ public class Model extends JSplitPane { private static LuytenTypeLoader typeLoader = new LuytenTypeLoader(); public static MetadataSystem metadataSystem = new MetadataSystem(typeLoader); - private JTree tree; + private final JTree tree; public JTabbedPane house; private File file; - private DecompilerSettings settings; - private DecompilationOptions decompilationOptions; + private final DecompilerSettings settings; + private final DecompilationOptions decompilationOptions; private Theme theme; - private MainWindow mainWindow; - private JProgressBar bar; + private final MainWindow mainWindow; + private final JProgressBar bar; private JLabel label; - private HashSet hmap = new HashSet(); + private final HashSet hmap = new HashSet<>(); private Set treeExpansionState; private boolean open = false; private State state; - private ConfigSaver configSaver; - private LuytenPreferences luytenPrefs; + private final ConfigSaver configSaver; + private final LuytenPreferences luytenPrefs; public Model(MainWindow mainWindow) { this.mainWindow = mainWindow; @@ -134,7 +133,7 @@ public void keyPressed(KeyEvent e) { }); JPanel panel2 = new JPanel(); - panel2.setLayout(new BoxLayout(panel2, 1)); + panel2.setLayout(new BoxLayout(panel2, BoxLayout.Y_AXIS)); panel2.setBorder(BorderFactory.createTitledBorder("Structure")); panel2.add(new JScrollPane(tree)); @@ -164,7 +163,7 @@ public void actionPerformed(ActionEvent e) { }); JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, 1)); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.setBorder(BorderFactory.createTitledBorder("Code")); panel.add(house); this.setOrientation(JSplitPane.HORIZONTAL_SPLIT); @@ -211,7 +210,7 @@ public void run() { house.setSelectedIndex(index); Tab ct = new Tab(title, new Callable() { @Override - public Void call() throws Exception { + public Void call() { int index = house.indexOfTab(title); closeOpenTab(index); return null; @@ -236,7 +235,7 @@ public void closeOpenTab(int index) { for (OpenFile file : hmap) if (pane.equals(file.textArea)) open = file; - if (open != null && hmap.contains(open)) + if (open != null) hmap.remove(open); house.remove(co); if (open != null) @@ -313,7 +312,7 @@ public void treeCollapsed(final TreeExpansionEvent event) { public void openEntryByTreePath(TreePath trp) { String name = ""; - String path = ""; + StringBuilder path = new StringBuilder(); try { bar.setVisible(true); if (trp.getPathCount() > 1) { @@ -323,10 +322,10 @@ public void openEntryByTreePath(TreePath trp) { if (i == trp.getPathCount() - 1) { name = userObject.getOriginalName(); } else { - path = path + userObject.getOriginalName() + "/"; + path.append(userObject.getOriginalName()).append("/"); } } - path = path + name; + path.append(name); if (file.getName().endsWith(".jar") || file.getName().endsWith(".zip")) { if (state == null) { @@ -337,7 +336,7 @@ public void openEntryByTreePath(TreePath trp) { state = new State(file.getCanonicalPath(), file, jfile, jarLoader); } - JarEntry entry = state.jarFile.getJarEntry(path); + JarEntry entry = state.jarFile.getJarEntry(path.toString()); if (entry == null) { throw new FileEntryNotFoundException(); } @@ -349,28 +348,28 @@ public void openEntryByTreePath(TreePath trp) { getLabel().setText("Extracting: " + name); String internalName = StringUtilities.removeRight(entryName, ".class"); TypeReference type = metadataSystem.lookupType(internalName); - extractClassToTextPane(type, name, path, null); + extractClassToTextPane(type, name, path.toString(), null); } else { getLabel().setText("Opening: " + name); - try (InputStream in = state.jarFile.getInputStream(entry);) { - extractSimpleFileEntryToTextPane(in, name, path); + try (InputStream in = state.jarFile.getInputStream(entry)) { + extractSimpleFileEntryToTextPane(in, name, path.toString()); } } } } else { name = file.getName(); - path = file.getPath().replaceAll("\\\\", "/"); + path = new StringBuilder(file.getPath().replaceAll("\\\\", "/")); if (file.length() > MAX_UNPACKED_FILE_SIZE_BYTES) { throw new TooLargeFileException(file.length()); } if (name.endsWith(".class")) { getLabel().setText("Extracting: " + name); - TypeReference type = metadataSystem.lookupType(path); - extractClassToTextPane(type, name, path, null); + TypeReference type = metadataSystem.lookupType(path.toString()); + extractClassToTextPane(type, name, path.toString(), null); } else { getLabel().setText("Opening: " + name); - try (InputStream in = new FileInputStream(file);) { - extractSimpleFileEntryToTextPane(in, name, path); + try (InputStream in = new FileInputStream(file)) { + extractSimpleFileEntryToTextPane(in, name, path.toString()); } } } @@ -390,7 +389,7 @@ public void openEntryByTreePath(TreePath trp) { } } - void extractClassToTextPane(TypeReference type, String tabTitle, String path, String navigatonLink) + void extractClassToTextPane(TypeReference type, String tabTitle, String path, String navigationLink) throws Exception { if (tabTitle == null || tabTitle.trim().length() < 1 || path == null) { throw new FileEntryNotFoundException(); @@ -403,13 +402,13 @@ void extractClassToTextPane(TypeReference type, String tabTitle, String path, St } } if (sameTitledOpen != null && sameTitledOpen.isContentValid()) { - sameTitledOpen.setInitialNavigationLink(navigatonLink); + sameTitledOpen.setInitialNavigationLink(navigationLink); addOrSwitchToTab(sameTitledOpen); return; } // resolve TypeDefinition - TypeDefinition resolvedType = null; + TypeDefinition resolvedType; if (type == null || ((resolvedType = type.resolve()) == null)) { throw new Exception("Unable to resolve type."); } @@ -420,7 +419,7 @@ void extractClassToTextPane(TypeReference type, String tabTitle, String path, St sameTitledOpen.invalidateContent(); sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); sameTitledOpen.setType(resolvedType); - sameTitledOpen.setInitialNavigationLink(navigatonLink); + sameTitledOpen.setInitialNavigationLink(navigationLink); sameTitledOpen.resetScrollPosition(); sameTitledOpen.decompile(); addOrSwitchToTab(sameTitledOpen); @@ -428,7 +427,7 @@ void extractClassToTextPane(TypeReference type, String tabTitle, String path, St OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); open.setType(resolvedType); - open.setInitialNavigationLink(navigatonLink); + open.setInitialNavigationLink(navigationLink); open.decompile(); hmap.add(open); addOrSwitchToTab(open); @@ -456,7 +455,7 @@ public void extractSimpleFileEntryToTextPane(InputStream inputStream, String tab StringBuilder sb = new StringBuilder(); long nonprintableCharactersCount = 0; try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - BufferedReader reader = new BufferedReader(inputStreamReader);) { + BufferedReader reader = new BufferedReader(inputStreamReader)) { String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); @@ -471,7 +470,7 @@ public void extractSimpleFileEntryToTextPane(InputStream inputStream, String tab } // guess binary or text - String extension = "." + tabTitle.replaceAll("^[^\\.]*$", "").replaceAll("[^\\.]*\\.", ""); + String extension = "." + tabTitle.replaceAll("^[^.]*$", "").replaceAll("[^.]*\\.", ""); boolean isTextFile = (OpenFile.WELL_KNOWN_TEXT_FILE_EXTENSIONS.contains(extension) || nonprintableCharactersCount < sb.length() / 5); if (!isTextFile) { @@ -589,8 +588,8 @@ public String getKey() { } public static class Tab extends JPanel { - private JLabel tabTitle; - private JLabel closeButton = new JLabel(new ImageIcon( + private final JLabel tabTitle; + private final JLabel closeButton = new JLabel(new ImageIcon( Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/icon_close.png")))); public Tab(String title, final Callable onCloseTabAction) { @@ -700,13 +699,12 @@ public void run() { throw new TooLargeFileException(file.length()); } if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) { - JarFile jfile; - jfile = new JarFile(file); + JarFile jfile = new JarFile(file); getLabel().setText("Loading: " + jfile.getName()); bar.setVisible(true); JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); - List mass = null; + List mass; if (luytenPrefs.isFilterOutInnerClassEntries()) { mass = jarEntryFilter.getEntriesWithoutInnerClasses(); } else { @@ -734,7 +732,7 @@ public void run() { public void run() { TreePath trp = new TreePath(top.getPath()); openEntryByTreePath(trp); - }; + } }.start(); } @@ -773,12 +771,12 @@ private void buildTreeFromMass(List mass) { private void buildDirectoryTreeFromMass(List mass) { TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); - List sort = new ArrayList(); + List sort = new ArrayList<>(); Collections.sort(mass, String.CASE_INSENSITIVE_ORDER); for (String m : mass) if (m.contains("META-INF") && !sort.contains(m)) sort.add(m); - Set set = new HashSet(); + Set set = new HashSet<>(); for (String m : mass) { if (m.contains("/")) { set.add(m.substring(0, m.lastIndexOf("/") + 1)); @@ -799,7 +797,7 @@ public int compare(String o1, String o2) { if (!m.contains("META-INF") && !m.contains("/") && !sort.contains(m)) sort.add(m); for (String pack : sort) { - LinkedList list = new LinkedList(Arrays.asList(pack.split("/"))); + LinkedList list = new LinkedList<>(Arrays.asList(pack.split("/"))); loadNodesByNames(top, list); } tree.setModel(new DefaultTreeModel(top)); @@ -816,7 +814,7 @@ private void buildFlatTreeFromMass(List mass) { // (assertion: mass does not contain null elements) @Override public int compare(String o1, String o2) { - int comp = o1.replaceAll("[^\\.]*\\.", "").compareTo(o2.replaceAll("[^\\.]*\\.", "")); + int comp = o1.replaceAll("[^.]*\\.", "").compareTo(o2.replaceAll("[^.]*\\.", "")); if (comp != 0) return comp; return o1.compareTo(o2); @@ -832,7 +830,7 @@ public int compare(String o1, String o2) { } String packageEntry = entry.replace(packagePath + "/", ""); if (!packages.containsKey(packagePath)) { - packages.put(packagePath, new TreeSet(sortByFileExtensionsComparator)); + packages.put(packagePath, new TreeSet<>(sortByFileExtensionsComparator)); } packages.get(packagePath).add(packageEntry); if (!entry.startsWith("META-INF") && packageRoot.trim().length() > 0 @@ -981,7 +979,7 @@ public void run() { Thread.sleep(500); String internalName = FindBox.class.getName(); TypeReference type = metadataSystem.lookupType(internalName); - TypeDefinition resolvedType = null; + TypeDefinition resolvedType; if ((type == null) || ((resolvedType = type.resolve()) == null)) { return; } diff --git a/src/us/deathmarine/luyten/OpenFile.java b/src/us/deathmarine/luyten/OpenFile.java index 497db079..14099be3 100644 --- a/src/us/deathmarine/luyten/OpenFile.java +++ b/src/us/deathmarine/luyten/OpenFile.java @@ -3,13 +3,11 @@ import java.awt.Component; import java.awt.Cursor; import java.awt.Font; -import java.awt.Panel; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; -import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseWheelEvent; @@ -55,8 +53,8 @@ public class OpenFile implements SyntaxConstants { // navigation links private TreeMap selectionToUniqueStrTreeMap = new TreeMap<>(); - private Map isNavigableCache = new ConcurrentHashMap<>(); - private Map readableLinksCache = new ConcurrentHashMap<>(); + private final Map isNavigableCache = new ConcurrentHashMap<>(); + private final Map readableLinksCache = new ConcurrentHashMap<>(); private volatile boolean isContentValid = false; private volatile boolean isNavigationLinksValid = false; @@ -69,13 +67,12 @@ public class OpenFile implements SyntaxConstants { MainWindow mainWindow; RTextScrollPane scrollPane; - Panel image_pane; RSyntaxTextArea textArea; String name; String path; - private ConfigSaver configSaver; - private LuytenPreferences luytenPrefs; + private final ConfigSaver configSaver; + private final LuytenPreferences luytenPrefs; // decompiler and type references (not needed for text files) private MetadataSystem metadataSystem; @@ -119,8 +116,6 @@ else if (name.toLowerCase().endsWith(".php") || name.toLowerCase().endsWith(".ph else if (name.toLowerCase().endsWith(".html") || name.toLowerCase().endsWith(".htm") || name.toLowerCase().endsWith(".xhtm") || name.toLowerCase().endsWith(".xhtml")) textArea.setSyntaxEditingStyle(SYNTAX_STYLE_HTML); - else if (name.toLowerCase().endsWith(".js")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_JAVASCRIPT); else if (name.toLowerCase().endsWith(".lua")) textArea.setSyntaxEditingStyle(SYNTAX_STYLE_LUA); else if (name.toLowerCase().endsWith(".bat")) @@ -228,7 +223,7 @@ public int getSourceOffset() { for (MouseWheelListener listeners : scrollPane.getMouseWheelListeners()) { scrollPane.removeMouseWheelListener(listeners); } - ; + scrollPane.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseWheelMoved(MouseWheelEvent e) { @@ -316,7 +311,7 @@ public void mouseWheelMoved(MouseWheelEvent e) { break; } } - } else if ((leftToRight && direction > 0) || (!leftToRight && direction < 0)) { + } else { viewRect.x += unitIncr; if (leftToRight) { if (viewRect.x > scrollMax) { @@ -324,8 +319,6 @@ public void mouseWheelMoved(MouseWheelEvent e) { break; } } - } else { - assert false : "Non-sensical ComponentOrientation / scroll direction"; } } } @@ -427,10 +420,10 @@ public synchronized void mouseMoved(MouseEvent e) { if (!linkText.equals(prevLinkText)) { setLinkLabel(label, linkText); } - } else if (isLinkLabel && !isLinkLabelPrev) { + } else if (isLinkLabel) { setLinkLabel(label, linkText); - } else if (!isLinkLabel && isLinkLabelPrev) { + } else if (isLinkLabelPrev) { setLinkLabel(label, null); } isLinkLabelPrev = isLinkLabel; @@ -634,10 +627,7 @@ private Integer getSelectionFromForOffset(int offset) { private String getLinkDescriptionForOffset(int offset) { String uniqueStr = getUniqueStrForOffset(offset); if (uniqueStr != null) { - String description = this.getLinkDescription(uniqueStr); - if (description != null) { - return description; - } + return this.getLinkDescription(uniqueStr); } return null; } @@ -694,7 +684,7 @@ private void onNavigationClicked(String clickedReferenceUniqueStr) { } private boolean isLocallyNavigable(String uniqueStr) { - return linkProvider.getDefinitionToSelectionMap().keySet().contains(uniqueStr); + return linkProvider.getDefinitionToSelectionMap().containsKey(uniqueStr); } private void onLocalNavigationRequest(String uniqueStr) { @@ -834,11 +824,8 @@ public boolean equals(Object obj) { return false; OpenFile other = (OpenFile) obj; if (path == null) { - if (other.path != null) - return false; - } else if (!path.equals(other.path)) - return false; - return true; + return other.path == null; + } else return path.equals(other.path); } } diff --git a/src/us/deathmarine/luyten/RecentFiles.java b/src/us/deathmarine/luyten/RecentFiles.java index c553d8bd..625650d4 100644 --- a/src/us/deathmarine/luyten/RecentFiles.java +++ b/src/us/deathmarine/luyten/RecentFiles.java @@ -2,12 +2,13 @@ import java.io.File; import java.util.ArrayList; +import java.util.List; import java.util.prefs.Preferences; public class RecentFiles { - public static ArrayList paths = new ArrayList<>(); - private static Preferences prefs = Preferences.userNodeForPackage(RecentFiles.class); + public static List paths = new ArrayList<>(); + private static final Preferences prefs = Preferences.userNodeForPackage(RecentFiles.class); public static int load() { boolean saveNeeded = false; @@ -63,4 +64,4 @@ public static void save() { prefs.put("recentFiles", sb.toString()); } -} \ No newline at end of file +} diff --git a/src/us/deathmarine/luyten/Selection.java b/src/us/deathmarine/luyten/Selection.java index fdf507a4..6af8bb8f 100644 --- a/src/us/deathmarine/luyten/Selection.java +++ b/src/us/deathmarine/luyten/Selection.java @@ -32,10 +32,7 @@ public boolean equals(Object obj) { return false; Selection other = (Selection) obj; if (from == null) { - if (other.from != null) - return false; - } else if (!from.equals(other.from)) - return false; - return true; + return other.from == null; + } else return from.equals(other.from); } } diff --git a/src/us/deathmarine/luyten/TooLargeFileException.java b/src/us/deathmarine/luyten/TooLargeFileException.java index 3dc5abbd..d4c867e1 100644 --- a/src/us/deathmarine/luyten/TooLargeFileException.java +++ b/src/us/deathmarine/luyten/TooLargeFileException.java @@ -4,7 +4,7 @@ public class TooLargeFileException extends Exception { private static final long serialVersionUID = 6091096838075139962L; - private long size; + private final long size; public TooLargeFileException(long size) { this.size = size; diff --git a/src/us/deathmarine/luyten/TreeUtil.java b/src/us/deathmarine/luyten/TreeUtil.java index 4fdb95a1..1ae83277 100644 --- a/src/us/deathmarine/luyten/TreeUtil.java +++ b/src/us/deathmarine/luyten/TreeUtil.java @@ -60,15 +60,15 @@ public void restoreExpanstionState(Set expansionState) { } private String getRowPathStr(TreePath trp) { - String pathStr = ""; + StringBuilder pathStr = new StringBuilder(); if (trp.getPathCount() > 1) { for (int i = 1; i < trp.getPathCount(); i++) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); - pathStr = pathStr + userObject.getOriginalName() + "/"; + pathStr.append(userObject.getOriginalName()).append("/"); } } - return pathStr; + return pathStr.toString(); } public JTree getTree() { diff --git a/src/us/deathmarine/luyten/WindowPosition.java b/src/us/deathmarine/luyten/WindowPosition.java index 8c70640e..c689cae7 100644 --- a/src/us/deathmarine/luyten/WindowPosition.java +++ b/src/us/deathmarine/luyten/WindowPosition.java @@ -44,11 +44,8 @@ public boolean isSavedWindowPositionValid() { if (windowWidth > screenSize.width + 50 || windowHeight > screenSize.height + 50) { return false; } - if (windowY < -20 || windowY > screenSize.height - 50 || windowX < 50 - windowWidth - || windowX > screenSize.width - 50) { - return false; - } - return true; + return windowY >= -20 && windowY <= screenSize.height - 50 && windowX >= 50 - windowWidth + && windowX <= screenSize.width - 50; } public boolean isFullScreen() { From 0723c49975d1ddf8262f1734ce845f226290c78c Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 8 Oct 2021 22:51:59 +0200 Subject: [PATCH 06/26] RSTA only works on Java 8+ --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 158f348c..e59b944b 100644 --- a/pom.xml +++ b/pom.xml @@ -94,8 +94,8 @@ maven-compiler-plugin 3.1 - 1.7 - 1.7 + 1.8 + 1.8 @@ -165,7 +165,7 @@ luyten.ico - 1.7.0 + 1.8.0 1.8.0 128 1024 @@ -228,7 +228,7 @@ mainclass="${project.groupId}.${project.artifactId}.LuytenOsx" version="${project.version}" copyright="2015" icon="${project.build.sourceDirectory}/resources/luyten.icns" - jvmversion="1.7+" screenmenu="true" + jvmversion="1.8+" screenmenu="true" antialiasedgraphics="true" highresolutioncapable="true" > From 76584c1193264d47a8fe07874394d493cef2bdaa Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 8 Oct 2021 22:52:56 +0200 Subject: [PATCH 07/26] Allow multi-file drop --- src/us/deathmarine/luyten/DropListener.java | 8 +------- src/us/deathmarine/luyten/MainWindow.java | 8 ++++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/us/deathmarine/luyten/DropListener.java b/src/us/deathmarine/luyten/DropListener.java index ffd45ecf..30cc73a0 100644 --- a/src/us/deathmarine/luyten/DropListener.java +++ b/src/us/deathmarine/luyten/DropListener.java @@ -35,13 +35,7 @@ public void drop(DropTargetDropEvent event) { try { if (flavor.isFlavorJavaFileListType()) { List files = (List) transferable.getTransferData(flavor); - if (files.size() > 1) { - event.rejectDrop(); - return; - } - if (files.size() == 1) { - mainWindow.onFileDropped(files.get(0)); - } + mainWindow.onFilesDropped(files); } } catch (Exception e) { Luyten.showExceptionDialog("Exception!", e); diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java index d1ec54e2..229b62cb 100644 --- a/src/us/deathmarine/luyten/MainWindow.java +++ b/src/us/deathmarine/luyten/MainWindow.java @@ -365,6 +365,14 @@ public void onTreeSettingsChanged() { } } + public void onFilesDropped(List files) { + if (files != null) { + for (File file : files) { + this.loadNewFile(file); + } + } + } + public void onFileDropped(File file) { if (file != null) { this.loadNewFile(file); From 5bad6aafab685ce2a2f9e55c08674f16da69abb8 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 8 Oct 2021 22:54:29 +0200 Subject: [PATCH 08/26] Fix classes on same path being open in different jars Up until now, if you open two jar files that contain the class e.g. `test.package.TestClass` and you open this class of the first jar, the one from the second jar will look exactly like the first one. --- src/us/deathmarine/luyten/FindAllBox.java | 15 ++++++++++----- src/us/deathmarine/luyten/MainWindow.java | 4 ++++ src/us/deathmarine/luyten/Model.java | 9 ++++++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/us/deathmarine/luyten/FindAllBox.java b/src/us/deathmarine/luyten/FindAllBox.java index 01661846..95f08841 100644 --- a/src/us/deathmarine/luyten/FindAllBox.java +++ b/src/us/deathmarine/luyten/FindAllBox.java @@ -95,12 +95,17 @@ public void mouseClicked(MouseEvent evt) { String[] array = entryName.split("/"); if (entryName.toLowerCase().endsWith(".class")) { String internalName = StringUtilities.removeRight(entryName, ".class"); - TypeReference type = Model.metadataSystem.lookupType(internalName); + TypeReference type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); try { mainWindow.getSelectedModel().extractClassToTextPane(type, array[array.length - 1], entryName, null); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); + } catch (Exception ignored) { + for (Model m : mainWindow.getModels()) { + try { + m.extractClassToTextPane(type, array[array.length - 1], entryName, null); + } catch (Exception ignored1) { + } + } } } else { @@ -216,8 +221,8 @@ public void run() { String internalName = StringUtilities.removeRight(entry.getName(), ".class"); TypeReference type; try { - type = Model.metadataSystem.lookupType(internalName); - TypeDefinition resolvedType = null; + type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); + TypeDefinition resolvedType; if (type != null && ((resolvedType = type.resolve()) != null)) { StringWriter stringwriter = new StringWriter(); DecompilationOptions decompilationOptions; diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java index 229b62cb..2ef13b82 100644 --- a/src/us/deathmarine/luyten/MainWindow.java +++ b/src/us/deathmarine/luyten/MainWindow.java @@ -496,6 +496,10 @@ public Model getSelectedModel() { return (Model) jarsTabbedPane.getSelectedComponent(); } + public Collection getModels() { + return jarModels.values(); + } + public JProgressBar getBar() { return bar; } diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index bfd4b2dc..57a8aed6 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -73,8 +73,8 @@ public class Model extends JSplitPane { private static final long MAX_JAR_FILE_SIZE_BYTES = 10_000_000_000L; private static final long MAX_UNPACKED_FILE_SIZE_BYTES = 10_000_000L; - private static LuytenTypeLoader typeLoader = new LuytenTypeLoader(); - public static MetadataSystem metadataSystem = new MetadataSystem(typeLoader); + private final LuytenTypeLoader typeLoader = new LuytenTypeLoader(); + private MetadataSystem metadataSystem = new MetadataSystem(typeLoader); private final JTree tree; public JTabbedPane house; @@ -573,7 +573,7 @@ private State(String key, File file, JarFile jarFile, ITypeLoader typeLoader) { @Override public void close() { if (typeLoader != null) { - Model.typeLoader.getTypeLoaders().remove(typeLoader); + Model.this.typeLoader.getTypeLoaders().remove(typeLoader); } Closer.tryClose(jarFile); } @@ -1057,4 +1057,7 @@ public void setTheme(Theme theme) { this.theme = theme; } + public MetadataSystem getMetadataSystem() { + return metadataSystem; + } } From 1932ccb3f13b0edf89e96d67ce71210c195cda29 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 8 Oct 2021 23:02:35 +0200 Subject: [PATCH 09/26] Some more cleanup --- pom.xml | 59 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index e59b944b..d2ba9715 100644 --- a/pom.xml +++ b/pom.xml @@ -3,6 +3,11 @@ us.deathmarine luyten 0.7.0 + + + UTF-8 + + com.fifesoft @@ -14,26 +19,26 @@ AppleJavaExtensions 1.4 - - org.bitbucket.mstrobel - procyon-core - 0.5.36 - - - org.bitbucket.mstrobel - procyon-expressions - 0.5.36 - - - org.bitbucket.mstrobel - procyon-reflection - 0.5.36 - - - org.bitbucket.mstrobel - procyon-compilertools - 0.5.36 - + + org.bitbucket.mstrobel + procyon-core + 0.5.36 + + + org.bitbucket.mstrobel + procyon-expressions + 0.5.36 + + + org.bitbucket.mstrobel + procyon-reflection + 0.5.36 + + + org.bitbucket.mstrobel + procyon-compilertools + 0.5.36 + com.akathist.maven.plugins.launch4j @@ -203,7 +208,7 @@ universalJavaApplicationStub.sh - + @@ -239,12 +244,12 @@ - - - + @@ -257,7 +262,7 @@ - + @@ -312,6 +317,6 @@ - + From 667f7b360af9c5825cbc33e4565a7629540e9a22 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 8 Oct 2021 23:04:14 +0200 Subject: [PATCH 10/26] Remove maxVersion attribute --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2ba9715..2d353fd3 100644 --- a/pom.xml +++ b/pom.xml @@ -171,7 +171,6 @@ luyten.ico 1.8.0 - 1.8.0 128 1024 From 71ba2679938cc285b250246a4521a5fab43d904c Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sat, 9 Oct 2021 10:25:00 +0200 Subject: [PATCH 11/26] Java 8 refactor --- src/us/deathmarine/luyten/FileDialog.java | 24 +- src/us/deathmarine/luyten/FileSaver.java | 117 ++--- src/us/deathmarine/luyten/FindAllBox.java | 212 ++++---- src/us/deathmarine/luyten/Luyten.java | 46 +- src/us/deathmarine/luyten/MainMenuBar.java | 250 +++------- src/us/deathmarine/luyten/MainWindow.java | 62 ++- src/us/deathmarine/luyten/Model.java | 379 +++++++------- src/us/deathmarine/luyten/OpenFile.java | 545 ++++++++++----------- 8 files changed, 724 insertions(+), 911 deletions(-) diff --git a/src/us/deathmarine/luyten/FileDialog.java b/src/us/deathmarine/luyten/FileDialog.java index cf11a4a2..b874a75a 100644 --- a/src/us/deathmarine/luyten/FileDialog.java +++ b/src/us/deathmarine/luyten/FileDialog.java @@ -22,19 +22,17 @@ public FileDialog(Component parent) { LuytenPreferences luytenPrefs = configSaver.getLuytenPreferences(); dirPreferences = new DirPreferences(luytenPrefs); - new Thread() { - public void run() { - try { - initOpenDialog(); - Thread.sleep(500); - initSaveAllDialog(); - Thread.sleep(500); - initSaveDialog(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }.start(); + new Thread(() -> { + try { + initOpenDialog(); + Thread.sleep(500); + initSaveAllDialog(); + Thread.sleep(500); + initSaveDialog(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }).start(); } public File doOpenDialog() { diff --git a/src/us/deathmarine/luyten/FileSaver.java b/src/us/deathmarine/luyten/FileSaver.java index 3233d49f..c88901e3 100644 --- a/src/us/deathmarine/luyten/FileSaver.java +++ b/src/us/deathmarine/luyten/FileSaver.java @@ -55,12 +55,7 @@ public FileSaver(JProgressBar bar, JLabel label) { this.label = label; final JPopupMenu menu = new JPopupMenu("Cancel"); final JMenuItem item = new JMenuItem("Cancel"); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent arg0) { - setCancel(true); - } - }); + item.addActionListener(arg0 -> setCancel(true)); menu.add(item); this.label.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent ev) { @@ -71,68 +66,62 @@ public void mouseClicked(MouseEvent ev) { } public void saveText(final String text, final File file) { - new Thread(new Runnable() { - @Override - public void run() { - DecompilerSettings settings = cloneSettings(); - boolean isUnicodeEnabled = settings.isUnicodeOutputEnabled(); - long time = System.currentTimeMillis(); - try (FileOutputStream fos = new FileOutputStream(file); - OutputStreamWriter writer = isUnicodeEnabled - ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) - : new OutputStreamWriter(fos); - BufferedWriter bw = new BufferedWriter(writer)) { - label.setText("Extracting: " + file.getName()); - bar.setVisible(true); - bw.write(text); - bw.flush(); - label.setText("Completed: " + getTime(time)); - } catch (Exception e1) { - label.setText("Cannot save file: " + file.getName()); - Luyten.showExceptionDialog("Unable to save file!\n", e1); - } finally { - setExtracting(false); - bar.setVisible(false); - } - } - }).start(); + new Thread(() -> { + DecompilerSettings settings = cloneSettings(); + boolean isUnicodeEnabled = settings.isUnicodeOutputEnabled(); + long time = System.currentTimeMillis(); + try (FileOutputStream fos = new FileOutputStream(file); + OutputStreamWriter writer = isUnicodeEnabled + ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) + : new OutputStreamWriter(fos); + BufferedWriter bw = new BufferedWriter(writer)) { + label.setText("Extracting: " + file.getName()); + bar.setVisible(true); + bw.write(text); + bw.flush(); + label.setText("Completed: " + getTime(time)); + } catch (Exception e1) { + label.setText("Cannot save file: " + file.getName()); + Luyten.showExceptionDialog("Unable to save file!\n", e1); + } finally { + setExtracting(false); + bar.setVisible(false); + } + }).start(); } public void saveAllDecompiled(final File inFile, final File outFile) { - new Thread(new Runnable() { - @Override - public void run() { - long time = System.currentTimeMillis(); - try { - bar.setVisible(true); - setExtracting(true); - label.setText("Extracting: " + outFile.getName()); - System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); - String inFileName = inFile.getName().toLowerCase(); + new Thread(() -> { + long time = System.currentTimeMillis(); + try { + bar.setVisible(true); + setExtracting(true); + label.setText("Extracting: " + outFile.getName()); + System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); + String inFileName = inFile.getName().toLowerCase(); - if (inFileName.endsWith(".jar") || inFileName.endsWith(".zip")) { - doSaveJarDecompiled(inFile, outFile); - } else if (inFileName.endsWith(".class")) { - doSaveClassDecompiled(inFile, outFile); - } else { - doSaveUnknownFile(inFile, outFile); - } - if (cancel) { - label.setText("Cancelled"); - outFile.delete(); - setCancel(false); - } else { - label.setText("Completed: " + getTime(time)); - } - } catch (Exception e1) { - label.setText("Cannot save file: " + outFile.getName()); - Luyten.showExceptionDialog("Unable to save file!\n", e1); - } finally { - setExtracting(false); - bar.setVisible(false); - } - } - }).start(); + if (inFileName.endsWith(".jar") || inFileName.endsWith(".zip")) { + doSaveJarDecompiled(inFile, outFile); + } else if (inFileName.endsWith(".class")) { + doSaveClassDecompiled(inFile, outFile); + } else { + doSaveUnknownFile(inFile, outFile); + } + if (cancel) { + label.setText("Cancelled"); + outFile.delete(); + setCancel(false); + } else { + label.setText("Completed: " + getTime(time)); + } + } catch (Exception e1) { + label.setText("Cannot save file: " + outFile.getName()); + Luyten.showExceptionDialog("Unable to save file!\n", e1); + } finally { + setExtracting(false); + bar.setVisible(false); + } + }).start(); } private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { diff --git a/src/us/deathmarine/luyten/FindAllBox.java b/src/us/deathmarine/luyten/FindAllBox.java index 95f08841..bc7f4713 100644 --- a/src/us/deathmarine/luyten/FindAllBox.java +++ b/src/us/deathmarine/luyten/FindAllBox.java @@ -184,113 +184,111 @@ private class FindButton extends AbstractAction { @Override public void actionPerformed(ActionEvent event) { - tmp_thread = new Thread() { - public void run() { - if (findButton.getText().equals("Stop")) { - if (tmp_thread != null) - tmp_thread.interrupt(); - setStatus("Stopped."); - findButton.setText("Find"); - locked = false; - } else { - findButton.setText("Stop"); - classesList.clear(); - ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); - DecompilerSettings settings = configSaver.getDecompilerSettings(); - File inFile = mainWindow.getSelectedModel().getOpenedFile(); - boolean filter = ConfigSaver.getLoadedInstance().getLuytenPreferences() - .isFilterOutInnerClassEntries(); - try { - JarFile jfile = new JarFile(inFile); - Enumeration entLength = jfile.entries(); - initProgressBar(Collections.list(entLength).size()); - Enumeration ent = jfile.entries(); - while (ent.hasMoreElements() && findButton.getText().equals("Stop")) { - JarEntry entry = ent.nextElement(); - String name = entry.getName(); - setStatus(name); - if (filter && name.contains("$")) - continue; - if(locked || classname.isSelected()){ - locked = true; - if(search(entry.getName())) - addClassName(entry.getName()); - }else{ - if (entry.getName().endsWith(".class")) { - synchronized (settings) { - String internalName = StringUtilities.removeRight(entry.getName(), ".class"); - TypeReference type; - try { - type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); - TypeDefinition resolvedType; - if (type != null && ((resolvedType = type.resolve()) != null)) { - StringWriter stringwriter = new StringWriter(); - DecompilationOptions decompilationOptions; - decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput.setUnicodeOutputEnabled( - decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, - decompilationOptions); - if (search(stringwriter.toString())) - addClassName(entry.getName()); - } - } catch (IllegalStateException ise) { - if (ise.getMessage().contains("Invalid BootstrapMethods attribute entry: 2 additional arguments required for method java/lang/invoke/StringConcatFactory.makeConcatWithConstants, but only 1 specified.")) { - // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to https://bitbucket.org/mstrobel/procyon/issues/336/ - // Searching in a WAR or JAR file could pop-up a lot of error dialogs for a lot of class files, we simply say nothing here - addClassName(entry.getName() + " (search failed due to known Exception in Procyon <= 0.5.35. Opening file will fail too)"); - } else { - // all other IllegalStateException cases - addClassName(entry.getName() + " (search failed due to Exception. Opening file will fail too)"); - Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), ise); - } - } catch (Exception e) { - addClassName(entry.getName() + " (search failed due to Exception. Opening file will fail too)"); - Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), e); - } - } - } else { - - StringBuilder sb = new StringBuilder(); - long nonprintableCharactersCount = 0; - try (InputStreamReader inputStreamReader = new InputStreamReader( - jfile.getInputStream(entry)); - BufferedReader reader = new BufferedReader(inputStreamReader)) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); - - for (byte nextByte : line.getBytes()) { - if (nextByte <= 0) { - nonprintableCharactersCount++; - } - } - - } - } - if (nonprintableCharactersCount < 5 && search(sb.toString())) - addClassName(entry.getName()); - } - } - } - setSearching(false); - if (findButton.getText().equals("Stop")) { - setStatus("Done."); - findButton.setText("Find"); - locked = false; - } - jfile.close(); - locked = false; - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - - } - } - }; + tmp_thread = new Thread(() -> { + if (findButton.getText().equals("Stop")) { + if (tmp_thread != null) + tmp_thread.interrupt(); + setStatus("Stopped."); + findButton.setText("Find"); + locked = false; + } else { + findButton.setText("Stop"); + classesList.clear(); + ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); + DecompilerSettings settings = configSaver.getDecompilerSettings(); + File inFile = mainWindow.getSelectedModel().getOpenedFile(); + boolean filter = ConfigSaver.getLoadedInstance().getLuytenPreferences() + .isFilterOutInnerClassEntries(); + try { + JarFile jfile = new JarFile(inFile); + Enumeration entLength = jfile.entries(); + initProgressBar(Collections.list(entLength).size()); + Enumeration ent = jfile.entries(); + while (ent.hasMoreElements() && findButton.getText().equals("Stop")) { + JarEntry entry = ent.nextElement(); + String name = entry.getName(); + setStatus(name); + if (filter && name.contains("$")) + continue; + if(locked || classname.isSelected()){ + locked = true; + if(search(entry.getName())) + addClassName(entry.getName()); + }else{ + if (entry.getName().endsWith(".class")) { + synchronized (settings) { + String internalName = StringUtilities.removeRight(entry.getName(), ".class"); + TypeReference type; + try { + type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); + TypeDefinition resolvedType; + if (type != null && ((resolvedType = type.resolve()) != null)) { + StringWriter stringwriter = new StringWriter(); + DecompilationOptions decompilationOptions; + decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput.setUnicodeOutputEnabled( + decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, + decompilationOptions); + if (search(stringwriter.toString())) + addClassName(entry.getName()); + } + } catch (IllegalStateException ise) { + if (ise.getMessage().contains("Invalid BootstrapMethods attribute entry: 2 additional arguments required for method java/lang/invoke/StringConcatFactory.makeConcatWithConstants, but only 1 specified.")) { + // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to https://bitbucket.org/mstrobel/procyon/issues/336/ + // Searching in a WAR or JAR file could pop-up a lot of error dialogs for a lot of class files, we simply say nothing here + addClassName(entry.getName() + " (search failed due to known Exception in Procyon <= 0.5.35. Opening file will fail too)"); + } else { + // all other IllegalStateException cases + addClassName(entry.getName() + " (search failed due to Exception. Opening file will fail too)"); + Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), ise); + } + } catch (Exception e) { + addClassName(entry.getName() + " (search failed due to Exception. Opening file will fail too)"); + Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), e); + } + } + } else { + + StringBuilder sb = new StringBuilder(); + long nonprintableCharactersCount = 0; + try (InputStreamReader inputStreamReader = new InputStreamReader( + jfile.getInputStream(entry)); + BufferedReader reader = new BufferedReader(inputStreamReader)) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + + for (byte nextByte : line.getBytes()) { + if (nextByte <= 0) { + nonprintableCharactersCount++; + } + } + + } + } + if (nonprintableCharactersCount < 5 && search(sb.toString())) + addClassName(entry.getName()); + } + } + } + setSearching(false); + if (findButton.getText().equals("Stop")) { + setStatus("Done."); + findButton.setText("Find"); + locked = false; + } + jfile.close(); + locked = false; + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + + } + }); tmp_thread.start(); } diff --git a/src/us/deathmarine/luyten/Luyten.java b/src/us/deathmarine/luyten/Luyten.java index 51c09761..3f5ec489 100644 --- a/src/us/deathmarine/luyten/Luyten.java +++ b/src/us/deathmarine/luyten/Luyten.java @@ -3,8 +3,6 @@ import java.awt.Cursor; import java.awt.Desktop; import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.*; @@ -40,15 +38,12 @@ public class Luyten { private static ServerSocket lockSocket = null; public static void main(final String[] args) { - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - @Override - public void run() { - try { - if (lockSocket != null) { - lockSocket.close(); - } - } catch (IOException ignored) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + if (lockSocket != null) { + lockSocket.close(); } + } catch (IOException ignored) { } })); @@ -84,25 +79,17 @@ public void run() { private static void launchMainInstance(final File fileFromCommandLine) throws IOException { lockSocket = new ServerSocket(3456); launchSession(fileFromCommandLine); - new Thread(new Runnable() { - @Override - public void run() { - launchServer(); - } - }).start(); + new Thread(Luyten::launchServer).start(); } private static void launchSession(final File fileFromCommandLine) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (!mainWindowRef.compareAndSet(null, new MainWindow(fileFromCommandLine))) { - // Already set - so add the files to open - addToPendingFiles(fileFromCommandLine); - } - processPendingFiles(); - mainWindowRef.get().setVisible(true); + SwingUtilities.invokeLater(() -> { + if (!mainWindowRef.compareAndSet(null, new MainWindow(fileFromCommandLine))) { + // Already set - so add the files to open + addToPendingFiles(fileFromCommandLine); } + processPendingFiles(); + mainWindowRef.get().setVisible(true); }); } @@ -224,12 +211,9 @@ public void mouseClicked(MouseEvent e) { new JPopupMenu() { { JMenuItem menuitem = new JMenuItem("Select All"); - menuitem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - exception.requestFocus(); - exception.selectAll(); - } + menuitem.addActionListener(e12 -> { + exception.requestFocus(); + exception.selectAll(); }); this.add(menuitem); menuitem = new JMenuItem("Copy"); diff --git a/src/us/deathmarine/luyten/MainMenuBar.java b/src/us/deathmarine/luyten/MainMenuBar.java index 6c364d7d..3a110062 100644 --- a/src/us/deathmarine/luyten/MainMenuBar.java +++ b/src/us/deathmarine/luyten/MainMenuBar.java @@ -164,12 +164,7 @@ public void updateRecentFiles() { } JMenuItem menuItem = new JMenuItem(path); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.loadNewFile(file); - } - }); + menuItem.addActionListener(e -> mainWindow.loadNewFile(file)); recentFiles.add(menuItem); } @@ -181,53 +176,35 @@ private void buildFileMenu(final JMenu fileMenu) { JMenuItem menuItem = new JMenuItem("Open File..."); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onOpenFileMenu(); - } - }); + menuItem.addActionListener(e -> mainWindow.onOpenFileMenu()); fileMenu.add(menuItem); fileMenu.addSeparator(); menuItem = new JMenuItem("Close File"); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JTabbedPane house = mainWindow.getSelectedModel().house; - - if (e.getModifiers() != InputEvent.CTRL_MASK || house.getTabCount() == 0) - mainWindow.onCloseFileMenu(); - else { - mainWindow.getSelectedModel().closeOpenTab(house.getSelectedIndex()); - } - } - }); + menuItem.addActionListener(e -> { + JTabbedPane house = mainWindow.getSelectedModel().house; + + if (e.getModifiers() != InputEvent.CTRL_MASK || house.getTabCount() == 0) + mainWindow.onCloseFileMenu(); + else { + mainWindow.getSelectedModel().closeOpenTab(house.getSelectedIndex()); + } + }); fileMenu.add(menuItem); fileMenu.addSeparator(); menuItem = new JMenuItem("Save As..."); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onSaveAsMenu(); - } - }); + menuItem.addActionListener(e -> mainWindow.onSaveAsMenu()); fileMenu.add(menuItem); menuItem = new JMenuItem("Save All..."); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onSaveAllMenu(); - } - }); + menuItem.addActionListener(e -> mainWindow.onSaveAllMenu()); fileMenu.add(menuItem); fileMenu.addSeparator(); @@ -235,14 +212,11 @@ public void actionPerformed(ActionEvent e) { fileMenu.add(recentFiles); clearRecentFiles = new JMenuItem("Clear Recent Files"); - clearRecentFiles.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - RecentFiles.paths.clear(); - RecentFiles.save(); - updateRecentFiles(); - } - }); + clearRecentFiles.addActionListener(e -> { + RecentFiles.paths.clear(); + RecentFiles.save(); + updateRecentFiles(); + }); fileMenu.add(clearRecentFiles); fileMenu.addSeparator(); @@ -252,12 +226,7 @@ public void actionPerformed(ActionEvent e) { if (!Boolean.getBoolean("apple.laf.useScreenMenuBar")) { menuItem = new JMenuItem("Exit"); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK)); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onExitMenu(); - } - }); + menuItem.addActionListener(e -> mainWindow.onExitMenu()); fileMenu.add(menuItem); } } @@ -287,58 +256,36 @@ private void buildEditMenu(JMenu editMenu) { menuItem = new JMenuItem("Select All"); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onSelectAllMenu(); - } - }); + menuItem.addActionListener(e -> mainWindow.onSelectAllMenu()); editMenu.add(menuItem); editMenu.addSeparator(); menuItem = new JMenuItem("Find..."); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onFindMenu(); - } - }); + menuItem.addActionListener(e -> mainWindow.onFindMenu()); editMenu.add(menuItem); menuItem = new JMenuItem("Find Next"); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if(mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(true); - } - }); + menuItem.addActionListener(e -> { + if(mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(true); + }); editMenu.add(menuItem); menuItem = new JMenuItem("Find Previous"); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK)); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if(mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(false); - } - }); + menuItem.addActionListener(e -> { + if(mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(false); + }); editMenu.add(menuItem); menuItem = new JMenuItem("Find All"); menuItem.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onFindAllMenu(); - - } - }); + menuItem.addActionListener(e -> mainWindow.onFindAllMenu()); editMenu.add(menuItem); } @@ -380,61 +327,37 @@ private void buildOperationMenu(JMenu operationMenu) { operationMenu.removeAll(); packageExplorerStyle = new JCheckBoxMenuItem("Package Explorer Style"); packageExplorerStyle.setSelected(luytenPrefs.isPackageExplorerStyle()); - packageExplorerStyle.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setPackageExplorerStyle(packageExplorerStyle.isSelected()); - mainWindow.onTreeSettingsChanged(); - } - }); + packageExplorerStyle.addActionListener(e -> { + luytenPrefs.setPackageExplorerStyle(packageExplorerStyle.isSelected()); + mainWindow.onTreeSettingsChanged(); + }); operationMenu.add(packageExplorerStyle); filterOutInnerClassEntries = new JCheckBoxMenuItem("Filter Out Inner Class Entries"); filterOutInnerClassEntries.setSelected(luytenPrefs.isFilterOutInnerClassEntries()); - filterOutInnerClassEntries.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setFilterOutInnerClassEntries(filterOutInnerClassEntries.isSelected()); - mainWindow.onTreeSettingsChanged(); - } - }); + filterOutInnerClassEntries.addActionListener(e -> { + luytenPrefs.setFilterOutInnerClassEntries(filterOutInnerClassEntries.isSelected()); + mainWindow.onTreeSettingsChanged(); + }); operationMenu.add(filterOutInnerClassEntries); singleClickOpenEnabled = new JCheckBoxMenuItem("Single Click Open"); singleClickOpenEnabled.setSelected(luytenPrefs.isSingleClickOpenEnabled()); - singleClickOpenEnabled.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setSingleClickOpenEnabled(singleClickOpenEnabled.isSelected()); - } - }); + singleClickOpenEnabled.addActionListener(e -> luytenPrefs.setSingleClickOpenEnabled(singleClickOpenEnabled.isSelected())); operationMenu.add(singleClickOpenEnabled); exitByEscEnabled = new JCheckBoxMenuItem("Exit By Esc"); exitByEscEnabled.setSelected(luytenPrefs.isExitByEscEnabled()); - exitByEscEnabled.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setExitByEscEnabled(exitByEscEnabled.isSelected()); - } - }); + exitByEscEnabled.addActionListener(e -> luytenPrefs.setExitByEscEnabled(exitByEscEnabled.isSelected())); operationMenu.add(exitByEscEnabled); } private void buildSettingsMenu(JMenu settingsMenu) { settingsMenu.removeAll(); - ActionListener settingsChanged = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - new Thread() { - @Override - public void run() { - populateSettingsFromSettingsMenu(); - mainWindow.onSettingsChanged(); - } - }.start(); - } - }; + ActionListener settingsChanged = e -> new Thread(() -> { + populateSettingsFromSettingsMenu(); + mainWindow.onSettingsChanged(); + }).start(); flattenSwitchBlocks = new JCheckBoxMenuItem("Flatten Switch Blocks"); flattenSwitchBlocks.setSelected(settings.getFlattenSwitchBlocks()); flattenSwitchBlocks.addActionListener(settingsChanged); @@ -528,63 +451,50 @@ public void run() { private void buildHelpMenu(JMenu helpMenu) { helpMenu.removeAll(); JMenuItem menuItem = new JMenuItem("Legal"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onLegalMenu(); - } - }); + menuItem.addActionListener(e -> mainWindow.onLegalMenu()); helpMenu.add(menuItem); JMenu menuDebug = new JMenu("Debug"); menuItem = new JMenuItem("List JVM Classes"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onListLoadedClasses(); - } - }); + menuItem.addActionListener(e -> mainWindow.onListLoadedClasses()); menuDebug.add(menuItem); helpMenu.add(menuDebug); menuItem = new JMenuItem("About"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - JPanel pane = new JPanel(); - pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); - JLabel title = new JLabel("Luyten " + Luyten.getVersion()); - title.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18)); - pane.add(title); - pane.add(new JLabel("by Deathmarine")); - String project = "https://github.com/deathmarine/Luyten/"; - JLabel link = new JLabel("" + project + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(project, link)); - pane.add(link); - pane.add(new JLabel("Contributions By:")); - pane.add(new JLabel("zerdei, toonetown, dstmath")); - pane.add(new JLabel("virustotalop, xtrafrancyz,")); - pane.add(new JLabel("mbax, quitten, mstrobel,")); - pane.add(new JLabel("FisheyLP, and Syquel")); - pane.add(new JLabel(" ")); - pane.add(new JLabel("Powered By:")); - String procyon = "https://bitbucket.org/mstrobel/procyon"; - link = new JLabel("" + procyon + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(procyon, link)); - pane.add(link); - pane.add(new JLabel("Version: " + Procyon.version())); - pane.add(new JLabel("(c) 2021 Mike Strobel")); - String rsyntax = "https://github.com/bobbylight/RSyntaxTextArea"; - link = new JLabel("" + rsyntax + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(rsyntax, link)); - pane.add(link); - pane.add(new JLabel("Version: 3.1.3")); - pane.add(new JLabel("(c) 2021 Robert Futrell")); - pane.add(new JLabel(" ")); - JOptionPane.showMessageDialog(null, pane); - } - }); + menuItem.addActionListener(event -> { + JPanel pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); + JLabel title = new JLabel("Luyten " + Luyten.getVersion()); + title.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18)); + pane.add(title); + pane.add(new JLabel("by Deathmarine")); + String project = "https://github.com/deathmarine/Luyten/"; + JLabel link = new JLabel("" + project + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(project, link)); + pane.add(link); + pane.add(new JLabel("Contributions By:")); + pane.add(new JLabel("zerdei, toonetown, dstmath")); + pane.add(new JLabel("virustotalop, xtrafrancyz,")); + pane.add(new JLabel("mbax, quitten, mstrobel,")); + pane.add(new JLabel("FisheyLP, and Syquel")); + pane.add(new JLabel(" ")); + pane.add(new JLabel("Powered By:")); + String procyon = "https://bitbucket.org/mstrobel/procyon"; + link = new JLabel("" + procyon + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(procyon, link)); + pane.add(link); + pane.add(new JLabel("Version: " + Procyon.version())); + pane.add(new JLabel("(c) 2021 Mike Strobel")); + String rsyntax = "https://github.com/bobbylight/RSyntaxTextArea"; + link = new JLabel("" + rsyntax + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(rsyntax, link)); + pane.add(link); + pane.add(new JLabel("Version: 3.1.3")); + pane.add(new JLabel("(c) 2021 Robert Futrell")); + pane.add(new JLabel(" ")); + JOptionPane.showMessageDialog(null, pane); + }); helpMenu.add(menuItem); } diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java index 2ef13b82..cc9c04c9 100644 --- a/src/us/deathmarine/luyten/MainWindow.java +++ b/src/us/deathmarine/luyten/MainWindow.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.*; -import java.util.concurrent.Callable; import javax.swing.*; import javax.swing.border.BevelBorder; @@ -33,17 +32,17 @@ public class MainWindow extends JFrame { private static final String TITLE = "Luyten"; private static final String DEFAULT_TAB = "#DEFAULT"; - private JProgressBar bar; - private JLabel label; + private final JProgressBar bar; + private final JLabel label; FindBox findBox; private FindAllBox findAllBox; - private ConfigSaver configSaver; - private WindowPosition windowPosition; - private LuytenPreferences luytenPrefs; - private FileDialog fileDialog; - private FileSaver fileSaver; - private JTabbedPane jarsTabbedPane; - private Map jarModels; + private final ConfigSaver configSaver; + private final WindowPosition windowPosition; + private final LuytenPreferences luytenPrefs; + private final FileDialog fileDialog; + private final FileSaver fileSaver; + private final JTabbedPane jarsTabbedPane; + private final Map jarModels; public MainMenuBar mainMenuBar; public MainWindow(File fileFromCommandLine) { @@ -174,18 +173,15 @@ public Model loadNewFile(final File file) { final String tabName = file.getName(); int index = jarsTabbedPane.indexOfTab(tabName); - Model.Tab tabUI = new Model.Tab(tabName, new Callable() { - @Override - public Void call() { - int index = jarsTabbedPane.indexOfTab(tabName); - jarModels.remove(file.getAbsolutePath()); - jarsTabbedPane.remove(index); - if (jarsTabbedPane.getTabCount() == 0) { - createDefaultTab(); - } - return null; - } - }); + Model.Tab tabUI = new Model.Tab(tabName, () -> { + int index1 = jarsTabbedPane.indexOfTab(tabName); + jarModels.remove(file.getAbsolutePath()); + jarsTabbedPane.remove(index1); + if (jarsTabbedPane.getTabCount() == 0) { + createDefaultTab(); + } + return null; + }); jarsTabbedPane.setTabComponentAt(index, tabUI); if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) != -1 && jarsTabbedPane.getTabCount() > 1) { removeDefaultTab(); @@ -275,19 +271,17 @@ public void onFindAllMenu() { } public void onLegalMenu() { - new Thread() { - public void run() { - try { - bar.setVisible(true); - bar.setIndeterminate(true); - String legalStr = getLegalStr(); - getSelectedModel().showLegal(legalStr); - } finally { - bar.setIndeterminate(false); - bar.setVisible(false); - } + new Thread(() -> { + try { + bar.setVisible(true); + bar.setIndeterminate(true); + String legalStr = getLegalStr(); + getSelectedModel().showLegal(legalStr); + } finally { + bar.setIndeterminate(false); + bar.setVisible(false); } - }.start(); + }).start(); } public void onListLoadedClasses() { diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index 57a8aed6..ffc6863e 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -188,44 +188,38 @@ public void show(String name, String contents) { } private void addOrSwitchToTab(final OpenFile open) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - try { - final String title = open.name; - RTextScrollPane rTextScrollPane = open.scrollPane; - int index = house.indexOfTab(title); - if (index > -1 && house.getTabComponentAt(index) != open.scrollPane) { - index = -1; - for (int i = 0; i < house.getTabCount(); i++) { - if (house.getComponentAt(i) == open.scrollPane) { - index = i; - break; - } - } - } - if (index < 0) { - house.addTab(title, rTextScrollPane); - index = house.indexOfComponent(rTextScrollPane); - house.setSelectedIndex(index); - Tab ct = new Tab(title, new Callable() { - @Override - public Void call() { - int index = house.indexOfTab(title); - closeOpenTab(index); - return null; - } - }); - house.setTabComponentAt(index, ct); - } else { - house.setSelectedIndex(index); - } - open.onAddedToScreen(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }); + SwingUtilities.invokeLater(() -> { + try { + final String title = open.name; + RTextScrollPane rTextScrollPane = open.scrollPane; + int index = house.indexOfTab(title); + if (index > -1 && house.getTabComponentAt(index) != open.scrollPane) { + index = -1; + for (int i = 0; i < house.getTabCount(); i++) { + if (house.getComponentAt(i) == open.scrollPane) { + index = i; + break; + } + } + } + if (index < 0) { + house.addTab(title, rTextScrollPane); + index = house.indexOfComponent(rTextScrollPane); + house.setSelectedIndex(index); + Tab ct = new Tab(title, () -> { + int index1 = house.indexOfTab(title); + closeOpenTab(index1); + return null; + }); + house.setTabComponentAt(index, ct); + } else { + house.setSelectedIndex(index); + } + open.onAddedToScreen(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }); } public void closeOpenTab(int index) { @@ -273,11 +267,7 @@ public void mousePressed(MouseEvent event) { if (!isLeaf) return; - new Thread() { - public void run() { - openEntryByTreePath(trp); - } - }.start(); + new Thread(() -> openEntryByTreePath(trp)).start(); } } @@ -533,22 +523,19 @@ private void updateOpenClass(final OpenFile open) { if (open.getType() == null) { return; } - new Thread(new Runnable() { - @Override - public void run() { - try { - bar.setVisible(true); - getLabel().setText("Extracting: " + open.name); - open.invalidateContent(); - open.decompile(); - getLabel().setText("Complete"); - } catch (Exception e) { - getLabel().setText("Error, cannot update: " + open.name); - } finally { - bar.setVisible(false); - } - } - }).start(); + new Thread(() -> { + try { + bar.setVisible(true); + getLabel().setText("Extracting: " + open.name); + open.invalidateContent(); + open.decompile(); + getLabel().setText("Complete"); + } catch (Exception e) { + getLabel().setText("Error, cannot update: " + open.name); + } finally { + bar.setVisible(false); + } + }).start(); } private boolean isTabInForeground(OpenFile open) { @@ -686,78 +673,72 @@ public void updateTree() { } public void loadTree() { - new Thread(new Runnable() { - @Override - public void run() { - try { - if (file == null) { - return; - } - tree.setModel(new DefaultTreeModel(null)); - - if (file.length() > MAX_JAR_FILE_SIZE_BYTES) { - throw new TooLargeFileException(file.length()); - } - if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) { - JarFile jfile = new JarFile(file); - getLabel().setText("Loading: " + jfile.getName()); - bar.setVisible(true); - - JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); - List mass; - if (luytenPrefs.isFilterOutInnerClassEntries()) { - mass = jarEntryFilter.getEntriesWithoutInnerClasses(); - } else { - mass = jarEntryFilter.getAllEntriesFromJar(); - } - buildTreeFromMass(mass); - - if (state == null) { - ITypeLoader jarLoader = new JarTypeLoader(jfile); - typeLoader.getTypeLoaders().add(jarLoader); - state = new State(file.getCanonicalPath(), file, jfile, jarLoader); - } - open = true; - getLabel().setText("Complete"); - } else { - TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); - final DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); - tree.setModel(new DefaultTreeModel(top)); - settings.setTypeLoader(new InputTypeLoader()); - open = true; - getLabel().setText("Complete"); - - // open it automatically - new Thread() { - public void run() { - TreePath trp = new TreePath(top.getPath()); - openEntryByTreePath(trp); - } - }.start(); - } - - if (treeExpansionState != null) { - try { - TreeUtil treeUtil = new TreeUtil(tree); - treeUtil.restoreExpanstionState(treeExpansionState); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - } catch (TooLargeFileException e) { - getLabel().setText("File is too large: " + file.getName() + " - size: " + e.getReadableFileSize()); - closeFile(); - } catch (Exception e1) { - Luyten.showExceptionDialog("Cannot open " + file.getName() + "!", e1); - getLabel().setText("Cannot open: " + file.getName()); - closeFile(); - } finally { - mainWindow.onFileLoadEnded(file, open); - bar.setVisible(false); - } - } - - }).start(); + new Thread(() -> { + try { + if (file == null) { + return; + } + tree.setModel(new DefaultTreeModel(null)); + + if (file.length() > MAX_JAR_FILE_SIZE_BYTES) { + throw new TooLargeFileException(file.length()); + } + if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) { + JarFile jfile = new JarFile(file); + getLabel().setText("Loading: " + jfile.getName()); + bar.setVisible(true); + + JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); + List mass; + if (luytenPrefs.isFilterOutInnerClassEntries()) { + mass = jarEntryFilter.getEntriesWithoutInnerClasses(); + } else { + mass = jarEntryFilter.getAllEntriesFromJar(); + } + buildTreeFromMass(mass); + + if (state == null) { + ITypeLoader jarLoader = new JarTypeLoader(jfile); + typeLoader.getTypeLoaders().add(jarLoader); + state = new State(file.getCanonicalPath(), file, jfile, jarLoader); + } + open = true; + getLabel().setText("Complete"); + } else { + TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); + final DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); + tree.setModel(new DefaultTreeModel(top)); + settings.setTypeLoader(new InputTypeLoader()); + open = true; + getLabel().setText("Complete"); + + // open it automatically + new Thread(() -> { + TreePath trp = new TreePath(top.getPath()); + openEntryByTreePath(trp); + }).start(); + } + + if (treeExpansionState != null) { + try { + TreeUtil treeUtil = new TreeUtil(tree); + treeUtil.restoreExpanstionState(treeExpansionState); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + } catch (TooLargeFileException e) { + getLabel().setText("File is too large: " + file.getName() + " - size: " + e.getReadableFileSize()); + closeFile(); + } catch (Exception e1) { + Luyten.showExceptionDialog("Cannot open " + file.getName() + "!", e1); + getLabel().setText("Cannot open: " + file.getName()); + closeFile(); + } finally { + mainWindow.onFileLoadEnded(file, open); + bar.setVisible(false); + } + }).start(); } private void buildTreeFromMass(List mass) { @@ -784,11 +765,7 @@ private void buildDirectoryTreeFromMass(List mass) { } List packs = Arrays.asList(set.toArray(new String[] {})); Collections.sort(packs, String.CASE_INSENSITIVE_ORDER); - Collections.sort(packs, new Comparator() { - public int compare(String o1, String o2) { - return o2.split("/").length - o1.split("/").length; - } - }); + Collections.sort(packs, (o1, o2) -> o2.split("/").length - o1.split("/").length); for (String pack : packs) for (String m : mass) if (!m.contains("META-INF") && m.contains(pack) && !m.replace(pack, "").contains("/")) @@ -810,16 +787,13 @@ private void buildFlatTreeFromMass(List mass) { TreeMap> packages = new TreeMap<>(); HashSet classContainingPackageRoots = new HashSet<>(); - Comparator sortByFileExtensionsComparator = new Comparator() { - // (assertion: mass does not contain null elements) - @Override - public int compare(String o1, String o2) { - int comp = o1.replaceAll("[^.]*\\.", "").compareTo(o2.replaceAll("[^.]*\\.", "")); - if (comp != 0) - return comp; - return o1.compareTo(o2); - } - }; + // (assertion: mass does not contain null elements) + Comparator sortByFileExtensionsComparator = (o1, o2) -> { + int comp = o1.replaceAll("[^.]*\\.", "").compareTo(o2.replaceAll("[^.]*\\.", "")); + if (comp != 0) + return comp; + return o1.compareTo(o2); + }; for (String entry : mass) { String packagePath = ""; @@ -973,68 +947,63 @@ public RSyntaxTextArea getCurrentTextArea() { } public void startWarmUpThread() { - new Thread() { - public void run() { - try { - Thread.sleep(500); - String internalName = FindBox.class.getName(); - TypeReference type = metadataSystem.lookupType(internalName); - TypeDefinition resolvedType; - if ((type == null) || ((resolvedType = type.resolve()) == null)) { - return; - } - StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput - .setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); - String decompiledSource = stringwriter.toString(); - OpenFile open = new OpenFile(internalName, "*/" + internalName, getTheme(), mainWindow); - open.setContent(decompiledSource); - JTabbedPane pane = new JTabbedPane(); - pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - pane.addTab("title", open.scrollPane); - pane.setSelectedIndex(pane.indexOfTab("title")); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }.start(); + new Thread(() -> { + try { + Thread.sleep(500); + String internalName = FindBox.class.getName(); + TypeReference type = metadataSystem.lookupType(internalName); + TypeDefinition resolvedType; + if ((type == null) || ((resolvedType = type.resolve()) == null)) { + return; + } + StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput + .setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); + String decompiledSource = stringwriter.toString(); + OpenFile open = new OpenFile(internalName, "*/" + internalName, getTheme(), mainWindow); + open.setContent(decompiledSource); + JTabbedPane pane = new JTabbedPane(); + pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + pane.addTab("title", open.scrollPane); + pane.setSelectedIndex(pane.indexOfTab("title")); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }).start(); } public void navigateTo(final String uniqueStr) { - new Thread(new Runnable() { - @Override - public void run() { - if (uniqueStr == null) - return; - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length <= 1) - return; - String destinationTypeStr = linkParts[1]; - try { - bar.setVisible(true); - getLabel().setText("Navigating: " + destinationTypeStr.replaceAll("/", ".")); - - TypeReference type = metadataSystem.lookupType(destinationTypeStr); - if (type == null) - throw new RuntimeException("Cannot lookup type: " + destinationTypeStr); - TypeDefinition typeDef = type.resolve(); - if (typeDef == null) - throw new RuntimeException("Cannot resolve type: " + destinationTypeStr); - - String tabTitle = typeDef.getName() + ".class"; - extractClassToTextPane(typeDef, tabTitle, destinationTypeStr, uniqueStr); - - getLabel().setText("Complete"); - } catch (Exception e) { - getLabel().setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); - Luyten.showExceptionDialog("Cannot Navigate!", e); - } finally { - bar.setVisible(false); - } - } - }).start(); + new Thread(() -> { + if (uniqueStr == null) + return; + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length <= 1) + return; + String destinationTypeStr = linkParts[1]; + try { + bar.setVisible(true); + getLabel().setText("Navigating: " + destinationTypeStr.replaceAll("/", ".")); + + TypeReference type = metadataSystem.lookupType(destinationTypeStr); + if (type == null) + throw new RuntimeException("Cannot lookup type: " + destinationTypeStr); + TypeDefinition typeDef = type.resolve(); + if (typeDef == null) + throw new RuntimeException("Cannot resolve type: " + destinationTypeStr); + + String tabTitle = typeDef.getName() + ".class"; + extractClassToTextPane(typeDef, tabTitle, destinationTypeStr, uniqueStr); + + getLabel().setText("Complete"); + } catch (Exception e) { + getLabel().setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); + Luyten.showExceptionDialog("Cannot Navigate!", e); + } finally { + bar.setVisible(false); + } + }).start(); } public JLabel getLabel() { diff --git a/src/us/deathmarine/luyten/OpenFile.java b/src/us/deathmarine/luyten/OpenFile.java index 14099be3..1d8a67a2 100644 --- a/src/us/deathmarine/luyten/OpenFile.java +++ b/src/us/deathmarine/luyten/OpenFile.java @@ -149,19 +149,16 @@ else if (name.toLowerCase().endsWith(".py")) JPopupMenu pop = textArea.getPopupMenu(); pop.addSeparator(); JMenuItem item = new JMenuItem("Font"); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JFontChooser fontChooser = new JFontChooser(); - fontChooser.setSelectedFont(textArea.getFont()); - fontChooser.setSelectedFontSize(textArea.getFont().getSize()); - int result = fontChooser.showDialog(mainWindow); - if (result == JFontChooser.OK_OPTION) { - textArea.setFont(fontChooser.getSelectedFont()); - luytenPrefs.setFont_size(fontChooser.getSelectedFontSize()); - } - } - }); + item.addActionListener(e -> { + JFontChooser fontChooser = new JFontChooser(); + fontChooser.setSelectedFont(textArea.getFont()); + fontChooser.setSelectedFontSize(textArea.getFont().getSize()); + int result = fontChooser.showDialog(mainWindow); + if (result == JFontChooser.OK_OPTION) { + textArea.setFont(fontChooser.getSelectedFont()); + luytenPrefs.setFont_size(fontChooser.getSelectedFontSize()); + } + }); pop.add(item); textArea.setPopupMenu(pop); @@ -172,49 +169,43 @@ public void actionPerformed(ActionEvent e) { scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); final JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); if (verticalScrollbar != null) { - verticalScrollbar.addAdjustmentListener(new AdjustmentListener() { - @Override - public void adjustmentValueChanged(AdjustmentEvent e) { - String content = textArea.getText(); - if (content == null || content.length() == 0) - return; - int scrollValue = verticalScrollbar.getValue() - verticalScrollbar.getMinimum(); - int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); - if (scrollMax < 1 || scrollValue < 0 || scrollValue > scrollMax) - return; - lastScrollPercent = (((double) scrollValue) / ((double) scrollMax)); - } - }); + verticalScrollbar.addAdjustmentListener(e -> { + String content = textArea.getText(); + if (content == null || content.length() == 0) + return; + int scrollValue = verticalScrollbar.getValue() - verticalScrollbar.getMinimum(); + int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); + if (scrollMax < 1 || scrollValue < 0 || scrollValue > scrollMax) + return; + lastScrollPercent = (((double) scrollValue) / ((double) scrollMax)); + }); } textArea.setHyperlinksEnabled(true); textArea.setLinkScanningMask(Keymap.ctrlDownModifier()); - textArea.setLinkGenerator(new LinkGenerator() { - @Override - public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, final int offs) { - final String uniqueStr = getUniqueStrForOffset(offs); - final Integer selectionFrom = getSelectionFromForOffset(offs); - if (uniqueStr != null && selectionFrom != null) { - return new LinkGeneratorResult() { - @Override - public HyperlinkEvent execute() { - if (isNavigationLinksValid) - onNavigationClicked(uniqueStr); - return null; - } - - @Override - public int getSourceOffset() { - if (isNavigationLinksValid) - return selectionFrom; - return offs; - } - }; - } - return null; - } - }); + textArea.setLinkGenerator((textArea, offs) -> { + final String uniqueStr = getUniqueStrForOffset(offs); + final Integer selectionFrom = getSelectionFromForOffset(offs); + if (uniqueStr != null && selectionFrom != null) { + return new LinkGeneratorResult() { + @Override + public HyperlinkEvent execute() { + if (isNavigationLinksValid) + onNavigationClicked(uniqueStr); + return null; + } + + @Override + public int getSourceOffset() { + if (isNavigationLinksValid) + return selectionFrom; + return offs; + } + }; + } + return null; + }); /* * Add Ctrl+Wheel Zoom for Text Size Removes all standard listeners and @@ -224,176 +215,173 @@ public int getSourceOffset() { scrollPane.removeMouseWheelListener(listeners); } - scrollPane.addMouseWheelListener(new MouseWheelListener() { - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - if (e.getWheelRotation() == 0) { - // Nothing to do here. This happens when scroll event is delivered from a touchbar - // or MagicMouse. There's getPreciseWheelRotation, however it looks like there's no - // trivial and consistent way to use that - // See https://github.com/JetBrains/intellij-community/blob/21c99af7c78fc82aefc4d05646389f4991b08b38/bin/idea.properties#L133-L156 - return; - } - - if ((e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0) { - Font font = textArea.getFont(); - int size = font.getSize(); - if (e.getWheelRotation() > 0) { - textArea.setFont(new Font(font.getName(), font.getStyle(), --size >= 8 ? --size : 8)); - } else { - textArea.setFont(new Font(font.getName(), font.getStyle(), ++size)); - } - luytenPrefs.setFont_size(size); - } else { - if (scrollPane.isWheelScrollingEnabled() && e.getWheelRotation() != 0) { - JScrollBar toScroll = scrollPane.getVerticalScrollBar(); - int direction = e.getWheelRotation() < 0 ? -1 : 1; - int orientation = SwingConstants.VERTICAL; - if (toScroll == null || !toScroll.isVisible()) { - toScroll = scrollPane.getHorizontalScrollBar(); - if (toScroll == null || !toScroll.isVisible()) { - return; - } - orientation = SwingConstants.HORIZONTAL; - } - e.consume(); - - if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { - JViewport vp = scrollPane.getViewport(); - if (vp == null) { - return; - } - Component comp = vp.getView(); - int units = Math.abs(e.getUnitsToScroll()); - boolean limitScroll = Math.abs(e.getWheelRotation()) == 1; - Object fastWheelScroll = toScroll.getClientProperty("JScrollBar.fastWheelScrolling"); - if (Boolean.TRUE == fastWheelScroll && comp instanceof Scrollable) { - Scrollable scrollComp = (Scrollable) comp; - Rectangle viewRect = vp.getViewRect(); - int startingX = viewRect.x; - boolean leftToRight = comp.getComponentOrientation().isLeftToRight(); - int scrollMin = toScroll.getMinimum(); - int scrollMax = toScroll.getMaximum() - toScroll.getModel().getExtent(); - - if (limitScroll) { - int blockIncr = scrollComp.getScrollableBlockIncrement(viewRect, orientation, - direction); - if (direction < 0) { - scrollMin = Math.max(scrollMin, toScroll.getValue() - blockIncr); - } else { - scrollMax = Math.min(scrollMax, toScroll.getValue() + blockIncr); - } - } - - for (int i = 0; i < units; i++) { - int unitIncr = scrollComp.getScrollableUnitIncrement(viewRect, orientation, - direction); - if (orientation == SwingConstants.VERTICAL) { - if (direction < 0) { - viewRect.y -= unitIncr; - if (viewRect.y <= scrollMin) { - viewRect.y = scrollMin; - break; - } - } else { // (direction > 0 - viewRect.y += unitIncr; - if (viewRect.y >= scrollMax) { - viewRect.y = scrollMax; - break; - } - } - } else { - if ((leftToRight && direction < 0) || (!leftToRight && direction > 0)) { - viewRect.x -= unitIncr; - if (leftToRight) { - if (viewRect.x < scrollMin) { - viewRect.x = scrollMin; - break; - } - } - } else { - viewRect.x += unitIncr; - if (leftToRight) { - if (viewRect.x > scrollMax) { - viewRect.x = scrollMax; - break; - } - } - } - } - } - if (orientation == SwingConstants.VERTICAL) { - toScroll.setValue(viewRect.y); - } else { - if (leftToRight) { - toScroll.setValue(viewRect.x); - } else { - int newPos = toScroll.getValue() - (viewRect.x - startingX); - if (newPos < scrollMin) { - newPos = scrollMin; - } else if (newPos > scrollMax) { - newPos = scrollMax; - } - toScroll.setValue(newPos); - } - } - } else { - int delta; - int limit = -1; - - if (limitScroll) { - if (direction < 0) { - limit = toScroll.getValue() - toScroll.getBlockIncrement(direction); - } else { - limit = toScroll.getValue() + toScroll.getBlockIncrement(direction); - } - } - - for (int i = 0; i < units; i++) { - if (direction > 0) { - delta = toScroll.getUnitIncrement(direction); - } else { - delta = -toScroll.getUnitIncrement(direction); - } - int oldValue = toScroll.getValue(); - int newValue = oldValue + delta; - if (delta > 0 && newValue < oldValue) { - newValue = toScroll.getMaximum(); - } else if (delta < 0 && newValue > oldValue) { - newValue = toScroll.getMinimum(); - } - if (oldValue == newValue) { - break; - } - if (limitScroll && i > 0) { - assert limit != -1; - if ((direction < 0 && newValue < limit) - || (direction > 0 && newValue > limit)) { - break; - } - } - toScroll.setValue(newValue); - } - - } - } else if (e.getScrollType() == MouseWheelEvent.WHEEL_BLOCK_SCROLL) { - int oldValue = toScroll.getValue(); - int blockIncrement = toScroll.getBlockIncrement(direction); - int delta = blockIncrement * ((direction > 0) ? +1 : -1); - int newValue = oldValue + delta; - if (delta > 0 && newValue < oldValue) { - newValue = toScroll.getMaximum(); - } else if (delta < 0 && newValue > oldValue) { - newValue = toScroll.getMinimum(); - } - toScroll.setValue(newValue); - } - } - } - - e.consume(); - } - }); + scrollPane.addMouseWheelListener(e -> { + if (e.getWheelRotation() == 0) { + // Nothing to do here. This happens when scroll event is delivered from a touchbar + // or MagicMouse. There's getPreciseWheelRotation, however it looks like there's no + // trivial and consistent way to use that + // See https://github.com/JetBrains/intellij-community/blob/21c99af7c78fc82aefc4d05646389f4991b08b38/bin/idea.properties#L133-L156 + return; + } + + if ((e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0) { + Font font = textArea.getFont(); + int size = font.getSize(); + if (e.getWheelRotation() > 0) { + textArea.setFont(new Font(font.getName(), font.getStyle(), --size >= 8 ? --size : 8)); + } else { + textArea.setFont(new Font(font.getName(), font.getStyle(), ++size)); + } + luytenPrefs.setFont_size(size); + } else { + if (scrollPane.isWheelScrollingEnabled() && e.getWheelRotation() != 0) { + JScrollBar toScroll = scrollPane.getVerticalScrollBar(); + int direction = e.getWheelRotation() < 0 ? -1 : 1; + int orientation = SwingConstants.VERTICAL; + if (toScroll == null || !toScroll.isVisible()) { + toScroll = scrollPane.getHorizontalScrollBar(); + if (toScroll == null || !toScroll.isVisible()) { + return; + } + orientation = SwingConstants.HORIZONTAL; + } + e.consume(); + + if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { + JViewport vp = scrollPane.getViewport(); + if (vp == null) { + return; + } + Component comp = vp.getView(); + int units = Math.abs(e.getUnitsToScroll()); + boolean limitScroll = Math.abs(e.getWheelRotation()) == 1; + Object fastWheelScroll = toScroll.getClientProperty("JScrollBar.fastWheelScrolling"); + if (Boolean.TRUE == fastWheelScroll && comp instanceof Scrollable) { + Scrollable scrollComp = (Scrollable) comp; + Rectangle viewRect = vp.getViewRect(); + int startingX = viewRect.x; + boolean leftToRight = comp.getComponentOrientation().isLeftToRight(); + int scrollMin = toScroll.getMinimum(); + int scrollMax = toScroll.getMaximum() - toScroll.getModel().getExtent(); + + if (limitScroll) { + int blockIncr = scrollComp.getScrollableBlockIncrement(viewRect, orientation, + direction); + if (direction < 0) { + scrollMin = Math.max(scrollMin, toScroll.getValue() - blockIncr); + } else { + scrollMax = Math.min(scrollMax, toScroll.getValue() + blockIncr); + } + } + + for (int i = 0; i < units; i++) { + int unitIncr = scrollComp.getScrollableUnitIncrement(viewRect, orientation, + direction); + if (orientation == SwingConstants.VERTICAL) { + if (direction < 0) { + viewRect.y -= unitIncr; + if (viewRect.y <= scrollMin) { + viewRect.y = scrollMin; + break; + } + } else { // (direction > 0 + viewRect.y += unitIncr; + if (viewRect.y >= scrollMax) { + viewRect.y = scrollMax; + break; + } + } + } else { + if ((leftToRight && direction < 0) || (!leftToRight && direction > 0)) { + viewRect.x -= unitIncr; + if (leftToRight) { + if (viewRect.x < scrollMin) { + viewRect.x = scrollMin; + break; + } + } + } else { + viewRect.x += unitIncr; + if (leftToRight) { + if (viewRect.x > scrollMax) { + viewRect.x = scrollMax; + break; + } + } + } + } + } + if (orientation == SwingConstants.VERTICAL) { + toScroll.setValue(viewRect.y); + } else { + if (leftToRight) { + toScroll.setValue(viewRect.x); + } else { + int newPos = toScroll.getValue() - (viewRect.x - startingX); + if (newPos < scrollMin) { + newPos = scrollMin; + } else if (newPos > scrollMax) { + newPos = scrollMax; + } + toScroll.setValue(newPos); + } + } + } else { + int delta; + int limit = -1; + + if (limitScroll) { + if (direction < 0) { + limit = toScroll.getValue() - toScroll.getBlockIncrement(direction); + } else { + limit = toScroll.getValue() + toScroll.getBlockIncrement(direction); + } + } + + for (int i = 0; i < units; i++) { + if (direction > 0) { + delta = toScroll.getUnitIncrement(direction); + } else { + delta = -toScroll.getUnitIncrement(direction); + } + int oldValue = toScroll.getValue(); + int newValue = oldValue + delta; + if (delta > 0 && newValue < oldValue) { + newValue = toScroll.getMaximum(); + } else if (delta < 0 && newValue > oldValue) { + newValue = toScroll.getMinimum(); + } + if (oldValue == newValue) { + break; + } + if (limitScroll && i > 0) { + assert limit != -1; + if ((direction < 0 && newValue < limit) + || (direction > 0 && newValue > limit)) { + break; + } + } + toScroll.setValue(newValue); + } + + } + } else if (e.getScrollType() == MouseWheelEvent.WHEEL_BLOCK_SCROLL) { + int oldValue = toScroll.getValue(); + int blockIncrement = toScroll.getBlockIncrement(direction); + int delta = blockIncrement * ((direction > 0) ? +1 : -1); + int newValue = oldValue + delta; + if (delta > 0 && newValue < oldValue) { + newValue = toScroll.getMaximum(); + } else if (delta < 0 && newValue > oldValue) { + newValue = toScroll.getMinimum(); + } + toScroll.setValue(newValue); + } + } + } + + e.consume(); + }); textArea.addMouseMotionListener(new MouseMotionAdapter() { private boolean isLinkLabelPrev = false; @@ -493,62 +481,48 @@ private void decompileWithNavigationLinks() { private void setContentPreserveLastScrollPosition(final String content) { final Double scrollPercent = lastScrollPercent; if (scrollPercent != null && initialNavigationLink == null) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - textArea.setText(content); - restoreScrollPosition(scrollPercent); - } - }); + SwingUtilities.invokeLater(() -> { + textArea.setText(content); + restoreScrollPosition(scrollPercent); + }); } else { textArea.setText(content); } } private void restoreScrollPosition(final double position) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); - if (verticalScrollbar == null) - return; - int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); - long newScrollValue = Math.round(position * scrollMax) + verticalScrollbar.getMinimum(); - if (newScrollValue < verticalScrollbar.getMinimum()) - newScrollValue = verticalScrollbar.getMinimum(); - if (newScrollValue > verticalScrollbar.getMaximum()) - newScrollValue = verticalScrollbar.getMaximum(); - verticalScrollbar.setValue((int) newScrollValue); - } - }); + SwingUtilities.invokeLater(() -> { + JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); + if (verticalScrollbar == null) + return; + int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); + long newScrollValue = Math.round(position * scrollMax) + verticalScrollbar.getMinimum(); + if (newScrollValue < verticalScrollbar.getMinimum()) + newScrollValue = verticalScrollbar.getMinimum(); + if (newScrollValue > verticalScrollbar.getMaximum()) + newScrollValue = verticalScrollbar.getMaximum(); + verticalScrollbar.setValue((int) newScrollValue); + }); } private void enableLinks() { if (initialNavigationLink != null) { doEnableLinks(); } else { - new Thread(new Runnable() { - @Override - public void run() { - try { - isWaitForLinksCursor = true; - doEnableLinks(); - } finally { - isWaitForLinksCursor = false; - resetCursor(); - } - } - }).start(); + new Thread(() -> { + try { + isWaitForLinksCursor = true; + doEnableLinks(); + } finally { + isWaitForLinksCursor = false; + resetCursor(); + } + }).start(); } } private void resetCursor() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - }); + SwingUtilities.invokeLater(() -> textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR))); } private void doEnableLinks() { @@ -713,32 +687,29 @@ private void doLocalNavigation(Selection selection) { } private void scrollToSelection(final int selectionBeginningOffset) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - try { - int fullHeight = textArea.getBounds().height; - int viewportHeight = textArea.getVisibleRect().height; - int viewportLineCount = viewportHeight / textArea.getLineHeight(); - int selectionLineNum = textArea.getLineOfOffset(selectionBeginningOffset); - int upperMarginToScroll = Math.round(viewportLineCount * 0.29f); - int upperLineToSet = selectionLineNum - upperMarginToScroll; - int currentUpperLine = textArea.getVisibleRect().y / textArea.getLineHeight(); - - if (selectionLineNum <= currentUpperLine + 2 - || selectionLineNum >= currentUpperLine + viewportLineCount - 4) { - Rectangle rectToScroll = new Rectangle(); - rectToScroll.x = 0; - rectToScroll.width = 1; - rectToScroll.y = Math.max(upperLineToSet * textArea.getLineHeight(), 0); - rectToScroll.height = Math.min(viewportHeight, fullHeight - rectToScroll.y); - textArea.scrollRectToVisible(rectToScroll); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }); + SwingUtilities.invokeLater(() -> { + try { + int fullHeight = textArea.getBounds().height; + int viewportHeight = textArea.getVisibleRect().height; + int viewportLineCount = viewportHeight / textArea.getLineHeight(); + int selectionLineNum = textArea.getLineOfOffset(selectionBeginningOffset); + int upperMarginToScroll = Math.round(viewportLineCount * 0.29f); + int upperLineToSet = selectionLineNum - upperMarginToScroll; + int currentUpperLine = textArea.getVisibleRect().y / textArea.getLineHeight(); + + if (selectionLineNum <= currentUpperLine + 2 + || selectionLineNum >= currentUpperLine + viewportLineCount - 4) { + Rectangle rectToScroll = new Rectangle(); + rectToScroll.x = 0; + rectToScroll.width = 1; + rectToScroll.y = Math.max(upperLineToSet * textArea.getLineHeight(), 0); + rectToScroll.height = Math.min(viewportHeight, fullHeight - rectToScroll.y); + textArea.scrollRectToVisible(rectToScroll); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }); } private void onOutboundNavigationRequest(String uniqueStr) { From 617c5e1b7142a7f6a545e39214e2ddf78d8f26b7 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sat, 9 Oct 2021 10:28:08 +0200 Subject: [PATCH 12/26] Optimize imports --- src/us/deathmarine/luyten/FileSaver.java | 2 -- src/us/deathmarine/luyten/OpenFile.java | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/us/deathmarine/luyten/FileSaver.java b/src/us/deathmarine/luyten/FileSaver.java index c88901e3..5b0f1112 100644 --- a/src/us/deathmarine/luyten/FileSaver.java +++ b/src/us/deathmarine/luyten/FileSaver.java @@ -1,7 +1,5 @@ package us.deathmarine.luyten; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedOutputStream; diff --git a/src/us/deathmarine/luyten/OpenFile.java b/src/us/deathmarine/luyten/OpenFile.java index 1d8a67a2..a3d295a2 100644 --- a/src/us/deathmarine/luyten/OpenFile.java +++ b/src/us/deathmarine/luyten/OpenFile.java @@ -4,10 +4,6 @@ import java.awt.Cursor; import java.awt.Font; import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.AdjustmentEvent; -import java.awt.event.AdjustmentListener; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseWheelEvent; @@ -30,7 +26,6 @@ import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; -import org.fife.ui.rsyntaxtextarea.LinkGenerator; import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; From 8514b8872f2aa38bc81ea2e8497b944fce14f3e1 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sat, 9 Oct 2021 11:21:31 +0200 Subject: [PATCH 13/26] Update procyon link --- src/us/deathmarine/luyten/MainMenuBar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/us/deathmarine/luyten/MainMenuBar.java b/src/us/deathmarine/luyten/MainMenuBar.java index 3a110062..b603b0d5 100644 --- a/src/us/deathmarine/luyten/MainMenuBar.java +++ b/src/us/deathmarine/luyten/MainMenuBar.java @@ -478,7 +478,7 @@ private void buildHelpMenu(JMenu helpMenu) { pane.add(new JLabel("FisheyLP, and Syquel")); pane.add(new JLabel(" ")); pane.add(new JLabel("Powered By:")); - String procyon = "https://bitbucket.org/mstrobel/procyon"; + String procyon = "https://github.com/mstrobel/procyon"; link = new JLabel("" + procyon + ""); link.setCursor(new Cursor(Cursor.HAND_CURSOR)); link.addMouseListener(new LinkListener(procyon, link)); From c3b95a15a15c84a2d013c4f4830272c059a5a629 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sat, 9 Oct 2021 11:45:08 +0200 Subject: [PATCH 14/26] Allow closing tabs via middle mouse button --- src/us/deathmarine/luyten/MainWindow.java | 1 - src/us/deathmarine/luyten/Model.java | 57 ++++++++++------------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java index cc9c04c9..2e52e8a4 100644 --- a/src/us/deathmarine/luyten/MainWindow.java +++ b/src/us/deathmarine/luyten/MainWindow.java @@ -180,7 +180,6 @@ public Model loadNewFile(final File file) { if (jarsTabbedPane.getTabCount() == 0) { createDefaultTab(); } - return null; }); jarsTabbedPane.setTabComponentAt(index, tabUI); if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) != -1 && jarsTabbedPane.getTabCount() > 1) { diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index ffc6863e..5cf6a06d 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -13,7 +13,6 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashSet; @@ -22,7 +21,6 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import java.util.concurrent.Callable; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -209,7 +207,6 @@ private void addOrSwitchToTab(final OpenFile open) { Tab ct = new Tab(title, () -> { int index1 = house.indexOfTab(title); closeOpenTab(index1); - return null; }); house.setTabComponentAt(index, ct); } else { @@ -579,20 +576,34 @@ public static class Tab extends JPanel { private final JLabel closeButton = new JLabel(new ImageIcon( Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/icon_close.png")))); - public Tab(String title, final Callable onCloseTabAction) { + public Tab(String title, final Runnable onCloseTabAction) { super(new GridBagLayout()); this.setOpaque(false); this.tabTitle = new JLabel(title); this.createTab(); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (onCloseTabAction != null && SwingUtilities.isMiddleMouseButton(e)) { + try { + onCloseTabAction.run(); + } catch (Exception ex) { + Luyten.showExceptionDialog("Exception!", ex); + } + } + } + }); + closeButton.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - try { - if (onCloseTabAction != null) { - onCloseTabAction.call(); + if (onCloseTabAction != null) { + try { + onCloseTabAction.run(); + } catch (Exception ex) { + Luyten.showExceptionDialog("Exception!", ex); } - } catch (Exception ex) { - Luyten.showExceptionDialog("Exception!", ex); } } }); @@ -611,20 +622,6 @@ public void createTab() { } } - private class CloseTab extends MouseAdapter { - String title; - - public CloseTab(String title) { - this.title = title; - } - - @Override - public void mouseClicked(MouseEvent e) { - int index = house.indexOfTab(title); - closeOpenTab(index); - } - } - public DefaultMutableTreeNode loadNodesByNames(DefaultMutableTreeNode node, List originalNames) { List args = new ArrayList<>(); for (String originalName : originalNames) { @@ -753,7 +750,7 @@ private void buildDirectoryTreeFromMass(List mass) { TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); List sort = new ArrayList<>(); - Collections.sort(mass, String.CASE_INSENSITIVE_ORDER); + mass.sort(String.CASE_INSENSITIVE_ORDER); for (String m : mass) if (m.contains("META-INF") && !sort.contains(m)) sort.add(m); @@ -764,8 +761,8 @@ private void buildDirectoryTreeFromMass(List mass) { } } List packs = Arrays.asList(set.toArray(new String[] {})); - Collections.sort(packs, String.CASE_INSENSITIVE_ORDER); - Collections.sort(packs, (o1, o2) -> o2.split("/").length - o1.split("/").length); + packs.sort(String.CASE_INSENSITIVE_ORDER); + packs.sort((o1, o2) -> o2.split("/").length - o1.split("/").length); for (String pack : packs) for (String m : mass) if (!m.contains("META-INF") && m.contains(pack) && !m.replace(pack, "").contains("/")) @@ -788,12 +785,8 @@ private void buildFlatTreeFromMass(List mass) { HashSet classContainingPackageRoots = new HashSet<>(); // (assertion: mass does not contain null elements) - Comparator sortByFileExtensionsComparator = (o1, o2) -> { - int comp = o1.replaceAll("[^.]*\\.", "").compareTo(o2.replaceAll("[^.]*\\.", "")); - if (comp != 0) - return comp; - return o1.compareTo(o2); - }; + Comparator sortByFileExtensionsComparator = Comparator.comparing( + (String o) -> o.replaceAll("[^.]*\\.", "")).thenComparing(o -> o); for (String entry : mass) { String packagePath = ""; From 09b2c0405a382d62708f6441fa0b6574020b40b2 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sat, 9 Oct 2021 12:36:15 +0200 Subject: [PATCH 15/26] Disable closing tabs via middle mouse button for now --- src/us/deathmarine/luyten/Model.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index 5cf6a06d..b02bd591 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -582,7 +582,8 @@ public Tab(String title, final Runnable onCloseTabAction) { this.tabTitle = new JLabel(title); this.createTab(); - addMouseListener(new MouseAdapter() { + // TODO: Disables tab switching... Is there a workaround? + /*addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (onCloseTabAction != null && SwingUtilities.isMiddleMouseButton(e)) { @@ -593,7 +594,7 @@ public void mouseClicked(MouseEvent e) { } } } - }); + });*/ closeButton.addMouseListener(new MouseAdapter() { @Override From fa559dbf7869254bfdd1a5b1ac8048d099b6cb5f Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sat, 9 Oct 2021 20:36:44 +0200 Subject: [PATCH 16/26] Fix resource leak --- src/us/deathmarine/luyten/MainWindow.java | 10 ++++++---- src/us/deathmarine/luyten/Model.java | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java index 2e52e8a4..5b25aa44 100644 --- a/src/us/deathmarine/luyten/MainWindow.java +++ b/src/us/deathmarine/luyten/MainWindow.java @@ -165,7 +165,7 @@ public Model loadNewFile(final File file) { jarsTabbedPane.remove(index); } - Model jarModel = new Model(this); + final Model jarModel = new Model(this); jarModel.loadFile(file); jarModels.put(file.getAbsolutePath(), jarModel); jarsTabbedPane.addTab(file.getName(), jarModel); @@ -177,6 +177,7 @@ public Model loadNewFile(final File file) { int index1 = jarsTabbedPane.indexOfTab(tabName); jarModels.remove(file.getAbsolutePath()); jarsTabbedPane.remove(index1); + jarModel.closeFile(); if (jarsTabbedPane.getTabCount() == 0) { createDefaultTab(); } @@ -372,10 +373,11 @@ public void onFileDropped(File file) { } } - public void onFileLoadEnded(File file, boolean isSuccess) { + public void onFileLoadEnded() { try { - if (file != null && isSuccess) { - this.setTitle(TITLE + " - " + file.getName()); + Model model = getSelectedModel(); + if (model != null && model.getFileName() != null) { + this.setTitle(TITLE + " - " + model.getFileName()); } else { this.setTitle(TITLE); } diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index b02bd591..3afe0b36 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -72,7 +72,7 @@ public class Model extends JSplitPane { private static final long MAX_UNPACKED_FILE_SIZE_BYTES = 10_000_000L; private final LuytenTypeLoader typeLoader = new LuytenTypeLoader(); - private MetadataSystem metadataSystem = new MetadataSystem(typeLoader); + private final MetadataSystem metadataSystem = new MetadataSystem(typeLoader); private final JTree tree; public JTabbedPane house; @@ -733,7 +733,7 @@ public void loadTree() { getLabel().setText("Cannot open: " + file.getName()); closeFile(); } finally { - mainWindow.onFileLoadEnded(file, open); + mainWindow.onFileLoadEnded(); bar.setVisible(false); } }).start(); @@ -866,19 +866,17 @@ public void closeFile() { co.close(); } - final State oldState = state; - Model.this.state = null; - if (oldState != null) { - Closer.tryClose(oldState); + if (state != null) { + Closer.tryClose(state); } + state = null; hmap.clear(); tree.setModel(new DefaultTreeModel(null)); - metadataSystem = new MetadataSystem(typeLoader); file = null; treeExpansionState = null; open = false; - mainWindow.onFileLoadEnded(file, open); + mainWindow.onFileLoadEnded(); } public void changeTheme(String xml) { @@ -1023,4 +1021,8 @@ public void setTheme(Theme theme) { public MetadataSystem getMetadataSystem() { return metadataSystem; } + + public String getFileName() { + return file == null ? null : getName(file.getName()); + } } From bd596b5990299e94a21b9c8b14670f6db554d10a Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sun, 10 Oct 2021 00:00:53 +0200 Subject: [PATCH 17/26] Fix rare crash --- src/us/deathmarine/luyten/Model.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index 3afe0b36..a83ed372 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -220,6 +220,9 @@ private void addOrSwitchToTab(final OpenFile open) { } public void closeOpenTab(int index) { + if (index < 0 || index >= house.getComponentCount()) + return; + RTextScrollPane co = (RTextScrollPane) house.getComponentAt(index); RSyntaxTextArea pane = (RSyntaxTextArea) co.getViewport().getView(); OpenFile open = null; From a8b7e4ab8839cdb9983111abac9f723ef2deb646 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sun, 10 Oct 2021 00:01:09 +0200 Subject: [PATCH 18/26] Ignore exception on shutdown --- src/us/deathmarine/luyten/Luyten.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/us/deathmarine/luyten/Luyten.java b/src/us/deathmarine/luyten/Luyten.java index 3f5ec489..d8d42d91 100644 --- a/src/us/deathmarine/luyten/Luyten.java +++ b/src/us/deathmarine/luyten/Luyten.java @@ -8,6 +8,7 @@ import java.io.*; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketException; import java.net.URI; import java.util.concurrent.atomic.AtomicReference; import java.util.List; @@ -103,6 +104,8 @@ private static void launchServer() { dis.close(); socket.close(); } + } catch (SocketException ignored) { + // Ignore exception on shutdown } catch (IOException e) { // Client showExceptionDialog("Exception", e); } From ff1ca19877aa8c9665e8d4fdc077c983740a0681 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sun, 10 Oct 2021 00:53:01 +0200 Subject: [PATCH 19/26] Fix maximize expression --- src/us/deathmarine/luyten/WindowPosition.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/us/deathmarine/luyten/WindowPosition.java b/src/us/deathmarine/luyten/WindowPosition.java index c689cae7..cfeb18c4 100644 --- a/src/us/deathmarine/luyten/WindowPosition.java +++ b/src/us/deathmarine/luyten/WindowPosition.java @@ -15,7 +15,7 @@ public class WindowPosition { private int windowY; public void readPositionFromWindow(JFrame window) { - isFullScreen = (window.getExtendedState() == JFrame.MAXIMIZED_BOTH); + isFullScreen = (window.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH; if (!isFullScreen) { this.readPositionFromComponent(window); } From ecee126955d7b22a7022890af6b87924d8aefc11 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sun, 10 Oct 2021 01:03:28 +0200 Subject: [PATCH 20/26] Fix Regex with some workaround --- src/us/deathmarine/luyten/DecompilerLinkProvider.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/us/deathmarine/luyten/DecompilerLinkProvider.java b/src/us/deathmarine/luyten/DecompilerLinkProvider.java index 1c707d32..df13e3b0 100644 --- a/src/us/deathmarine/luyten/DecompilerLinkProvider.java +++ b/src/us/deathmarine/luyten/DecompilerLinkProvider.java @@ -362,8 +362,10 @@ public String getLinkDescription(String uniqueStr) { } private String erasePackageInfoFromDesc(String desc) { - String limiters = "\\(\\)<>\\[]\\?\\s,"; - desc = desc.replaceAll("(?<=[^" + limiters + "]*)([^" + limiters + "]*)\\.", ""); + // Use {0,1024} instead of * as it can't be used together with a lookbehind. + // If errors occur, increase this limit. + String limiters = "()<>\\[\\]?\\s,"; + desc = desc.replaceAll("(?<=[^" + limiters + "]{0,1024})([^" + limiters + "]*)\\.", ""); return desc; } From 589ed3c0d9ab33bbefc9d0ded2bc8ef951ace9da Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Thu, 6 Jan 2022 16:47:31 +0100 Subject: [PATCH 21/26] Update dependencies --- pom.xml | 26 +++++++-- src/us/deathmarine/luyten/ConfigSaver.java | 4 +- src/us/deathmarine/luyten/MainMenuBar.java | 4 +- src/us/deathmarine/luyten/OpenFile.java | 67 +++++++--------------- 4 files changed, 46 insertions(+), 55 deletions(-) diff --git a/pom.xml b/pom.xml index 2d353fd3..18b41c67 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.fifesoft rsyntaxtextarea - 3.1.3 + 3.1.5 com.apple @@ -97,7 +97,7 @@ --> maven-compiler-plugin - 3.1 + 3.8.1 1.8 1.8 @@ -106,7 +106,7 @@ org.apache.maven.plugins maven-shade-plugin - 2.4.2 + 3.2.4 package @@ -120,6 +120,20 @@ ${project.groupId}.${project.artifactId}.Luyten + + + *:* + + module-info.class + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/*LICENSE* + META-INF/*NOTICE* + META-INF/MANIFEST.MF + + + @@ -152,7 +166,7 @@ com.akathist.maven.plugins.launch4j launch4j-maven-plugin - 1.7.4 + 2.1.2 l4j-gui @@ -193,7 +207,7 @@ com.googlecode.maven-download-plugin download-maven-plugin - 1.3.0 + 1.6.7 process-resources @@ -212,7 +226,7 @@ maven-antrun-plugin - 1.7 + 3.0.0 jarbundler-gui diff --git a/src/us/deathmarine/luyten/ConfigSaver.java b/src/us/deathmarine/luyten/ConfigSaver.java index 77b41e75..bdce7020 100644 --- a/src/us/deathmarine/luyten/ConfigSaver.java +++ b/src/us/deathmarine/luyten/ConfigSaver.java @@ -125,7 +125,7 @@ private LuytenPreferences loadLuytenPreferences(Preferences prefs) throws Except field.setBoolean(newLuytenPrefs, prefs.getBoolean(prefId, defaultBool)); } else if (field.getType() == Integer.class || field.getType() == int.class) { - Integer defaultInt = (Integer) (defaultVal == null ? new Integer(0) : defaultVal); + Integer defaultInt = (Integer) (defaultVal == null ? Integer.valueOf(0) : defaultVal); field.setInt(newLuytenPrefs, prefs.getInt(prefId, defaultInt)); } } @@ -180,7 +180,7 @@ private void saveLuytenPreferences(Preferences prefs) throws Exception { prefs.putBoolean(prefId, (Boolean) (value == null ? Boolean.FALSE : value)); } else if (field.getType() == Integer.class || field.getType() == int.class) { - prefs.putInt(prefId, (Integer) (value == null ? new Integer(0) : value)); + prefs.putInt(prefId, (Integer) (value == null ? Integer.valueOf(0) : value)); } } } diff --git a/src/us/deathmarine/luyten/MainMenuBar.java b/src/us/deathmarine/luyten/MainMenuBar.java index b603b0d5..86aa7fef 100644 --- a/src/us/deathmarine/luyten/MainMenuBar.java +++ b/src/us/deathmarine/luyten/MainMenuBar.java @@ -475,7 +475,7 @@ private void buildHelpMenu(JMenu helpMenu) { pane.add(new JLabel("zerdei, toonetown, dstmath")); pane.add(new JLabel("virustotalop, xtrafrancyz,")); pane.add(new JLabel("mbax, quitten, mstrobel,")); - pane.add(new JLabel("FisheyLP, and Syquel")); + pane.add(new JLabel("FisheyLP, Syquel, and ThexXTURBOXx")); pane.add(new JLabel(" ")); pane.add(new JLabel("Powered By:")); String procyon = "https://github.com/mstrobel/procyon"; @@ -490,7 +490,7 @@ private void buildHelpMenu(JMenu helpMenu) { link.setCursor(new Cursor(Cursor.HAND_CURSOR)); link.addMouseListener(new LinkListener(rsyntax, link)); pane.add(link); - pane.add(new JLabel("Version: 3.1.3")); + pane.add(new JLabel("Version: 3.1.5")); pane.add(new JLabel("(c) 2021 Robert Futrell")); pane.add(new JLabel(" ")); JOptionPane.showMessageDialog(null, pane); diff --git a/src/us/deathmarine/luyten/OpenFile.java b/src/us/deathmarine/luyten/OpenFile.java index a3d295a2..fb535214 100644 --- a/src/us/deathmarine/luyten/OpenFile.java +++ b/src/us/deathmarine/luyten/OpenFile.java @@ -8,6 +8,7 @@ import java.awt.event.MouseMotionAdapter; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; +import java.io.File; import java.io.StringWriter; import java.util.Arrays; import java.util.HashSet; @@ -26,6 +27,7 @@ import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; +import org.fife.ui.rsyntaxtextarea.FileTypeUtil; import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; @@ -39,13 +41,15 @@ import com.strobel.decompiler.PlainTextOutput; import com.strobel.decompiler.languages.Languages; -public class OpenFile implements SyntaxConstants { +public class OpenFile { public static final HashSet WELL_KNOWN_TEXT_FILE_EXTENSIONS = new HashSet<>( Arrays.asList(".java", ".xml", ".rss", ".project", ".classpath", ".h", ".c", ".cpp", ".yaml", ".yml", ".ini", ".sql", ".js", ".php", ".php5", ".phtml", ".html", ".htm", ".xhtm", ".xhtml", ".lua", ".bat", ".pl", ".sh", ".css", ".json", ".txt", ".rb", ".make", ".mak", ".py", ".properties", ".prop")); + private static final FileTypeUtil FILE_TYPE_UTIL = FileTypeUtil.get(); + // navigation links private TreeMap selectionToUniqueStrTreeMap = new TreeMap<>(); private final Map isNavigableCache = new ConcurrentHashMap<>(); @@ -91,50 +95,8 @@ public OpenFile(String name, String path, Theme theme, final MainWindow mainWind textArea.setEditable(false); textArea.setAntiAliasingEnabled(true); textArea.setCodeFoldingEnabled(true); - - if (name.toLowerCase().endsWith(".class") || name.toLowerCase().endsWith(".java")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_JAVA); - else if (name.toLowerCase().endsWith(".xml") || name.toLowerCase().endsWith(".rss") - || name.toLowerCase().endsWith(".project") || name.toLowerCase().endsWith(".classpath")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_XML); - else if (name.toLowerCase().endsWith(".h") || name.toLowerCase().endsWith(".c")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_C); - else if (name.toLowerCase().endsWith(".cpp")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_CPLUSPLUS); - else if (name.toLowerCase().endsWith(".sql")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_SQL); - else if (name.toLowerCase().endsWith(".js")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_JAVASCRIPT); - else if (name.toLowerCase().endsWith(".php") || name.toLowerCase().endsWith(".php5") - || name.toLowerCase().endsWith(".phtml")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_PHP); - else if (name.toLowerCase().endsWith(".html") || name.toLowerCase().endsWith(".htm") - || name.toLowerCase().endsWith(".xhtm") || name.toLowerCase().endsWith(".xhtml")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_HTML); - else if (name.toLowerCase().endsWith(".lua")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_LUA); - else if (name.toLowerCase().endsWith(".bat")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_WINDOWS_BATCH); - else if (name.toLowerCase().endsWith(".pl")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_PERL); - else if (name.toLowerCase().endsWith(".sh")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_UNIX_SHELL); - else if (name.toLowerCase().endsWith(".css")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_CSS); - else if (name.toLowerCase().endsWith(".json")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_JSON); - else if (name.toLowerCase().endsWith(".ini")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_INI); - else if (name.toLowerCase().endsWith(".yaml") || name.toLowerCase().endsWith(".yml")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_YAML); - else if (name.toLowerCase().endsWith(".rb")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_RUBY); - else if (name.toLowerCase().endsWith(".make") || name.toLowerCase().endsWith(".mak")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_MAKEFILE); - else if (name.toLowerCase().endsWith(".py")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_PYTHON); - else - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_NONE); + + setLanguage(textArea, name); scrollPane = new RTextScrollPane(textArea, true); scrollPane.setIconRowHeaderEnabled(true); @@ -431,8 +393,23 @@ private String createLinkLabel(MouseEvent e) { }); } + public static void setLanguage(RSyntaxTextArea area, String fileName) { + // Hard code class -> Java mapping + if (fileName.toLowerCase().endsWith(".class")) { + area.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); + return; + } + + String type = FILE_TYPE_UTIL.guessContentType(new File(fileName)); + if (type == null || type.equals(SyntaxConstants.SYNTAX_STYLE_NONE)) { + type = FILE_TYPE_UTIL.guessContentType(area); + } + area.setSyntaxEditingStyle(type); + } + public void setContent(String content) { textArea.setText(content); + setLanguage(textArea, name); } public void decompile() { From 5b9482536e933a6ce5f42598bc1d241d1b921fd6 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 7 Jan 2022 14:38:48 +0100 Subject: [PATCH 22/26] Restructuring --- pom.xml | 547 ++++----- .../us/deathmarine/luyten/CellRenderer.java | 60 + .../java/us/deathmarine/luyten/Closer.java | 24 + .../us/deathmarine/luyten/ConfigSaver.java | 224 ++++ .../luyten/DecompilerLinkProvider.java | 383 ++++++ .../us/deathmarine/luyten/DirPreferences.java | 4 +- .../us/deathmarine/luyten/DropListener.java | 104 ++ .../us/deathmarine/luyten/FileDialog.java | 136 +++ .../luyten/FileEntryNotFoundException.java | 3 +- .../luyten/FileIsBinaryException.java | 3 +- .../java/us/deathmarine/luyten/FileSaver.java | 322 +++++ .../us/deathmarine/luyten/FindAllBox.java | 406 +++++++ .../java/us/deathmarine/luyten/FindBox.java | 234 ++++ .../us/deathmarine/luyten/JFontChooser.java | 755 ++++++++++++ .../us/deathmarine/luyten/JarEntryFilter.java | 83 ++ .../java}/us/deathmarine/luyten/Keymap.java | 2 + .../us/deathmarine/luyten/LinkProvider.java | 22 + .../java/us/deathmarine/luyten/Luyten.java | 269 +++++ .../java/us/deathmarine/luyten/LuytenOsx.java | 33 + .../deathmarine/luyten/LuytenPreferences.java | 89 ++ .../deathmarine/luyten/LuytenTypeLoader.java | 35 + .../us/deathmarine/luyten/MainMenuBar.java | 587 ++++++++++ .../us/deathmarine/luyten/MainWindow.java | 521 +++++++++ .../java/us/deathmarine/luyten/Model.java | 1031 +++++++++++++++++ .../java/us/deathmarine/luyten/OpenFile.java | 747 ++++++++++++ .../us/deathmarine/luyten/RecentFiles.java | 68 ++ .../java/us/deathmarine/luyten/Selection.java | 40 + .../us/deathmarine/luyten/SystemInfo.java | 2 + .../luyten/TooLargeFileException.java | 22 + .../luyten/TreeNodeUserObject.java | 38 + .../java/us/deathmarine/luyten/TreeUtil.java | 82 ++ .../us/deathmarine/luyten/WindowPosition.java | 91 ++ src/{ => main}/resources/Luyten.icns | Bin src/{ => main}/resources/Luyten.png | Bin .../resources}/distfiles/Procyon.License.txt | 62 +- .../distfiles/RSyntaxTextArea.License.txt | 48 +- src/{ => main}/resources/file.png | Bin src/{ => main}/resources/icon_close.png | Bin src/{ => main}/resources/java.png | Bin src/{ => main}/resources/package_obj.png | Bin src/{ => main}/resources/yml.png | Bin src/us/deathmarine/luyten/CellRenderer.java | 52 - src/us/deathmarine/luyten/Closer.java | 22 - src/us/deathmarine/luyten/ConfigSaver.java | 223 ---- .../luyten/DecompilerLinkProvider.java | 382 ------ src/us/deathmarine/luyten/DropListener.java | 102 -- src/us/deathmarine/luyten/FileDialog.java | 133 --- src/us/deathmarine/luyten/FileSaver.java | 323 ------ src/us/deathmarine/luyten/FindAllBox.java | 381 ------ src/us/deathmarine/luyten/FindBox.java | 232 ---- src/us/deathmarine/luyten/JFontChooser.java | 769 ------------ src/us/deathmarine/luyten/JarEntryFilter.java | 82 -- src/us/deathmarine/luyten/LinkProvider.java | 22 - src/us/deathmarine/luyten/Luyten.java | 262 ----- src/us/deathmarine/luyten/LuytenOsx.java | 31 - .../deathmarine/luyten/LuytenPreferences.java | 87 -- .../deathmarine/luyten/LuytenTypeLoader.java | 34 - src/us/deathmarine/luyten/MainMenuBar.java | 587 ---------- src/us/deathmarine/luyten/MainWindow.java | 505 -------- src/us/deathmarine/luyten/Model.java | 1031 ----------------- src/us/deathmarine/luyten/OpenFile.java | 774 ------------- src/us/deathmarine/luyten/RecentFiles.java | 67 -- src/us/deathmarine/luyten/Selection.java | 38 - .../luyten/TooLargeFileException.java | 20 - .../luyten/TreeNodeUserObject.java | 37 - src/us/deathmarine/luyten/TreeUtil.java | 81 -- src/us/deathmarine/luyten/WindowPosition.java | 90 -- 67 files changed, 6712 insertions(+), 6732 deletions(-) create mode 100644 src/main/java/us/deathmarine/luyten/CellRenderer.java create mode 100644 src/main/java/us/deathmarine/luyten/Closer.java create mode 100644 src/main/java/us/deathmarine/luyten/ConfigSaver.java create mode 100644 src/main/java/us/deathmarine/luyten/DecompilerLinkProvider.java rename src/{ => main/java}/us/deathmarine/luyten/DirPreferences.java (98%) create mode 100644 src/main/java/us/deathmarine/luyten/DropListener.java create mode 100644 src/main/java/us/deathmarine/luyten/FileDialog.java rename src/{ => main/java}/us/deathmarine/luyten/FileEntryNotFoundException.java (56%) rename src/{ => main/java}/us/deathmarine/luyten/FileIsBinaryException.java (55%) create mode 100644 src/main/java/us/deathmarine/luyten/FileSaver.java create mode 100644 src/main/java/us/deathmarine/luyten/FindAllBox.java create mode 100644 src/main/java/us/deathmarine/luyten/FindBox.java create mode 100644 src/main/java/us/deathmarine/luyten/JFontChooser.java create mode 100644 src/main/java/us/deathmarine/luyten/JarEntryFilter.java rename src/{ => main/java}/us/deathmarine/luyten/Keymap.java (99%) create mode 100644 src/main/java/us/deathmarine/luyten/LinkProvider.java create mode 100644 src/main/java/us/deathmarine/luyten/Luyten.java create mode 100644 src/main/java/us/deathmarine/luyten/LuytenOsx.java create mode 100644 src/main/java/us/deathmarine/luyten/LuytenPreferences.java create mode 100644 src/main/java/us/deathmarine/luyten/LuytenTypeLoader.java create mode 100644 src/main/java/us/deathmarine/luyten/MainMenuBar.java create mode 100644 src/main/java/us/deathmarine/luyten/MainWindow.java create mode 100644 src/main/java/us/deathmarine/luyten/Model.java create mode 100644 src/main/java/us/deathmarine/luyten/OpenFile.java create mode 100644 src/main/java/us/deathmarine/luyten/RecentFiles.java create mode 100644 src/main/java/us/deathmarine/luyten/Selection.java rename src/{ => main/java}/us/deathmarine/luyten/SystemInfo.java (99%) create mode 100644 src/main/java/us/deathmarine/luyten/TooLargeFileException.java create mode 100644 src/main/java/us/deathmarine/luyten/TreeNodeUserObject.java create mode 100644 src/main/java/us/deathmarine/luyten/TreeUtil.java create mode 100644 src/main/java/us/deathmarine/luyten/WindowPosition.java rename src/{ => main}/resources/Luyten.icns (100%) rename src/{ => main}/resources/Luyten.png (100%) rename src/{ => main/resources}/distfiles/Procyon.License.txt (99%) rename src/{ => main/resources}/distfiles/RSyntaxTextArea.License.txt (98%) rename src/{ => main}/resources/file.png (100%) rename src/{ => main}/resources/icon_close.png (100%) rename src/{ => main}/resources/java.png (100%) rename src/{ => main}/resources/package_obj.png (100%) rename src/{ => main}/resources/yml.png (100%) delete mode 100644 src/us/deathmarine/luyten/CellRenderer.java delete mode 100644 src/us/deathmarine/luyten/Closer.java delete mode 100644 src/us/deathmarine/luyten/ConfigSaver.java delete mode 100644 src/us/deathmarine/luyten/DecompilerLinkProvider.java delete mode 100644 src/us/deathmarine/luyten/DropListener.java delete mode 100644 src/us/deathmarine/luyten/FileDialog.java delete mode 100644 src/us/deathmarine/luyten/FileSaver.java delete mode 100644 src/us/deathmarine/luyten/FindAllBox.java delete mode 100644 src/us/deathmarine/luyten/FindBox.java delete mode 100644 src/us/deathmarine/luyten/JFontChooser.java delete mode 100644 src/us/deathmarine/luyten/JarEntryFilter.java delete mode 100644 src/us/deathmarine/luyten/LinkProvider.java delete mode 100644 src/us/deathmarine/luyten/Luyten.java delete mode 100644 src/us/deathmarine/luyten/LuytenOsx.java delete mode 100644 src/us/deathmarine/luyten/LuytenPreferences.java delete mode 100644 src/us/deathmarine/luyten/LuytenTypeLoader.java delete mode 100644 src/us/deathmarine/luyten/MainMenuBar.java delete mode 100644 src/us/deathmarine/luyten/MainWindow.java delete mode 100644 src/us/deathmarine/luyten/Model.java delete mode 100644 src/us/deathmarine/luyten/OpenFile.java delete mode 100644 src/us/deathmarine/luyten/RecentFiles.java delete mode 100644 src/us/deathmarine/luyten/Selection.java delete mode 100644 src/us/deathmarine/luyten/TooLargeFileException.java delete mode 100644 src/us/deathmarine/luyten/TreeNodeUserObject.java delete mode 100644 src/us/deathmarine/luyten/TreeUtil.java delete mode 100644 src/us/deathmarine/luyten/WindowPosition.java diff --git a/pom.xml b/pom.xml index 18b41c67..fa983b37 100644 --- a/pom.xml +++ b/pom.xml @@ -1,208 +1,149 @@ - - 4.0.0 - us.deathmarine - luyten - 0.7.0 + + 4.0.0 - - UTF-8 - + us.deathmarine + luyten + 0.7.0 - - - com.fifesoft - rsyntaxtextarea - 3.1.5 - - - com.apple - AppleJavaExtensions - 1.4 - - - org.bitbucket.mstrobel - procyon-core - 0.5.36 - - - org.bitbucket.mstrobel - procyon-expressions - 0.5.36 - - - org.bitbucket.mstrobel - procyon-reflection - 0.5.36 - - - org.bitbucket.mstrobel - procyon-compilertools - 0.5.36 - - - - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - src - ${project.artifactId}-${project.version}-lib - - - src - - **/*.java - - - - target - - **/*.* - - - - - - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - package - - shade - - - ${project.artifactId}-${project.version} - - - ${project.groupId}.${project.artifactId}.Luyten - - - - - *:* - - module-info.class - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - META-INF/*LICENSE* - META-INF/*NOTICE* - META-INF/MANIFEST.MF - - - - - - - - - - com.akathist.maven.plugins.launch4j - launch4j-maven-plugin - 2.1.2 - - - l4j-gui - package - - launch4j - - - gui - target/${project.artifactId}-${project.version}.exe - target/${project.artifactId}-${project.version}.jar - App Err - - ${project.groupId}.${project.artifactId}.Luyten - - luyten.ico - - 1.8.0 - 128 - 1024 - - - 0.${project.version} - 0.${project.version} - Java Decompiler - 2015 - 0.${project.version} - 0.${project.version} - ${project.artifactId} - ${project.artifactId} - ${project.artifactId}-${project.version}.exe - - - - - + + + 1.8 + ${java.version} + ${java.version} + UTF-8 + + + 1.4 + 0.5.36 + 3.1.5 + + + + + com.fifesoft + rsyntaxtextarea + ${rsyntaxtextarea.version} + + + com.apple + AppleJavaExtensions + ${applejavaext.version} + + + org.bitbucket.mstrobel + procyon-core + ${procyon.version} + + + org.bitbucket.mstrobel + procyon-expressions + ${procyon.version} + + + org.bitbucket.mstrobel + procyon-reflection + ${procyon.version} + + + org.bitbucket.mstrobel + procyon-compilertools + ${procyon.version} + + + + + ${project.artifactId}-${project.version}-lib + + + maven-compiler-plugin + 3.8.1 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + ${project.artifactId}-${project.version} + + + *:* + + module-info.class + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/*LICENSE* + META-INF/*NOTICE* + META-INF/MANIFEST.MF + + + + + + ${project.groupId}.${project.artifactId}.Luyten + + ${project.version} + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + + + + com.akathist.maven.plugins.launch4j + launch4j-maven-plugin + 2.1.2 + + + l4j-gui + package + + launch4j + + + gui + target/${project.artifactId}-${project.version}.exe + target/${project.artifactId}-${project.version}.jar + App Err + + ${project.groupId}.${project.artifactId}.Luyten + + luyten.ico + + 1.8.0 + 128 + 1024 + + + 0.${project.version} + 0.${project.version} + Java Decompiler + 2021 + 0.${project.version} + 0.${project.version} + ${project.artifactId} + ${project.artifactId} + ${project.artifactId}-${project.version}.exe + + + + + com.googlecode.maven-download-plugin @@ -215,7 +156,9 @@ wget - https://raw.githubusercontent.com/tofi86/universalJavaApplicationStub/master/src/universalJavaApplicationStub + + https://raw.githubusercontent.com/tofi86/universalJavaApplicationStub/master/src/universalJavaApplicationStub + ${project.build.directory}/resources universalJavaApplicationStub.sh @@ -223,113 +166,103 @@ - + - maven-antrun-plugin - 3.0.0 - - - jarbundler-gui - package - - run - - - - - - - - - - - - - + maven-antrun-plugin + 3.0.0 + + + jarbundler-gui + package + + run + + + + + + + + + + + + + - - + + - - - - - - - com.ultramixer.jarbundler - jarbundler-core - 3.3.0 - - - - + + + + + + + com.ultramixer.jarbundler + jarbundler-core + 3.3.0 + + + + - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-install-plugin - - - [2.4,) - - - install-file - - - - - - - - - - com.googlecode.maven-download-plugin - - - download-maven-plugin - - - [1.3.0,) - - - wget - - - - - - - - - - - - + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-install-plugin + [2.5.2,) + + install-file + + + + + + + + + com.googlecode.maven-download-plugin + download-maven-plugin + [1.6.7,) + + wget + + + + + + + + + + + + + - diff --git a/src/main/java/us/deathmarine/luyten/CellRenderer.java b/src/main/java/us/deathmarine/luyten/CellRenderer.java new file mode 100644 index 00000000..d2ab0a34 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/CellRenderer.java @@ -0,0 +1,60 @@ +package us.deathmarine.luyten; + +import java.awt.Component; +import java.awt.Toolkit; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; + +public class CellRenderer extends DefaultTreeCellRenderer { + + private static final long serialVersionUID = -5691181006363313993L; + + Icon pack; + Icon java_image; + Icon yml_image; + Icon file_image; + + public CellRenderer() { + this.pack = new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/package_obj.png"))); + this.java_image = new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/java.png"))); + this.yml_image = new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/yml.png"))); + this.file_image = new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/file.png"))); + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, + int row, boolean hasFocus) { + super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; + if (node.getChildCount() > 0) { + setIcon(this.pack); + } else { + switch (FileUtil.getLanguage(getFileName(node))) { + case SyntaxConstants.SYNTAX_STYLE_JAVA: + setIcon(this.java_image); + break; + case SyntaxConstants.SYNTAX_STYLE_YAML: + setIcon(this.yml_image); + break; + default: + setIcon(this.file_image); + break; + } + } + + return this; + } + + public String getFileName(DefaultMutableTreeNode node) { + return ((TreeNodeUserObject) node.getUserObject()).getOriginalName(); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/Closer.java b/src/main/java/us/deathmarine/luyten/Closer.java new file mode 100644 index 00000000..b4a489e6 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/Closer.java @@ -0,0 +1,24 @@ +package us.deathmarine.luyten; + +public final class Closer { + + public static void tryClose(final AutoCloseable c) { + if (c == null) { + return; + } + try { + c.close(); + } catch (Throwable ignored) { + } + } + + public static void tryClose(final AutoCloseable... items) { + if (items == null) { + return; + } + for (AutoCloseable c : items) { + tryClose(c); + } + } + +} diff --git a/src/main/java/us/deathmarine/luyten/ConfigSaver.java b/src/main/java/us/deathmarine/luyten/ConfigSaver.java new file mode 100644 index 00000000..e21c28c0 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/ConfigSaver.java @@ -0,0 +1,224 @@ +package us.deathmarine.luyten; + +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.languages.Language; +import com.strobel.decompiler.languages.Languages; +import com.strobel.decompiler.languages.java.JavaFormattingOptions; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.prefs.Preferences; + +public class ConfigSaver { + + private static final String FLATTEN_SWITCH_BLOCKS_ID = "flattenSwitchBlocks"; + private static final String FORCE_EXPLICIT_IMPORTS_ID = "forceExplicitImports"; + private static final String SHOW_SYNTHETIC_MEMBERS_ID = "showSyntheticMembers"; + private static final String EXCLUDE_NESTED_TYPES_ID = "excludeNestedTypes"; + private static final String FORCE_EXPLICIT_TYPE_ARGUMENTS_ID = "forceExplicitTypeArguments"; + private static final String RETAIN_REDUNDANT_CASTS_ID = "retainRedundantCasts"; + private static final String INCLUDE_ERROR_DIAGNOSTICS_ID = "includeErrorDiagnostics"; + private static final String UNICODE_REPLACE_ENABLED_ID = "unicodeReplaceEnabled"; + private static final String LANGUAGE_NAME_ID = "languageName"; + + private static final String MAIN_WINDOW_ID_PREFIX = "main"; + private static final String FIND_WINDOW_ID_PREFIX = "find"; + private static final String WINDOW_IS_FULL_SCREEN_ID = "WindowIsFullScreen"; + private static final String WINDOW_WIDTH_ID = "WindowWidth"; + private static final String WINDOW_HEIGHT_ID = "WindowHeight"; + private static final String WINDOW_X_ID = "WindowX"; + private static final String WINDOW_Y_ID = "WindowY"; + + private DecompilerSettings decompilerSettings; + private WindowPosition mainWindowPosition; + private WindowPosition findWindowPosition; + private LuytenPreferences luytenPreferences; + + private static volatile ConfigSaver theLoadedInstance; + + /** + * Do not instantiate, get the loaded instance + */ + private ConfigSaver() { + } + + public static ConfigSaver getLoadedInstance() { + if (theLoadedInstance == null) { + synchronized (ConfigSaver.class) { + if (theLoadedInstance == null) { + theLoadedInstance = new ConfigSaver(); + theLoadedInstance.loadConfig(); + } + } + } + return theLoadedInstance; + } + + /** + * Do not load, get the loaded instance + */ + private void loadConfig() { + decompilerSettings = new DecompilerSettings(); + if (decompilerSettings.getJavaFormattingOptions() == null) { + decompilerSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); + } + luytenPreferences = new LuytenPreferences(); + mainWindowPosition = new WindowPosition(); + findWindowPosition = new WindowPosition(); + try { + Preferences prefs = Preferences.userNodeForPackage(ConfigSaver.class); + if (!prefs.get(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()) + .equals(decompilerSettings.getLanguage().getName())) + prefs.put(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()); + + decompilerSettings.setFlattenSwitchBlocks( + prefs.getBoolean(FLATTEN_SWITCH_BLOCKS_ID, decompilerSettings.getFlattenSwitchBlocks())); + decompilerSettings.setForceExplicitImports( + prefs.getBoolean(FORCE_EXPLICIT_IMPORTS_ID, decompilerSettings.getForceExplicitImports())); + decompilerSettings.setShowSyntheticMembers( + prefs.getBoolean(SHOW_SYNTHETIC_MEMBERS_ID, decompilerSettings.getShowSyntheticMembers())); + decompilerSettings.setExcludeNestedTypes( + prefs.getBoolean(EXCLUDE_NESTED_TYPES_ID, decompilerSettings.getExcludeNestedTypes())); + decompilerSettings.setForceExplicitTypeArguments(prefs.getBoolean(FORCE_EXPLICIT_TYPE_ARGUMENTS_ID, + decompilerSettings.getForceExplicitTypeArguments())); + decompilerSettings.setRetainRedundantCasts( + prefs.getBoolean(RETAIN_REDUNDANT_CASTS_ID, decompilerSettings.getRetainRedundantCasts())); + decompilerSettings.setIncludeErrorDiagnostics( + prefs.getBoolean(INCLUDE_ERROR_DIAGNOSTICS_ID, decompilerSettings.getIncludeErrorDiagnostics())); + decompilerSettings.setLanguage( + findLanguageByName(prefs.get(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()))); + decompilerSettings.setUnicodeOutputEnabled(prefs.getBoolean(UNICODE_REPLACE_ENABLED_ID, false)); + + mainWindowPosition = loadWindowPosition(prefs, MAIN_WINDOW_ID_PREFIX); + findWindowPosition = loadWindowPosition(prefs, FIND_WINDOW_ID_PREFIX); + luytenPreferences = loadLuytenPreferences(prefs); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private WindowPosition loadWindowPosition(Preferences prefs, String windowIdPrefix) { + WindowPosition windowPosition = new WindowPosition(); + windowPosition.setFullScreen(prefs.getBoolean(windowIdPrefix + WINDOW_IS_FULL_SCREEN_ID, false)); + windowPosition.setWindowWidth(prefs.getInt(windowIdPrefix + WINDOW_WIDTH_ID, 0)); + windowPosition.setWindowHeight(prefs.getInt(windowIdPrefix + WINDOW_HEIGHT_ID, 0)); + windowPosition.setWindowX(prefs.getInt(windowIdPrefix + WINDOW_X_ID, 0)); + windowPosition.setWindowY(prefs.getInt(windowIdPrefix + WINDOW_Y_ID, 0)); + return windowPosition; + } + + // load preferences by their java variable names + private LuytenPreferences loadLuytenPreferences(Preferences prefs) throws Exception { + LuytenPreferences newLuytenPrefs = new LuytenPreferences(); + for (Field field : LuytenPreferences.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) + continue; + field.setAccessible(true); + String prefId = field.getName(); + Object defaultVal = field.get(newLuytenPrefs); + + if (field.getType() == String.class) { + String defaultStr = (String) (defaultVal == null ? "" : defaultVal); + field.set(newLuytenPrefs, prefs.get(prefId, defaultStr)); + + } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { + Boolean defaultBool = (Boolean) (defaultVal == null ? Boolean.FALSE : defaultVal); + field.setBoolean(newLuytenPrefs, prefs.getBoolean(prefId, defaultBool)); + + } else if (field.getType() == Integer.class || field.getType() == int.class) { + Integer defaultInt = (Integer) (defaultVal == null ? Integer.valueOf(0) : defaultVal); + field.setInt(newLuytenPrefs, prefs.getInt(prefId, defaultInt)); + } + } + return newLuytenPrefs; + } + + public void saveConfig() { + // Registry path on Windows Xp: + // HKEY_CURRENT_USER/Software/JavaSoft/Prefs/us/deathmarine/luyten + try { + Preferences prefs = Preferences.userNodeForPackage(ConfigSaver.class); + + prefs.putBoolean(FLATTEN_SWITCH_BLOCKS_ID, decompilerSettings.getFlattenSwitchBlocks()); + prefs.putBoolean(FORCE_EXPLICIT_IMPORTS_ID, decompilerSettings.getForceExplicitImports()); + prefs.putBoolean(SHOW_SYNTHETIC_MEMBERS_ID, decompilerSettings.getShowSyntheticMembers()); + prefs.putBoolean(EXCLUDE_NESTED_TYPES_ID, decompilerSettings.getExcludeNestedTypes()); + prefs.putBoolean(FORCE_EXPLICIT_TYPE_ARGUMENTS_ID, decompilerSettings.getForceExplicitTypeArguments()); + prefs.putBoolean(RETAIN_REDUNDANT_CASTS_ID, decompilerSettings.getRetainRedundantCasts()); + prefs.putBoolean(INCLUDE_ERROR_DIAGNOSTICS_ID, decompilerSettings.getIncludeErrorDiagnostics()); + prefs.putBoolean(UNICODE_REPLACE_ENABLED_ID, decompilerSettings.isUnicodeOutputEnabled()); + prefs.put(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()); + + saveWindowPosition(prefs, MAIN_WINDOW_ID_PREFIX, mainWindowPosition); + saveWindowPosition(prefs, FIND_WINDOW_ID_PREFIX, findWindowPosition); + saveLuytenPreferences(prefs); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private void saveWindowPosition(Preferences prefs, String windowIdPrefix, WindowPosition windowPosition) { + prefs.putBoolean(windowIdPrefix + WINDOW_IS_FULL_SCREEN_ID, windowPosition.isFullScreen()); + prefs.putInt(windowIdPrefix + WINDOW_WIDTH_ID, windowPosition.getWindowWidth()); + prefs.putInt(windowIdPrefix + WINDOW_HEIGHT_ID, windowPosition.getWindowHeight()); + prefs.putInt(windowIdPrefix + WINDOW_X_ID, windowPosition.getWindowX()); + prefs.putInt(windowIdPrefix + WINDOW_Y_ID, windowPosition.getWindowY()); + } + + // save preferences by their java variable names + private void saveLuytenPreferences(Preferences prefs) throws Exception { + for (Field field : LuytenPreferences.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) + continue; + field.setAccessible(true); + String prefId = field.getName(); + Object value = field.get(luytenPreferences); + + if (field.getType() == String.class) { + prefs.put(prefId, (String) (value == null ? "" : value)); + + } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { + prefs.putBoolean(prefId, (Boolean) (value == null ? Boolean.FALSE : value)); + + } else if (field.getType() == Integer.class || field.getType() == int.class) { + prefs.putInt(prefId, (Integer) (value == null ? Integer.valueOf(0) : value)); + } + } + } + + private Language findLanguageByName(String languageName) { + if (languageName != null) { + + if (languageName.equals(Languages.java().getName())) { + return Languages.java(); + } else if (languageName.equals(Languages.bytecode().getName())) { + return Languages.bytecode(); + } else if (languageName.equals(Languages.bytecodeAst().getName())) { + return Languages.bytecodeAst(); + } + + for (Language language : Languages.debug()) { + if (languageName.equals(language.getName())) { + return language; + } + } + } + return Languages.java(); + } + + public DecompilerSettings getDecompilerSettings() { + return decompilerSettings; + } + + public WindowPosition getMainWindowPosition() { + return mainWindowPosition; + } + + public WindowPosition getFindWindowPosition() { + return findWindowPosition; + } + + public LuytenPreferences getLuytenPreferences() { + return luytenPreferences; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/DecompilerLinkProvider.java b/src/main/java/us/deathmarine/luyten/DecompilerLinkProvider.java new file mode 100644 index 00000000..7aa0b796 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/DecompilerLinkProvider.java @@ -0,0 +1,383 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.FieldReference; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.MethodReference; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class DecompilerLinkProvider implements LinkProvider { + + private Map definitionToSelectionMap = new HashMap<>(); + private Map> referenceToSelectionsMap = new HashMap<>(); + private boolean isSelectionMapsPopulated = false; + + private MetadataSystem metadataSystem; + private DecompilerSettings settings; + private DecompilationOptions decompilationOptions; + private TypeDefinition type; + + private String currentTypeQualifiedName; + private String textContent = ""; + + @Override + public void generateContent() { + definitionToSelectionMap = new HashMap<>(); + referenceToSelectionsMap = new HashMap<>(); + currentTypeQualifiedName = type.getPackageName() + "." + type.getName(); + final StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter) { + @Override + public void writeDefinition(String text, Object definition, boolean isLocal) { + super.writeDefinition(text, definition, isLocal); + try { + if (text != null && definition != null) { + String uniqueStr = createUniqueStrForReference(definition); + if (uniqueStr != null) { + // fix link's underline length: _java.util.HashSet_ + // -> _HashSet_ + text = text.replaceAll("[^.]*\\.", ""); + int from = stringwriter.getBuffer().length() - text.length(); + int to = stringwriter.getBuffer().length(); + definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); + } + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + @Override + public void writeReference(String text, Object reference, boolean isLocal) { + super.writeReference(text, reference, isLocal); + try { + if (text != null && reference != null) { + String uniqueStr = createUniqueStrForReference(reference); + if (uniqueStr != null) { + text = text.replaceAll("[^.]*\\.", ""); + int from = stringwriter.getBuffer().length() - text.length(); + int to = stringwriter.getBuffer().length(); + if (reference instanceof FieldReference) { + // fix enum definition links (note: could not fix enum reference links) + if (((FieldReference) reference).isDefinition()) { + definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); + return; + } + } + if (referenceToSelectionsMap.containsKey(uniqueStr)) { + Set selectionsSet = referenceToSelectionsMap.get(uniqueStr); + if (selectionsSet != null) { + selectionsSet.add(new Selection(from, to)); + } + } else { + Set selectionsSet = new HashSet<>(); + selectionsSet.add(new Selection(from, to)); + referenceToSelectionsMap.put(uniqueStr, selectionsSet); + } + } + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + }; + plainTextOutput.setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(type, plainTextOutput, decompilationOptions); + textContent = stringwriter.toString(); + isSelectionMapsPopulated = true; + } + + private String createUniqueStrForReference(Object reference) { + String uniqueStr = null; + if (reference instanceof TypeReference) { + TypeReference type = (TypeReference) reference; + String pathAndTypeStr = getPathAndTypeStr(type); + if (pathAndTypeStr != null) { + uniqueStr = "type|" + pathAndTypeStr; + } + } else if (reference instanceof MethodReference) { + MethodReference method = (MethodReference) reference; + String pathAndTypeStr = getPathAndTypeStr(method.getDeclaringType()); + if (pathAndTypeStr != null) { + uniqueStr = "method|" + pathAndTypeStr + "|" + method.getName() + "|" + method.getErasedSignature(); + } + } else if (reference instanceof FieldReference) { + FieldReference field = (FieldReference) reference; + String pathAndTypeStr = getPathAndTypeStr(field.getDeclaringType()); + if (pathAndTypeStr != null) { + uniqueStr = "field|" + pathAndTypeStr + "|" + field.getName(); + } + } + return uniqueStr; + } + + private String getPathAndTypeStr(TypeReference typeRef) { + String name = typeRef.getName(); + String packageStr = typeRef.getPackageName(); + TypeReference mostOuterTypeRef = getMostOuterTypeRef(typeRef); + String mostOuterTypeName = mostOuterTypeRef.getName(); + if (name != null && packageStr != null && mostOuterTypeName != null && name.trim().length() > 0 + && mostOuterTypeName.trim().length() > 0) { + String pathStr = packageStr.replaceAll("\\.", "/") + "/" + mostOuterTypeName; + String typeStr = packageStr + "." + name.replace(".", "$"); + return pathStr + "|" + typeStr; + } + return null; + } + + private TypeReference getMostOuterTypeRef(TypeReference typeRef) { + int maxDecraringDepth = typeRef.getFullName().split("([.$])").length; + for (int i = 0; i < maxDecraringDepth; i++) { + TypeReference declaringTypeRef = typeRef.getDeclaringType(); + if (declaringTypeRef == null) { + break; + } else { + typeRef = declaringTypeRef; + } + } + if (typeRef.getName().contains("$")) { + return getMostOuterTypeRefBySlowLookuping(typeRef); + } + return typeRef; + } + + private TypeReference getMostOuterTypeRefBySlowLookuping(TypeReference typeRef) { + String name = typeRef.getName(); + if (name == null) + return typeRef; + String packageName = typeRef.getPackageName(); + if (packageName == null) + return typeRef; + String[] nameParts = name.split("\\$"); + StringBuilder newName = new StringBuilder(); + String sep = ""; + for (int i = 0; i < nameParts.length - 1; i++) { + newName.append(sep).append(nameParts[i]); + sep = "$"; + String newInternalName = packageName.replaceAll("\\.", "/") + "/" + newName; + TypeReference newTypeRef = metadataSystem.lookupType(newInternalName); + if (newTypeRef != null) { + TypeDefinition newTypeDef = newTypeRef.resolve(); + if (newTypeDef != null) { + return newTypeRef; + } + } + } + return typeRef; + } + + @Override + public String getTextContent() { + return textContent; + } + + @Override + public void processLinks() { + } + + @Override + public Map getDefinitionToSelectionMap() { + return definitionToSelectionMap; + } + + @Override + public Map> getReferenceToSelectionsMap() { + return referenceToSelectionsMap; + } + + @Override + public boolean isLinkNavigable(String uniqueStr) { + if (isSelectionMapsPopulated && definitionToSelectionMap.containsKey(uniqueStr)) + return true; + if (uniqueStr == null) + return false; + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length < 3) + return false; + String typeStr = linkParts[2]; + if (typeStr.trim().length() <= 0) + return false; + TypeReference typeRef = metadataSystem.lookupType(typeStr.replaceAll("\\.", "/")); + if (typeRef == null) + return false; + TypeDefinition typeDef = typeRef.resolve(); + if (typeDef == null) + return false; + if (typeDef.isSynthetic()) + return false; + + if (isSelectionMapsPopulated) { + // current type's navigable definitions checked already, now it's erroneous + if (currentTypeQualifiedName == null || currentTypeQualifiedName.trim().length() <= 0) + return false; + if (typeStr.equals(currentTypeQualifiedName) || typeStr.startsWith(currentTypeQualifiedName + ".") + || typeStr.startsWith(currentTypeQualifiedName + "$")) + return false; + } + + // check linked field/method exists + if (uniqueStr.startsWith("method")) { + return findMethodInType(typeDef, uniqueStr) != null; + } else if (uniqueStr.startsWith("field")) { + return findFieldInType(typeDef, uniqueStr) != null; + } + return true; + } + + private MethodDefinition findMethodInType(TypeDefinition typeDef, String uniqueStr) { + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length != 5) + return null; + String methodName = linkParts[3]; + String methodErasedSignature = linkParts[4]; + if (methodName.trim().length() <= 0 || methodErasedSignature.trim().length() <= 0) + return null; + List declaredMethods = typeDef.getDeclaredMethods(); + if (declaredMethods == null) + return null; + boolean isFound; + for (MethodDefinition declaredMethod : declaredMethods) { + isFound = (declaredMethod != null && methodName.equals(declaredMethod.getName())); + isFound = (isFound && methodErasedSignature.equals(declaredMethod.getErasedSignature())); + if (isFound) { + if (declaredMethod.isSynthetic() && !settings.getShowSyntheticMembers()) { + return null; + } else { + return declaredMethod; + } + } + } + return null; + } + + private FieldDefinition findFieldInType(TypeDefinition typeDef, String uniqueStr) { + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length != 4) + return null; + String fieldName = linkParts[3]; + if (fieldName.trim().length() <= 0) + return null; + List declaredFields = typeDef.getDeclaredFields(); + if (declaredFields == null) + return null; + boolean isFound; + for (FieldDefinition declaredField : declaredFields) { + isFound = (declaredField != null && fieldName.equals(declaredField.getName())); + if (isFound) { + if (declaredField.isSynthetic()) { + return null; + } else { + return declaredField; + } + } + } + return null; + } + + @Override + public String getLinkDescription(String uniqueStr) { + String readableLink = null; + try { + if (uniqueStr == null) + return null; + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length < 3) + return null; + String typeStr = linkParts[2]; + TypeReference typeRef = metadataSystem.lookupType(typeStr.replaceAll("\\.", "/")); + if (typeRef == null) + return null; + TypeDefinition typeDef = typeRef.resolve(); + if (typeDef == null) + return null; + + String declaredSuffix = ""; + String mostOuterTypeStr = linkParts[1].replaceAll("/", "."); + boolean isOwnFile = mostOuterTypeStr.equals(currentTypeQualifiedName); + if (!isOwnFile) { + declaredSuffix = " - Declared: " + mostOuterTypeStr; + } + + if (uniqueStr.startsWith("type")) { + String desc = typeDef.getBriefDescription(); + if (desc != null && desc.trim().length() > 0) { + readableLink = desc; + } + } else if (uniqueStr.startsWith("method")) { + MethodDefinition methodDef = findMethodInType(typeDef, uniqueStr); + if (methodDef == null) + return null; + String desc = methodDef.getBriefDescription(); + if (desc != null && desc.trim().length() > 0) { + + if (desc.contains("void ")) { + String constructorName = typeDef.getName(); + TypeReference declaringTypeRef = typeRef.getDeclaringType(); + if (declaringTypeRef != null) { + TypeDefinition declaringTypeDef = declaringTypeRef.resolve(); + if (declaringTypeDef != null) { + String declaringTypeName = declaringTypeDef.getName(); + if (declaringTypeName != null) { + constructorName = StringUtilities.removeLeft(constructorName, declaringTypeName); + constructorName = constructorName.replaceAll("^([.$])", ""); + } + } + } + desc = desc.replace("void ", constructorName); + readableLink = "Constructor: " + erasePackageInfoFromDesc(desc) + declaredSuffix; + } else { + readableLink = erasePackageInfoFromDesc(desc) + declaredSuffix; + } + } + } else if (uniqueStr.startsWith("field")) { + FieldDefinition fieldDef = findFieldInType(typeDef, uniqueStr); + if (fieldDef == null) + return null; + String desc = fieldDef.getBriefDescription(); + if (desc != null && desc.trim().length() > 0) { + readableLink = erasePackageInfoFromDesc(desc) + declaredSuffix; + } + + } + if (readableLink != null) { + readableLink = readableLink.replace("$", "."); + } + } catch (Exception e) { + readableLink = null; + Luyten.showExceptionDialog("Exception!", e); + } + return readableLink; + } + + private String erasePackageInfoFromDesc(String desc) { + // Use {0,1024} instead of * as it can't be used together with a lookbehind. + // If errors occur, increase this limit. + String limiters = "()<>\\[\\]?\\s,"; + desc = desc.replaceAll("(?<=[^" + limiters + "]{0,1024})([^" + limiters + "]*)\\.", ""); + return desc; + } + + public void setDecompilerReferences(MetadataSystem metadataSystem, DecompilerSettings settings, + DecompilationOptions decompilationOptions) { + this.metadataSystem = metadataSystem; + this.settings = settings; + this.decompilationOptions = decompilationOptions; + } + + public void setType(TypeDefinition type) { + this.type = type; + } + +} diff --git a/src/us/deathmarine/luyten/DirPreferences.java b/src/main/java/us/deathmarine/luyten/DirPreferences.java similarity index 98% rename from src/us/deathmarine/luyten/DirPreferences.java rename to src/main/java/us/deathmarine/luyten/DirPreferences.java index 30ff250a..a687d85e 100644 --- a/src/us/deathmarine/luyten/DirPreferences.java +++ b/src/main/java/us/deathmarine/luyten/DirPreferences.java @@ -1,9 +1,10 @@ package us.deathmarine.luyten; -import javax.swing.*; import java.io.File; +import javax.swing.JFileChooser; class DirPreferences { + private final LuytenPreferences luytenPrefs; public DirPreferences(LuytenPreferences luytenPrefs) { @@ -59,4 +60,5 @@ void saveSaveDialogDir(JFileChooser fc) { Luyten.showExceptionDialog("Exception!", e); } } + } diff --git a/src/main/java/us/deathmarine/luyten/DropListener.java b/src/main/java/us/deathmarine/luyten/DropListener.java new file mode 100644 index 00000000..17c2a574 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/DropListener.java @@ -0,0 +1,104 @@ +package us.deathmarine.luyten; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.Reader; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +/** + * Drag-Drop (only MainWindow should be called from here) + */ +public class DropListener implements DropTargetListener { + + private final MainWindow mainWindow; + + public DropListener(MainWindow mainWindow) { + this.mainWindow = mainWindow; + } + + @SuppressWarnings("unchecked") + @Override + public void drop(DropTargetDropEvent event) { + event.acceptDrop(DnDConstants.ACTION_COPY); + Transferable transferable = event.getTransferable(); + if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { + DataFlavor[] flavors = transferable.getTransferDataFlavors(); + for (DataFlavor flavor : flavors) { + try { + if (flavor.isFlavorJavaFileListType()) { + List files = (List) transferable.getTransferData(flavor); + mainWindow.onFilesDropped(files); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + event.dropComplete(true); + } else { + DataFlavor[] flavors = transferable.getTransferDataFlavors(); + boolean handled = false; + for (DataFlavor flavor : flavors) { + if (flavor.isRepresentationClassReader()) { + try { + Reader reader = flavor.getReaderForText(transferable); + BufferedReader br = new BufferedReader(reader); + List list = new ArrayList<>(); + String line; + while ((line = br.readLine()) != null) { + try { + if (("" + (char) 0).equals(line)) + continue; + File file = new File(new URI(line)); + list.add(file); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + if (list.size() > 1) { + event.rejectDrop(); + return; + } + if (list.size() == 1) { + mainWindow.onFileDropped(list.get(0)); + } + event.getDropTargetContext().dropComplete(true); + handled = true; + } catch (Exception e) { + e.printStackTrace(); + } + break; + } + } + if (!handled) { + event.rejectDrop(); + } + } + + } + + @Override + public void dragEnter(DropTargetDragEvent arg0) { + } + + @Override + public void dragExit(DropTargetEvent arg0) { + } + + @Override + public void dragOver(DropTargetDragEvent arg0) { + } + + @Override + public void dropActionChanged(DropTargetDragEvent arg0) { + } + +} diff --git a/src/main/java/us/deathmarine/luyten/FileDialog.java b/src/main/java/us/deathmarine/luyten/FileDialog.java new file mode 100644 index 00000000..63df69b4 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FileDialog.java @@ -0,0 +1,136 @@ +package us.deathmarine.luyten; + +import java.awt.Component; +import java.io.File; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; + +/** + * FileChoosers for Open and Save + */ +public class FileDialog { + + private final DirPreferences dirPreferences; + private final ConfigSaver configSaver; + private final Component parent; + private JFileChooser fcOpen; + private JFileChooser fcSave; + private JFileChooser fcSaveAll; + + public FileDialog(Component parent) { + this.parent = parent; + configSaver = ConfigSaver.getLoadedInstance(); + LuytenPreferences luytenPrefs = configSaver.getLuytenPreferences(); + dirPreferences = new DirPreferences(luytenPrefs); + + new Thread(() -> { + try { + initOpenDialog(); + Thread.sleep(500); + initSaveAllDialog(); + Thread.sleep(500); + initSaveDialog(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }).start(); + } + + public File doOpenDialog() { + File selectedFile = null; + initOpenDialog(); + + dirPreferences.retrieveOpenDialogDir(fcOpen); + int returnVal = fcOpen.showOpenDialog(parent); + dirPreferences.saveOpenDialogDir(fcOpen); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + selectedFile = fcOpen.getSelectedFile(); + } + return selectedFile; + } + + public File doSaveDialog(String recommendedFileName) { + File selectedFile = null; + initSaveDialog(); + + dirPreferences.retrieveSaveDialogDir(fcSave); + fcSave.setSelectedFile(new File(recommendedFileName)); + int returnVal = fcSave.showSaveDialog(parent); + dirPreferences.saveSaveDialogDir(fcSave); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + selectedFile = fcSave.getSelectedFile(); + } + return selectedFile; + } + + public File doSaveAllDialog(String recommendedFileName) { + File selectedFile = null; + initSaveAllDialog(); + + dirPreferences.retrieveSaveDialogDir(fcSaveAll); + fcSaveAll.setSelectedFile(new File(recommendedFileName)); + int returnVal = fcSaveAll.showSaveDialog(parent); + dirPreferences.saveSaveDialogDir(fcSaveAll); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + selectedFile = fcSaveAll.getSelectedFile(); + } + return selectedFile; + } + + public synchronized void initOpenDialog() { + if (fcOpen == null) { + fcOpen = createFileChooser("*.jar", "*.zip", "*.class"); + dirPreferences.retrieveOpenDialogDir(fcOpen); + } + } + + public synchronized void initSaveDialog() { + if (fcSave == null) { + fcSave = createFileChooser("*.txt", "*.java"); + dirPreferences.retrieveSaveDialogDir(fcSave); + } + } + + public synchronized void initSaveAllDialog() { + if (fcSaveAll == null) { + fcSaveAll = createFileChooser("*.jar", "*.zip"); + dirPreferences.retrieveSaveDialogDir(fcSaveAll); + } + } + + private JFileChooser createFileChooser(String... fileFilters) { + JFileChooser fc = new JFileChooser(); + for (String fileFilter : fileFilters) { + fc.addChoosableFileFilter(new FileChooserFileFilter(fileFilter)); + } + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + return fc; + } + + public static class FileChooserFileFilter extends FileFilter { + + String objType; + + public FileChooserFileFilter(String string) { + objType = string; + } + + @Override + public boolean accept(File f) { + if (f.isDirectory()) + return true; + return f.getName().toLowerCase().endsWith(objType.substring(1)); + } + + @Override + public String getDescription() { + return objType; + } + + } + +} diff --git a/src/us/deathmarine/luyten/FileEntryNotFoundException.java b/src/main/java/us/deathmarine/luyten/FileEntryNotFoundException.java similarity index 56% rename from src/us/deathmarine/luyten/FileEntryNotFoundException.java rename to src/main/java/us/deathmarine/luyten/FileEntryNotFoundException.java index b625b589..697431a2 100644 --- a/src/us/deathmarine/luyten/FileEntryNotFoundException.java +++ b/src/main/java/us/deathmarine/luyten/FileEntryNotFoundException.java @@ -1,6 +1,7 @@ package us.deathmarine.luyten; public class FileEntryNotFoundException extends Exception { - private static final long serialVersionUID = -1019729947179642460L; + + private static final long serialVersionUID = -1019729947179642460L; } diff --git a/src/us/deathmarine/luyten/FileIsBinaryException.java b/src/main/java/us/deathmarine/luyten/FileIsBinaryException.java similarity index 55% rename from src/us/deathmarine/luyten/FileIsBinaryException.java rename to src/main/java/us/deathmarine/luyten/FileIsBinaryException.java index 19b6c4ed..f645870f 100644 --- a/src/us/deathmarine/luyten/FileIsBinaryException.java +++ b/src/main/java/us/deathmarine/luyten/FileIsBinaryException.java @@ -1,6 +1,7 @@ package us.deathmarine.luyten; public class FileIsBinaryException extends Exception { - private static final long serialVersionUID = -1497200371383232314L; + + private static final long serialVersionUID = -1497200371383232314L; } diff --git a/src/main/java/us/deathmarine/luyten/FileSaver.java b/src/main/java/us/deathmarine/luyten/FileSaver.java new file mode 100644 index 00000000..89359b13 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FileSaver.java @@ -0,0 +1,322 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.metadata.ITypeLoader; +import com.strobel.assembler.metadata.JarTypeLoader; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import com.strobel.decompiler.languages.java.JavaFormattingOptions; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipException; +import java.util.zip.ZipOutputStream; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; + +/** + * Performs Save and Save All + */ +public class FileSaver { + + private final JProgressBar bar; + private final JLabel label; + private boolean cancel; + private boolean extracting; + + public FileSaver(JProgressBar bar, JLabel label) { + this.bar = bar; + this.label = label; + final JPopupMenu menu = new JPopupMenu("Cancel"); + final JMenuItem item = new JMenuItem("Cancel"); + item.addActionListener(arg0 -> setCancel(true)); + menu.add(item); + this.label.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent ev) { + if (SwingUtilities.isRightMouseButton(ev) && isExtracting()) + menu.show(ev.getComponent(), ev.getX(), ev.getY()); + } + }); + } + + public void saveText(final String text, final File file) { + new Thread(() -> { + DecompilerSettings settings = cloneSettings(); + boolean isUnicodeEnabled = settings.isUnicodeOutputEnabled(); + long time = System.currentTimeMillis(); + try (FileOutputStream fos = new FileOutputStream(file); + OutputStreamWriter writer = isUnicodeEnabled + ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) + : new OutputStreamWriter(fos); + BufferedWriter bw = new BufferedWriter(writer)) { + label.setText("Extracting: " + file.getName()); + bar.setVisible(true); + bw.write(text); + bw.flush(); + label.setText("Completed: " + getTime(time)); + } catch (Exception e1) { + label.setText("Cannot save file: " + file.getName()); + Luyten.showExceptionDialog("Unable to save file!\n", e1); + } finally { + setExtracting(false); + bar.setVisible(false); + } + }).start(); + } + + public void saveAllDecompiled(final File inFile, final File outFile) { + new Thread(() -> { + long time = System.currentTimeMillis(); + try { + bar.setVisible(true); + setExtracting(true); + label.setText("Extracting: " + outFile.getName()); + System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); + String inFileName = inFile.getName().toLowerCase(); + + if (inFileName.endsWith(".jar") || inFileName.endsWith(".zip")) { + doSaveJarDecompiled(inFile, outFile); + } else if (inFileName.endsWith(".class")) { + doSaveClassDecompiled(inFile, outFile); + } else { + doSaveUnknownFile(inFile, outFile); + } + if (cancel) { + label.setText("Cancelled"); + outFile.delete(); + setCancel(false); + } else { + label.setText("Completed: " + getTime(time)); + } + } catch (Exception e1) { + label.setText("Cannot save file: " + outFile.getName()); + Luyten.showExceptionDialog("Unable to save file!\n", e1); + } finally { + setExtracting(false); + bar.setVisible(false); + } + }).start(); + } + + private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { + try (JarFile jfile = new JarFile(inFile); + FileOutputStream dest = new FileOutputStream(outFile); + BufferedOutputStream buffDest = new BufferedOutputStream(dest); + ZipOutputStream out = new ZipOutputStream(buffDest)) { + bar.setMinimum(0); + bar.setMaximum(jfile.size()); + byte[] data = new byte[1024]; + DecompilerSettings settings = cloneSettings(); + LuytenTypeLoader typeLoader = new LuytenTypeLoader(); + MetadataSystem metadataSystem = new MetadataSystem(typeLoader); + ITypeLoader jarLoader = new JarTypeLoader(jfile); + typeLoader.getTypeLoaders().add(jarLoader); + + DecompilationOptions decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + + List mass; + JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); + LuytenPreferences luytenPrefs = ConfigSaver.getLoadedInstance().getLuytenPreferences(); + if (luytenPrefs.isFilterOutInnerClassEntries()) { + mass = jarEntryFilter.getEntriesWithoutInnerClasses(); + } else { + mass = jarEntryFilter.getAllEntriesFromJar(); + } + + Enumeration ent = jfile.entries(); + Set history = new HashSet<>(); + int tick = 0; + while (ent.hasMoreElements() && !cancel) { + bar.setValue(++tick); + JarEntry entry = ent.nextElement(); + if (!mass.contains(entry.getName())) + continue; + label.setText("Extracting: " + entry.getName()); + bar.setVisible(true); + if (entry.getName().endsWith(".class")) { + JarEntry etn = new JarEntry(entry.getName().replace(".class", ".java")); + label.setText("Extracting: " + etn.getName()); + System.out.println("[SaveAll]: " + etn.getName() + " -> " + outFile.getName()); + + if (history.add(etn.getName())) { + out.putNextEntry(etn); + try { + boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); + String internalName = StringUtilities.removeRight(entry.getName(), ".class"); + TypeReference type = metadataSystem.lookupType(internalName); + TypeDefinition resolvedType; + if ((type == null) || ((resolvedType = type.resolve()) == null)) { + throw new Exception("Unable to resolve type."); + } + Writer writer = isUnicodeEnabled ? new OutputStreamWriter(out, StandardCharsets.UTF_8) + : new OutputStreamWriter(out); + PlainTextOutput plainTextOutput = new PlainTextOutput(writer); + plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); + writer.flush(); + } catch (Exception e) { + label.setText("Cannot decompile file: " + entry.getName()); + Luyten.showExceptionDialog("Unable to Decompile file!\nSkipping file...", e); + } finally { + out.closeEntry(); + } + } + } else { + try { + JarEntry etn = new JarEntry(entry.getName()); + if (entry.getName().endsWith(".java")) + etn = new JarEntry(entry.getName().replace(".java", ".src.java")); + if (history.add(etn.getName())) { + out.putNextEntry(etn); + try { + InputStream in = jfile.getInputStream(etn); + if (in != null) { + try { + int count; + while ((count = in.read(data, 0, 1024)) != -1) { + out.write(data, 0, count); + } + } finally { + in.close(); + } + } + } finally { + out.closeEntry(); + } + } + } catch (ZipException ze) { + if (!ze.getMessage().contains("duplicate")) { + throw ze; + } + } + } + } + } + } + + private void doSaveClassDecompiled(File inFile, File outFile) throws Exception { + DecompilerSettings settings = cloneSettings(); + LuytenTypeLoader typeLoader = new LuytenTypeLoader(); + MetadataSystem metadataSystem = new MetadataSystem(typeLoader); + TypeReference type = metadataSystem.lookupType(inFile.getCanonicalPath()); + + DecompilationOptions decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + + boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); + TypeDefinition resolvedType; + if (type == null || ((resolvedType = type.resolve()) == null)) { + throw new Exception("Unable to resolve type."); + } + StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); + String decompiledSource = stringwriter.toString(); + + System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); + try (FileOutputStream fos = new FileOutputStream(outFile); + OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) + : new OutputStreamWriter(fos); + BufferedWriter bw = new BufferedWriter(writer)) { + bw.write(decompiledSource); + bw.flush(); + } + } + + private void doSaveUnknownFile(File inFile, File outFile) throws Exception { + try (FileInputStream in = new FileInputStream(inFile); FileOutputStream out = new FileOutputStream(outFile)) { + System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); + + byte[] data = new byte[1024]; + int count; + while ((count = in.read(data, 0, 1024)) != -1) { + out.write(data, 0, count); + } + } + } + + private DecompilerSettings cloneSettings() { + DecompilerSettings settings = ConfigSaver.getLoadedInstance().getDecompilerSettings(); + DecompilerSettings newSettings = new DecompilerSettings(); + if (newSettings.getJavaFormattingOptions() == null) { + newSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); + } + // synchronized: against main menu changes + synchronized (settings) { + newSettings.setExcludeNestedTypes(settings.getExcludeNestedTypes()); + newSettings.setFlattenSwitchBlocks(settings.getFlattenSwitchBlocks()); + newSettings.setForceExplicitImports(settings.getForceExplicitImports()); + newSettings.setForceExplicitTypeArguments(settings.getForceExplicitTypeArguments()); + newSettings.setOutputFileHeaderText(settings.getOutputFileHeaderText()); + newSettings.setLanguage(settings.getLanguage()); + newSettings.setShowSyntheticMembers(settings.getShowSyntheticMembers()); + newSettings.setAlwaysGenerateExceptionVariableForCatchBlocks( + settings.getAlwaysGenerateExceptionVariableForCatchBlocks()); + newSettings.setOutputDirectory(settings.getOutputDirectory()); + newSettings.setRetainRedundantCasts(settings.getRetainRedundantCasts()); + newSettings.setIncludeErrorDiagnostics(settings.getIncludeErrorDiagnostics()); + newSettings.setIncludeLineNumbersInBytecode(settings.getIncludeLineNumbersInBytecode()); + newSettings.setRetainPointlessSwitches(settings.getRetainPointlessSwitches()); + newSettings.setUnicodeOutputEnabled(settings.isUnicodeOutputEnabled()); + newSettings.setMergeVariables(settings.getMergeVariables()); + newSettings.setShowDebugLineNumbers(settings.getShowDebugLineNumbers()); + } + return newSettings; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public boolean isExtracting() { + return extracting; + } + + public void setExtracting(boolean extracting) { + this.extracting = extracting; + } + + public static String getTime(long time) { + long lap = System.currentTimeMillis() - time; + lap = lap / 1000; + StringBuilder sb = new StringBuilder(); + long hour = ((lap / 60) / 60); + long min = ((lap - (hour * 60 * 60)) / 60); + long sec = ((lap - (hour * 60 * 60) - (min * 60))); + if (hour > 0) + sb.append("Hour:").append(hour).append(" "); + sb.append("Min(s): ").append(min).append(" Sec: ").append(sec); + return sb.toString(); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/FindAllBox.java b/src/main/java/us/deathmarine/luyten/FindAllBox.java new file mode 100644 index 00000000..0f131a4c --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FindAllBox.java @@ -0,0 +1,406 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.Collections; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Pattern; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.DefaultListModel; +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; + +/** + * this is the Find All Dialog + *

+ * Change with 1.1 + * Adjust the find all box width + *

+ * + * @author clevertension + * @version 1.1 + */ +public class FindAllBox extends JDialog { + + private static final long serialVersionUID = -4125409760166690462L; + private static final int MIN_WIDTH = 640; + private boolean searching; + + private final JButton findButton; + private final JTextField textField; + private final JCheckBox mcase; + private final JCheckBox regex; + private final JCheckBox wholew; + private final JCheckBox classname; + private final JList list; + private final JProgressBar progressBar; + boolean locked; + + private final JLabel statusLabel = new JLabel(""); + + private final DefaultListModel classesList = new DefaultListModel<>(); + + private Thread tmp_thread; + + private final MainWindow mainWindow; + + public FindAllBox(final MainWindow mainWindow) { + this.setDefaultCloseOperation(HIDE_ON_CLOSE); + this.setHideOnEscapeButton(); + + progressBar = new JProgressBar(0, 100); + this.mainWindow = mainWindow; + + JLabel label = new JLabel("Find What:"); + textField = new JTextField(); + findButton = new JButton("Find"); + findButton.addActionListener(new FindButton()); + + mcase = new JCheckBox("Match Case"); + regex = new JCheckBox("Regex"); + wholew = new JCheckBox("Whole Words"); + classname = new JCheckBox("Classnames"); + + this.getRootPane().setDefaultButton(findButton); + + list = new JList<>(classesList); + list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + list.setLayoutOrientation(JList.VERTICAL_WRAP); + list.setVisibleRowCount(-1); + list.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + @SuppressWarnings("unchecked") + JList list = (JList) evt.getSource(); + if (evt.getClickCount() == 2) { + int index = list.locationToIndex(evt.getPoint()); + String entryName = list.getModel().getElementAt(index); + String[] array = entryName.split("/"); + if (entryName.toLowerCase().endsWith(".class")) { + String internalName = StringUtilities.removeRight(entryName, ".class"); + TypeReference type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); + try { + mainWindow.getSelectedModel().extractClassToTextPane(type, array[array.length - 1], + entryName, + null); + } catch (Exception ignored) { + for (Model m : mainWindow.getModels()) { + try { + m.extractClassToTextPane(type, array[array.length - 1], entryName, null); + } catch (Exception ignored1) { + } + } + } + + } else { + try { + JarFile jfile = new JarFile(mainWindow.getSelectedModel().getOpenedFile()); + mainWindow.getSelectedModel().extractSimpleFileEntryToTextPane( + jfile.getInputStream(jfile.getEntry(entryName)), array[array.length - 1], + entryName); + jfile.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + } + }); + list.setLayoutOrientation(JList.VERTICAL); + JScrollPane listScroller = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + int width = (int) (screenSize.width * 0.35); + if (width < MIN_WIDTH) { + width = MIN_WIDTH; + } + final Dimension center = new Dimension(width, 500); + final int x = (int) (center.width * 0.2); + final int y = (int) (center.height * 0.2); + this.setBounds(x, y, center.width, center.height); + this.setResizable(false); + + GroupLayout layout = new GroupLayout(getRootPane()); + getRootPane().setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + layout.setHorizontalGroup( + layout.createSequentialGroup().addComponent(label) + .addGroup( + layout.createParallelGroup(Alignment.LEADING).addComponent(statusLabel) + .addComponent(textField) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(mcase)) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(wholew)) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(regex)) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(classname))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(listScroller) + .addComponent(progressBar)))) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(findButton)) + + ); + + layout.linkSize(SwingConstants.HORIZONTAL, findButton); + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(textField) + .addComponent(findButton)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(mcase).addComponent(wholew) + .addComponent(regex).addComponent(classname)) + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(listScroller)))) + .addGroup(layout.createParallelGroup(Alignment.LEADING)).addComponent(statusLabel) + .addComponent(progressBar)); + this.adjustWindowPositionBySavedState(); + this.setSaveWindowPositionOnClosing(); + + this.setName("Find All"); + this.setTitle("Find All"); + } + + private class FindButton extends AbstractAction { + private static final long serialVersionUID = 75954129199541874L; + + @Override + public void actionPerformed(ActionEvent event) { + tmp_thread = new Thread(() -> { + if (findButton.getText().equals("Stop")) { + if (tmp_thread != null) + tmp_thread.interrupt(); + setStatus("Stopped."); + findButton.setText("Find"); + locked = false; + } else { + findButton.setText("Stop"); + classesList.clear(); + ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); + DecompilerSettings settings = configSaver.getDecompilerSettings(); + File inFile = mainWindow.getSelectedModel().getOpenedFile(); + boolean filter = ConfigSaver.getLoadedInstance().getLuytenPreferences() + .isFilterOutInnerClassEntries(); + try { + JarFile jfile = new JarFile(inFile); + Enumeration entLength = jfile.entries(); + initProgressBar(Collections.list(entLength).size()); + Enumeration ent = jfile.entries(); + while (ent.hasMoreElements() && findButton.getText().equals("Stop")) { + JarEntry entry = ent.nextElement(); + String name = entry.getName(); + setStatus(name); + if (filter && name.contains("$")) + continue; + if (locked || classname.isSelected()) { + locked = true; + if (search(entry.getName())) + addClassName(entry.getName()); + } else { + if (entry.getName().endsWith(".class")) { + synchronized (settings) { + String internalName = StringUtilities.removeRight(entry.getName(), ".class"); + TypeReference type; + try { + type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); + TypeDefinition resolvedType; + if (type != null && ((resolvedType = type.resolve()) != null)) { + StringWriter stringwriter = new StringWriter(); + DecompilationOptions decompilationOptions; + decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput.setUnicodeOutputEnabled( + decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, + decompilationOptions); + if (search(stringwriter.toString())) + addClassName(entry.getName()); + } + } catch (IllegalStateException ise) { + if (ise.getMessage().contains("Invalid BootstrapMethods attribute entry: " + + "2 additional arguments required for method " + + "java/lang/invoke/StringConcatFactory.makeConcatWithConstants, " + + "but only 1 specified.")) { + // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to https://bitbucket.org/mstrobel/procyon/issues/336/ + // Searching in a WAR or JAR file could pop-up a lot of error dialogs + // for a lot of class files, we simply say nothing here + addClassName(entry.getName() + " (search failed due to known " + + "Exception in Procyon <= 0.5.35. Opening file will fail " + + "too)"); + } else { + // all other IllegalStateException cases + addClassName(entry.getName() + " (search failed due to Exception. " + + "Opening file will fail too)"); + Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), + ise); + } + } catch (Exception e) { + addClassName(entry.getName() + " (search failed due to Exception. " + + "Opening file will fail too)"); + Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), e); + } + } + } else { + + StringBuilder sb = new StringBuilder(); + double ascii = 0; + double other = 0; + try (InputStreamReader inputStreamReader = new InputStreamReader( + jfile.getInputStream(entry)); + BufferedReader reader = new BufferedReader(inputStreamReader)) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + // Source: https://stackoverflow.com/a/13533390/5894824 + for (byte b : line.getBytes()) { + if (b == 0x09 || b == 0x0A || b == 0x0C || b == 0x0D || (b >= 0x20 && b <= 0x7E)) + ascii++; + else other++; + } + } + } + + if ((other == 0 || ascii / (ascii + other) > 0.5) && search(sb.toString())) + addClassName(entry.getName()); + } + } + } + setSearching(false); + if (findButton.getText().equals("Stop")) { + setStatus("Done."); + findButton.setText("Find"); + locked = false; + } + jfile.close(); + locked = false; + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + + } + }); + tmp_thread.start(); + + } + + } + + private boolean search(String bulk) { + String a = textField.getText(); + String b = bulk; + if (regex.isSelected()) + return Pattern.matches(a, b); + if (wholew.isSelected()) + a = " " + a + " "; + if (!mcase.isSelected()) { + a = a.toLowerCase(); + b = b.toLowerCase(); + } + return b.contains(a); + } + + private void setHideOnEscapeButton() { + Action escapeAction = new AbstractAction() { + private static final long serialVersionUID = 6846566740472934801L; + + @Override + public void actionPerformed(ActionEvent e) { + FindAllBox.this.setVisible(false); + } + }; + + KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE"); + this.getRootPane().getActionMap().put("ESCAPE", escapeAction); + } + + private void adjustWindowPositionBySavedState() { + WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); + + if (windowPosition.isSavedWindowPositionValid()) { + this.setLocation(windowPosition.getWindowX(), windowPosition.getWindowY()); + } + } + + private void setSaveWindowPositionOnClosing() { + this.addWindowListener(new WindowAdapter() { + @Override + public void windowDeactivated(WindowEvent e) { + WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); + windowPosition.readPositionFromDialog(FindAllBox.this); + } + }); + } + + public void showFindBox() { + this.setVisible(true); + this.textField.requestFocus(); + } + + public void hideFindBox() { + this.setVisible(false); + } + + public void setStatus(String text) { + if (text.length() > 25) { + this.statusLabel.setText("Searching in file: ..." + text.substring(text.length() - 25)); + } else { + this.statusLabel.setText("Searching in file: " + text); + } + + progressBar.setValue(progressBar.getValue() + 1); + } + + public void addClassName(String className) { + this.classesList.addElement(className); + } + + public void initProgressBar(Integer length) { + progressBar.setMaximum(length); + progressBar.setValue(0); + progressBar.setStringPainted(true); + } + + public boolean isSearching() { + return searching; + } + + public void setSearching(boolean searching) { + this.searching = searching; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/FindBox.java b/src/main/java/us/deathmarine/luyten/FindBox.java new file mode 100644 index 00000000..3b316adb --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FindBox.java @@ -0,0 +1,234 @@ +package us.deathmarine.luyten; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rtextarea.SearchContext; +import org.fife.ui.rtextarea.SearchEngine; + +public class FindBox extends JDialog { + + private static final long serialVersionUID = -4125409760166690462L; + + JCheckBox mcase; + JCheckBox regex; + JCheckBox wholew; + JCheckBox reverse; + JCheckBox wrap; + private final JButton findButton; + JTextField textField; + private final MainWindow mainWindow; + + public void showFindBox() { + this.setVisible(true); + this.textField.requestFocus(); + this.textField.selectAll(); + } + + public void hideFindBox() { + this.setVisible(false); + } + + public FindBox(final MainWindow mainWindow) { + this.mainWindow = mainWindow; + this.setDefaultCloseOperation(HIDE_ON_CLOSE); + this.setHideOnEscapeButton(); + + JLabel label = new JLabel("Find What:"); + textField = new JTextField(); + + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); + if (pane != null) { + textField.setText(pane.getSelectedText()); + } + mcase = new JCheckBox("Match Case"); + regex = new JCheckBox("Regex"); + wholew = new JCheckBox("Whole Words"); + reverse = new JCheckBox("Search Backwards"); + wrap = new JCheckBox("Wrap"); + + findButton = new JButton("Find"); + findButton.addActionListener(new FindButton()); + this.getRootPane().setDefaultButton(findButton); + + KeyStroke funcF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0, false); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(funcF3, "FindNext"); + this.getRootPane().getActionMap().put("FindNext", new FindExploreAction(true)); + + KeyStroke sfuncF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK, false); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sfuncF3, "FindPrevious"); + this.getRootPane().getActionMap().put("FindPrevious", new FindExploreAction(false)); + + mcase.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + regex.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + wholew.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + reverse.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + wrap.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + final Dimension center = new Dimension((int) (screenSize.width * 0.35), + Math.min((int) (screenSize.height * 0.20), 200)); + final int x = (int) (center.width * 0.2); + final int y = (int) (center.height * 0.2); + this.setBounds(x, y, center.width, center.height); + this.setResizable(false); + + GroupLayout layout = new GroupLayout(getRootPane()); + getRootPane().setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(label) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(textField) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(mcase) + .addComponent(wholew).addComponent(wrap)) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(regex) + .addComponent(reverse)))) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(findButton))); + + layout.linkSize(SwingConstants.HORIZONTAL, findButton); + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(textField) + .addComponent(findButton)) + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(mcase) + .addComponent(regex)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(wholew) + .addComponent(reverse)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(wrap))))); + + this.adjustWindowPositionBySavedState(); + this.setSaveWindowPositionOnClosing(); + + this.setName("Find"); + this.setTitle("Find"); + this.setVisible(true); + } + + private class FindButton extends AbstractAction { + private static final long serialVersionUID = 75954129199541874L; + + @Override + public void actionPerformed(ActionEvent event) { + if (textField.getText().length() == 0) + return; + + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); + if (pane == null) + return; + + SearchContext context = new SearchContext(); + context.setSearchFor(textField.getText()); + context.setMatchCase(mcase.isSelected()); + context.setRegularExpression(regex.isSelected()); + context.setSearchForward(!reverse.isSelected()); + context.setWholeWord(wholew.isSelected()); + + if (!SearchEngine.find(pane, context).wasFound()) { + if (wrap.isSelected()) { + pane.setSelectionStart(0); + pane.setSelectionEnd(0); + } else { + mainWindow.getLabel().setText("Search Complete"); + } + } + } + + } + + private void setHideOnEscapeButton() { + Action escapeAction = new AbstractAction() { + private static final long serialVersionUID = 5572504000935312338L; + + @Override + public void actionPerformed(ActionEvent e) { + FindBox.this.setVisible(false); + } + }; + + KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE"); + this.getRootPane().getActionMap().put("ESCAPE", escapeAction); + } + + private void adjustWindowPositionBySavedState() { + WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); + + if (windowPosition.isSavedWindowPositionValid()) { + this.setLocation(windowPosition.getWindowX(), windowPosition.getWindowY()); + } + } + + private void setSaveWindowPositionOnClosing() { + this.addWindowListener(new WindowAdapter() { + @Override + public void windowDeactivated(WindowEvent e) { + WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); + windowPosition.readPositionFromDialog(FindBox.this); + } + }); + } + + public void fireExploreAction(boolean direction) { + new FindExploreAction(direction).actionPerformed(null); + } + + class FindExploreAction extends AbstractAction { + /** + * + */ + private static final long serialVersionUID = -4391670062679240573L; + boolean direction; + + public FindExploreAction(boolean forward) { + direction = forward; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (textField.getText().length() == 0) + return; + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); + if (pane == null) + return; + SearchContext context = new SearchContext(); + context.setSearchFor(textField.getText()); + context.setMatchCase(mcase.isSelected()); + context.setRegularExpression(regex.isSelected()); + context.setSearchForward(direction); + context.setWholeWord(wholew.isSelected()); + + if (!SearchEngine.find(pane, context).wasFound()) { + if (wrap.isSelected()) { + pane.setSelectionStart(0); + pane.setSelectionEnd(0); + } else { + mainWindow.getLabel().setText("Search Complete"); + } + } + + } + + } + +} diff --git a/src/main/java/us/deathmarine/luyten/JFontChooser.java b/src/main/java/us/deathmarine/luyten/JFontChooser.java new file mode 100644 index 00000000..55786131 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/JFontChooser.java @@ -0,0 +1,755 @@ +package us.deathmarine.luyten; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.DefaultListCellRenderer; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.text.Position; + +/** + * The JFontChooser class is a swing component for font selection. + * This class has JFileChooser like APIs. The following code pops + * up a font chooser dialog. + **/ +public class JFontChooser extends JComponent { + + /** + * + */ + private static final long serialVersionUID = 8856126034081661L; + // class variables + /** + * Return value from showDialog(). + * + * @see #showDialog + **/ + public static final int OK_OPTION = 0; + /** + * Return value from showDialog(). + * + * @see #showDialog + **/ + public static final int CANCEL_OPTION = 1; + /** + * Return value from showDialog(). + * + * @see #showDialog + **/ + public static final int ERROR_OPTION = -1; + private static final Font DEFAULT_SELECTED_FONT = new Font("Serif", Font.PLAIN, 12); + private static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 10); + private static final int[] FONT_STYLE_CODES = {Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC}; + private static final String[] DEFAULT_FONT_SIZE_STRINGS = {"8", "9", "10", "11", "12", "14", "16", "18", "20", + "22", "24", "26", "28", "36", "48", "72",}; + + // instance variables + protected int dialogResultValue = ERROR_OPTION; + + protected DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer(); + + private String[] fontStyleNames; + private String[] fontFamilyNames; + private final String[] fontSizeStrings; + private JTextField fontFamilyTextField; + private JTextField fontStyleTextField; + private JTextField fontSizeTextField; + private JList fontNameList; + private JList fontStyleList; + private JList fontSizeList; + private JPanel fontNamePanel; + private JPanel fontStylePanel; + private JPanel fontSizePanel; + private JPanel samplePanel; + private JTextField sampleText; + + /** + * Constructs a JFontChooser object. + **/ + public JFontChooser() { + this(DEFAULT_FONT_SIZE_STRINGS); + } + + /** + * Constructs a JFontChooser object using the given font size + * array. + * + * @param fontSizeStrings the array of font size string. + **/ + public JFontChooser(String[] fontSizeStrings) { + if (fontSizeStrings == null) { + fontSizeStrings = DEFAULT_FONT_SIZE_STRINGS; + } + this.fontSizeStrings = fontSizeStrings; + + JPanel selectPanel = new JPanel(); + selectPanel.setLayout(new BoxLayout(selectPanel, BoxLayout.X_AXIS)); + selectPanel.add(getFontFamilyPanel()); + selectPanel.add(getFontStylePanel()); + selectPanel.add(getFontSizePanel()); + + JPanel contentsPanel = new JPanel(); + contentsPanel.setLayout(new GridLayout(2, 1)); + contentsPanel.add(selectPanel, BorderLayout.NORTH); + contentsPanel.add(getSamplePanel(), BorderLayout.CENTER); + + this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + this.add(contentsPanel); + this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + this.setSelectedFont(DEFAULT_SELECTED_FONT); + } + + public JTextField getFontFamilyTextField() { + if (fontFamilyTextField == null) { + fontFamilyTextField = new JTextField(); + fontFamilyTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontFamilyTextField)); + fontFamilyTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontFamilyList())); + fontFamilyTextField.getDocument() + .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontFamilyList())); + fontFamilyTextField.setFont(DEFAULT_FONT); + + } + return fontFamilyTextField; + } + + public JTextField getFontStyleTextField() { + if (fontStyleTextField == null) { + fontStyleTextField = new JTextField(); + fontStyleTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontStyleTextField)); + fontStyleTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontStyleList())); + fontStyleTextField.getDocument() + .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontStyleList())); + fontStyleTextField.setFont(DEFAULT_FONT); + + } + return fontStyleTextField; + } + + public JTextField getFontSizeTextField() { + if (fontSizeTextField == null) { + fontSizeTextField = new JTextField(); + fontSizeTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontSizeTextField)); + fontSizeTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontSizeList())); + fontSizeTextField.getDocument() + .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontSizeList())); + fontSizeTextField.setFont(DEFAULT_FONT); + } + return fontSizeTextField; + } + + public JList getFontFamilyList() { + if (fontNameList == null) { + fontNameList = new JList(getFontFamilies()); + fontNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + fontNameList.addListSelectionListener(new ListSelectionHandler(getFontFamilyTextField())); + fontNameList.setSelectedIndex(0); + + // Draw Fonts + fontNameList.setCellRenderer(new DefaultListCellRenderer() { + /** + * + */ + private static final long serialVersionUID = -6753380853569310954L; + + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + + JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, + isSelected, cellHasFocus); + + if (value instanceof String) { + renderer.setText((String) value); + renderer.setFont(new Font((String) value, DEFAULT_FONT.getStyle(), DEFAULT_FONT.getSize() + 2)); + } else { + renderer.setText(""); + } + return renderer; + } + }); + fontNameList.setFocusable(false); + } + return fontNameList; + } + + public JList getFontStyleList() { + if (fontStyleList == null) { + fontStyleList = new JList(getFontStyleNames()); + fontStyleList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + fontStyleList.addListSelectionListener(new ListSelectionHandler(getFontStyleTextField())); + fontStyleList.setSelectedIndex(0); + fontStyleList.setFocusable(false); + fontStyleList.setCellRenderer(new DefaultListCellRenderer() { + /** + * + */ + private static final long serialVersionUID = -3904668242514776943L; + + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + + JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, + isSelected, cellHasFocus); + + if (value instanceof String) { + renderer.setText((String) value); + renderer.setFont( + new Font(DEFAULT_FONT.getName(), FONT_STYLE_CODES[index], DEFAULT_FONT.getSize() + 2)); + } else { + renderer.setText(""); + } + return renderer; + } + }); + } + return fontStyleList; + } + + public JList getFontSizeList() { + if (fontSizeList == null) { + fontSizeList = new JList(this.fontSizeStrings); + fontSizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + fontSizeList.addListSelectionListener(new ListSelectionHandler(getFontSizeTextField())); + fontSizeList.setSelectedIndex(0); + fontSizeList.setFont(DEFAULT_FONT); + fontSizeList.setFocusable(false); + } + return fontSizeList; + } + + /** + * Get the family name of the selected font. + * + * @return the font family of the selected font. + * @see #setSelectedFontFamily + **/ + public String getSelectedFontFamily() { + return (String) getFontFamilyList().getSelectedValue(); + } + + /** + * Get the style of the selected font. + * + * @return the style of the selected font. Font.PLAIN, + * Font.BOLD, Font.ITALIC, + * Font.BOLD|Font.ITALIC + * @see java.awt.Font#PLAIN + * @see java.awt.Font#BOLD + * @see java.awt.Font#ITALIC + * @see #setSelectedFontStyle + **/ + public int getSelectedFontStyle() { + int index = getFontStyleList().getSelectedIndex(); + return FONT_STYLE_CODES[index]; + } + + /** + * Get the size of the selected font. + * + * @return the size of the selected font + * @see #setSelectedFontSize + **/ + public int getSelectedFontSize() { + int fontSize; + String fontSizeString = getFontSizeTextField().getText(); + while (true) { + try { + fontSize = Integer.parseInt(fontSizeString); + break; + } catch (NumberFormatException e) { + fontSizeString = (String) getFontSizeList().getSelectedValue(); + getFontSizeTextField().setText(fontSizeString); + } + } + + return fontSize; + } + + /** + * Get the selected font. + * + * @return the selected font + * @see #setSelectedFont + * @see java.awt.Font + **/ + public Font getSelectedFont() { + return new Font(getSelectedFontFamily(), getSelectedFontStyle(), getSelectedFontSize()); + } + + /** + * Set the family name of the selected font. + * + * @param name the family name of the selected font. + * @see #getSelectedFontFamily + **/ + public void setSelectedFontFamily(String name) { + String[] names = getFontFamilies(); + for (int i = 0; i < names.length; i++) { + if (names[i].equalsIgnoreCase(name)) { + getFontFamilyList().setSelectedIndex(i); + break; + } + } + updateSampleFont(); + } + + /** + * Set the style of the selected font. + * + * @param style the size of the selected font. Font.PLAIN, + * Font.BOLD, Font.ITALIC, or + * Font.BOLD|Font.ITALIC. + * @see java.awt.Font#PLAIN + * @see java.awt.Font#BOLD + * @see java.awt.Font#ITALIC + * @see #getSelectedFontStyle + **/ + public void setSelectedFontStyle(int style) { + for (int i = 0; i < FONT_STYLE_CODES.length; i++) { + if (FONT_STYLE_CODES[i] == style) { + getFontStyleList().setSelectedIndex(i); + break; + } + } + updateSampleFont(); + } + + /** + * Set the size of the selected font. + * + * @param size the size of the selected font + * @see #getSelectedFontSize + **/ + public void setSelectedFontSize(int size) { + String sizeString = String.valueOf(size); + for (int i = 0; i < this.fontSizeStrings.length; i++) { + if (this.fontSizeStrings[i].equals(sizeString)) { + getFontSizeList().setSelectedIndex(i); + break; + } + } + getFontSizeTextField().setText(sizeString); + updateSampleFont(); + } + + /** + * Set the selected font. + * + * @param font the selected font + * @see #getSelectedFont + * @see java.awt.Font + **/ + public void setSelectedFont(Font font) { + setSelectedFontFamily(font.getFamily()); + setSelectedFontStyle(font.getStyle()); + setSelectedFontSize(font.getSize()); + } + + public String getVersionString() { + return ("Version"); + } + + /** + * Show font selection dialog. + * + * @param parent Dialog's Parent component. + * @return OK_OPTION, CANCEL_OPTION or ERROR_OPTION + * @see #OK_OPTION + * @see #CANCEL_OPTION + * @see #ERROR_OPTION + **/ + public int showDialog(Component parent) { + dialogResultValue = ERROR_OPTION; + JDialog dialog = createDialog(parent); + dialog.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + dialogResultValue = CANCEL_OPTION; + } + }); + + dialog.setVisible(true); + dialog.dispose(); + + return dialogResultValue; + } + + protected class ListSelectionHandler implements ListSelectionListener { + private final JTextComponent textComponent; + + ListSelectionHandler(JTextComponent textComponent) { + this.textComponent = textComponent; + } + + public void valueChanged(ListSelectionEvent e) { + if (!e.getValueIsAdjusting()) { + JList list = (JList) e.getSource(); + String selectedValue = (String) list.getSelectedValue(); + + String oldValue = textComponent.getText(); + textComponent.setText(selectedValue); + if (!oldValue.equalsIgnoreCase(selectedValue)) { + textComponent.selectAll(); + textComponent.requestFocus(); + } + + updateSampleFont(); + } + } + } + + protected class TextFieldFocusHandlerForTextSelection extends FocusAdapter { + private final JTextComponent textComponent; + + public TextFieldFocusHandlerForTextSelection(JTextComponent textComponent) { + this.textComponent = textComponent; + } + + public void focusGained(FocusEvent e) { + textComponent.selectAll(); + } + + public void focusLost(FocusEvent e) { + textComponent.select(0, 0); + updateSampleFont(); + } + } + + protected static class TextFieldKeyHandlerForListSelectionUpDown extends KeyAdapter { + private final JList targetList; + + public TextFieldKeyHandlerForListSelectionUpDown(JList list) { + this.targetList = list; + } + + public void keyPressed(KeyEvent e) { + int i; + switch (e.getKeyCode()) { + case KeyEvent.VK_UP: + i = targetList.getSelectedIndex() - 1; + if (i < 0) { + i = 0; + } + targetList.setSelectedIndex(i); + break; + case KeyEvent.VK_DOWN: + int listSize = targetList.getModel().getSize(); + i = targetList.getSelectedIndex() + 1; + if (i >= listSize) { + i = listSize - 1; + } + targetList.setSelectedIndex(i); + break; + default: + break; + } + } + } + + protected static class ListSearchTextFieldDocumentHandler implements DocumentListener { + JList targetList; + + public ListSearchTextFieldDocumentHandler(JList targetList) { + this.targetList = targetList; + } + + public void insertUpdate(DocumentEvent e) { + update(e); + } + + public void removeUpdate(DocumentEvent e) { + update(e); + } + + public void changedUpdate(DocumentEvent e) { + update(e); + } + + private void update(DocumentEvent event) { + String newValue = ""; + try { + Document doc = event.getDocument(); + newValue = doc.getText(0, doc.getLength()); + } catch (BadLocationException e) { + Luyten.showExceptionDialog("Exception!", e); + } + + if (newValue.length() > 0) { + int index = targetList.getNextMatch(newValue, 0, Position.Bias.Forward); + if (index < 0) { + index = 0; + } + targetList.ensureIndexIsVisible(index); + + String matchedName = targetList.getModel().getElementAt(index).toString(); + if (newValue.equalsIgnoreCase(matchedName)) { + if (index != targetList.getSelectedIndex()) { + SwingUtilities.invokeLater(new ListSelector(index)); + } + } + } + } + + public class ListSelector implements Runnable { + private final int index; + + public ListSelector(int index) { + this.index = index; + } + + public void run() { + targetList.setSelectedIndex(this.index); + } + } + } + + protected class DialogOKAction extends AbstractAction { + /** + * + */ + private static final long serialVersionUID = 1618273732543947323L; + protected static final String ACTION_NAME = "OK"; + private final JDialog dialog; + + protected DialogOKAction(JDialog dialog) { + this.dialog = dialog; + putValue(Action.DEFAULT, ACTION_NAME); + putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); + putValue(Action.NAME, (ACTION_NAME)); + } + + public void actionPerformed(ActionEvent e) { + dialogResultValue = OK_OPTION; + dialog.setVisible(false); + } + } + + protected class DialogCancelAction extends AbstractAction { + /** + * + */ + private static final long serialVersionUID = -4941763616565382601L; + protected static final String ACTION_NAME = "Cancel"; + private final JDialog dialog; + + protected DialogCancelAction(JDialog dialog) { + this.dialog = dialog; + putValue(Action.DEFAULT, ACTION_NAME); + putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); + putValue(Action.NAME, (ACTION_NAME)); + } + + public void actionPerformed(ActionEvent e) { + dialogResultValue = CANCEL_OPTION; + dialog.setVisible(false); + } + } + + protected JDialog createDialog(Component parent) { + Frame frame = parent instanceof Frame ? (Frame) parent + : (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); + JDialog dialog = new JDialog(frame, ("Select Font"), true); + + Action okAction = new DialogOKAction(dialog); + Action cancelAction = new DialogCancelAction(dialog); + + JButton okButton = new JButton(okAction); + okButton.setFont(DEFAULT_FONT); + JButton cancelButton = new JButton(cancelAction); + cancelButton.setFont(DEFAULT_FONT); + + JPanel buttonsPanel = new JPanel(); + buttonsPanel.setLayout(new GridLayout(2, 1)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + buttonsPanel.setBorder(BorderFactory.createEmptyBorder(25, 0, 10, 10)); + + ActionMap actionMap = buttonsPanel.getActionMap(); + actionMap.put(cancelAction.getValue(Action.DEFAULT), cancelAction); + actionMap.put(okAction.getValue(Action.DEFAULT), okAction); + InputMap inputMap = buttonsPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), cancelAction.getValue(Action.DEFAULT)); + inputMap.put(KeyStroke.getKeyStroke("ENTER"), okAction.getValue(Action.DEFAULT)); + + JPanel dialogEastPanel = new JPanel(); + dialogEastPanel.setLayout(new BorderLayout()); + dialogEastPanel.add(buttonsPanel, BorderLayout.NORTH); + + dialog.getContentPane().add(this, BorderLayout.CENTER); + dialog.getContentPane().add(dialogEastPanel, BorderLayout.EAST); + dialog.pack(); + dialog.setLocationRelativeTo(frame); + return dialog; + } + + protected void updateSampleFont() { + Font font = getSelectedFont(); + getSampleTextField().setFont(font); + } + + protected JPanel getFontFamilyPanel() { + if (fontNamePanel == null) { + fontNamePanel = new JPanel(); + fontNamePanel.setLayout(new BorderLayout()); + fontNamePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + fontNamePanel.setPreferredSize(new Dimension(180, 130)); + + JScrollPane scrollPane = new JScrollPane(getFontFamilyList()); + scrollPane.getVerticalScrollBar().setFocusable(false); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.add(getFontFamilyTextField(), BorderLayout.NORTH); + p.add(scrollPane, BorderLayout.CENTER); + + JLabel label = new JLabel(("Font Name")); + label.setHorizontalAlignment(JLabel.LEFT); + label.setHorizontalTextPosition(JLabel.LEFT); + label.setLabelFor(getFontFamilyTextField()); + label.setDisplayedMnemonic('F'); + + fontNamePanel.add(label, BorderLayout.NORTH); + fontNamePanel.add(p, BorderLayout.CENTER); + + } + return fontNamePanel; + } + + protected JPanel getFontStylePanel() { + if (fontStylePanel == null) { + fontStylePanel = new JPanel(); + fontStylePanel.setLayout(new BorderLayout()); + fontStylePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + fontStylePanel.setPreferredSize(new Dimension(140, 130)); + + JScrollPane scrollPane = new JScrollPane(getFontStyleList()); + scrollPane.getVerticalScrollBar().setFocusable(false); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.add(getFontStyleTextField(), BorderLayout.NORTH); + p.add(scrollPane, BorderLayout.CENTER); + + JLabel label = new JLabel(("Font Style")); + label.setHorizontalAlignment(JLabel.LEFT); + label.setHorizontalTextPosition(JLabel.LEFT); + label.setLabelFor(getFontStyleTextField()); + label.setDisplayedMnemonic('Y'); + + fontStylePanel.add(label, BorderLayout.NORTH); + fontStylePanel.add(p, BorderLayout.CENTER); + } + return fontStylePanel; + } + + protected JPanel getFontSizePanel() { + if (fontSizePanel == null) { + fontSizePanel = new JPanel(); + fontSizePanel.setLayout(new BorderLayout()); + fontSizePanel.setPreferredSize(new Dimension(70, 130)); + fontSizePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + JScrollPane scrollPane = new JScrollPane(getFontSizeList()); + scrollPane.getVerticalScrollBar().setFocusable(false); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.add(getFontSizeTextField(), BorderLayout.NORTH); + p.add(scrollPane, BorderLayout.CENTER); + + JLabel label = new JLabel(("Font Size")); + label.setHorizontalAlignment(JLabel.LEFT); + label.setHorizontalTextPosition(JLabel.LEFT); + label.setLabelFor(getFontSizeTextField()); + label.setDisplayedMnemonic('S'); + + fontSizePanel.add(label, BorderLayout.NORTH); + fontSizePanel.add(p, BorderLayout.CENTER); + } + return fontSizePanel; + } + + protected JPanel getSamplePanel() { + if (samplePanel == null) { + Border titledBorder = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), ("Sample")); + Border empty = BorderFactory.createEmptyBorder(5, 10, 10, 10); + Border border = BorderFactory.createCompoundBorder(titledBorder, empty); + + samplePanel = new JPanel(); + samplePanel.setLayout(new BorderLayout()); + samplePanel.setBorder(border); + + samplePanel.add(getSampleTextField(), BorderLayout.CENTER); + } + return samplePanel; + } + + protected JTextField getSampleTextField() { + if (sampleText == null) { + Border lowered = BorderFactory.createLoweredBevelBorder(); + + sampleText = new JTextField(("AaBbYyZz")); + sampleText.setHorizontalAlignment(JTextField.CENTER); + sampleText.setBorder(lowered); + sampleText.setPreferredSize(new Dimension(300, 100)); + } + return sampleText; + } + + protected String[] getFontFamilies() { + if (fontFamilyNames == null) { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + fontFamilyNames = env.getAvailableFontFamilyNames(); + } + return fontFamilyNames; + } + + protected String[] getFontStyleNames() { + if (fontStyleNames == null) { + int i = 0; + fontStyleNames = new String[4]; + fontStyleNames[i++] = ("Plain"); + fontStyleNames[i++] = ("Bold"); + fontStyleNames[i++] = ("Italic"); + fontStyleNames[i++] = ("BoldItalic"); + } + return fontStyleNames; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/JarEntryFilter.java b/src/main/java/us/deathmarine/luyten/JarEntryFilter.java new file mode 100644 index 00000000..39c4a964 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/JarEntryFilter.java @@ -0,0 +1,83 @@ +package us.deathmarine.luyten; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class JarEntryFilter { + + private JarFile jfile; + + public JarEntryFilter() { + } + + public JarEntryFilter(JarFile jfile) { + this.jfile = jfile; + } + + public List getAllEntriesFromJar() { + List mass = new ArrayList<>(); + Enumeration entries = jfile.entries(); + while (entries.hasMoreElements()) { + JarEntry e = entries.nextElement(); + if (!e.isDirectory()) { + mass.add(e.getName()); + } + } + return mass; + } + + public List getEntriesWithoutInnerClasses() { + List mass = new ArrayList<>(); + Enumeration entries = jfile.entries(); + Set possibleInnerClasses = new HashSet<>(); + Set baseClasses = new HashSet<>(); + + while (entries.hasMoreElements()) { + JarEntry e = entries.nextElement(); + if (!e.isDirectory()) { + String entryName = e.getName(); + + if (entryName.trim().length() > 0) { + entryName = entryName.trim(); + + if (!entryName.endsWith(".class")) { + mass.add(entryName); + + // com/acme/Model$16.class + } else if (entryName.matches(".*[^(/|\\\\)]+\\$[^(/|\\\\)]+$")) { + possibleInnerClasses.add(entryName); + + } else { + baseClasses.add(entryName); + mass.add(entryName); + } + } + } + } + + // keep Badly$Named but not inner classes + for (String inner : possibleInnerClasses) { + + // com/acme/Connection$Conn$1.class -> com/acme/Connection + String innerWithoutTail = inner.replaceAll("\\$[^(/|\\\\)]+\\.class$", ""); + if (!baseClasses.contains(innerWithoutTail + ".class")) { + mass.add(inner); + } + } + return mass; + } + + public JarFile getJfile() { + return jfile; + } + + public void setJfile(JarFile jfile) { + this.jfile = jfile; + } + +} diff --git a/src/us/deathmarine/luyten/Keymap.java b/src/main/java/us/deathmarine/luyten/Keymap.java similarity index 99% rename from src/us/deathmarine/luyten/Keymap.java rename to src/main/java/us/deathmarine/luyten/Keymap.java index c84f26ba..68196b9c 100644 --- a/src/us/deathmarine/luyten/Keymap.java +++ b/src/main/java/us/deathmarine/luyten/Keymap.java @@ -3,6 +3,7 @@ import java.awt.event.InputEvent; public final class Keymap { + /** * Ctrl+click defaults to "context menu" in macOS, so META+click is used there. * @@ -11,4 +12,5 @@ public final class Keymap { public static int ctrlDownModifier() { return SystemInfo.IS_MAC ? InputEvent.META_DOWN_MASK : InputEvent.CTRL_DOWN_MASK; } + } diff --git a/src/main/java/us/deathmarine/luyten/LinkProvider.java b/src/main/java/us/deathmarine/luyten/LinkProvider.java new file mode 100644 index 00000000..cf104a38 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/LinkProvider.java @@ -0,0 +1,22 @@ +package us.deathmarine.luyten; + +import java.util.Map; +import java.util.Set; + +public interface LinkProvider { + + void generateContent(); + + String getTextContent(); + + void processLinks(); + + Map getDefinitionToSelectionMap(); + + Map> getReferenceToSelectionsMap(); + + boolean isLinkNavigable(String uniqueStr); + + String getLinkDescription(String uniqueStr); + +} diff --git a/src/main/java/us/deathmarine/luyten/Luyten.java b/src/main/java/us/deathmarine/luyten/Luyten.java new file mode 100644 index 00000000..1c9effe9 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/Luyten.java @@ -0,0 +1,269 @@ +package us.deathmarine.luyten; + +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.BevelBorder; +import javax.swing.border.CompoundBorder; +import javax.swing.text.DefaultEditorKit; + +/** + * Starter, the main class + */ +public class Luyten { + + private static final AtomicReference mainWindowRef = new AtomicReference<>(); + private static final List pendingFiles = new ArrayList<>(); + private static ServerSocket lockSocket = null; + + public static void main(final String[] args) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + if (lockSocket != null) { + lockSocket.close(); + } + } catch (IOException ignored) { + } + })); + + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + + // for TotalCommander External Viewer setting: + // javaw -jar "c:\Program Files\Luyten\luyten.jar" + // (TC will not complain about temporary file when opening .class from + // .zip or .jar) + final File fileFromCommandLine = getFileFromCommandLine(args); + + try { + launchMainInstance(fileFromCommandLine); + } catch (Exception e) { + // Instance already exists. Open new file in running instance + try { + Socket socket = new Socket("localhost", 3456); + DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); + dos.writeUTF(args[0]); + dos.flush(); + dos.close(); + socket.close(); + } catch (IOException ex) { + showExceptionDialog("Exception", e); + } + } + } + + private static void launchMainInstance(final File fileFromCommandLine) throws IOException { + lockSocket = new ServerSocket(3456); + launchSession(fileFromCommandLine); + new Thread(Luyten::launchServer).start(); + } + + private static void launchSession(final File fileFromCommandLine) { + SwingUtilities.invokeLater(() -> { + if (!mainWindowRef.compareAndSet(null, new MainWindow(fileFromCommandLine))) { + // Already set - so add the files to open + addToPendingFiles(fileFromCommandLine); + } + processPendingFiles(); + mainWindowRef.get().setVisible(true); + }); + } + + private static void launchServer() { + try { // Server + while (true) { + Socket socket = lockSocket.accept(); + DataInputStream dis = new DataInputStream(socket.getInputStream()); + addToPendingFiles(getFileFromCommandLine(dis.readUTF())); + processPendingFiles(); + dis.close(); + socket.close(); + } + } catch (SocketException ignored) { + // Ignore exception on shutdown + } catch (IOException e) { // Client + showExceptionDialog("Exception", e); + } + } + + // Private function which processes all pending files - synchronized on the + // list of pending files + public static void processPendingFiles() { + final MainWindow mainWindow = mainWindowRef.get(); + if (mainWindow != null) { + synchronized (pendingFiles) { + for (File f : pendingFiles) { + mainWindow.loadNewFile(f); + } + pendingFiles.clear(); + } + } + } + + // Function which opens the given file in the instance, if it's running - + // and if not, it processes the files + public static void addToPendingFiles(File fileToOpen) { + synchronized (pendingFiles) { + if (fileToOpen != null) { + pendingFiles.add(fileToOpen); + } + } + } + + // Function which exits the application if it's running + public static void quitInstance() { + final MainWindow mainWindow = mainWindowRef.get(); + if (mainWindow != null) { + mainWindow.onExitMenu(); + } + } + + public static File getFileFromCommandLine(String... args) { + File fileFromCommandLine = null; + try { + if (args.length > 0) { + String realFileName = new File(args[0]).getCanonicalPath(); + fileFromCommandLine = new File(realFileName); + } + } catch (Exception e) { + e.printStackTrace(); + } + return fileFromCommandLine; + } + + public static String getVersion() { + String result = ""; + try { + String line; + BufferedReader br = new BufferedReader(new InputStreamReader( + ClassLoader.getSystemResourceAsStream("META-INF/maven/us.deathmarine/luyten/pom.properties"))); + while ((line = br.readLine()) != null) { + if (line.contains("version")) + result = line.split("=")[1]; + } + br.close(); + } catch (Exception e) { + return result; + } + return result; + + } + + /** + * Method allows for users to copy the stacktrace for reporting any issues. + * Add Cool Hyperlink Enhanced for mouse users. + * + * @param message + * @param e + */ + public static void showExceptionDialog(String message, Exception e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + String stacktrace = sw.toString(); + try { + sw.close(); + pw.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + System.out.println(stacktrace); + + JPanel pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); + if (message.contains("\n")) { + for (String s : message.split("\n")) { + pane.add(new JLabel(s)); + } + } else { + pane.add(new JLabel(message)); + } + pane.add(new JLabel(" \n")); // Whitespace + final JTextArea exception = new JTextArea(25, 100); + exception.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 10)); + exception.setText(stacktrace); + exception.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + new JPopupMenu() { + { + JMenuItem menuitem = new JMenuItem("Select All"); + menuitem.addActionListener(e12 -> { + exception.requestFocus(); + exception.selectAll(); + }); + this.add(menuitem); + menuitem = new JMenuItem("Copy"); + menuitem.addActionListener(new DefaultEditorKit.CopyAction()); + this.add(menuitem); + } + + private static final long serialVersionUID = 562054483562666832L; + }.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + JScrollPane scroll = new JScrollPane(exception); + scroll.setBorder(new CompoundBorder(BorderFactory.createTitledBorder("Stacktrace"), + new BevelBorder(BevelBorder.LOWERED))); + pane.add(scroll); + final String issue = "https://github.com/deathmarine/Luyten/issues"; + final JLabel link = new JLabel("Submit to " + issue + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + try { + Desktop.getDesktop().browse(new URI(issue)); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + link.setText("Submit to " + issue + ""); + } + + @Override + public void mouseExited(MouseEvent e) { + link.setText("Submit to " + issue + ""); + } + }); + pane.add(link); + JOptionPane.showMessageDialog(null, pane, "Error!", JOptionPane.ERROR_MESSAGE); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/LuytenOsx.java b/src/main/java/us/deathmarine/luyten/LuytenOsx.java new file mode 100644 index 00000000..3a574fae --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/LuytenOsx.java @@ -0,0 +1,33 @@ +package us.deathmarine.luyten; + +import com.apple.eawt.Application; +import com.apple.eawt.ApplicationAdapter; +import com.apple.eawt.ApplicationEvent; +import java.io.File; + +/** + * An OS X-specific initialization method for dragging/dropping + */ +public class LuytenOsx extends Luyten { + + public static void main(String[] args) { + // Add an adapter as the handler to a new instance of the application + // class + @SuppressWarnings("deprecation") + Application app = new Application(); + app.addApplicationListener(new ApplicationAdapter() { + public void handleOpenFile(ApplicationEvent e) { + Luyten.addToPendingFiles(new File(e.getFilename())); + Luyten.processPendingFiles(); + } + + public void handleQuit(ApplicationEvent e) { + Luyten.quitInstance(); + } + }); + + // Call the superclass's main function + Luyten.main(args); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/LuytenPreferences.java b/src/main/java/us/deathmarine/luyten/LuytenPreferences.java new file mode 100644 index 00000000..09b6a9e7 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/LuytenPreferences.java @@ -0,0 +1,89 @@ +package us.deathmarine.luyten; + +/** + * Do not instantiate this class, get the instance from ConfigSaver. All + * not-static fields will be saved automatically named by the field's java + * variable name. (Watch for collisions with existing IDs defined in + * ConfigSaver.) Only String, boolean and int fields are supported. Write + * default values into the field declarations. + */ +public class LuytenPreferences { + + public static final String THEME_XML_PATH = "/org/fife/ui/rsyntaxtextarea/themes/"; + public static final String DEFAULT_THEME_XML = "eclipse.xml"; + + private String themeXml = DEFAULT_THEME_XML; + private String fileOpenCurrentDirectory = ""; + private String fileSaveCurrentDirectory = ""; + private int font_size = 10; + + private boolean isPackageExplorerStyle = true; + private boolean isFilterOutInnerClassEntries = true; + private boolean isSingleClickOpenEnabled = true; + private boolean isExitByEscEnabled = false; + + public String getThemeXml() { + return themeXml; + } + + public void setThemeXml(String themeXml) { + this.themeXml = themeXml; + } + + public String getFileOpenCurrentDirectory() { + return fileOpenCurrentDirectory; + } + + public void setFileOpenCurrentDirectory(String fileOpenCurrentDirectory) { + this.fileOpenCurrentDirectory = fileOpenCurrentDirectory; + } + + public String getFileSaveCurrentDirectory() { + return fileSaveCurrentDirectory; + } + + public void setFileSaveCurrentDirectory(String fileSaveCurrentDirectory) { + this.fileSaveCurrentDirectory = fileSaveCurrentDirectory; + } + + public boolean isPackageExplorerStyle() { + return isPackageExplorerStyle; + } + + public void setPackageExplorerStyle(boolean isPackageExplorerStyle) { + this.isPackageExplorerStyle = isPackageExplorerStyle; + } + + public boolean isFilterOutInnerClassEntries() { + return isFilterOutInnerClassEntries; + } + + public void setFilterOutInnerClassEntries(boolean isFilterOutInnerClassEntries) { + this.isFilterOutInnerClassEntries = isFilterOutInnerClassEntries; + } + + public boolean isSingleClickOpenEnabled() { + return isSingleClickOpenEnabled; + } + + public void setSingleClickOpenEnabled(boolean isSingleClickOpenEnabled) { + this.isSingleClickOpenEnabled = isSingleClickOpenEnabled; + } + + public boolean isExitByEscEnabled() { + return isExitByEscEnabled; + } + + public void setExitByEscEnabled(boolean isExitByEscEnabled) { + this.isExitByEscEnabled = isExitByEscEnabled; + } + + public int getFont_size() { + return font_size; + } + + public void setFont_size(int font_size) { + this.font_size = font_size; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/LuytenTypeLoader.java b/src/main/java/us/deathmarine/luyten/LuytenTypeLoader.java new file mode 100644 index 00000000..9005b46e --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/LuytenTypeLoader.java @@ -0,0 +1,35 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.InputTypeLoader; +import com.strobel.assembler.metadata.Buffer; +import com.strobel.assembler.metadata.ITypeLoader; +import java.util.ArrayList; +import java.util.List; + +public final class LuytenTypeLoader implements ITypeLoader { + + private final List _typeLoaders; + + public LuytenTypeLoader() { + _typeLoaders = new ArrayList<>(); + _typeLoaders.add(new InputTypeLoader()); + } + + public List getTypeLoaders() { + return _typeLoaders; + } + + @Override + public boolean tryLoadType(final String internalName, final Buffer buffer) { + for (final ITypeLoader typeLoader : _typeLoaders) { + if (typeLoader.tryLoadType(internalName, buffer)) { + return true; + } + + buffer.reset(); + } + + return false; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/MainMenuBar.java b/src/main/java/us/deathmarine/luyten/MainMenuBar.java new file mode 100644 index 00000000..62070a68 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/MainMenuBar.java @@ -0,0 +1,587 @@ +package us.deathmarine.luyten; + +import com.strobel.Procyon; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.languages.Language; +import com.strobel.decompiler.languages.Languages; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.ListIterator; +import java.util.Map; +import javax.swing.AbstractAction; +import javax.swing.AbstractButton; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.ButtonModel; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JTabbedPane; +import javax.swing.KeyStroke; +import javax.swing.text.DefaultEditorKit; + +/** + * Main menu (only MainWindow should be called from here) + */ +public class MainMenuBar extends JMenuBar { + + private static final long serialVersionUID = -7949855817172562075L; + private final MainWindow mainWindow; + private final Map languageLookup = new HashMap<>(); + + private JMenu recentFiles; + private JMenuItem clearRecentFiles; + private JCheckBoxMenuItem flattenSwitchBlocks; + private JCheckBoxMenuItem forceExplicitImports; + private JCheckBoxMenuItem forceExplicitTypes; + private JCheckBoxMenuItem showSyntheticMembers; + private JCheckBoxMenuItem excludeNestedTypes; + private JCheckBoxMenuItem retainRedundantCasts; + private JCheckBoxMenuItem unicodeReplacement; + private JCheckBoxMenuItem debugLineNumbers; + private JCheckBoxMenuItem showDebugInfo; + private JCheckBoxMenuItem bytecodeLineNumbers; + private JRadioButtonMenuItem java; + private JRadioButtonMenuItem bytecode; + private JRadioButtonMenuItem bytecodeAST; + private ButtonGroup languagesGroup; + private ButtonGroup themesGroup; + private JCheckBoxMenuItem packageExplorerStyle; + private JCheckBoxMenuItem filterOutInnerClassEntries; + private JCheckBoxMenuItem singleClickOpenEnabled; + private JCheckBoxMenuItem exitByEscEnabled; + private final DecompilerSettings settings; + private final LuytenPreferences luytenPrefs; + + public MainMenuBar(MainWindow mainWnd) { + this.mainWindow = mainWnd; + final ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); + settings = configSaver.getDecompilerSettings(); + luytenPrefs = configSaver.getLuytenPreferences(); + + final JMenu fileMenu = new JMenu("File"); + fileMenu.add(new JMenuItem("...")); + this.add(fileMenu); + final JMenu editMenu = new JMenu("Edit"); + editMenu.add(new JMenuItem("...")); + this.add(editMenu); + final JMenu themesMenu = new JMenu("Themes"); + themesMenu.add(new JMenuItem("...")); + this.add(themesMenu); + final JMenu operationMenu = new JMenu("Operation"); + operationMenu.add(new JMenuItem("...")); + this.add(operationMenu); + final JMenu settingsMenu = new JMenu("Settings"); + settingsMenu.add(new JMenuItem("...")); + this.add(settingsMenu); + final JMenu helpMenu = new JMenu("Help"); + helpMenu.add(new JMenuItem("...")); + this.add(helpMenu); + + // start quicker + new Thread() { + public void run() { + try { + // build menu later + buildFileMenu(fileMenu); + refreshMenuPopup(fileMenu); + + buildEditMenu(editMenu); + refreshMenuPopup(editMenu); + + buildThemesMenu(themesMenu); + refreshMenuPopup(themesMenu); + + buildOperationMenu(operationMenu); + refreshMenuPopup(operationMenu); + + buildSettingsMenu(settingsMenu); + refreshMenuPopup(settingsMenu); + + buildHelpMenu(helpMenu); + refreshMenuPopup(helpMenu); + + updateRecentFiles(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + // refresh currently opened menu + // (if user selected a menu before it was ready) + private void refreshMenuPopup(JMenu menu) { + try { + if (menu.isPopupMenuVisible()) { + menu.getPopupMenu().setVisible(false); + menu.getPopupMenu().setVisible(true); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + }.start(); + } + + public void updateRecentFiles() { + if (RecentFiles.paths.isEmpty()) { + recentFiles.setEnabled(false); + clearRecentFiles.setEnabled(false); + return; + } else { + recentFiles.setEnabled(true); + clearRecentFiles.setEnabled(true); + } + + recentFiles.removeAll(); + ListIterator li = RecentFiles.paths.listIterator(RecentFiles.paths.size()); + boolean rfSaveNeeded = false; + + while (li.hasPrevious()) { + String path = li.previous(); + final File file = new File(path); + + if (!file.exists()) { + rfSaveNeeded = true; + continue; + } + + JMenuItem menuItem = new JMenuItem(path); + menuItem.addActionListener(e -> mainWindow.loadNewFile(file)); + recentFiles.add(menuItem); + } + + if (rfSaveNeeded) RecentFiles.save(); + } + + private void buildFileMenu(final JMenu fileMenu) { + fileMenu.removeAll(); + JMenuItem menuItem = new JMenuItem("Open File..."); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onOpenFileMenu()); + fileMenu.add(menuItem); + fileMenu.addSeparator(); + + menuItem = new JMenuItem("Close File"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> { + JTabbedPane house = mainWindow.getSelectedModel().house; + + if (e.getModifiers() != InputEvent.CTRL_MASK || house.getTabCount() == 0) + mainWindow.onCloseFileMenu(); + else { + mainWindow.getSelectedModel().closeOpenTab(house.getSelectedIndex()); + } + }); + fileMenu.add(menuItem); + fileMenu.addSeparator(); + + menuItem = new JMenuItem("Save As..."); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onSaveAsMenu()); + fileMenu.add(menuItem); + + menuItem = new JMenuItem("Save All..."); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onSaveAllMenu()); + fileMenu.add(menuItem); + fileMenu.addSeparator(); + + recentFiles = new JMenu("Recent Files"); + fileMenu.add(recentFiles); + + clearRecentFiles = new JMenuItem("Clear Recent Files"); + clearRecentFiles.addActionListener(e -> { + RecentFiles.paths.clear(); + RecentFiles.save(); + updateRecentFiles(); + }); + fileMenu.add(clearRecentFiles); + + fileMenu.addSeparator(); + + // Only add the exit command for non-OS X. OS X handles its close + // automatically + if (!Boolean.getBoolean("apple.laf.useScreenMenuBar")) { + menuItem = new JMenuItem("Exit"); + menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK)); + menuItem.addActionListener(e -> mainWindow.onExitMenu()); + fileMenu.add(menuItem); + } + } + + private void buildEditMenu(JMenu editMenu) { + editMenu.removeAll(); + JMenuItem menuItem = new JMenuItem("Cut"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.setEnabled(false); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Copy"); + menuItem.addActionListener(new DefaultEditorKit.CopyAction()); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Paste"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.setEnabled(false); + editMenu.add(menuItem); + + editMenu.addSeparator(); + + menuItem = new JMenuItem("Select All"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onSelectAllMenu()); + editMenu.add(menuItem); + editMenu.addSeparator(); + + menuItem = new JMenuItem("Find..."); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onFindMenu()); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Find Next"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)); + menuItem.addActionListener(e -> { + if (mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(true); + }); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Find Previous"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK)); + menuItem.addActionListener(e -> { + if (mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(false); + }); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Find All"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onFindAllMenu()); + editMenu.add(menuItem); + } + + private void buildThemesMenu(JMenu themesMenu) { + themesMenu.removeAll(); + themesGroup = new ButtonGroup(); + JRadioButtonMenuItem a = new JRadioButtonMenuItem(new ThemeAction("Default", "default.xml")); + a.setSelected("default.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("Default-Alt", "default-alt.xml")); + a.setSelected("default-alt.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("Dark", "dark.xml")); + a.setSelected("dark.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("Eclipse", "eclipse.xml")); + a.setSelected("eclipse.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("Visual Studio", "vs.xml")); + a.setSelected("vs.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("IntelliJ", "idea.xml")); + a.setSelected("idea.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + } + + private void buildOperationMenu(JMenu operationMenu) { + operationMenu.removeAll(); + packageExplorerStyle = new JCheckBoxMenuItem("Package Explorer Style"); + packageExplorerStyle.setSelected(luytenPrefs.isPackageExplorerStyle()); + packageExplorerStyle.addActionListener(e -> { + luytenPrefs.setPackageExplorerStyle(packageExplorerStyle.isSelected()); + mainWindow.onTreeSettingsChanged(); + }); + operationMenu.add(packageExplorerStyle); + + filterOutInnerClassEntries = new JCheckBoxMenuItem("Filter Out Inner Class Entries"); + filterOutInnerClassEntries.setSelected(luytenPrefs.isFilterOutInnerClassEntries()); + filterOutInnerClassEntries.addActionListener(e -> { + luytenPrefs.setFilterOutInnerClassEntries(filterOutInnerClassEntries.isSelected()); + mainWindow.onTreeSettingsChanged(); + }); + operationMenu.add(filterOutInnerClassEntries); + + singleClickOpenEnabled = new JCheckBoxMenuItem("Single Click Open"); + singleClickOpenEnabled.setSelected(luytenPrefs.isSingleClickOpenEnabled()); + singleClickOpenEnabled.addActionListener(e -> luytenPrefs.setSingleClickOpenEnabled(singleClickOpenEnabled.isSelected())); + operationMenu.add(singleClickOpenEnabled); + + exitByEscEnabled = new JCheckBoxMenuItem("Exit By Esc"); + exitByEscEnabled.setSelected(luytenPrefs.isExitByEscEnabled()); + exitByEscEnabled.addActionListener(e -> luytenPrefs.setExitByEscEnabled(exitByEscEnabled.isSelected())); + operationMenu.add(exitByEscEnabled); + } + + private void buildSettingsMenu(JMenu settingsMenu) { + settingsMenu.removeAll(); + ActionListener settingsChanged = e -> new Thread(() -> { + populateSettingsFromSettingsMenu(); + mainWindow.onSettingsChanged(); + }).start(); + flattenSwitchBlocks = new JCheckBoxMenuItem("Flatten Switch Blocks"); + flattenSwitchBlocks.setSelected(settings.getFlattenSwitchBlocks()); + flattenSwitchBlocks.addActionListener(settingsChanged); + settingsMenu.add(flattenSwitchBlocks); + + forceExplicitImports = new JCheckBoxMenuItem("Force Explicit Imports"); + forceExplicitImports.setSelected(settings.getForceExplicitImports()); + forceExplicitImports.addActionListener(settingsChanged); + settingsMenu.add(forceExplicitImports); + + forceExplicitTypes = new JCheckBoxMenuItem("Force Explicit Types"); + forceExplicitTypes.setSelected(settings.getForceExplicitTypeArguments()); + forceExplicitTypes.addActionListener(settingsChanged); + settingsMenu.add(forceExplicitTypes); + + showSyntheticMembers = new JCheckBoxMenuItem("Show Synthetic Members"); + showSyntheticMembers.setSelected(settings.getShowSyntheticMembers()); + showSyntheticMembers.addActionListener(settingsChanged); + settingsMenu.add(showSyntheticMembers); + + excludeNestedTypes = new JCheckBoxMenuItem("Exclude Nested Types"); + excludeNestedTypes.setSelected(settings.getExcludeNestedTypes()); + excludeNestedTypes.addActionListener(settingsChanged); + settingsMenu.add(excludeNestedTypes); + + retainRedundantCasts = new JCheckBoxMenuItem("Retain Redundant Casts"); + retainRedundantCasts.setSelected(settings.getRetainRedundantCasts()); + retainRedundantCasts.addActionListener(settingsChanged); + settingsMenu.add(retainRedundantCasts); + + unicodeReplacement = new JCheckBoxMenuItem("Enable Unicode Replacement"); + unicodeReplacement.setSelected(settings.isUnicodeOutputEnabled()); + unicodeReplacement.addActionListener(settingsChanged); + settingsMenu.add(unicodeReplacement); + + debugLineNumbers = new JCheckBoxMenuItem("Show Debug Line Numbers"); + debugLineNumbers.setSelected(settings.getShowDebugLineNumbers()); + debugLineNumbers.addActionListener(settingsChanged); + settingsMenu.add(debugLineNumbers); + + JMenu debugSettingsMenu = new JMenu("Debug Settings"); + showDebugInfo = new JCheckBoxMenuItem("Include Error Diagnostics"); + showDebugInfo.setSelected(settings.getIncludeErrorDiagnostics()); + showDebugInfo.addActionListener(settingsChanged); + + debugSettingsMenu.add(showDebugInfo); + settingsMenu.add(debugSettingsMenu); + settingsMenu.addSeparator(); + + languageLookup.put(Languages.java().getName(), Languages.java()); + languageLookup.put(Languages.bytecode().getName(), Languages.bytecode()); + languageLookup.put(Languages.bytecodeAst().getName(), Languages.bytecodeAst()); + + languagesGroup = new ButtonGroup(); + java = new JRadioButtonMenuItem(Languages.java().getName()); + java.getModel().setActionCommand(Languages.java().getName()); + java.setSelected(Languages.java().getName().equals(settings.getLanguage().getName())); + languagesGroup.add(java); + settingsMenu.add(java); + bytecode = new JRadioButtonMenuItem(Languages.bytecode().getName()); + bytecode.getModel().setActionCommand(Languages.bytecode().getName()); + bytecode.setSelected(Languages.bytecode().getName().equals(settings.getLanguage().getName())); + languagesGroup.add(bytecode); + settingsMenu.add(bytecode); + bytecodeAST = new JRadioButtonMenuItem(Languages.bytecodeAst().getName()); + bytecodeAST.getModel().setActionCommand(Languages.bytecodeAst().getName()); + bytecodeAST.setSelected(Languages.bytecodeAst().getName().equals(settings.getLanguage().getName())); + languagesGroup.add(bytecodeAST); + settingsMenu.add(bytecodeAST); + + JMenu debugLanguagesMenu = new JMenu("Debug Languages"); + for (final Language language : Languages.debug()) { + final JRadioButtonMenuItem m = new JRadioButtonMenuItem(language.getName()); + m.getModel().setActionCommand(language.getName()); + m.setSelected(language.getName().equals(settings.getLanguage().getName())); + languagesGroup.add(m); + debugLanguagesMenu.add(m); + languageLookup.put(language.getName(), language); + } + for (AbstractButton button : Collections.list(languagesGroup.getElements())) { + button.addActionListener(settingsChanged); + } + settingsMenu.add(debugLanguagesMenu); + + bytecodeLineNumbers = new JCheckBoxMenuItem("Show Line Numbers In Bytecode"); + bytecodeLineNumbers.setSelected(settings.getIncludeLineNumbersInBytecode()); + bytecodeLineNumbers.addActionListener(settingsChanged); + settingsMenu.add(bytecodeLineNumbers); + } + + private void buildHelpMenu(JMenu helpMenu) { + helpMenu.removeAll(); + JMenuItem menuItem = new JMenuItem("Legal"); + menuItem.addActionListener(e -> mainWindow.onLegalMenu()); + helpMenu.add(menuItem); + JMenu menuDebug = new JMenu("Debug"); + menuItem = new JMenuItem("List JVM Classes"); + menuItem.addActionListener(e -> mainWindow.onListLoadedClasses()); + menuDebug.add(menuItem); + helpMenu.add(menuDebug); + menuItem = new JMenuItem("About"); + menuItem.addActionListener(event -> { + JPanel pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); + JLabel title = new JLabel("Luyten " + Luyten.getVersion()); + title.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18)); + pane.add(title); + pane.add(new JLabel("by Deathmarine")); + String project = "https://github.com/deathmarine/Luyten/"; + JLabel link = new JLabel("" + project + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(project, link)); + pane.add(link); + pane.add(new JLabel("Contributions By:")); + pane.add(new JLabel("zerdei, toonetown, dstmath")); + pane.add(new JLabel("virustotalop, xtrafrancyz,")); + pane.add(new JLabel("mbax, quitten, mstrobel,")); + pane.add(new JLabel("FisheyLP, Syquel, and ThexXTURBOXx")); + pane.add(new JLabel(" ")); + pane.add(new JLabel("Powered By:")); + String procyon = "https://github.com/mstrobel/procyon"; + link = new JLabel("" + procyon + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(procyon, link)); + pane.add(link); + pane.add(new JLabel("Version: " + Procyon.version())); + pane.add(new JLabel("(c) 2021 Mike Strobel")); + String rsyntax = "https://github.com/bobbylight/RSyntaxTextArea"; + link = new JLabel("" + rsyntax + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(rsyntax, link)); + pane.add(link); + pane.add(new JLabel("Version: 3.1.5")); + pane.add(new JLabel("(c) 2021 Robert Futrell")); + pane.add(new JLabel(" ")); + JOptionPane.showMessageDialog(null, pane); + }); + helpMenu.add(menuItem); + } + + private void populateSettingsFromSettingsMenu() { + // synchronized: do not disturb decompiler at work (synchronize every + // time before run decompiler) + synchronized (settings) { + settings.setFlattenSwitchBlocks(flattenSwitchBlocks.isSelected()); + settings.setForceExplicitImports(forceExplicitImports.isSelected()); + settings.setShowSyntheticMembers(showSyntheticMembers.isSelected()); + settings.setExcludeNestedTypes(excludeNestedTypes.isSelected()); + settings.setForceExplicitTypeArguments(forceExplicitTypes.isSelected()); + settings.setRetainRedundantCasts(retainRedundantCasts.isSelected()); + settings.setIncludeErrorDiagnostics(showDebugInfo.isSelected()); + settings.setUnicodeOutputEnabled(unicodeReplacement.isSelected()); + settings.setShowDebugLineNumbers(debugLineNumbers.isSelected()); + // + // Note: You shouldn't ever need to set this. It's only for + // languages that support catch + // blocks without an exception variable. Java doesn't allow this. I + // think Scala does. + // + // settings.setAlwaysGenerateExceptionVariableForCatchBlocks(true); + // + + final ButtonModel selectedLanguage = languagesGroup.getSelection(); + if (selectedLanguage != null) { + final Language language = languageLookup.get(selectedLanguage.getActionCommand()); + + if (language != null) + settings.setLanguage(language); + } + + if (java.isSelected()) { + settings.setLanguage(Languages.java()); + } else if (bytecode.isSelected()) { + settings.setLanguage(Languages.bytecode()); + } else if (bytecodeAST.isSelected()) { + settings.setLanguage(Languages.bytecodeAst()); + } + settings.setIncludeLineNumbersInBytecode(bytecodeLineNumbers.isSelected()); + } + } + + private class ThemeAction extends AbstractAction { + private static final long serialVersionUID = -6618680171943723199L; + private final String xml; + + public ThemeAction(String name, String xml) { + putValue(NAME, name); + this.xml = xml; + } + + @Override + public void actionPerformed(ActionEvent e) { + luytenPrefs.setThemeXml(xml); + mainWindow.onThemesChanged(); + } + } + + private static class LinkListener extends MouseAdapter { + String link; + JLabel label; + + public LinkListener(String link, JLabel label) { + this.link = link; + this.label = label; + } + + @Override + public void mouseClicked(MouseEvent e) { + try { + Desktop.getDesktop().browse(new URI(link)); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + label.setText("" + link + ""); + } + + @Override + public void mouseExited(MouseEvent e) { + label.setText("" + link + ""); + } + + } + +} diff --git a/src/main/java/us/deathmarine/luyten/MainWindow.java b/src/main/java/us/deathmarine/luyten/MainWindow.java new file mode 100644 index 00000000..9f7456f5 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/MainWindow.java @@ -0,0 +1,521 @@ +package us.deathmarine.luyten; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Toolkit; +import java.awt.dnd.DropTarget; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.border.BevelBorder; +import javax.swing.plaf.basic.BasicTabbedPaneUI; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; + +/** + * Dispatcher + */ +public class MainWindow extends JFrame { + + private static final long serialVersionUID = 5265556630724988013L; + + private static final String TITLE = "Luyten"; + private static final String DEFAULT_TAB = "#DEFAULT"; + + private final JProgressBar bar; + private final JLabel label; + FindBox findBox; + private FindAllBox findAllBox; + private final ConfigSaver configSaver; + private final WindowPosition windowPosition; + private final LuytenPreferences luytenPrefs; + private final FileDialog fileDialog; + private final FileSaver fileSaver; + private final JTabbedPane jarsTabbedPane; + private final Map jarModels; + public MainMenuBar mainMenuBar; + + public MainWindow(File fileFromCommandLine) { + configSaver = ConfigSaver.getLoadedInstance(); + windowPosition = configSaver.getMainWindowPosition(); + luytenPrefs = configSaver.getLuytenPreferences(); + + jarModels = new HashMap<>(); + mainMenuBar = new MainMenuBar(this); + this.setJMenuBar(mainMenuBar); + + this.adjustWindowPositionBySavedState(); + this.setHideFindBoxOnMainWindowFocus(); + this.setShowFindAllBoxOnMainWindowFocus(); + this.setQuitOnWindowClosing(); + this.setTitle(TITLE); + this.setIconImage(new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/Luyten.png"))).getImage()); + + JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.LEFT)); + label = new JLabel(); + label.setHorizontalAlignment(JLabel.LEFT); + panel1.setBorder(new BevelBorder(BevelBorder.LOWERED)); + panel1.setPreferredSize(new Dimension(this.getWidth() / 2, 20)); + panel1.add(label); + + JPanel panel2 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + bar = new JProgressBar(); + + bar.setStringPainted(true); + bar.setOpaque(false); + bar.setVisible(false); + panel2.setPreferredSize(new Dimension(this.getWidth() / 3, 20)); + panel2.add(bar); + + jarsTabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); + jarsTabbedPane.setUI(new BasicTabbedPaneUI() { + @Override + protected int calculateTabAreaHeight(int tab_placement, int run_count, int max_tab_height) { + if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) == -1) + return super.calculateTabAreaHeight(tab_placement, run_count, max_tab_height); + else + return 0; + } + }); + jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); + this.getContentPane().add(jarsTabbedPane); + + JSplitPane spt = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel1, panel2) { + private static final long serialVersionUID = 2189946972124687305L; + private final int location = 400; + + { + setDividerLocation(location); + } + + @Override + public int getDividerLocation() { + return location; + } + + @Override + public int getLastDividerLocation() { + return location; + } + }; + spt.setBorder(new BevelBorder(BevelBorder.LOWERED)); + spt.setPreferredSize(new Dimension(this.getWidth(), 24)); + this.add(spt, BorderLayout.SOUTH); + Model jarModel = null; + if (fileFromCommandLine != null) { + jarModel = loadNewFile(fileFromCommandLine); + } + + try { + DropTarget dt = new DropTarget(); + dt.addDropTargetListener(new DropListener(this)); + this.setDropTarget(dt); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + + fileDialog = new FileDialog(this); + fileSaver = new FileSaver(bar, label); + + if (jarModel != null) { + this.setExitOnEscWhenEnabled(jarModel); + } + + if (jarModel != null && (fileFromCommandLine.getName().toLowerCase().endsWith(".jar") + || fileFromCommandLine.getName().toLowerCase().endsWith(".zip"))) { + jarModel.startWarmUpThread(); + } + + if (RecentFiles.load() > 0) mainMenuBar.updateRecentFiles(); + } + + private void createDefaultTab() { + jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); + } + + private void removeDefaultTab() { + jarsTabbedPane.remove(jarsTabbedPane.indexOfTab(DEFAULT_TAB)); + } + + public void onOpenFileMenu() { + File selectedFile = fileDialog.doOpenDialog(); + if (selectedFile != null) { + System.out.println("[Open]: Opening " + selectedFile.getAbsolutePath()); + this.loadNewFile(selectedFile); + } + } + + public Model loadNewFile(final File file) { + // In case we open the same file again + // we remove the old entry to force a refresh + if (jarModels.containsKey(file.getAbsolutePath())) { + jarModels.remove(file.getAbsolutePath()); + int index = jarsTabbedPane.indexOfTab(file.getName()); + jarsTabbedPane.remove(index); + } + + final Model jarModel = new Model(this); + jarModel.loadFile(file); + jarModels.put(file.getAbsolutePath(), jarModel); + jarsTabbedPane.addTab(file.getName(), jarModel); + jarsTabbedPane.setSelectedComponent(jarModel); + + final String tabName = file.getName(); + int index = jarsTabbedPane.indexOfTab(tabName); + Model.Tab tabUI = new Model.Tab(tabName, () -> { + int index1 = jarsTabbedPane.indexOfTab(tabName); + jarModels.remove(file.getAbsolutePath()); + jarsTabbedPane.remove(index1); + jarModel.closeFile(); + if (jarsTabbedPane.getTabCount() == 0) { + createDefaultTab(); + } + }); + jarsTabbedPane.setTabComponentAt(index, tabUI); + if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) != -1 && jarsTabbedPane.getTabCount() > 1) { + removeDefaultTab(); + } + return jarModel; + } + + public void onCloseFileMenu() { + this.getSelectedModel().closeFile(); + jarModels.remove(getSelectedModel()); + } + + public void onSaveAsMenu() { + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); + if (pane == null) + return; + String tabTitle = this.getSelectedModel().getCurrentTabTitle(); + if (tabTitle == null) + return; + + String recommendedFileName = tabTitle.replace(".class", ".java"); + File selectedFile = fileDialog.doSaveDialog(recommendedFileName); + if (selectedFile != null) { + fileSaver.saveText(pane.getText(), selectedFile); + } + } + + public void onSaveAllMenu() { + File openedFile = this.getSelectedModel().getOpenedFile(); + if (openedFile == null) + return; + + String fileName = openedFile.getName(); + if (fileName.endsWith(".class")) { + fileName = fileName.replace(".class", ".java"); + } else if (fileName.toLowerCase().endsWith(".jar")) { + fileName = "decompiled-" + fileName.replaceAll("\\.[jJ][aA][rR]", ".zip"); + } else { + fileName = "saved-" + fileName; + } + + File selectedFileToSave = fileDialog.doSaveAllDialog(fileName); + if (selectedFileToSave != null) { + fileSaver.saveAllDecompiled(openedFile, selectedFileToSave); + } + } + + public void onExitMenu() { + quit(); + } + + public void onSelectAllMenu() { + try { + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); + if (pane != null) { + pane.requestFocusInWindow(); + pane.setSelectionStart(0); + pane.setSelectionEnd(pane.getText().length()); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + public void onFindMenu() { + try { + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); + if (pane != null) { + if (findBox == null) + findBox = new FindBox(this); + findBox.showFindBox(); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + public void onFindAllMenu() { + try { + if (findAllBox == null) + findAllBox = new FindAllBox(this); + findAllBox.showFindBox(); + + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + public void onLegalMenu() { + new Thread(() -> { + try { + bar.setVisible(true); + bar.setIndeterminate(true); + String legalStr = getLegalStr(); + getSelectedModel().showLegal(legalStr); + } finally { + bar.setIndeterminate(false); + bar.setVisible(false); + } + }).start(); + } + + public void onListLoadedClasses() { + try { + StringBuilder sb = new StringBuilder(); + ClassLoader myCL = Thread.currentThread().getContextClassLoader(); + bar.setVisible(true); + bar.setIndeterminate(true); + while (myCL != null) { + sb.append("ClassLoader: ").append(myCL).append("\n"); + for (Iterator iter = list(myCL); iter.hasNext(); ) { + sb.append("\t").append(iter.next()).append("\n"); + } + myCL = myCL.getParent(); + } + this.getSelectedModel().show("Debug", sb.toString()); + } finally { + bar.setIndeterminate(false); + bar.setVisible(false); + } + } + + private static Iterator list(ClassLoader CL) { + Class CL_class = CL.getClass(); + while (CL_class != java.lang.ClassLoader.class) { + CL_class = CL_class.getSuperclass(); + } + java.lang.reflect.Field ClassLoader_classes_field; + try { + ClassLoader_classes_field = CL_class.getDeclaredField("classes"); + ClassLoader_classes_field.setAccessible(true); + Vector classes = (Vector) ClassLoader_classes_field.get(CL); + return classes.iterator(); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + Luyten.showExceptionDialog("Exception!", e); + } + return null; + } + + private String getLegalStr() { + StringBuilder sb = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader( + new InputStreamReader(getClass().getResourceAsStream("/distfiles/Procyon.License.txt"))); + String line; + while ((line = reader.readLine()) != null) + sb.append(line).append("\n"); + sb.append("\n\n\n\n\n"); + reader = new BufferedReader( + new InputStreamReader(getClass().getResourceAsStream("/distfiles/RSyntaxTextArea.License.txt"))); + while ((line = reader.readLine()) != null) + sb.append(line).append("\n"); + } catch (IOException e) { + Luyten.showExceptionDialog("Exception!", e); + } + return sb.toString(); + } + + public void onThemesChanged() { + for (Model jarModel : jarModels.values()) { + jarModel.changeTheme(luytenPrefs.getThemeXml()); + luytenPrefs.setFont_size(jarModel.getTheme().baseFont.getSize()); + } + } + + public void onSettingsChanged() { + for (Model jarModel : jarModels.values()) { + jarModel.updateOpenClasses(); + } + } + + public void onTreeSettingsChanged() { + for (Model jarModel : jarModels.values()) { + jarModel.updateTree(); + } + } + + public void onFilesDropped(List files) { + if (files != null) { + for (File file : files) { + this.loadNewFile(file); + } + } + } + + public void onFileDropped(File file) { + if (file != null) { + this.loadNewFile(file); + } + } + + public void onFileLoadEnded() { + try { + Model model = getSelectedModel(); + if (model != null && model.getFileName() != null) { + this.setTitle(TITLE + " - " + model.getFileName()); + } else { + this.setTitle(TITLE); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + public void onNavigationRequest(String uniqueStr) { + this.getSelectedModel().navigateTo(uniqueStr); + } + + private void adjustWindowPositionBySavedState() { + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + if (!windowPosition.isSavedWindowPositionValid()) { + final Dimension center = new Dimension((int) (screenSize.width * 0.75), (int) (screenSize.height * 0.75)); + final int x = (int) (center.width * 0.2); + final int y = (int) (center.height * 0.2); + this.setBounds(x, y, center.width, center.height); + + } else if (windowPosition.isFullScreen()) { + int heightMinusTray = screenSize.height; + if (screenSize.height > 30) + heightMinusTray -= 30; + this.setBounds(0, 0, screenSize.width, heightMinusTray); + this.setExtendedState(JFrame.MAXIMIZED_BOTH); + + this.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + if (MainWindow.this.getExtendedState() != JFrame.MAXIMIZED_BOTH) { + windowPosition.setFullScreen(false); + if (windowPosition.isSavedWindowPositionValid()) { + MainWindow.this.setBounds(windowPosition.getWindowX(), windowPosition.getWindowY(), + windowPosition.getWindowWidth(), windowPosition.getWindowHeight()); + } + MainWindow.this.removeComponentListener(this); + } + } + }); + + } else { + this.setBounds(windowPosition.getWindowX(), windowPosition.getWindowY(), windowPosition.getWindowWidth(), + windowPosition.getWindowHeight()); + } + } + + private void setHideFindBoxOnMainWindowFocus() { + this.addWindowFocusListener(new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent e) { + if (findBox != null && findBox.isVisible()) { + findBox.setVisible(false); + } + } + }); + } + + private void setShowFindAllBoxOnMainWindowFocus() { + this.addWindowFocusListener(new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent e) { + if (findAllBox != null && findAllBox.isVisible()) { + findAllBox.setVisible(false); + } + } + }); + } + + private void setQuitOnWindowClosing() { + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + quit(); + } + }); + } + + private void quit() { + try { + windowPosition.readPositionFromWindow(this); + configSaver.saveConfig(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } finally { + try { + this.dispose(); + } finally { + System.exit(0); + } + } + } + + private void setExitOnEscWhenEnabled(JComponent mainComponent) { + Action escapeAction = new AbstractAction() { + private static final long serialVersionUID = -3460391555954575248L; + + @Override + public void actionPerformed(ActionEvent e) { + if (luytenPrefs.isExitByEscEnabled()) { + quit(); + } + } + }; + KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); + mainComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(escapeKeyStroke, "ESCAPE"); + mainComponent.getActionMap().put("ESCAPE", escapeAction); + } + + public Model getSelectedModel() { + return (Model) jarsTabbedPane.getSelectedComponent(); + } + + public Collection getModels() { + return jarModels.values(); + } + + public JProgressBar getBar() { + return bar; + } + + public JLabel getLabel() { + return label; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/Model.java b/src/main/java/us/deathmarine/luyten/Model.java new file mode 100644 index 00000000..1eca0cd2 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/Model.java @@ -0,0 +1,1031 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.InputTypeLoader; +import com.strobel.assembler.metadata.ITypeLoader; +import com.strobel.assembler.metadata.JarTypeLoader; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.core.VerifyArgument; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.Theme; +import org.fife.ui.rtextarea.RTextScrollPane; + +/** + * Jar-level model + */ +public class Model extends JSplitPane { + + private static final long serialVersionUID = 6896857630400910200L; + + private static final long MAX_JAR_FILE_SIZE_BYTES = 10_000_000_000L; + private static final long MAX_UNPACKED_FILE_SIZE_BYTES = 10_000_000L; + + private final LuytenTypeLoader typeLoader = new LuytenTypeLoader(); + private final MetadataSystem metadataSystem = new MetadataSystem(typeLoader); + + private final JTree tree; + public JTabbedPane house; + private File file; + private final DecompilerSettings settings; + private final DecompilationOptions decompilationOptions; + private Theme theme; + private final MainWindow mainWindow; + private final JProgressBar bar; + private JLabel label; + private final HashSet hmap = new HashSet<>(); + private Set treeExpansionState; + private boolean open = false; + private State state; + private final ConfigSaver configSaver; + private final LuytenPreferences luytenPrefs; + + public Model(MainWindow mainWindow) { + this.mainWindow = mainWindow; + this.bar = mainWindow.getBar(); + this.setLabel(mainWindow.getLabel()); + + configSaver = ConfigSaver.getLoadedInstance(); + settings = configSaver.getDecompilerSettings(); + luytenPrefs = configSaver.getLuytenPreferences(); + + try { + String themeXml = luytenPrefs.getThemeXml(); + setTheme(Theme.load(getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + themeXml))); + } catch (Exception e1) { + try { + Luyten.showExceptionDialog("Exception!", e1); + String themeXml = LuytenPreferences.DEFAULT_THEME_XML; + luytenPrefs.setThemeXml(themeXml); + setTheme(Theme.load(getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + themeXml))); + } catch (Exception e2) { + Luyten.showExceptionDialog("Exception!", e2); + } + } + + tree = new JTree(); + tree.setModel(new DefaultTreeModel(null)); + tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + tree.setCellRenderer(new CellRenderer()); + TreeListener tl = new TreeListener(); + tree.addMouseListener(tl); + tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener()); + tree.addKeyListener(new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + openEntryByTreePath(tree.getSelectionPath()); + } + } + }); + + JPanel panel2 = new JPanel(); + panel2.setLayout(new BoxLayout(panel2, BoxLayout.Y_AXIS)); + panel2.setBorder(BorderFactory.createTitledBorder("Structure")); + panel2.add(new JScrollPane(tree)); + + house = new JTabbedPane(); + house.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + house.addChangeListener(new TabChangeListener()); + house.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isMiddleMouseButton(e)) { + closeOpenTab(house.getSelectedIndex()); + } + } + }); + + KeyStroke sfuncF4 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, Keymap.ctrlDownModifier(), false); + mainWindow.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sfuncF4, "CloseTab"); + + mainWindow.getRootPane().getActionMap().put("CloseTab", new AbstractAction() { + private static final long serialVersionUID = -885398399200419492L; + + @Override + public void actionPerformed(ActionEvent e) { + closeOpenTab(house.getSelectedIndex()); + } + + }); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createTitledBorder("Code")); + panel.add(house); + this.setOrientation(JSplitPane.HORIZONTAL_SPLIT); + this.setDividerLocation(250 % mainWindow.getWidth()); + this.setLeftComponent(panel2); + this.setRightComponent(panel); + + decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + } + + public void showLegal(String legalStr) { + show("Legal", legalStr); + } + + public void show(String name, String contents) { + OpenFile open = new OpenFile(name, "*/" + name, getTheme(), mainWindow); + open.setContent(contents); + hmap.add(open); + addOrSwitchToTab(open); + } + + private void addOrSwitchToTab(final OpenFile open) { + SwingUtilities.invokeLater(() -> { + try { + final String title = open.name; + RTextScrollPane rTextScrollPane = open.scrollPane; + int index = house.indexOfTab(title); + if (index > -1 && house.getTabComponentAt(index) != open.scrollPane) { + index = -1; + for (int i = 0; i < house.getTabCount(); i++) { + if (house.getComponentAt(i) == open.scrollPane) { + index = i; + break; + } + } + } + if (index < 0) { + house.addTab(title, rTextScrollPane); + index = house.indexOfComponent(rTextScrollPane); + house.setSelectedIndex(index); + Tab ct = new Tab(title, () -> { + int index1 = house.indexOfTab(title); + closeOpenTab(index1); + }); + house.setTabComponentAt(index, ct); + } else { + house.setSelectedIndex(index); + } + open.onAddedToScreen(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }); + } + + public void closeOpenTab(int index) { + if (index < 0 || index >= house.getComponentCount()) + return; + + RTextScrollPane co = (RTextScrollPane) house.getComponentAt(index); + RSyntaxTextArea pane = (RSyntaxTextArea) co.getViewport().getView(); + OpenFile open = null; + for (OpenFile file : hmap) + if (pane.equals(file.textArea)) + open = file; + if (open != null) + hmap.remove(open); + house.remove(co); + if (open != null) + open.close(); + } + + private String getName(String path) { + if (path == null) + return ""; + int i = path.lastIndexOf("/"); + if (i == -1) + i = path.lastIndexOf("\\"); + if (i != -1) + return path.substring(i + 1); + return path; + } + + private class TreeListener extends MouseAdapter { + @Override + public void mousePressed(MouseEvent event) { + boolean isClickCountMatches = (event.getClickCount() == 1 && luytenPrefs.isSingleClickOpenEnabled()) + || (event.getClickCount() == 2 && !luytenPrefs.isSingleClickOpenEnabled()); + if (!isClickCountMatches) + return; + + if (!SwingUtilities.isLeftMouseButton(event)) + return; + + final TreePath trp = tree.getPathForLocation(event.getX(), event.getY()); + if (trp == null) + return; + + Object lastPathComponent = trp.getLastPathComponent(); + boolean isLeaf = (lastPathComponent instanceof TreeNode && ((TreeNode) lastPathComponent).isLeaf()); + if (!isLeaf) + return; + + new Thread(() -> openEntryByTreePath(trp)).start(); + } + } + + private class FurtherExpandingTreeExpansionListener implements TreeExpansionListener { + @Override + public void treeExpanded(final TreeExpansionEvent event) { + final TreePath treePath = event.getPath(); + + final Object expandedTreePathObject = treePath.getLastPathComponent(); + if (!(expandedTreePathObject instanceof TreeNode)) { + return; + } + + final TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject; + if (expandedTreeNode.getChildCount() == 1) { + final TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0); + + if (descendantTreeNode.isLeaf()) { + return; + } + + final TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode); + tree.expandPath(nextTreePath); + } + } + + @Override + public void treeCollapsed(final TreeExpansionEvent event) { + + } + } + + public void openEntryByTreePath(TreePath trp) { + String name = ""; + StringBuilder path = new StringBuilder(); + try { + bar.setVisible(true); + if (trp.getPathCount() > 1) { + for (int i = 1; i < trp.getPathCount(); i++) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); + TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); + if (i == trp.getPathCount() - 1) { + name = userObject.getOriginalName(); + } else { + path.append(userObject.getOriginalName()).append("/"); + } + } + path.append(name); + + if (file.getName().endsWith(".jar") || file.getName().endsWith(".zip")) { + if (state == null) { + JarFile jfile = new JarFile(file); + ITypeLoader jarLoader = new JarTypeLoader(jfile); + + typeLoader.getTypeLoaders().add(jarLoader); + state = new State(file.getCanonicalPath(), file, jfile, jarLoader); + } + + JarEntry entry = state.jarFile.getJarEntry(path.toString()); + if (entry == null) { + throw new FileEntryNotFoundException(); + } + if (entry.getSize() > MAX_UNPACKED_FILE_SIZE_BYTES) { + throw new TooLargeFileException(entry.getSize()); + } + String entryName = entry.getName(); + if (entryName.endsWith(".class")) { + getLabel().setText("Extracting: " + name); + String internalName = StringUtilities.removeRight(entryName, ".class"); + TypeReference type = metadataSystem.lookupType(internalName); + extractClassToTextPane(type, name, path.toString(), null); + } else { + getLabel().setText("Opening: " + name); + try (InputStream in = state.jarFile.getInputStream(entry)) { + extractSimpleFileEntryToTextPane(in, name, path.toString()); + } + } + } + } else { + name = file.getName(); + path = new StringBuilder(file.getPath().replaceAll("\\\\", "/")); + if (file.length() > MAX_UNPACKED_FILE_SIZE_BYTES) { + throw new TooLargeFileException(file.length()); + } + if (name.endsWith(".class")) { + getLabel().setText("Extracting: " + name); + TypeReference type = metadataSystem.lookupType(path.toString()); + extractClassToTextPane(type, name, path.toString(), null); + } else { + getLabel().setText("Opening: " + name); + try (InputStream in = new FileInputStream(file)) { + extractSimpleFileEntryToTextPane(in, name, path.toString()); + } + } + } + + getLabel().setText("Complete"); + } catch (FileEntryNotFoundException e) { + getLabel().setText("File not found: " + name); + } catch (FileIsBinaryException e) { + getLabel().setText("Binary resource: " + name); + } catch (TooLargeFileException e) { + getLabel().setText("File is too large: " + name + " - size: " + e.getReadableFileSize()); + } catch (Exception e) { + getLabel().setText("Cannot open: " + name); + Luyten.showExceptionDialog("Unable to open file!", e); + } finally { + bar.setVisible(false); + } + } + + void extractClassToTextPane(TypeReference type, String tabTitle, String path, String navigationLink) + throws Exception { + if (tabTitle == null || tabTitle.trim().length() < 1 || path == null) { + throw new FileEntryNotFoundException(); + } + OpenFile sameTitledOpen = null; + for (OpenFile nextOpen : hmap) { + if (tabTitle.equals(nextOpen.name) && path.equals(nextOpen.path) && type.equals(nextOpen.getType())) { + sameTitledOpen = nextOpen; + break; + } + } + if (sameTitledOpen != null && sameTitledOpen.isContentValid()) { + sameTitledOpen.setInitialNavigationLink(navigationLink); + addOrSwitchToTab(sameTitledOpen); + return; + } + + // resolve TypeDefinition + TypeDefinition resolvedType; + if (type == null || ((resolvedType = type.resolve()) == null)) { + throw new Exception("Unable to resolve type."); + } + + // open tab, store type information, start decompilation + if (sameTitledOpen != null) { + sameTitledOpen.path = path; + sameTitledOpen.invalidateContent(); + sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + sameTitledOpen.setType(resolvedType); + sameTitledOpen.setInitialNavigationLink(navigationLink); + sameTitledOpen.resetScrollPosition(); + sameTitledOpen.decompile(); + addOrSwitchToTab(sameTitledOpen); + } else { + OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); + open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + open.setType(resolvedType); + open.setInitialNavigationLink(navigationLink); + open.decompile(); + hmap.add(open); + addOrSwitchToTab(open); + } + } + + public void extractSimpleFileEntryToTextPane(InputStream inputStream, String tabTitle, String path) + throws Exception { + if (inputStream == null || tabTitle == null || tabTitle.trim().length() < 1 || path == null) { + throw new FileEntryNotFoundException(); + } + OpenFile sameTitledOpen = null; + for (OpenFile nextOpen : hmap) { + if (tabTitle.equals(nextOpen.name) && path.equals(nextOpen.path)) { + sameTitledOpen = nextOpen; + break; + } + } + if (sameTitledOpen != null) { + addOrSwitchToTab(sameTitledOpen); + return; + } + + // build tab content and check if file is binary + double ascii = 0; + double other = 0; + StringBuilder sb = new StringBuilder(); + try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + BufferedReader reader = new BufferedReader(inputStreamReader)) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + // Source: https://stackoverflow.com/a/13533390/5894824 + for (byte b : line.getBytes()) { + if (b == 0x09 || b == 0x0A || b == 0x0C || b == 0x0D || (b >= 0x20 && b <= 0x7E)) ascii++; + else other++; + } + } + } + + if (other != 0 && other / (ascii + other) > 0.5) { + throw new FileIsBinaryException(); + } + + // open tab + if (sameTitledOpen != null) { + sameTitledOpen.path = path; + sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + sameTitledOpen.resetScrollPosition(); + sameTitledOpen.setContent(sb.toString()); + addOrSwitchToTab(sameTitledOpen); + } else { + OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); + open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + open.setContent(sb.toString()); + hmap.add(open); + addOrSwitchToTab(open); + } + } + + private class TabChangeListener implements ChangeListener { + @Override + public void stateChanged(ChangeEvent e) { + int selectedIndex = house.getSelectedIndex(); + if (selectedIndex < 0) { + return; + } + for (OpenFile open : hmap) { + if (house.indexOfTab(open.name) == selectedIndex) { + + if (open.getType() != null && !open.isContentValid()) { + updateOpenClass(open); + break; + } + + } + } + } + } + + public void updateOpenClasses() { + // invalidate all open classes (update will hapen at tab change) + for (OpenFile open : hmap) { + if (open.getType() != null) { + open.invalidateContent(); + } + } + // update the current open tab - if it is a class + for (OpenFile open : hmap) { + if (open.getType() != null && isTabInForeground(open)) { + updateOpenClass(open); + break; + } + } + } + + private void updateOpenClass(final OpenFile open) { + if (open.getType() == null) { + return; + } + new Thread(() -> { + try { + bar.setVisible(true); + getLabel().setText("Extracting: " + open.name); + open.invalidateContent(); + open.decompile(); + getLabel().setText("Complete"); + } catch (Exception e) { + getLabel().setText("Error, cannot update: " + open.name); + } finally { + bar.setVisible(false); + } + }).start(); + } + + private boolean isTabInForeground(OpenFile open) { + String title = open.name; + int selectedIndex = house.getSelectedIndex(); + return (selectedIndex >= 0 && selectedIndex == house.indexOfTab(title)); + } + + final class State implements AutoCloseable { + private final String key; + private final File file; + final JarFile jarFile; + final ITypeLoader typeLoader; + + private State(String key, File file, JarFile jarFile, ITypeLoader typeLoader) { + this.key = VerifyArgument.notNull(key, "key"); + this.file = VerifyArgument.notNull(file, "file"); + this.jarFile = jarFile; + this.typeLoader = typeLoader; + } + + @Override + public void close() { + if (typeLoader != null) { + Model.this.typeLoader.getTypeLoaders().remove(typeLoader); + } + Closer.tryClose(jarFile); + } + + public File getFile() { + return file; + } + + public String getKey() { + return key; + } + } + + public static class Tab extends JPanel { + private final JLabel tabTitle; + private final JLabel closeButton = new JLabel(new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/icon_close.png")))); + + public Tab(String title, final Runnable onCloseTabAction) { + super(new GridBagLayout()); + this.setOpaque(false); + this.tabTitle = new JLabel(title); + this.createTab(); + + // TODO: Disables tab switching... Is there a workaround? + /*addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (onCloseTabAction != null && SwingUtilities.isMiddleMouseButton(e)) { + try { + onCloseTabAction.run(); + } catch (Exception ex) { + Luyten.showExceptionDialog("Exception!", ex); + } + } + } + });*/ + + closeButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (onCloseTabAction != null) { + try { + onCloseTabAction.run(); + } catch (Exception ex) { + Luyten.showExceptionDialog("Exception!", ex); + } + } + } + }); + } + + public void createTab() { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 1; + this.add(tabTitle, gbc); + gbc.gridx++; + gbc.insets = new Insets(0, 5, 0, 0); + gbc.anchor = GridBagConstraints.EAST; + this.add(closeButton, gbc); + } + } + + public DefaultMutableTreeNode loadNodesByNames(DefaultMutableTreeNode node, List originalNames) { + List args = new ArrayList<>(); + for (String originalName : originalNames) { + args.add(new TreeNodeUserObject(originalName)); + } + return loadNodesByUserObj(node, args); + } + + public DefaultMutableTreeNode loadNodesByUserObj(DefaultMutableTreeNode node, List args) { + if (args.size() > 0) { + TreeNodeUserObject name = args.remove(0); + DefaultMutableTreeNode nod = getChild(node, name); + if (nod == null) + nod = new DefaultMutableTreeNode(name); + node.add(loadNodesByUserObj(nod, args)); + } + return node; + } + + @SuppressWarnings("unchecked") + public DefaultMutableTreeNode getChild(DefaultMutableTreeNode node, TreeNodeUserObject name) { + Enumeration entry = node.children(); + while (entry.hasMoreElements()) { + DefaultMutableTreeNode nods = (DefaultMutableTreeNode) entry.nextElement(); + if (((TreeNodeUserObject) nods.getUserObject()).getOriginalName().equals(name.getOriginalName())) { + return nods; + } + } + return null; + } + + public void loadFile(File file) { + if (open) + closeFile(); + this.file = file; + + RecentFiles.add(file.getAbsolutePath()); + mainWindow.mainMenuBar.updateRecentFiles(); + loadTree(); + } + + public void updateTree() { + TreeUtil treeUtil = new TreeUtil(tree); + treeExpansionState = treeUtil.getExpansionState(); + loadTree(); + } + + public void loadTree() { + new Thread(() -> { + try { + if (file == null) { + return; + } + tree.setModel(new DefaultTreeModel(null)); + + if (file.length() > MAX_JAR_FILE_SIZE_BYTES) { + throw new TooLargeFileException(file.length()); + } + if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) { + JarFile jfile = new JarFile(file); + getLabel().setText("Loading: " + jfile.getName()); + bar.setVisible(true); + + JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); + List mass; + if (luytenPrefs.isFilterOutInnerClassEntries()) { + mass = jarEntryFilter.getEntriesWithoutInnerClasses(); + } else { + mass = jarEntryFilter.getAllEntriesFromJar(); + } + buildTreeFromMass(mass); + + if (state == null) { + ITypeLoader jarLoader = new JarTypeLoader(jfile); + typeLoader.getTypeLoaders().add(jarLoader); + state = new State(file.getCanonicalPath(), file, jfile, jarLoader); + } + open = true; + getLabel().setText("Complete"); + } else { + TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); + final DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); + tree.setModel(new DefaultTreeModel(top)); + settings.setTypeLoader(new InputTypeLoader()); + open = true; + getLabel().setText("Complete"); + + // open it automatically + new Thread(() -> { + TreePath trp = new TreePath(top.getPath()); + openEntryByTreePath(trp); + }).start(); + } + + if (treeExpansionState != null) { + try { + TreeUtil treeUtil = new TreeUtil(tree); + treeUtil.restoreExpanstionState(treeExpansionState); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + } catch (TooLargeFileException e) { + getLabel().setText("File is too large: " + file.getName() + " - size: " + e.getReadableFileSize()); + closeFile(); + } catch (Exception e1) { + Luyten.showExceptionDialog("Cannot open " + file.getName() + "!", e1); + getLabel().setText("Cannot open: " + file.getName()); + closeFile(); + } finally { + mainWindow.onFileLoadEnded(); + bar.setVisible(false); + } + }).start(); + } + + private void buildTreeFromMass(List mass) { + if (luytenPrefs.isPackageExplorerStyle()) { + buildFlatTreeFromMass(mass); + } else { + buildDirectoryTreeFromMass(mass); + } + } + + private void buildDirectoryTreeFromMass(List mass) { + TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); + DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); + List sort = new ArrayList<>(); + mass.sort(String.CASE_INSENSITIVE_ORDER); + for (String m : mass) + if (m.contains("META-INF") && !sort.contains(m)) + sort.add(m); + Set set = new HashSet<>(); + for (String m : mass) { + if (m.contains("/")) { + set.add(m.substring(0, m.lastIndexOf("/") + 1)); + } + } + List packs = Arrays.asList(set.toArray(new String[]{})); + packs.sort(String.CASE_INSENSITIVE_ORDER); + packs.sort((o1, o2) -> o2.split("/").length - o1.split("/").length); + for (String pack : packs) + for (String m : mass) + if (!m.contains("META-INF") && m.contains(pack) && !m.replace(pack, "").contains("/")) + sort.add(m); + for (String m : mass) + if (!m.contains("META-INF") && !m.contains("/") && !sort.contains(m)) + sort.add(m); + for (String pack : sort) { + LinkedList list = new LinkedList<>(Arrays.asList(pack.split("/"))); + loadNodesByNames(top, list); + } + tree.setModel(new DefaultTreeModel(top)); + } + + private void buildFlatTreeFromMass(List mass) { + TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); + DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); + + TreeMap> packages = new TreeMap<>(); + HashSet classContainingPackageRoots = new HashSet<>(); + + // (assertion: mass does not contain null elements) + Comparator sortByFileExtensionsComparator = Comparator.comparing( + (String o) -> o.replaceAll("[^.]*\\.", "")).thenComparing(o -> o); + + for (String entry : mass) { + String packagePath = ""; + String packageRoot = ""; + if (entry.contains("/")) { + packagePath = entry.replaceAll("/[^/]*$", ""); + packageRoot = entry.replaceAll("/.*$", ""); + } + String packageEntry = entry.replace(packagePath + "/", ""); + if (!packages.containsKey(packagePath)) { + packages.put(packagePath, new TreeSet<>(sortByFileExtensionsComparator)); + } + packages.get(packagePath).add(packageEntry); + if (!entry.startsWith("META-INF") && packageRoot.trim().length() > 0 + && entry.matches(".*\\.(class|java|prop|properties)$")) { + classContainingPackageRoots.add(packageRoot); + } + } + + // META-INF comes first -> not flat + for (String packagePath : packages.keySet()) { + if (packagePath.startsWith("META-INF")) { + List packagePathElements = Arrays.asList(packagePath.split("/")); + for (String entry : packages.get(packagePath)) { + ArrayList list = new ArrayList<>(packagePathElements); + list.add(entry); + loadNodesByNames(top, list); + } + } + } + + // real packages: path starts with a classContainingPackageRoot -> flat + for (String packagePath : packages.keySet()) { + String packageRoot = packagePath.replaceAll("/.*$", ""); + if (classContainingPackageRoots.contains(packageRoot)) { + for (String entry : packages.get(packagePath)) { + ArrayList list = new ArrayList<>(); + list.add(new TreeNodeUserObject(packagePath, packagePath.replaceAll("/", "."))); + list.add(new TreeNodeUserObject(entry)); + loadNodesByUserObj(top, list); + } + } + } + + // the rest, not real packages but directories -> not flat + for (String packagePath : packages.keySet()) { + String packageRoot = packagePath.replaceAll("/.*$", ""); + if (!classContainingPackageRoots.contains(packageRoot) && !packagePath.startsWith("META-INF") + && packagePath.length() > 0) { + List packagePathElements = Arrays.asList(packagePath.split("/")); + for (String entry : packages.get(packagePath)) { + ArrayList list = new ArrayList<>(packagePathElements); + list.add(entry); + loadNodesByNames(top, list); + } + } + } + + // the default package -> not flat + String packagePath = ""; + if (packages.containsKey(packagePath)) { + for (String entry : packages.get(packagePath)) { + ArrayList list = new ArrayList<>(); + list.add(entry); + loadNodesByNames(top, list); + } + } + tree.setModel(new DefaultTreeModel(top)); + } + + public void closeFile() { + for (OpenFile co : hmap) { + int pos = house.indexOfTab(co.name); + if (pos >= 0) + house.remove(pos); + co.close(); + } + + if (state != null) { + Closer.tryClose(state); + } + state = null; + + hmap.clear(); + tree.setModel(new DefaultTreeModel(null)); + file = null; + treeExpansionState = null; + open = false; + mainWindow.onFileLoadEnded(); + } + + public void changeTheme(String xml) { + InputStream in = getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + xml); + try { + if (in != null) { + setTheme(Theme.load(in)); + for (OpenFile f : hmap) { + getTheme().apply(f.textArea); + } + } + } catch (Exception e1) { + Luyten.showExceptionDialog("Exception!", e1); + } + } + + public File getOpenedFile() { + File openedFile = null; + if (file != null && open) { + openedFile = file; + } + if (openedFile == null) { + getLabel().setText("No open file"); + } + return openedFile; + } + + public String getCurrentTabTitle() { + String tabTitle = null; + try { + int pos = house.getSelectedIndex(); + if (pos >= 0) { + tabTitle = house.getTitleAt(pos); + } + } catch (Exception e1) { + Luyten.showExceptionDialog("Exception!", e1); + } + if (tabTitle == null) { + getLabel().setText("No open tab"); + } + return tabTitle; + } + + public RSyntaxTextArea getCurrentTextArea() { + RSyntaxTextArea currentTextArea = null; + try { + int pos = house.getSelectedIndex(); + System.out.println(pos); + if (pos >= 0) { + RTextScrollPane co = (RTextScrollPane) house.getComponentAt(pos); + currentTextArea = (RSyntaxTextArea) co.getViewport().getView(); + } + } catch (Exception e1) { + Luyten.showExceptionDialog("Exception!", e1); + } + if (currentTextArea == null) { + getLabel().setText("No open tab"); + } + return currentTextArea; + } + + public void startWarmUpThread() { + new Thread(() -> { + try { + Thread.sleep(500); + String internalName = FindBox.class.getName(); + TypeReference type = metadataSystem.lookupType(internalName); + TypeDefinition resolvedType; + if ((type == null) || ((resolvedType = type.resolve()) == null)) { + return; + } + StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput + .setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); + String decompiledSource = stringwriter.toString(); + OpenFile open = new OpenFile(internalName, "*/" + internalName, getTheme(), mainWindow); + open.setContent(decompiledSource); + JTabbedPane pane = new JTabbedPane(); + pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + pane.addTab("title", open.scrollPane); + pane.setSelectedIndex(pane.indexOfTab("title")); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }).start(); + } + + public void navigateTo(final String uniqueStr) { + new Thread(() -> { + if (uniqueStr == null) + return; + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length <= 1) + return; + String destinationTypeStr = linkParts[1]; + try { + bar.setVisible(true); + getLabel().setText("Navigating: " + destinationTypeStr.replaceAll("/", ".")); + + TypeReference type = metadataSystem.lookupType(destinationTypeStr); + if (type == null) + throw new RuntimeException("Cannot lookup type: " + destinationTypeStr); + TypeDefinition typeDef = type.resolve(); + if (typeDef == null) + throw new RuntimeException("Cannot resolve type: " + destinationTypeStr); + + String tabTitle = typeDef.getName() + ".class"; + extractClassToTextPane(typeDef, tabTitle, destinationTypeStr, uniqueStr); + + getLabel().setText("Complete"); + } catch (Exception e) { + getLabel().setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); + Luyten.showExceptionDialog("Cannot Navigate!", e); + } finally { + bar.setVisible(false); + } + }).start(); + } + + public JLabel getLabel() { + return label; + } + + public void setLabel(JLabel label) { + this.label = label; + } + + public State getState() { + return state; + } + + public Theme getTheme() { + return theme; + } + + public void setTheme(Theme theme) { + this.theme = theme; + } + + public MetadataSystem getMetadataSystem() { + return metadataSystem; + } + + public String getFileName() { + return file == null ? null : getName(file.getName()); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/OpenFile.java b/src/main/java/us/deathmarine/luyten/OpenFile.java new file mode 100644 index 00000000..3a13db12 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/OpenFile.java @@ -0,0 +1,747 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import com.strobel.decompiler.languages.Languages; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.io.StringWriter; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JScrollBar; +import javax.swing.JViewport; +import javax.swing.ScrollPaneConstants; +import javax.swing.Scrollable; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.event.HyperlinkEvent; +import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.Theme; +import org.fife.ui.rtextarea.RTextScrollPane; + +public class OpenFile { + + // navigation links + private TreeMap selectionToUniqueStrTreeMap = new TreeMap<>(); + private final Map isNavigableCache = new ConcurrentHashMap<>(); + private final Map readableLinksCache = new ConcurrentHashMap<>(); + + private volatile boolean isContentValid = false; + private volatile boolean isNavigationLinksValid = false; + private volatile boolean isWaitForLinksCursor = false; + private volatile Double lastScrollPercent = null; + + private LinkProvider linkProvider; + private String initialNavigationLink; + private boolean isFirstTimeRun = true; + + MainWindow mainWindow; + RTextScrollPane scrollPane; + RSyntaxTextArea textArea; + String name; + String path; + + private final ConfigSaver configSaver; + private final LuytenPreferences luytenPrefs; + + // decompiler and type references (not needed for text files) + private MetadataSystem metadataSystem; + private DecompilerSettings settings; + private DecompilationOptions decompilationOptions; + private TypeDefinition type; + + public OpenFile(String name, String path, Theme theme, final MainWindow mainWindow) { + this.name = name; + this.path = path; + this.mainWindow = mainWindow; + + configSaver = ConfigSaver.getLoadedInstance(); + luytenPrefs = configSaver.getLuytenPreferences(); + + textArea = new RSyntaxTextArea(25, 70); + textArea.setCaretPosition(0); + textArea.requestFocusInWindow(); + textArea.setMarkOccurrences(true); + textArea.setClearWhitespaceLinesEnabled(false); + textArea.setEditable(false); + textArea.setAntiAliasingEnabled(true); + textArea.setCodeFoldingEnabled(true); + + FileUtil.setLanguage(textArea, name); + scrollPane = new RTextScrollPane(textArea, true); + + scrollPane.setIconRowHeaderEnabled(true); + textArea.setText(""); + + // Edit RTextArea's PopupMenu + JPopupMenu pop = textArea.getPopupMenu(); + pop.addSeparator(); + JMenuItem item = new JMenuItem("Font"); + item.addActionListener(e -> { + JFontChooser fontChooser = new JFontChooser(); + fontChooser.setSelectedFont(textArea.getFont()); + fontChooser.setSelectedFontSize(textArea.getFont().getSize()); + int result = fontChooser.showDialog(mainWindow); + if (result == JFontChooser.OK_OPTION) { + textArea.setFont(fontChooser.getSelectedFont()); + luytenPrefs.setFont_size(fontChooser.getSelectedFontSize()); + } + }); + pop.add(item); + textArea.setPopupMenu(pop); + + theme.apply(textArea); + + textArea.setFont(new Font(textArea.getFont().getName(), textArea.getFont().getStyle(), + luytenPrefs.getFont_size())); + + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + final JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); + if (verticalScrollbar != null) { + verticalScrollbar.addAdjustmentListener(e -> { + String content = textArea.getText(); + if (content == null || content.length() == 0) + return; + int scrollValue = verticalScrollbar.getValue() - verticalScrollbar.getMinimum(); + int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); + if (scrollMax < 1 || scrollValue < 0 || scrollValue > scrollMax) + return; + lastScrollPercent = (((double) scrollValue) / ((double) scrollMax)); + }); + } + + textArea.setHyperlinksEnabled(true); + textArea.setLinkScanningMask(Keymap.ctrlDownModifier()); + + textArea.setLinkGenerator((textArea, offs) -> { + final String uniqueStr = getUniqueStrForOffset(offs); + final Integer selectionFrom = getSelectionFromForOffset(offs); + if (uniqueStr != null && selectionFrom != null) { + return new LinkGeneratorResult() { + @Override + public HyperlinkEvent execute() { + if (isNavigationLinksValid) + onNavigationClicked(uniqueStr); + return null; + } + + @Override + public int getSourceOffset() { + if (isNavigationLinksValid) + return selectionFrom; + return offs; + } + }; + } + return null; + }); + + /* + * Add Ctrl+Wheel Zoom for Text Size Removes all standard listeners and + * writes new listeners for wheelscroll movement. + */ + for (MouseWheelListener listeners : scrollPane.getMouseWheelListeners()) { + scrollPane.removeMouseWheelListener(listeners); + } + + scrollPane.addMouseWheelListener(e -> { + if (e.getWheelRotation() == 0) { + // Nothing to do here. This happens when scroll event is delivered from a touchbar + // or MagicMouse. There's getPreciseWheelRotation, however it looks like there's no + // trivial and consistent way to use that + // See https://github.com/JetBrains/intellij-community/blob/21c99af7c78fc82aefc4d05646389f4991b08b38/bin/idea.properties#L133-L156 + return; + } + + if ((e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0) { + Font font = textArea.getFont(); + int size = font.getSize(); + if (e.getWheelRotation() > 0) { + textArea.setFont(new Font(font.getName(), font.getStyle(), --size >= 8 ? --size : 8)); + } else { + textArea.setFont(new Font(font.getName(), font.getStyle(), ++size)); + } + luytenPrefs.setFont_size(size); + } else { + if (scrollPane.isWheelScrollingEnabled() && e.getWheelRotation() != 0) { + JScrollBar toScroll = scrollPane.getVerticalScrollBar(); + int direction = e.getWheelRotation() < 0 ? -1 : 1; + int orientation = SwingConstants.VERTICAL; + if (toScroll == null || !toScroll.isVisible()) { + toScroll = scrollPane.getHorizontalScrollBar(); + if (toScroll == null || !toScroll.isVisible()) { + return; + } + orientation = SwingConstants.HORIZONTAL; + } + e.consume(); + + if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { + JViewport vp = scrollPane.getViewport(); + if (vp == null) { + return; + } + Component comp = vp.getView(); + int units = Math.abs(e.getUnitsToScroll()); + boolean limitScroll = Math.abs(e.getWheelRotation()) == 1; + Object fastWheelScroll = toScroll.getClientProperty("JScrollBar.fastWheelScrolling"); + if (Boolean.TRUE == fastWheelScroll && comp instanceof Scrollable) { + Scrollable scrollComp = (Scrollable) comp; + Rectangle viewRect = vp.getViewRect(); + int startingX = viewRect.x; + boolean leftToRight = comp.getComponentOrientation().isLeftToRight(); + int scrollMin = toScroll.getMinimum(); + int scrollMax = toScroll.getMaximum() - toScroll.getModel().getExtent(); + + if (limitScroll) { + int blockIncr = scrollComp.getScrollableBlockIncrement(viewRect, orientation, + direction); + if (direction < 0) { + scrollMin = Math.max(scrollMin, toScroll.getValue() - blockIncr); + } else { + scrollMax = Math.min(scrollMax, toScroll.getValue() + blockIncr); + } + } + + for (int i = 0; i < units; i++) { + int unitIncr = scrollComp.getScrollableUnitIncrement(viewRect, orientation, + direction); + if (orientation == SwingConstants.VERTICAL) { + if (direction < 0) { + viewRect.y -= unitIncr; + if (viewRect.y <= scrollMin) { + viewRect.y = scrollMin; + break; + } + } else { // (direction > 0 + viewRect.y += unitIncr; + if (viewRect.y >= scrollMax) { + viewRect.y = scrollMax; + break; + } + } + } else { + if ((leftToRight && direction < 0) || (!leftToRight && direction > 0)) { + viewRect.x -= unitIncr; + if (leftToRight) { + if (viewRect.x < scrollMin) { + viewRect.x = scrollMin; + break; + } + } + } else { + viewRect.x += unitIncr; + if (leftToRight) { + if (viewRect.x > scrollMax) { + viewRect.x = scrollMax; + break; + } + } + } + } + } + if (orientation == SwingConstants.VERTICAL) { + toScroll.setValue(viewRect.y); + } else { + if (leftToRight) { + toScroll.setValue(viewRect.x); + } else { + int newPos = toScroll.getValue() - (viewRect.x - startingX); + if (newPos < scrollMin) { + newPos = scrollMin; + } else if (newPos > scrollMax) { + newPos = scrollMax; + } + toScroll.setValue(newPos); + } + } + } else { + int delta; + int limit = -1; + + if (limitScroll) { + if (direction < 0) { + limit = toScroll.getValue() - toScroll.getBlockIncrement(direction); + } else { + limit = toScroll.getValue() + toScroll.getBlockIncrement(direction); + } + } + + for (int i = 0; i < units; i++) { + if (direction > 0) { + delta = toScroll.getUnitIncrement(direction); + } else { + delta = -toScroll.getUnitIncrement(direction); + } + int oldValue = toScroll.getValue(); + int newValue = oldValue + delta; + if (delta > 0 && newValue < oldValue) { + newValue = toScroll.getMaximum(); + } else if (delta < 0 && newValue > oldValue) { + newValue = toScroll.getMinimum(); + } + if (oldValue == newValue) { + break; + } + if (limitScroll && i > 0) { + assert limit != -1; + if ((direction < 0 && newValue < limit) + || (direction > 0 && newValue > limit)) { + break; + } + } + toScroll.setValue(newValue); + } + + } + } else if (e.getScrollType() == MouseWheelEvent.WHEEL_BLOCK_SCROLL) { + int oldValue = toScroll.getValue(); + int blockIncrement = toScroll.getBlockIncrement(direction); + int delta = blockIncrement * ((direction > 0) ? +1 : -1); + int newValue = oldValue + delta; + if (delta > 0 && newValue < oldValue) { + newValue = toScroll.getMaximum(); + } else if (delta < 0 && newValue > oldValue) { + newValue = toScroll.getMinimum(); + } + toScroll.setValue(newValue); + } + } + } + + e.consume(); + }); + + textArea.addMouseMotionListener(new MouseMotionAdapter() { + private boolean isLinkLabelPrev = false; + private String prevLinkText = null; + + @Override + public synchronized void mouseMoved(MouseEvent e) { + String linkText = null; + boolean isLinkLabel = false; + boolean isCtrlDown = (e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0; + if (isCtrlDown) { + linkText = createLinkLabel(e); + isLinkLabel = linkText != null; + } + if (isCtrlDown && isWaitForLinksCursor) { + textArea.setCursor(new Cursor(Cursor.WAIT_CURSOR)); + } else if (textArea.getCursor().getType() == Cursor.WAIT_CURSOR) { + textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + + JLabel label = OpenFile.this.mainWindow.getLabel(); + + if (isLinkLabel && isLinkLabelPrev) { + if (!linkText.equals(prevLinkText)) { + setLinkLabel(label, linkText); + } + } else if (isLinkLabel) { + setLinkLabel(label, linkText); + + } else if (isLinkLabelPrev) { + setLinkLabel(label, null); + } + isLinkLabelPrev = isLinkLabel; + prevLinkText = linkText; + } + + private void setLinkLabel(JLabel label, String text) { + String current = label.getText(); + if (text == null && current != null) + if (current.startsWith("Navigating:") || current.startsWith("Cannot navigate:")) + return; + label.setText(text != null ? text : "Complete"); + } + + private String createLinkLabel(MouseEvent e) { + int offs = textArea.viewToModel(e.getPoint()); + if (isNavigationLinksValid) { + return getLinkDescriptionForOffset(offs); + } + return null; + } + }); + } + + public void setContent(String content) { + textArea.setText(content); + FileUtil.setLanguage(textArea, name); + } + + public void decompile() { + this.invalidateContent(); + // synchronized: do not accept changes from menu while running + synchronized (settings) { + if (Languages.java().getName().equals(settings.getLanguage().getName())) { + decompileWithNavigationLinks(); + } else { + decompileWithoutLinks(); + } + } + } + + private void decompileWithoutLinks() { + this.invalidateContent(); + isNavigationLinksValid = false; + textArea.setHyperlinksEnabled(false); + + StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput.setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(type, plainTextOutput, decompilationOptions); + setContentPreserveLastScrollPosition(stringwriter.toString()); + this.isContentValid = true; + } + + private void decompileWithNavigationLinks() { + this.invalidateContent(); + DecompilerLinkProvider newLinkProvider = new DecompilerLinkProvider(); + newLinkProvider.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + newLinkProvider.setType(type); + linkProvider = newLinkProvider; + + linkProvider.generateContent(); + setContentPreserveLastScrollPosition(linkProvider.getTextContent()); + this.isContentValid = true; + enableLinks(); + } + + private void setContentPreserveLastScrollPosition(final String content) { + final Double scrollPercent = lastScrollPercent; + if (scrollPercent != null && initialNavigationLink == null) { + SwingUtilities.invokeLater(() -> { + textArea.setText(content); + restoreScrollPosition(scrollPercent); + }); + } else { + textArea.setText(content); + } + } + + private void restoreScrollPosition(final double position) { + SwingUtilities.invokeLater(() -> { + JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); + if (verticalScrollbar == null) + return; + int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); + long newScrollValue = Math.round(position * scrollMax) + verticalScrollbar.getMinimum(); + if (newScrollValue < verticalScrollbar.getMinimum()) + newScrollValue = verticalScrollbar.getMinimum(); + if (newScrollValue > verticalScrollbar.getMaximum()) + newScrollValue = verticalScrollbar.getMaximum(); + verticalScrollbar.setValue((int) newScrollValue); + }); + } + + private void enableLinks() { + if (initialNavigationLink != null) { + doEnableLinks(); + } else { + new Thread(() -> { + try { + isWaitForLinksCursor = true; + doEnableLinks(); + } finally { + isWaitForLinksCursor = false; + resetCursor(); + } + }).start(); + } + } + + private void resetCursor() { + SwingUtilities.invokeLater(() -> textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR))); + } + + private void doEnableLinks() { + isNavigationLinksValid = false; + linkProvider.processLinks(); + buildSelectionToUniqueStrTreeMap(); + clearLinksCache(); + isNavigationLinksValid = true; + textArea.setHyperlinksEnabled(true); + warmUpWithFirstLink(); + } + + private void warmUpWithFirstLink() { + if (selectionToUniqueStrTreeMap.keySet().size() > 0) { + Selection selection = selectionToUniqueStrTreeMap.keySet().iterator().next(); + getLinkDescriptionForOffset(selection.from); + } + } + + public void clearLinksCache() { + try { + isNavigableCache.clear(); + readableLinksCache.clear(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private void buildSelectionToUniqueStrTreeMap() { + TreeMap treeMap = new TreeMap<>(); + Map definitionToSelectionMap = linkProvider.getDefinitionToSelectionMap(); + Map> referenceToSelectionsMap = linkProvider.getReferenceToSelectionsMap(); + + for (String key : definitionToSelectionMap.keySet()) { + Selection selection = definitionToSelectionMap.get(key); + treeMap.put(selection, key); + } + for (String key : referenceToSelectionsMap.keySet()) { + for (Selection selection : referenceToSelectionsMap.get(key)) { + treeMap.put(selection, key); + } + } + selectionToUniqueStrTreeMap = treeMap; + } + + private Selection getSelectionForOffset(int offset) { + if (isNavigationLinksValid) { + Selection offsetSelection = new Selection(offset, offset); + Selection floorSelection = selectionToUniqueStrTreeMap.floorKey(offsetSelection); + if (floorSelection != null && floorSelection.from <= offset && floorSelection.to > offset) { + return floorSelection; + } + } + return null; + } + + private String getUniqueStrForOffset(int offset) { + Selection selection = getSelectionForOffset(offset); + if (selection != null) { + String uniqueStr = selectionToUniqueStrTreeMap.get(selection); + if (this.isLinkNavigable(uniqueStr) && this.getLinkDescription(uniqueStr) != null) { + return uniqueStr; + } + } + return null; + } + + private Integer getSelectionFromForOffset(int offset) { + Selection selection = getSelectionForOffset(offset); + if (selection != null) { + return selection.from; + } + return null; + } + + private String getLinkDescriptionForOffset(int offset) { + String uniqueStr = getUniqueStrForOffset(offset); + if (uniqueStr != null) { + return this.getLinkDescription(uniqueStr); + } + return null; + } + + private boolean isLinkNavigable(String uniqueStr) { + try { + Boolean isNavigableCached = isNavigableCache.get(uniqueStr); + if (isNavigableCached != null) + return isNavigableCached; + + boolean isNavigable = linkProvider.isLinkNavigable(uniqueStr); + isNavigableCache.put(uniqueStr, isNavigable); + return isNavigable; + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + return false; + } + + private String getLinkDescription(String uniqueStr) { + try { + String descriptionCached = readableLinksCache.get(uniqueStr); + if (descriptionCached != null) + return descriptionCached; + + String description = linkProvider.getLinkDescription(uniqueStr); + if (description != null && description.trim().length() > 0) { + readableLinksCache.put(uniqueStr, description); + return description; + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + return null; + } + + private void onNavigationClicked(String clickedReferenceUniqueStr) { + if (isLocallyNavigable(clickedReferenceUniqueStr)) { + onLocalNavigationRequest(clickedReferenceUniqueStr); + } else if (linkProvider.isLinkNavigable(clickedReferenceUniqueStr)) { + onOutboundNavigationRequest(clickedReferenceUniqueStr); + } else { + JLabel label = this.mainWindow.getLabel(); + if (label == null) + return; + String[] linkParts = clickedReferenceUniqueStr.split("\\|"); + if (linkParts.length <= 1) { + label.setText("Cannot navigate: " + clickedReferenceUniqueStr); + return; + } + String destinationTypeStr = linkParts[1]; + label.setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); + } + } + + private boolean isLocallyNavigable(String uniqueStr) { + return linkProvider.getDefinitionToSelectionMap().containsKey(uniqueStr); + } + + private void onLocalNavigationRequest(String uniqueStr) { + try { + Selection selection = linkProvider.getDefinitionToSelectionMap().get(uniqueStr); + doLocalNavigation(selection); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private void doLocalNavigation(Selection selection) { + try { + textArea.requestFocusInWindow(); + if (selection != null) { + textArea.setSelectionStart(selection.from); + textArea.setSelectionEnd(selection.to); + scrollToSelection(selection.from); + } else { + textArea.setSelectionStart(0); + textArea.setSelectionEnd(0); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private void scrollToSelection(final int selectionBeginningOffset) { + SwingUtilities.invokeLater(() -> { + try { + int fullHeight = textArea.getBounds().height; + int viewportHeight = textArea.getVisibleRect().height; + int viewportLineCount = viewportHeight / textArea.getLineHeight(); + int selectionLineNum = textArea.getLineOfOffset(selectionBeginningOffset); + int upperMarginToScroll = Math.round(viewportLineCount * 0.29f); + int upperLineToSet = selectionLineNum - upperMarginToScroll; + int currentUpperLine = textArea.getVisibleRect().y / textArea.getLineHeight(); + + if (selectionLineNum <= currentUpperLine + 2 + || selectionLineNum >= currentUpperLine + viewportLineCount - 4) { + Rectangle rectToScroll = new Rectangle(); + rectToScroll.x = 0; + rectToScroll.width = 1; + rectToScroll.y = Math.max(upperLineToSet * textArea.getLineHeight(), 0); + rectToScroll.height = Math.min(viewportHeight, fullHeight - rectToScroll.y); + textArea.scrollRectToVisible(rectToScroll); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }); + } + + private void onOutboundNavigationRequest(String uniqueStr) { + mainWindow.onNavigationRequest(uniqueStr); + } + + public void setDecompilerReferences(MetadataSystem metadataSystem, DecompilerSettings settings, + DecompilationOptions decompilationOptions) { + this.metadataSystem = metadataSystem; + this.settings = settings; + this.decompilationOptions = decompilationOptions; + } + + public TypeDefinition getType() { + return type; + } + + public void setType(TypeDefinition type) { + this.type = type; + } + + public boolean isContentValid() { + return isContentValid; + } + + public void invalidateContent() { + try { + this.setContent(""); + } finally { + this.isContentValid = false; + this.isNavigationLinksValid = false; + } + } + + public void resetScrollPosition() { + lastScrollPercent = null; + } + + public void setInitialNavigationLink(String initialNavigationLink) { + this.initialNavigationLink = initialNavigationLink; + } + + public void onAddedToScreen() { + try { + if (initialNavigationLink != null) { + onLocalNavigationRequest(initialNavigationLink); + } else if (isFirstTimeRun) { + // warm up scrolling + isFirstTimeRun = false; + doLocalNavigation(new Selection(0, 0)); + } + } finally { + initialNavigationLink = null; + } + } + + /** + * sun.swing.CachedPainter holds on OpenFile for a while even after + * JTabbedPane.remove(component) + */ + public void close() { + linkProvider = null; + type = null; + invalidateContent(); + clearLinksCache(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((path == null) ? 0 : path.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + OpenFile other = (OpenFile) obj; + if (path == null) { + return other.path == null; + } else return path.equals(other.path); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/RecentFiles.java b/src/main/java/us/deathmarine/luyten/RecentFiles.java new file mode 100644 index 00000000..4633e258 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/RecentFiles.java @@ -0,0 +1,68 @@ +package us.deathmarine.luyten; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.prefs.Preferences; + +public class RecentFiles { + + public static List paths = new ArrayList<>(); + private static final Preferences prefs = Preferences.userNodeForPackage(RecentFiles.class); + + public static int load() { + boolean saveNeeded = false; + + String serializedPaths = prefs.get("recentFiles", null); + + if (serializedPaths == null) return 0; + + // "test","asdf.txt","text2" + for (String path : serializedPaths.split("\",\"")) { + + path = path.replace("\"", ""); + + if (!path.trim().isEmpty() && !new File(path).exists()) saveNeeded = true; + else paths.add(path); + } + + if (saveNeeded) save(); + + return paths.size(); + } + + public static void add(String path) { + if (paths.contains(path)) { + paths.remove(path); + paths.add(path); + return; + } + + if (paths.size() >= 10) paths.remove(0); + paths.add(path); + + save(); + } + + public static void save() { + if (paths.size() == 0) { + prefs.put("recentFiles", ""); + return; + } + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < paths.size(); i++) { + if (i != 0) sb.append(','); + + /*if (!new File(paths.get(i)).exists()) { + paths.remove(i); + continue; + }*/ + sb.append("\"").append(paths.get(i)).append("\""); + } + + prefs.put("recentFiles", sb.toString()); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/Selection.java b/src/main/java/us/deathmarine/luyten/Selection.java new file mode 100644 index 00000000..c53e27fb --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/Selection.java @@ -0,0 +1,40 @@ +package us.deathmarine.luyten; + +public class Selection implements Comparable { + + public final Integer from; + public final Integer to; + + public Selection(Integer from, Integer to) { + this.from = from; + this.to = to; + } + + @Override + public int compareTo(Selection o) { + return from.compareTo(o.from); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((from == null) ? 0 : from.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Selection other = (Selection) obj; + if (from == null) { + return other.from == null; + } else return from.equals(other.from); + } + +} diff --git a/src/us/deathmarine/luyten/SystemInfo.java b/src/main/java/us/deathmarine/luyten/SystemInfo.java similarity index 99% rename from src/us/deathmarine/luyten/SystemInfo.java rename to src/main/java/us/deathmarine/luyten/SystemInfo.java index 77722ef3..7f3730cf 100644 --- a/src/us/deathmarine/luyten/SystemInfo.java +++ b/src/main/java/us/deathmarine/luyten/SystemInfo.java @@ -3,8 +3,10 @@ import java.util.Locale; public class SystemInfo { + private static final String OS_NAME = System.getProperty("os.name"); private static final String OS_NAME_LOWER = OS_NAME.toLowerCase(Locale.US); public static boolean IS_MAC = OS_NAME_LOWER.startsWith("mac"); + } diff --git a/src/main/java/us/deathmarine/luyten/TooLargeFileException.java b/src/main/java/us/deathmarine/luyten/TooLargeFileException.java new file mode 100644 index 00000000..79e8b52e --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/TooLargeFileException.java @@ -0,0 +1,22 @@ +package us.deathmarine.luyten; + +import java.text.DecimalFormat; + +public class TooLargeFileException extends Exception { + + private static final long serialVersionUID = 6091096838075139962L; + private final long size; + + public TooLargeFileException(long size) { + this.size = size; + } + + public String getReadableFileSize() { + if (size <= 0) + return "0"; + final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"}; + int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); + return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/TreeNodeUserObject.java b/src/main/java/us/deathmarine/luyten/TreeNodeUserObject.java new file mode 100644 index 00000000..136dc3d2 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/TreeNodeUserObject.java @@ -0,0 +1,38 @@ +package us.deathmarine.luyten; + +public class TreeNodeUserObject { + + private String originalName; + private String displayName; + + public TreeNodeUserObject(String name) { + this(name, name); + } + + public TreeNodeUserObject(String originalName, String displayName) { + this.originalName = originalName; + this.displayName = displayName; + } + + public String getOriginalName() { + return originalName; + } + + public void setOriginalName(String originalName) { + this.originalName = originalName; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + @Override + public String toString() { + return displayName; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/TreeUtil.java b/src/main/java/us/deathmarine/luyten/TreeUtil.java new file mode 100644 index 00000000..21548784 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/TreeUtil.java @@ -0,0 +1,82 @@ +package us.deathmarine.luyten; + +import java.util.HashSet; +import java.util.Set; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; + +public class TreeUtil { + + private JTree tree; + + public TreeUtil() { + } + + public TreeUtil(JTree tree) { + this.tree = tree; + } + + public Set getExpansionState() { + Set openedSet = new HashSet<>(); + if (tree != null) { + int rowCount = tree.getRowCount(); + for (int i = 0; i < rowCount; i++) { + TreePath path = tree.getPathForRow(i); + if (tree.isExpanded(path)) { + String rowPathStr = getRowPathStr(path); + // for switching Package Explorer on/off + openedSet.addAll(getAllParentPathsStr(rowPathStr)); + } + } + } + return openedSet; + } + + private Set getAllParentPathsStr(String rowPathStr) { + Set parents = new HashSet<>(); + parents.add(rowPathStr); + if (rowPathStr.contains("/")) { + String[] pathElements = rowPathStr.split("/"); + String path = ""; + for (String pathElement : pathElements) { + path = path + pathElement + "/"; + parents.add(path); + } + } + return parents; + } + + public void restoreExpanstionState(Set expansionState) { + if (tree != null && expansionState != null) { + // tree.getRowCount() changes at tree.expandRow() + for (int i = 0; i < tree.getRowCount(); i++) { + TreePath path = tree.getPathForRow(i); + if (expansionState.contains(getRowPathStr(path))) { + tree.expandRow(i); + } + } + } + } + + private String getRowPathStr(TreePath trp) { + StringBuilder pathStr = new StringBuilder(); + if (trp.getPathCount() > 1) { + for (int i = 1; i < trp.getPathCount(); i++) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); + TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); + pathStr.append(userObject.getOriginalName()).append("/"); + } + } + return pathStr.toString(); + } + + public JTree getTree() { + return tree; + } + + public void setTree(JTree tree) { + this.tree = tree; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/WindowPosition.java b/src/main/java/us/deathmarine/luyten/WindowPosition.java new file mode 100644 index 00000000..547ec1eb --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/WindowPosition.java @@ -0,0 +1,91 @@ +package us.deathmarine.luyten; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Toolkit; +import javax.swing.JDialog; +import javax.swing.JFrame; + +public class WindowPosition { + + private boolean isFullScreen; + private int windowWidth; + private int windowHeight; + private int windowX; + private int windowY; + + public void readPositionFromWindow(JFrame window) { + isFullScreen = (window.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH; + if (!isFullScreen) { + this.readPositionFromComponent(window); + } + } + + public void readPositionFromDialog(JDialog dialog) { + this.readPositionFromComponent(dialog); + } + + private void readPositionFromComponent(Component component) { + isFullScreen = false; + windowWidth = component.getWidth(); + windowHeight = component.getHeight(); + windowX = component.getX(); + windowY = component.getY(); + } + + public boolean isSavedWindowPositionValid() { + if (isFullScreen) { + return true; + } + if (windowWidth < 100 || windowHeight < 100) { + return false; + } + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + if (windowWidth > screenSize.width + 50 || windowHeight > screenSize.height + 50) { + return false; + } + return windowY >= -20 && windowY <= screenSize.height - 50 && windowX >= 50 - windowWidth + && windowX <= screenSize.width - 50; + } + + public boolean isFullScreen() { + return isFullScreen; + } + + public void setFullScreen(boolean isFullScreen) { + this.isFullScreen = isFullScreen; + } + + public int getWindowWidth() { + return windowWidth; + } + + public void setWindowWidth(int windowWidth) { + this.windowWidth = windowWidth; + } + + public int getWindowHeight() { + return windowHeight; + } + + public void setWindowHeight(int windowHeight) { + this.windowHeight = windowHeight; + } + + public int getWindowX() { + return windowX; + } + + public void setWindowX(int windowX) { + this.windowX = windowX; + } + + public int getWindowY() { + return windowY; + } + + public void setWindowY(int windowY) { + this.windowY = windowY; + } + +} diff --git a/src/resources/Luyten.icns b/src/main/resources/Luyten.icns similarity index 100% rename from src/resources/Luyten.icns rename to src/main/resources/Luyten.icns diff --git a/src/resources/Luyten.png b/src/main/resources/Luyten.png similarity index 100% rename from src/resources/Luyten.png rename to src/main/resources/Luyten.png diff --git a/src/distfiles/Procyon.License.txt b/src/main/resources/distfiles/Procyon.License.txt similarity index 99% rename from src/distfiles/Procyon.License.txt rename to src/main/resources/distfiles/Procyon.License.txt index 0325c58b..01a08e08 100644 --- a/src/distfiles/Procyon.License.txt +++ b/src/main/resources/distfiles/Procyon.License.txt @@ -1,32 +1,32 @@ -Procyon -Copyright (c) 2013, Mike Strobel - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -1. Definitions. -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: -You must give any other recipients of the Work or Derivative Works a copy of this License; and -You must cause any modified files to carry prominent notices stating that You changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +Procyon +Copyright (c) 2013, Mike Strobel + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +1. Definitions. +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/src/distfiles/RSyntaxTextArea.License.txt b/src/main/resources/distfiles/RSyntaxTextArea.License.txt similarity index 98% rename from src/distfiles/RSyntaxTextArea.License.txt rename to src/main/resources/distfiles/RSyntaxTextArea.License.txt index 398ef073..75344190 100644 --- a/src/distfiles/RSyntaxTextArea.License.txt +++ b/src/main/resources/distfiles/RSyntaxTextArea.License.txt @@ -1,25 +1,25 @@ -RSyntaxTextArea -Copyright (c) 2012, Robert Futrell -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +RSyntaxTextArea +Copyright (c) 2012, Robert Futrell +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/resources/file.png b/src/main/resources/file.png similarity index 100% rename from src/resources/file.png rename to src/main/resources/file.png diff --git a/src/resources/icon_close.png b/src/main/resources/icon_close.png similarity index 100% rename from src/resources/icon_close.png rename to src/main/resources/icon_close.png diff --git a/src/resources/java.png b/src/main/resources/java.png similarity index 100% rename from src/resources/java.png rename to src/main/resources/java.png diff --git a/src/resources/package_obj.png b/src/main/resources/package_obj.png similarity index 100% rename from src/resources/package_obj.png rename to src/main/resources/package_obj.png diff --git a/src/resources/yml.png b/src/main/resources/yml.png similarity index 100% rename from src/resources/yml.png rename to src/main/resources/yml.png diff --git a/src/us/deathmarine/luyten/CellRenderer.java b/src/us/deathmarine/luyten/CellRenderer.java deleted file mode 100644 index b0fe32df..00000000 --- a/src/us/deathmarine/luyten/CellRenderer.java +++ /dev/null @@ -1,52 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.awt.Toolkit; - -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JTree; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; - -public class CellRenderer extends DefaultTreeCellRenderer { - private static final long serialVersionUID = -5691181006363313993L; - Icon pack; - Icon java_image; - Icon yml_image; - Icon file_image; - - public CellRenderer() { - this.pack = new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/package_obj.png"))); - this.java_image = new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/java.png"))); - this.yml_image = new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/yml.png"))); - this.file_image = new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/file.png"))); - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, - int row, boolean hasFocus) { - super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; - if (node.getChildCount() > 0) { - setIcon(this.pack); - } else if (getFileName(node).endsWith(".class") || getFileName(node).endsWith(".java")) { - setIcon(this.java_image); - } else if (getFileName(node).endsWith(".yml") || getFileName(node).endsWith(".yaml")) { - setIcon(this.yml_image); - } else { - setIcon(this.file_image); - } - - return this; - } - - public String getFileName(DefaultMutableTreeNode node) { - return ((TreeNodeUserObject) node.getUserObject()).getOriginalName(); - } - -} diff --git a/src/us/deathmarine/luyten/Closer.java b/src/us/deathmarine/luyten/Closer.java deleted file mode 100644 index 31dcc15e..00000000 --- a/src/us/deathmarine/luyten/Closer.java +++ /dev/null @@ -1,22 +0,0 @@ -package us.deathmarine.luyten; - -public final class Closer { - public static void tryClose(final AutoCloseable c) { - if (c == null) { - return; - } - try { - c.close(); - } catch (Throwable ignored) { - } - } - - public static void tryClose(final AutoCloseable... items) { - if (items == null) { - return; - } - for (AutoCloseable c : items) { - tryClose(c); - } - } -} diff --git a/src/us/deathmarine/luyten/ConfigSaver.java b/src/us/deathmarine/luyten/ConfigSaver.java deleted file mode 100644 index bdce7020..00000000 --- a/src/us/deathmarine/luyten/ConfigSaver.java +++ /dev/null @@ -1,223 +0,0 @@ -package us.deathmarine.luyten; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.prefs.Preferences; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.languages.Language; -import com.strobel.decompiler.languages.Languages; -import com.strobel.decompiler.languages.java.JavaFormattingOptions; - -public class ConfigSaver { - - private static final String FLATTEN_SWITCH_BLOCKS_ID = "flattenSwitchBlocks"; - private static final String FORCE_EXPLICIT_IMPORTS_ID = "forceExplicitImports"; - private static final String SHOW_SYNTHETIC_MEMBERS_ID = "showSyntheticMembers"; - private static final String EXCLUDE_NESTED_TYPES_ID = "excludeNestedTypes"; - private static final String FORCE_EXPLICIT_TYPE_ARGUMENTS_ID = "forceExplicitTypeArguments"; - private static final String RETAIN_REDUNDANT_CASTS_ID = "retainRedundantCasts"; - private static final String INCLUDE_ERROR_DIAGNOSTICS_ID = "includeErrorDiagnostics"; - private static final String UNICODE_REPLACE_ENABLED_ID = "unicodeReplaceEnabled"; - private static final String LANGUAGE_NAME_ID = "languageName"; - - private static final String MAIN_WINDOW_ID_PREFIX = "main"; - private static final String FIND_WINDOW_ID_PREFIX = "find"; - private static final String WINDOW_IS_FULL_SCREEN_ID = "WindowIsFullScreen"; - private static final String WINDOW_WIDTH_ID = "WindowWidth"; - private static final String WINDOW_HEIGHT_ID = "WindowHeight"; - private static final String WINDOW_X_ID = "WindowX"; - private static final String WINDOW_Y_ID = "WindowY"; - - private DecompilerSettings decompilerSettings; - private WindowPosition mainWindowPosition; - private WindowPosition findWindowPosition; - private LuytenPreferences luytenPreferences; - - private static volatile ConfigSaver theLoadedInstance; - - /** - * Do not instantiate, get the loaded instance - */ - private ConfigSaver() { - } - - public static ConfigSaver getLoadedInstance() { - if (theLoadedInstance == null) { - synchronized (ConfigSaver.class) { - if (theLoadedInstance == null) { - theLoadedInstance = new ConfigSaver(); - theLoadedInstance.loadConfig(); - } - } - } - return theLoadedInstance; - } - - /** - * Do not load, get the loaded instance - */ - private void loadConfig() { - decompilerSettings = new DecompilerSettings(); - if (decompilerSettings.getJavaFormattingOptions() == null) { - decompilerSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); - } - luytenPreferences = new LuytenPreferences(); - mainWindowPosition = new WindowPosition(); - findWindowPosition = new WindowPosition(); - try { - Preferences prefs = Preferences.userNodeForPackage(ConfigSaver.class); - if (!prefs.get(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()) - .equals(decompilerSettings.getLanguage().getName())) - prefs.put(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()); - - decompilerSettings.setFlattenSwitchBlocks( - prefs.getBoolean(FLATTEN_SWITCH_BLOCKS_ID, decompilerSettings.getFlattenSwitchBlocks())); - decompilerSettings.setForceExplicitImports( - prefs.getBoolean(FORCE_EXPLICIT_IMPORTS_ID, decompilerSettings.getForceExplicitImports())); - decompilerSettings.setShowSyntheticMembers( - prefs.getBoolean(SHOW_SYNTHETIC_MEMBERS_ID, decompilerSettings.getShowSyntheticMembers())); - decompilerSettings.setExcludeNestedTypes( - prefs.getBoolean(EXCLUDE_NESTED_TYPES_ID, decompilerSettings.getExcludeNestedTypes())); - decompilerSettings.setForceExplicitTypeArguments(prefs.getBoolean(FORCE_EXPLICIT_TYPE_ARGUMENTS_ID, - decompilerSettings.getForceExplicitTypeArguments())); - decompilerSettings.setRetainRedundantCasts( - prefs.getBoolean(RETAIN_REDUNDANT_CASTS_ID, decompilerSettings.getRetainRedundantCasts())); - decompilerSettings.setIncludeErrorDiagnostics( - prefs.getBoolean(INCLUDE_ERROR_DIAGNOSTICS_ID, decompilerSettings.getIncludeErrorDiagnostics())); - decompilerSettings.setLanguage( - findLanguageByName(prefs.get(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()))); - decompilerSettings.setUnicodeOutputEnabled(prefs.getBoolean(UNICODE_REPLACE_ENABLED_ID, false)); - - mainWindowPosition = loadWindowPosition(prefs, MAIN_WINDOW_ID_PREFIX); - findWindowPosition = loadWindowPosition(prefs, FIND_WINDOW_ID_PREFIX); - luytenPreferences = loadLuytenPreferences(prefs); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private WindowPosition loadWindowPosition(Preferences prefs, String windowIdPrefix) { - WindowPosition windowPosition = new WindowPosition(); - windowPosition.setFullScreen(prefs.getBoolean(windowIdPrefix + WINDOW_IS_FULL_SCREEN_ID, false)); - windowPosition.setWindowWidth(prefs.getInt(windowIdPrefix + WINDOW_WIDTH_ID, 0)); - windowPosition.setWindowHeight(prefs.getInt(windowIdPrefix + WINDOW_HEIGHT_ID, 0)); - windowPosition.setWindowX(prefs.getInt(windowIdPrefix + WINDOW_X_ID, 0)); - windowPosition.setWindowY(prefs.getInt(windowIdPrefix + WINDOW_Y_ID, 0)); - return windowPosition; - } - - // load preferences by their java variable names - private LuytenPreferences loadLuytenPreferences(Preferences prefs) throws Exception { - LuytenPreferences newLuytenPrefs = new LuytenPreferences(); - for (Field field : LuytenPreferences.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) - continue; - field.setAccessible(true); - String prefId = field.getName(); - Object defaultVal = field.get(newLuytenPrefs); - - if (field.getType() == String.class) { - String defaultStr = (String) (defaultVal == null ? "" : defaultVal); - field.set(newLuytenPrefs, prefs.get(prefId, defaultStr)); - - } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { - Boolean defaultBool = (Boolean) (defaultVal == null ? Boolean.FALSE : defaultVal); - field.setBoolean(newLuytenPrefs, prefs.getBoolean(prefId, defaultBool)); - - } else if (field.getType() == Integer.class || field.getType() == int.class) { - Integer defaultInt = (Integer) (defaultVal == null ? Integer.valueOf(0) : defaultVal); - field.setInt(newLuytenPrefs, prefs.getInt(prefId, defaultInt)); - } - } - return newLuytenPrefs; - } - - public void saveConfig() { - // Registry path on Windows Xp: - // HKEY_CURRENT_USER/Software/JavaSoft/Prefs/us/deathmarine/luyten - try { - Preferences prefs = Preferences.userNodeForPackage(ConfigSaver.class); - - prefs.putBoolean(FLATTEN_SWITCH_BLOCKS_ID, decompilerSettings.getFlattenSwitchBlocks()); - prefs.putBoolean(FORCE_EXPLICIT_IMPORTS_ID, decompilerSettings.getForceExplicitImports()); - prefs.putBoolean(SHOW_SYNTHETIC_MEMBERS_ID, decompilerSettings.getShowSyntheticMembers()); - prefs.putBoolean(EXCLUDE_NESTED_TYPES_ID, decompilerSettings.getExcludeNestedTypes()); - prefs.putBoolean(FORCE_EXPLICIT_TYPE_ARGUMENTS_ID, decompilerSettings.getForceExplicitTypeArguments()); - prefs.putBoolean(RETAIN_REDUNDANT_CASTS_ID, decompilerSettings.getRetainRedundantCasts()); - prefs.putBoolean(INCLUDE_ERROR_DIAGNOSTICS_ID, decompilerSettings.getIncludeErrorDiagnostics()); - prefs.putBoolean(UNICODE_REPLACE_ENABLED_ID, decompilerSettings.isUnicodeOutputEnabled()); - prefs.put(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()); - - saveWindowPosition(prefs, MAIN_WINDOW_ID_PREFIX, mainWindowPosition); - saveWindowPosition(prefs, FIND_WINDOW_ID_PREFIX, findWindowPosition); - saveLuytenPreferences(prefs); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private void saveWindowPosition(Preferences prefs, String windowIdPrefix, WindowPosition windowPosition) { - prefs.putBoolean(windowIdPrefix + WINDOW_IS_FULL_SCREEN_ID, windowPosition.isFullScreen()); - prefs.putInt(windowIdPrefix + WINDOW_WIDTH_ID, windowPosition.getWindowWidth()); - prefs.putInt(windowIdPrefix + WINDOW_HEIGHT_ID, windowPosition.getWindowHeight()); - prefs.putInt(windowIdPrefix + WINDOW_X_ID, windowPosition.getWindowX()); - prefs.putInt(windowIdPrefix + WINDOW_Y_ID, windowPosition.getWindowY()); - } - - // save preferences by their java variable names - private void saveLuytenPreferences(Preferences prefs) throws Exception { - for (Field field : LuytenPreferences.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) - continue; - field.setAccessible(true); - String prefId = field.getName(); - Object value = field.get(luytenPreferences); - - if (field.getType() == String.class) { - prefs.put(prefId, (String) (value == null ? "" : value)); - - } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { - prefs.putBoolean(prefId, (Boolean) (value == null ? Boolean.FALSE : value)); - - } else if (field.getType() == Integer.class || field.getType() == int.class) { - prefs.putInt(prefId, (Integer) (value == null ? Integer.valueOf(0) : value)); - } - } - } - - private Language findLanguageByName(String languageName) { - if (languageName != null) { - - if (languageName.equals(Languages.java().getName())) { - return Languages.java(); - } else if (languageName.equals(Languages.bytecode().getName())) { - return Languages.bytecode(); - } else if (languageName.equals(Languages.bytecodeAst().getName())) { - return Languages.bytecodeAst(); - } - - for (Language language : Languages.debug()) { - if (languageName.equals(language.getName())) { - return language; - } - } - } - return Languages.java(); - } - - public DecompilerSettings getDecompilerSettings() { - return decompilerSettings; - } - - public WindowPosition getMainWindowPosition() { - return mainWindowPosition; - } - - public WindowPosition getFindWindowPosition() { - return findWindowPosition; - } - - public LuytenPreferences getLuytenPreferences() { - return luytenPreferences; - } -} diff --git a/src/us/deathmarine/luyten/DecompilerLinkProvider.java b/src/us/deathmarine/luyten/DecompilerLinkProvider.java deleted file mode 100644 index df13e3b0..00000000 --- a/src/us/deathmarine/luyten/DecompilerLinkProvider.java +++ /dev/null @@ -1,382 +0,0 @@ -package us.deathmarine.luyten; - -import java.io.StringWriter; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import com.strobel.assembler.metadata.FieldDefinition; -import com.strobel.assembler.metadata.FieldReference; -import com.strobel.assembler.metadata.MetadataSystem; -import com.strobel.assembler.metadata.MethodDefinition; -import com.strobel.assembler.metadata.MethodReference; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.core.StringUtilities; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; - -public class DecompilerLinkProvider implements LinkProvider { - - private Map definitionToSelectionMap = new HashMap<>(); - private Map> referenceToSelectionsMap = new HashMap<>(); - private boolean isSelectionMapsPopulated = false; - - private MetadataSystem metadataSystem; - private DecompilerSettings settings; - private DecompilationOptions decompilationOptions; - private TypeDefinition type; - - private String currentTypeQualifiedName; - private String textContent = ""; - - @Override - public void generateContent() { - definitionToSelectionMap = new HashMap<>(); - referenceToSelectionsMap = new HashMap<>(); - currentTypeQualifiedName = type.getPackageName() + "." + type.getName(); - final StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter) { - @Override - public void writeDefinition(String text, Object definition, boolean isLocal) { - super.writeDefinition(text, definition, isLocal); - try { - if (text != null && definition != null) { - String uniqueStr = createUniqueStrForReference(definition); - if (uniqueStr != null) { - // fix link's underline length: _java.util.HashSet_ - // -> _HashSet_ - text = text.replaceAll("[^.]*\\.", ""); - int from = stringwriter.getBuffer().length() - text.length(); - int to = stringwriter.getBuffer().length(); - definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); - } - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - @Override - public void writeReference(String text, Object reference, boolean isLocal) { - super.writeReference(text, reference, isLocal); - try { - if (text != null && reference != null) { - String uniqueStr = createUniqueStrForReference(reference); - if (uniqueStr != null) { - text = text.replaceAll("[^.]*\\.", ""); - int from = stringwriter.getBuffer().length() - text.length(); - int to = stringwriter.getBuffer().length(); - if (reference instanceof FieldReference) { - // fix enum definition links (note: could not fix enum reference links) - if (((FieldReference) reference).isDefinition()) { - definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); - return; - } - } - if (referenceToSelectionsMap.containsKey(uniqueStr)) { - Set selectionsSet = referenceToSelectionsMap.get(uniqueStr); - if (selectionsSet != null) { - selectionsSet.add(new Selection(from, to)); - } - } else { - Set selectionsSet = new HashSet<>(); - selectionsSet.add(new Selection(from, to)); - referenceToSelectionsMap.put(uniqueStr, selectionsSet); - } - } - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }; - plainTextOutput.setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(type, plainTextOutput, decompilationOptions); - textContent = stringwriter.toString(); - isSelectionMapsPopulated = true; - } - - private String createUniqueStrForReference(Object reference) { - String uniqueStr = null; - if (reference instanceof TypeReference) { - TypeReference type = (TypeReference) reference; - String pathAndTypeStr = getPathAndTypeStr(type); - if (pathAndTypeStr != null) { - uniqueStr = "type|" + pathAndTypeStr; - } - } else if (reference instanceof MethodReference) { - MethodReference method = (MethodReference) reference; - String pathAndTypeStr = getPathAndTypeStr(method.getDeclaringType()); - if (pathAndTypeStr != null) { - uniqueStr = "method|" + pathAndTypeStr + "|" + method.getName() + "|" + method.getErasedSignature(); - } - } else if (reference instanceof FieldReference) { - FieldReference field = (FieldReference) reference; - String pathAndTypeStr = getPathAndTypeStr(field.getDeclaringType()); - if (pathAndTypeStr != null) { - uniqueStr = "field|" + pathAndTypeStr + "|" + field.getName(); - } - } - return uniqueStr; - } - - private String getPathAndTypeStr(TypeReference typeRef) { - String name = typeRef.getName(); - String packageStr = typeRef.getPackageName(); - TypeReference mostOuterTypeRef = getMostOuterTypeRef(typeRef); - String mostOuterTypeName = mostOuterTypeRef.getName(); - if (name != null && packageStr != null && mostOuterTypeName != null && name.trim().length() > 0 - && mostOuterTypeName.trim().length() > 0) { - String pathStr = packageStr.replaceAll("\\.", "/") + "/" + mostOuterTypeName; - String typeStr = packageStr + "." + name.replace(".", "$"); - return pathStr + "|" + typeStr; - } - return null; - } - - private TypeReference getMostOuterTypeRef(TypeReference typeRef) { - int maxDecraringDepth = typeRef.getFullName().split("([.$])").length; - for (int i = 0; i < maxDecraringDepth; i++) { - TypeReference declaringTypeRef = typeRef.getDeclaringType(); - if (declaringTypeRef == null) { - break; - } else { - typeRef = declaringTypeRef; - } - } - if (typeRef.getName().contains("$")) { - return getMostOuterTypeRefBySlowLookuping(typeRef); - } - return typeRef; - } - - private TypeReference getMostOuterTypeRefBySlowLookuping(TypeReference typeRef) { - String name = typeRef.getName(); - if (name == null) - return typeRef; - String packageName = typeRef.getPackageName(); - if (packageName == null) - return typeRef; - String[] nameParts = name.split("\\$"); - StringBuilder newName = new StringBuilder(); - String sep = ""; - for (int i = 0; i < nameParts.length - 1; i++) { - newName.append(sep).append(nameParts[i]); - sep = "$"; - String newInternalName = packageName.replaceAll("\\.", "/") + "/" + newName; - TypeReference newTypeRef = metadataSystem.lookupType(newInternalName); - if (newTypeRef != null) { - TypeDefinition newTypeDef = newTypeRef.resolve(); - if (newTypeDef != null) { - return newTypeRef; - } - } - } - return typeRef; - } - - @Override - public String getTextContent() { - return textContent; - } - - @Override - public void processLinks() { - } - - @Override - public Map getDefinitionToSelectionMap() { - return definitionToSelectionMap; - } - - @Override - public Map> getReferenceToSelectionsMap() { - return referenceToSelectionsMap; - } - - @Override - public boolean isLinkNavigable(String uniqueStr) { - if (isSelectionMapsPopulated && definitionToSelectionMap.containsKey(uniqueStr)) - return true; - if (uniqueStr == null) - return false; - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length < 3) - return false; - String typeStr = linkParts[2]; - if (typeStr.trim().length() <= 0) - return false; - TypeReference typeRef = metadataSystem.lookupType(typeStr.replaceAll("\\.", "/")); - if (typeRef == null) - return false; - TypeDefinition typeDef = typeRef.resolve(); - if (typeDef == null) - return false; - if (typeDef.isSynthetic()) - return false; - - if (isSelectionMapsPopulated) { - // current type's navigable definitions checked already, now it's erroneous - if (currentTypeQualifiedName == null || currentTypeQualifiedName.trim().length() <= 0) - return false; - if (typeStr.equals(currentTypeQualifiedName) || typeStr.startsWith(currentTypeQualifiedName + ".") - || typeStr.startsWith(currentTypeQualifiedName + "$")) - return false; - } - - // check linked field/method exists - if (uniqueStr.startsWith("method")) { - return findMethodInType(typeDef, uniqueStr) != null; - } else if (uniqueStr.startsWith("field")) { - return findFieldInType(typeDef, uniqueStr) != null; - } - return true; - } - - private MethodDefinition findMethodInType(TypeDefinition typeDef, String uniqueStr) { - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length != 5) - return null; - String methodName = linkParts[3]; - String methodErasedSignature = linkParts[4]; - if (methodName.trim().length() <= 0 || methodErasedSignature.trim().length() <= 0) - return null; - List declaredMethods = typeDef.getDeclaredMethods(); - if (declaredMethods == null) - return null; - boolean isFound; - for (MethodDefinition declaredMethod : declaredMethods) { - isFound = (declaredMethod != null && methodName.equals(declaredMethod.getName())); - isFound = (isFound && methodErasedSignature.equals(declaredMethod.getErasedSignature())); - if (isFound) { - if (declaredMethod.isSynthetic() && !settings.getShowSyntheticMembers()) { - return null; - } else { - return declaredMethod; - } - } - } - return null; - } - - private FieldDefinition findFieldInType(TypeDefinition typeDef, String uniqueStr) { - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length != 4) - return null; - String fieldName = linkParts[3]; - if (fieldName.trim().length() <= 0) - return null; - List declaredFields = typeDef.getDeclaredFields(); - if (declaredFields == null) - return null; - boolean isFound; - for (FieldDefinition declaredField : declaredFields) { - isFound = (declaredField != null && fieldName.equals(declaredField.getName())); - if (isFound) { - if (declaredField.isSynthetic()) { - return null; - } else { - return declaredField; - } - } - } - return null; - } - - @Override - public String getLinkDescription(String uniqueStr) { - String readableLink = null; - try { - if (uniqueStr == null) - return null; - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length < 3) - return null; - String typeStr = linkParts[2]; - TypeReference typeRef = metadataSystem.lookupType(typeStr.replaceAll("\\.", "/")); - if (typeRef == null) - return null; - TypeDefinition typeDef = typeRef.resolve(); - if (typeDef == null) - return null; - - String declaredSuffix = ""; - String mostOuterTypeStr = linkParts[1].replaceAll("/", "."); - boolean isOwnFile = mostOuterTypeStr.equals(currentTypeQualifiedName); - if (!isOwnFile) { - declaredSuffix = " - Declared: " + mostOuterTypeStr; - } - - if (uniqueStr.startsWith("type")) { - String desc = typeDef.getBriefDescription(); - if (desc != null && desc.trim().length() > 0) { - readableLink = desc; - } - } else if (uniqueStr.startsWith("method")) { - MethodDefinition methodDef = findMethodInType(typeDef, uniqueStr); - if (methodDef == null) - return null; - String desc = methodDef.getBriefDescription(); - if (desc != null && desc.trim().length() > 0) { - - if (desc.contains("void ")) { - String constructorName = typeDef.getName(); - TypeReference declaringTypeRef = typeRef.getDeclaringType(); - if (declaringTypeRef != null) { - TypeDefinition declaringTypeDef = declaringTypeRef.resolve(); - if (declaringTypeDef != null) { - String declaringTypeName = declaringTypeDef.getName(); - if (declaringTypeName != null) { - constructorName = StringUtilities.removeLeft(constructorName, declaringTypeName); - constructorName = constructorName.replaceAll("^([.$])", ""); - } - } - } - desc = desc.replace("void ", constructorName); - readableLink = "Constructor: " + erasePackageInfoFromDesc(desc) + declaredSuffix; - } else { - readableLink = erasePackageInfoFromDesc(desc) + declaredSuffix; - } - } - } else if (uniqueStr.startsWith("field")) { - FieldDefinition fieldDef = findFieldInType(typeDef, uniqueStr); - if (fieldDef == null) - return null; - String desc = fieldDef.getBriefDescription(); - if (desc != null && desc.trim().length() > 0) { - readableLink = erasePackageInfoFromDesc(desc) + declaredSuffix; - } - - } - if (readableLink != null) { - readableLink = readableLink.replace("$", "."); - } - } catch (Exception e) { - readableLink = null; - Luyten.showExceptionDialog("Exception!", e); - } - return readableLink; - } - - private String erasePackageInfoFromDesc(String desc) { - // Use {0,1024} instead of * as it can't be used together with a lookbehind. - // If errors occur, increase this limit. - String limiters = "()<>\\[\\]?\\s,"; - desc = desc.replaceAll("(?<=[^" + limiters + "]{0,1024})([^" + limiters + "]*)\\.", ""); - return desc; - } - - public void setDecompilerReferences(MetadataSystem metadataSystem, DecompilerSettings settings, - DecompilationOptions decompilationOptions) { - this.metadataSystem = metadataSystem; - this.settings = settings; - this.decompilationOptions = decompilationOptions; - } - - public void setType(TypeDefinition type) { - this.type = type; - } -} diff --git a/src/us/deathmarine/luyten/DropListener.java b/src/us/deathmarine/luyten/DropListener.java deleted file mode 100644 index 30cc73a0..00000000 --- a/src/us/deathmarine/luyten/DropListener.java +++ /dev/null @@ -1,102 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DropTargetDragEvent; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.dnd.DropTargetEvent; -import java.awt.dnd.DropTargetListener; -import java.io.BufferedReader; -import java.io.File; -import java.io.Reader; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -/** - * Drag-Drop (only MainWindow should be called from here) - */ -public class DropListener implements DropTargetListener { - private final MainWindow mainWindow; - - public DropListener(MainWindow mainWindow) { - this.mainWindow = mainWindow; - } - - @SuppressWarnings("unchecked") - @Override - public void drop(DropTargetDropEvent event) { - event.acceptDrop(DnDConstants.ACTION_COPY); - Transferable transferable = event.getTransferable(); - if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { - DataFlavor[] flavors = transferable.getTransferDataFlavors(); - for (DataFlavor flavor : flavors) { - try { - if (flavor.isFlavorJavaFileListType()) { - List files = (List) transferable.getTransferData(flavor); - mainWindow.onFilesDropped(files); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - event.dropComplete(true); - } else { - DataFlavor[] flavors = transferable.getTransferDataFlavors(); - boolean handled = false; - for (DataFlavor flavor : flavors) { - if (flavor.isRepresentationClassReader()) { - try { - Reader reader = flavor.getReaderForText(transferable); - BufferedReader br = new BufferedReader(reader); - List list = new ArrayList<>(); - String line; - while ((line = br.readLine()) != null) { - try { - if (("" + (char) 0).equals(line)) - continue; - File file = new File(new URI(line)); - list.add(file); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - if (list.size() > 1) { - event.rejectDrop(); - return; - } - if (list.size() == 1) { - mainWindow.onFileDropped(list.get(0)); - } - event.getDropTargetContext().dropComplete(true); - handled = true; - } catch (Exception e) { - e.printStackTrace(); - } - break; - } - } - if (!handled) { - event.rejectDrop(); - } - } - - } - - @Override - public void dragEnter(DropTargetDragEvent arg0) { - } - - @Override - public void dragExit(DropTargetEvent arg0) { - } - - @Override - public void dragOver(DropTargetDragEvent arg0) { - } - - @Override - public void dropActionChanged(DropTargetDragEvent arg0) { - } -} diff --git a/src/us/deathmarine/luyten/FileDialog.java b/src/us/deathmarine/luyten/FileDialog.java deleted file mode 100644 index b874a75a..00000000 --- a/src/us/deathmarine/luyten/FileDialog.java +++ /dev/null @@ -1,133 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.io.File; -import javax.swing.JFileChooser; -import javax.swing.filechooser.FileFilter; - -/** - * FileChoosers for Open and Save - */ -public class FileDialog { - private final DirPreferences dirPreferences; - private final ConfigSaver configSaver; - private final Component parent; - private JFileChooser fcOpen; - private JFileChooser fcSave; - private JFileChooser fcSaveAll; - - public FileDialog(Component parent) { - this.parent = parent; - configSaver = ConfigSaver.getLoadedInstance(); - LuytenPreferences luytenPrefs = configSaver.getLuytenPreferences(); - dirPreferences = new DirPreferences(luytenPrefs); - - new Thread(() -> { - try { - initOpenDialog(); - Thread.sleep(500); - initSaveAllDialog(); - Thread.sleep(500); - initSaveDialog(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - }).start(); - } - - public File doOpenDialog() { - File selectedFile = null; - initOpenDialog(); - - dirPreferences.retrieveOpenDialogDir(fcOpen); - int returnVal = fcOpen.showOpenDialog(parent); - dirPreferences.saveOpenDialogDir(fcOpen); - - if (returnVal == JFileChooser.APPROVE_OPTION) { - selectedFile = fcOpen.getSelectedFile(); - } - return selectedFile; - } - - public File doSaveDialog(String recommendedFileName) { - File selectedFile = null; - initSaveDialog(); - - dirPreferences.retrieveSaveDialogDir(fcSave); - fcSave.setSelectedFile(new File(recommendedFileName)); - int returnVal = fcSave.showSaveDialog(parent); - dirPreferences.saveSaveDialogDir(fcSave); - - if (returnVal == JFileChooser.APPROVE_OPTION) { - selectedFile = fcSave.getSelectedFile(); - } - return selectedFile; - } - - public File doSaveAllDialog(String recommendedFileName) { - File selectedFile = null; - initSaveAllDialog(); - - dirPreferences.retrieveSaveDialogDir(fcSaveAll); - fcSaveAll.setSelectedFile(new File(recommendedFileName)); - int returnVal = fcSaveAll.showSaveDialog(parent); - dirPreferences.saveSaveDialogDir(fcSaveAll); - - if (returnVal == JFileChooser.APPROVE_OPTION) { - selectedFile = fcSaveAll.getSelectedFile(); - } - return selectedFile; - } - - public synchronized void initOpenDialog() { - if (fcOpen == null) { - fcOpen = createFileChooser("*.jar", "*.zip", "*.class"); - dirPreferences.retrieveOpenDialogDir(fcOpen); - } - } - - public synchronized void initSaveDialog() { - if (fcSave == null) { - fcSave = createFileChooser("*.txt", "*.java"); - dirPreferences.retrieveSaveDialogDir(fcSave); - } - } - - public synchronized void initSaveAllDialog() { - if (fcSaveAll == null) { - fcSaveAll = createFileChooser("*.jar", "*.zip"); - dirPreferences.retrieveSaveDialogDir(fcSaveAll); - } - } - - private JFileChooser createFileChooser(String... fileFilters) { - JFileChooser fc = new JFileChooser(); - for (String fileFilter : fileFilters) { - fc.addChoosableFileFilter(new FileChooserFileFilter(fileFilter)); - } - fc.setFileSelectionMode(JFileChooser.FILES_ONLY); - fc.setMultiSelectionEnabled(false); - return fc; - } - - public static class FileChooserFileFilter extends FileFilter { - String objType; - - public FileChooserFileFilter(String string) { - objType = string; - } - - @Override - public boolean accept(File f) { - if (f.isDirectory()) - return true; - return f.getName().toLowerCase().endsWith(objType.substring(1)); - } - - @Override - public String getDescription() { - return objType; - } - } - -} diff --git a/src/us/deathmarine/luyten/FileSaver.java b/src/us/deathmarine/luyten/FileSaver.java deleted file mode 100644 index 5b0f1112..00000000 --- a/src/us/deathmarine/luyten/FileSaver.java +++ /dev/null @@ -1,323 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.BufferedOutputStream; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.zip.ZipException; -import java.util.zip.ZipOutputStream; - -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JProgressBar; -import javax.swing.SwingUtilities; - -import com.strobel.assembler.metadata.ITypeLoader; -import com.strobel.assembler.metadata.JarTypeLoader; -import com.strobel.assembler.metadata.MetadataSystem; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.core.StringUtilities; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; -import com.strobel.decompiler.languages.java.JavaFormattingOptions; - -/** - * Performs Save and Save All - */ -public class FileSaver { - - private final JProgressBar bar; - private final JLabel label; - private boolean cancel; - private boolean extracting; - - public FileSaver(JProgressBar bar, JLabel label) { - this.bar = bar; - this.label = label; - final JPopupMenu menu = new JPopupMenu("Cancel"); - final JMenuItem item = new JMenuItem("Cancel"); - item.addActionListener(arg0 -> setCancel(true)); - menu.add(item); - this.label.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent ev) { - if (SwingUtilities.isRightMouseButton(ev) && isExtracting()) - menu.show(ev.getComponent(), ev.getX(), ev.getY()); - } - }); - } - - public void saveText(final String text, final File file) { - new Thread(() -> { - DecompilerSettings settings = cloneSettings(); - boolean isUnicodeEnabled = settings.isUnicodeOutputEnabled(); - long time = System.currentTimeMillis(); - try (FileOutputStream fos = new FileOutputStream(file); - OutputStreamWriter writer = isUnicodeEnabled - ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) - : new OutputStreamWriter(fos); - BufferedWriter bw = new BufferedWriter(writer)) { - label.setText("Extracting: " + file.getName()); - bar.setVisible(true); - bw.write(text); - bw.flush(); - label.setText("Completed: " + getTime(time)); - } catch (Exception e1) { - label.setText("Cannot save file: " + file.getName()); - Luyten.showExceptionDialog("Unable to save file!\n", e1); - } finally { - setExtracting(false); - bar.setVisible(false); - } - }).start(); - } - - public void saveAllDecompiled(final File inFile, final File outFile) { - new Thread(() -> { - long time = System.currentTimeMillis(); - try { - bar.setVisible(true); - setExtracting(true); - label.setText("Extracting: " + outFile.getName()); - System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); - String inFileName = inFile.getName().toLowerCase(); - - if (inFileName.endsWith(".jar") || inFileName.endsWith(".zip")) { - doSaveJarDecompiled(inFile, outFile); - } else if (inFileName.endsWith(".class")) { - doSaveClassDecompiled(inFile, outFile); - } else { - doSaveUnknownFile(inFile, outFile); - } - if (cancel) { - label.setText("Cancelled"); - outFile.delete(); - setCancel(false); - } else { - label.setText("Completed: " + getTime(time)); - } - } catch (Exception e1) { - label.setText("Cannot save file: " + outFile.getName()); - Luyten.showExceptionDialog("Unable to save file!\n", e1); - } finally { - setExtracting(false); - bar.setVisible(false); - } - }).start(); - } - - private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { - try (JarFile jfile = new JarFile(inFile); - FileOutputStream dest = new FileOutputStream(outFile); - BufferedOutputStream buffDest = new BufferedOutputStream(dest); - ZipOutputStream out = new ZipOutputStream(buffDest)) { - bar.setMinimum(0); - bar.setMaximum(jfile.size()); - byte[] data = new byte[1024]; - DecompilerSettings settings = cloneSettings(); - LuytenTypeLoader typeLoader = new LuytenTypeLoader(); - MetadataSystem metadataSystem = new MetadataSystem(typeLoader); - ITypeLoader jarLoader = new JarTypeLoader(jfile); - typeLoader.getTypeLoaders().add(jarLoader); - - DecompilationOptions decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - - List mass; - JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); - LuytenPreferences luytenPrefs = ConfigSaver.getLoadedInstance().getLuytenPreferences(); - if (luytenPrefs.isFilterOutInnerClassEntries()) { - mass = jarEntryFilter.getEntriesWithoutInnerClasses(); - } else { - mass = jarEntryFilter.getAllEntriesFromJar(); - } - - Enumeration ent = jfile.entries(); - Set history = new HashSet<>(); - int tick = 0; - while (ent.hasMoreElements() && !cancel) { - bar.setValue(++tick); - JarEntry entry = ent.nextElement(); - if (!mass.contains(entry.getName())) - continue; - label.setText("Extracting: " + entry.getName()); - bar.setVisible(true); - if (entry.getName().endsWith(".class")) { - JarEntry etn = new JarEntry(entry.getName().replace(".class", ".java")); - label.setText("Extracting: " + etn.getName()); - System.out.println("[SaveAll]: " + etn.getName() + " -> " + outFile.getName()); - - if (history.add(etn.getName())) { - out.putNextEntry(etn); - try { - boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); - String internalName = StringUtilities.removeRight(entry.getName(), ".class"); - TypeReference type = metadataSystem.lookupType(internalName); - TypeDefinition resolvedType; - if ((type == null) || ((resolvedType = type.resolve()) == null)) { - throw new Exception("Unable to resolve type."); - } - Writer writer = isUnicodeEnabled ? new OutputStreamWriter(out, StandardCharsets.UTF_8) - : new OutputStreamWriter(out); - PlainTextOutput plainTextOutput = new PlainTextOutput(writer); - plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); - writer.flush(); - } catch (Exception e) { - label.setText("Cannot decompile file: " + entry.getName()); - Luyten.showExceptionDialog("Unable to Decompile file!\nSkipping file...", e); - } finally { - out.closeEntry(); - } - } - } else { - try { - JarEntry etn = new JarEntry(entry.getName()); - if (entry.getName().endsWith(".java")) - etn = new JarEntry(entry.getName().replace(".java", ".src.java")); - if (history.add(etn.getName())) { - out.putNextEntry(etn); - try { - InputStream in = jfile.getInputStream(etn); - if (in != null) { - try { - int count; - while ((count = in.read(data, 0, 1024)) != -1) { - out.write(data, 0, count); - } - } finally { - in.close(); - } - } - } finally { - out.closeEntry(); - } - } - } catch (ZipException ze) { - if (!ze.getMessage().contains("duplicate")) { - throw ze; - } - } - } - } - } - } - - private void doSaveClassDecompiled(File inFile, File outFile) throws Exception { - DecompilerSettings settings = cloneSettings(); - LuytenTypeLoader typeLoader = new LuytenTypeLoader(); - MetadataSystem metadataSystem = new MetadataSystem(typeLoader); - TypeReference type = metadataSystem.lookupType(inFile.getCanonicalPath()); - - DecompilationOptions decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - - boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); - TypeDefinition resolvedType; - if (type == null || ((resolvedType = type.resolve()) == null)) { - throw new Exception("Unable to resolve type."); - } - StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); - String decompiledSource = stringwriter.toString(); - - System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); - try (FileOutputStream fos = new FileOutputStream(outFile); - OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) - : new OutputStreamWriter(fos); - BufferedWriter bw = new BufferedWriter(writer)) { - bw.write(decompiledSource); - bw.flush(); - } - } - - private void doSaveUnknownFile(File inFile, File outFile) throws Exception { - try (FileInputStream in = new FileInputStream(inFile); FileOutputStream out = new FileOutputStream(outFile)) { - System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); - - byte[] data = new byte[1024]; - int count; - while ((count = in.read(data, 0, 1024)) != -1) { - out.write(data, 0, count); - } - } - } - - private DecompilerSettings cloneSettings() { - DecompilerSettings settings = ConfigSaver.getLoadedInstance().getDecompilerSettings(); - DecompilerSettings newSettings = new DecompilerSettings(); - if (newSettings.getJavaFormattingOptions() == null) { - newSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); - } - // synchronized: against main menu changes - synchronized (settings) { - newSettings.setExcludeNestedTypes(settings.getExcludeNestedTypes()); - newSettings.setFlattenSwitchBlocks(settings.getFlattenSwitchBlocks()); - newSettings.setForceExplicitImports(settings.getForceExplicitImports()); - newSettings.setForceExplicitTypeArguments(settings.getForceExplicitTypeArguments()); - newSettings.setOutputFileHeaderText(settings.getOutputFileHeaderText()); - newSettings.setLanguage(settings.getLanguage()); - newSettings.setShowSyntheticMembers(settings.getShowSyntheticMembers()); - newSettings.setAlwaysGenerateExceptionVariableForCatchBlocks( - settings.getAlwaysGenerateExceptionVariableForCatchBlocks()); - newSettings.setOutputDirectory(settings.getOutputDirectory()); - newSettings.setRetainRedundantCasts(settings.getRetainRedundantCasts()); - newSettings.setIncludeErrorDiagnostics(settings.getIncludeErrorDiagnostics()); - newSettings.setIncludeLineNumbersInBytecode(settings.getIncludeLineNumbersInBytecode()); - newSettings.setRetainPointlessSwitches(settings.getRetainPointlessSwitches()); - newSettings.setUnicodeOutputEnabled(settings.isUnicodeOutputEnabled()); - newSettings.setMergeVariables(settings.getMergeVariables()); - newSettings.setShowDebugLineNumbers(settings.getShowDebugLineNumbers()); - } - return newSettings; - } - - public boolean isCancel() { - return cancel; - } - - public void setCancel(boolean cancel) { - this.cancel = cancel; - } - - public boolean isExtracting() { - return extracting; - } - - public void setExtracting(boolean extracting) { - this.extracting = extracting; - } - - public static String getTime(long time) { - long lap = System.currentTimeMillis() - time; - lap = lap / 1000; - StringBuilder sb = new StringBuilder(); - long hour = ((lap / 60) / 60); - long min = ((lap - (hour * 60 * 60)) / 60); - long sec = ((lap - (hour * 60 * 60) - (min * 60))); - if (hour > 0) - sb.append("Hour:").append(hour).append(" "); - sb.append("Min(s): ").append(min).append(" Sec: ").append(sec); - return sb.toString(); - } -} diff --git a/src/us/deathmarine/luyten/FindAllBox.java b/src/us/deathmarine/luyten/FindAllBox.java deleted file mode 100644 index bc7f4713..00000000 --- a/src/us/deathmarine/luyten/FindAllBox.java +++ /dev/null @@ -1,381 +0,0 @@ -package us.deathmarine.luyten; - -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.core.StringUtilities; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; - -import java.awt.Dimension; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.util.Collections; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.regex.Pattern; - -import javax.swing.*; -import javax.swing.GroupLayout.Alignment; - -/** - * - * this is the Find All Dialog - *

- * Change with 1.1 - * Adjust the find all box width - *

- * - * @author clevertension - * @version 1.1 - */ -public class FindAllBox extends JDialog { - private static final long serialVersionUID = -4125409760166690462L; - private static final int MIN_WIDTH = 640; - private boolean searching; - - private JButton findButton; - private JTextField textField; - private JCheckBox mcase; - private JCheckBox regex; - private JCheckBox wholew; - private JCheckBox classname; - private JList list; - private JProgressBar progressBar; - boolean locked; - - private final JLabel statusLabel = new JLabel(""); - - private final DefaultListModel classesList = new DefaultListModel<>(); - - private Thread tmp_thread; - - private final MainWindow mainWindow; - - public FindAllBox(final MainWindow mainWindow) { - this.setDefaultCloseOperation(HIDE_ON_CLOSE); - this.setHideOnEscapeButton(); - - progressBar = new JProgressBar(0, 100); - this.mainWindow = mainWindow; - - JLabel label = new JLabel("Find What:"); - textField = new JTextField(); - findButton = new JButton("Find"); - findButton.addActionListener(new FindButton()); - - mcase = new JCheckBox("Match Case"); - regex = new JCheckBox("Regex"); - wholew = new JCheckBox("Whole Words"); - classname = new JCheckBox("Classnames"); - - this.getRootPane().setDefaultButton(findButton); - - list = new JList<>(classesList); - list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); - list.setLayoutOrientation(JList.VERTICAL_WRAP); - list.setVisibleRowCount(-1); - list.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent evt) { - @SuppressWarnings("unchecked") - JList list = (JList) evt.getSource(); - if (evt.getClickCount() == 2) { - int index = list.locationToIndex(evt.getPoint()); - String entryName = list.getModel().getElementAt(index); - String[] array = entryName.split("/"); - if (entryName.toLowerCase().endsWith(".class")) { - String internalName = StringUtilities.removeRight(entryName, ".class"); - TypeReference type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); - try { - mainWindow.getSelectedModel().extractClassToTextPane(type, array[array.length - 1], entryName, - null); - } catch (Exception ignored) { - for (Model m : mainWindow.getModels()) { - try { - m.extractClassToTextPane(type, array[array.length - 1], entryName, null); - } catch (Exception ignored1) { - } - } - } - - } else { - try { - JarFile jfile = new JarFile(mainWindow.getSelectedModel().getOpenedFile()); - mainWindow.getSelectedModel().extractSimpleFileEntryToTextPane( - jfile.getInputStream(jfile.getEntry(entryName)), array[array.length - 1], - entryName); - jfile.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - } - } - }); - list.setLayoutOrientation(JList.VERTICAL); - JScrollPane listScroller = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - int width = (int) (screenSize.width * 0.35); - if (width < MIN_WIDTH) { - width = MIN_WIDTH; - } - final Dimension center = new Dimension(width, 500); - final int x = (int) (center.width * 0.2); - final int y = (int) (center.height * 0.2); - this.setBounds(x, y, center.width, center.height); - this.setResizable(false); - - GroupLayout layout = new GroupLayout(getRootPane()); - getRootPane().setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setHorizontalGroup( - layout.createSequentialGroup().addComponent(label) - .addGroup( - layout.createParallelGroup(Alignment.LEADING).addComponent(statusLabel) - .addComponent(textField) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(mcase)) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(wholew)) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(regex)) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(classname))) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(listScroller) - .addComponent(progressBar)))) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(findButton)) - - ); - - layout.linkSize(SwingConstants.HORIZONTAL, findButton); - layout.setVerticalGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(textField) - .addComponent(findButton)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(mcase).addComponent(wholew) - .addComponent(regex).addComponent(classname)) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(listScroller)))) - .addGroup(layout.createParallelGroup(Alignment.LEADING)).addComponent(statusLabel) - .addComponent(progressBar)); - this.adjustWindowPositionBySavedState(); - this.setSaveWindowPositionOnClosing(); - - this.setName("Find All"); - this.setTitle("Find All"); - } - - private class FindButton extends AbstractAction { - private static final long serialVersionUID = 75954129199541874L; - - @Override - public void actionPerformed(ActionEvent event) { - tmp_thread = new Thread(() -> { - if (findButton.getText().equals("Stop")) { - if (tmp_thread != null) - tmp_thread.interrupt(); - setStatus("Stopped."); - findButton.setText("Find"); - locked = false; - } else { - findButton.setText("Stop"); - classesList.clear(); - ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); - DecompilerSettings settings = configSaver.getDecompilerSettings(); - File inFile = mainWindow.getSelectedModel().getOpenedFile(); - boolean filter = ConfigSaver.getLoadedInstance().getLuytenPreferences() - .isFilterOutInnerClassEntries(); - try { - JarFile jfile = new JarFile(inFile); - Enumeration entLength = jfile.entries(); - initProgressBar(Collections.list(entLength).size()); - Enumeration ent = jfile.entries(); - while (ent.hasMoreElements() && findButton.getText().equals("Stop")) { - JarEntry entry = ent.nextElement(); - String name = entry.getName(); - setStatus(name); - if (filter && name.contains("$")) - continue; - if(locked || classname.isSelected()){ - locked = true; - if(search(entry.getName())) - addClassName(entry.getName()); - }else{ - if (entry.getName().endsWith(".class")) { - synchronized (settings) { - String internalName = StringUtilities.removeRight(entry.getName(), ".class"); - TypeReference type; - try { - type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); - TypeDefinition resolvedType; - if (type != null && ((resolvedType = type.resolve()) != null)) { - StringWriter stringwriter = new StringWriter(); - DecompilationOptions decompilationOptions; - decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput.setUnicodeOutputEnabled( - decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, - decompilationOptions); - if (search(stringwriter.toString())) - addClassName(entry.getName()); - } - } catch (IllegalStateException ise) { - if (ise.getMessage().contains("Invalid BootstrapMethods attribute entry: 2 additional arguments required for method java/lang/invoke/StringConcatFactory.makeConcatWithConstants, but only 1 specified.")) { - // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to https://bitbucket.org/mstrobel/procyon/issues/336/ - // Searching in a WAR or JAR file could pop-up a lot of error dialogs for a lot of class files, we simply say nothing here - addClassName(entry.getName() + " (search failed due to known Exception in Procyon <= 0.5.35. Opening file will fail too)"); - } else { - // all other IllegalStateException cases - addClassName(entry.getName() + " (search failed due to Exception. Opening file will fail too)"); - Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), ise); - } - } catch (Exception e) { - addClassName(entry.getName() + " (search failed due to Exception. Opening file will fail too)"); - Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), e); - } - } - } else { - - StringBuilder sb = new StringBuilder(); - long nonprintableCharactersCount = 0; - try (InputStreamReader inputStreamReader = new InputStreamReader( - jfile.getInputStream(entry)); - BufferedReader reader = new BufferedReader(inputStreamReader)) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); - - for (byte nextByte : line.getBytes()) { - if (nextByte <= 0) { - nonprintableCharactersCount++; - } - } - - } - } - if (nonprintableCharactersCount < 5 && search(sb.toString())) - addClassName(entry.getName()); - } - } - } - setSearching(false); - if (findButton.getText().equals("Stop")) { - setStatus("Done."); - findButton.setText("Find"); - locked = false; - } - jfile.close(); - locked = false; - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - - } - }); - tmp_thread.start(); - - } - - } - - private boolean search(String bulk) { - String a = textField.getText(); - String b = bulk; - if (regex.isSelected()) - return Pattern.matches(a, b); - if (wholew.isSelected()) - a = " " + a + " "; - if (!mcase.isSelected()) { - a = a.toLowerCase(); - b = b.toLowerCase(); - } - return b.contains(a); - } - - private void setHideOnEscapeButton() { - Action escapeAction = new AbstractAction() { - private static final long serialVersionUID = 6846566740472934801L; - - @Override - public void actionPerformed(ActionEvent e) { - FindAllBox.this.setVisible(false); - } - }; - - KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); - this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE"); - this.getRootPane().getActionMap().put("ESCAPE", escapeAction); - } - - private void adjustWindowPositionBySavedState() { - WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); - - if (windowPosition.isSavedWindowPositionValid()) { - this.setLocation(windowPosition.getWindowX(), windowPosition.getWindowY()); - } - } - - private void setSaveWindowPositionOnClosing() { - this.addWindowListener(new WindowAdapter() { - @Override - public void windowDeactivated(WindowEvent e) { - WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); - windowPosition.readPositionFromDialog(FindAllBox.this); - } - }); - } - - public void showFindBox() { - this.setVisible(true); - this.textField.requestFocus(); - } - - public void hideFindBox() { - this.setVisible(false); - } - - public void setStatus(String text) { - if (text.length() > 25) { - this.statusLabel.setText("Searching in file: ..." + text.substring(text.length() - 25)); - } else { - this.statusLabel.setText("Searching in file: " + text); - } - - progressBar.setValue(progressBar.getValue() + 1); - } - - public void addClassName(String className) { - this.classesList.addElement(className); - } - - public void initProgressBar(Integer length) { - progressBar.setMaximum(length); - progressBar.setValue(0); - progressBar.setStringPainted(true); - } - - public boolean isSearching() { - return searching; - } - - public void setSearching(boolean searching) { - this.searching = searching; - } -} diff --git a/src/us/deathmarine/luyten/FindBox.java b/src/us/deathmarine/luyten/FindBox.java deleted file mode 100644 index d06b92a1..00000000 --- a/src/us/deathmarine/luyten/FindBox.java +++ /dev/null @@ -1,232 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Dimension; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.BorderFactory; -import javax.swing.GroupLayout; -import javax.swing.GroupLayout.Alignment; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JTextField; -import javax.swing.KeyStroke; -import javax.swing.SwingConstants; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rtextarea.SearchContext; -import org.fife.ui.rtextarea.SearchEngine; - -public class FindBox extends JDialog { - private static final long serialVersionUID = -4125409760166690462L; - - JCheckBox mcase; - JCheckBox regex; - JCheckBox wholew; - JCheckBox reverse; - JCheckBox wrap; - private final JButton findButton; - JTextField textField; - private final MainWindow mainWindow; - - public void showFindBox() { - this.setVisible(true); - this.textField.requestFocus(); - this.textField.selectAll(); - } - - public void hideFindBox() { - this.setVisible(false); - } - - public FindBox(final MainWindow mainWindow) { - this.mainWindow = mainWindow; - this.setDefaultCloseOperation(HIDE_ON_CLOSE); - this.setHideOnEscapeButton(); - - JLabel label = new JLabel("Find What:"); - textField = new JTextField(); - - RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); - if (pane != null) { - textField.setText(pane.getSelectedText()); - } - mcase = new JCheckBox("Match Case"); - regex = new JCheckBox("Regex"); - wholew = new JCheckBox("Whole Words"); - reverse = new JCheckBox("Search Backwards"); - wrap = new JCheckBox("Wrap"); - - findButton = new JButton("Find"); - findButton.addActionListener(new FindButton()); - this.getRootPane().setDefaultButton(findButton); - - KeyStroke funcF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0, false); - this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(funcF3, "FindNext"); - this.getRootPane().getActionMap().put("FindNext", new FindExploreAction(true)); - - KeyStroke sfuncF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK, false); - this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sfuncF3, "FindPrevious"); - this.getRootPane().getActionMap().put("FindPrevious", new FindExploreAction(false)); - - mcase.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - regex.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - wholew.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - reverse.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - wrap.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - final Dimension center = new Dimension((int) (screenSize.width * 0.35), - Math.min((int) (screenSize.height * 0.20), 200)); - final int x = (int) (center.width * 0.2); - final int y = (int) (center.height * 0.2); - this.setBounds(x, y, center.width, center.height); - this.setResizable(false); - - GroupLayout layout = new GroupLayout(getRootPane()); - getRootPane().setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(label) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(textField) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(mcase) - .addComponent(wholew).addComponent(wrap)) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(regex) - .addComponent(reverse)))) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(findButton))); - - layout.linkSize(SwingConstants.HORIZONTAL, findButton); - layout.setVerticalGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(textField) - .addComponent(findButton)) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(mcase) - .addComponent(regex)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(wholew) - .addComponent(reverse)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(wrap))))); - - this.adjustWindowPositionBySavedState(); - this.setSaveWindowPositionOnClosing(); - - this.setName("Find"); - this.setTitle("Find"); - this.setVisible(true); - } - - private class FindButton extends AbstractAction { - private static final long serialVersionUID = 75954129199541874L; - - @Override - public void actionPerformed(ActionEvent event) { - if (textField.getText().length() == 0) - return; - - RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); - if (pane == null) - return; - - SearchContext context = new SearchContext(); - context.setSearchFor(textField.getText()); - context.setMatchCase(mcase.isSelected()); - context.setRegularExpression(regex.isSelected()); - context.setSearchForward(!reverse.isSelected()); - context.setWholeWord(wholew.isSelected()); - - if (!SearchEngine.find(pane, context).wasFound()) { - if (wrap.isSelected()) { - pane.setSelectionStart(0); - pane.setSelectionEnd(0); - } else { - mainWindow.getLabel().setText("Search Complete"); - } - } - } - - } - - private void setHideOnEscapeButton() { - Action escapeAction = new AbstractAction() { - private static final long serialVersionUID = 5572504000935312338L; - - @Override - public void actionPerformed(ActionEvent e) { - FindBox.this.setVisible(false); - } - }; - - KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); - this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE"); - this.getRootPane().getActionMap().put("ESCAPE", escapeAction); - } - - private void adjustWindowPositionBySavedState() { - WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); - - if (windowPosition.isSavedWindowPositionValid()) { - this.setLocation(windowPosition.getWindowX(), windowPosition.getWindowY()); - } - } - - private void setSaveWindowPositionOnClosing() { - this.addWindowListener(new WindowAdapter() { - @Override - public void windowDeactivated(WindowEvent e) { - WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); - windowPosition.readPositionFromDialog(FindBox.this); - } - }); - } - - public void fireExploreAction(boolean direction) { - new FindExploreAction(direction).actionPerformed(null); - } - - class FindExploreAction extends AbstractAction { - /** - * - */ - private static final long serialVersionUID = -4391670062679240573L; - boolean direction; - - public FindExploreAction(boolean forward) { - direction = forward; - } - - @Override - public void actionPerformed(ActionEvent e) { - if (textField.getText().length() == 0) - return; - RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); - if (pane == null) - return; - SearchContext context = new SearchContext(); - context.setSearchFor(textField.getText()); - context.setMatchCase(mcase.isSelected()); - context.setRegularExpression(regex.isSelected()); - context.setSearchForward(direction); - context.setWholeWord(wholew.isSelected()); - - if (!SearchEngine.find(pane, context).wasFound()) { - if (wrap.isSelected()) { - pane.setSelectionStart(0); - pane.setSelectionEnd(0); - } else { - mainWindow.getLabel().setText("Search Complete"); - } - } - - } - - } -} diff --git a/src/us/deathmarine/luyten/JFontChooser.java b/src/us/deathmarine/luyten/JFontChooser.java deleted file mode 100644 index 1db91060..00000000 --- a/src/us/deathmarine/luyten/JFontChooser.java +++ /dev/null @@ -1,769 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Frame; -import java.awt.GraphicsEnvironment; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.ActionMap; -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.DefaultListCellRenderer; -import javax.swing.InputMap; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.KeyStroke; -import javax.swing.ListSelectionModel; -import javax.swing.SwingUtilities; -import javax.swing.border.Border; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.JTextComponent; -import javax.swing.text.Position; - -/** - * The JFontChooser class is a swing component for font selection. - * This class has JFileChooser like APIs. The following code pops - * up a font chooser dialog. - * - **/ -public class JFontChooser extends JComponent { - /** - * - */ - private static final long serialVersionUID = 8856126034081661L; - // class variables - /** - * Return value from showDialog(). - * - * @see #showDialog - **/ - public static final int OK_OPTION = 0; - /** - * Return value from showDialog(). - * - * @see #showDialog - **/ - public static final int CANCEL_OPTION = 1; - /** - * Return value from showDialog(). - * - * @see #showDialog - **/ - public static final int ERROR_OPTION = -1; - private static final Font DEFAULT_SELECTED_FONT = new Font("Serif", Font.PLAIN, 12); - private static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 10); - private static final int[] FONT_STYLE_CODES = { Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC }; - private static final String[] DEFAULT_FONT_SIZE_STRINGS = { "8", "9", "10", "11", "12", "14", "16", "18", "20", - "22", "24", "26", "28", "36", "48", "72", }; - - // instance variables - protected int dialogResultValue = ERROR_OPTION; - - protected DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer(); - - private String[] fontStyleNames; - private String[] fontFamilyNames; - private String[] fontSizeStrings; - private JTextField fontFamilyTextField; - private JTextField fontStyleTextField; - private JTextField fontSizeTextField; - private JList fontNameList; - private JList fontStyleList; - private JList fontSizeList; - private JPanel fontNamePanel; - private JPanel fontStylePanel; - private JPanel fontSizePanel; - private JPanel samplePanel; - private JTextField sampleText; - - /** - * Constructs a JFontChooser object. - **/ - public JFontChooser() { - this(DEFAULT_FONT_SIZE_STRINGS); - } - - /** - * Constructs a JFontChooser object using the given font size - * array. - * - * @param fontSizeStrings - * the array of font size string. - **/ - public JFontChooser(String[] fontSizeStrings) { - if (fontSizeStrings == null) { - fontSizeStrings = DEFAULT_FONT_SIZE_STRINGS; - } - this.fontSizeStrings = fontSizeStrings; - - JPanel selectPanel = new JPanel(); - selectPanel.setLayout(new BoxLayout(selectPanel, BoxLayout.X_AXIS)); - selectPanel.add(getFontFamilyPanel()); - selectPanel.add(getFontStylePanel()); - selectPanel.add(getFontSizePanel()); - - JPanel contentsPanel = new JPanel(); - contentsPanel.setLayout(new GridLayout(2, 1)); - contentsPanel.add(selectPanel, BorderLayout.NORTH); - contentsPanel.add(getSamplePanel(), BorderLayout.CENTER); - - this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); - this.add(contentsPanel); - this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - this.setSelectedFont(DEFAULT_SELECTED_FONT); - } - - public JTextField getFontFamilyTextField() { - if (fontFamilyTextField == null) { - fontFamilyTextField = new JTextField(); - fontFamilyTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontFamilyTextField)); - fontFamilyTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontFamilyList())); - fontFamilyTextField.getDocument() - .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontFamilyList())); - fontFamilyTextField.setFont(DEFAULT_FONT); - - } - return fontFamilyTextField; - } - - public JTextField getFontStyleTextField() { - if (fontStyleTextField == null) { - fontStyleTextField = new JTextField(); - fontStyleTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontStyleTextField)); - fontStyleTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontStyleList())); - fontStyleTextField.getDocument() - .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontStyleList())); - fontStyleTextField.setFont(DEFAULT_FONT); - - } - return fontStyleTextField; - } - - public JTextField getFontSizeTextField() { - if (fontSizeTextField == null) { - fontSizeTextField = new JTextField(); - fontSizeTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontSizeTextField)); - fontSizeTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontSizeList())); - fontSizeTextField.getDocument() - .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontSizeList())); - fontSizeTextField.setFont(DEFAULT_FONT); - } - return fontSizeTextField; - } - - public JList getFontFamilyList() { - if (fontNameList == null) { - fontNameList = new JList(getFontFamilies()); - fontNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - fontNameList.addListSelectionListener(new ListSelectionHandler(getFontFamilyTextField())); - fontNameList.setSelectedIndex(0); - - // Draw Fonts - fontNameList.setCellRenderer(new DefaultListCellRenderer() { - /** - * - */ - private static final long serialVersionUID = -6753380853569310954L; - - public Component getListCellRendererComponent(JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - - JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, - isSelected, cellHasFocus); - - if (value instanceof String) { - renderer.setText((String) value); - renderer.setFont(new Font((String) value, DEFAULT_FONT.getStyle(), DEFAULT_FONT.getSize() + 2)); - } else { - renderer.setText(""); - } - return renderer; - } - }); - fontNameList.setFocusable(false); - } - return fontNameList; - } - - public JList getFontStyleList() { - if (fontStyleList == null) { - fontStyleList = new JList(getFontStyleNames()); - fontStyleList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - fontStyleList.addListSelectionListener(new ListSelectionHandler(getFontStyleTextField())); - fontStyleList.setSelectedIndex(0); - fontStyleList.setFocusable(false); - fontStyleList.setCellRenderer(new DefaultListCellRenderer() { - /** - * - */ - private static final long serialVersionUID = -3904668242514776943L; - - public Component getListCellRendererComponent(JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - - JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, - isSelected, cellHasFocus); - - if (value instanceof String) { - renderer.setText((String) value); - renderer.setFont( - new Font(DEFAULT_FONT.getName(), FONT_STYLE_CODES[index], DEFAULT_FONT.getSize() + 2)); - } else { - renderer.setText(""); - } - return renderer; - } - }); - } - return fontStyleList; - } - - public JList getFontSizeList() { - if (fontSizeList == null) { - fontSizeList = new JList(this.fontSizeStrings); - fontSizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - fontSizeList.addListSelectionListener(new ListSelectionHandler(getFontSizeTextField())); - fontSizeList.setSelectedIndex(0); - fontSizeList.setFont(DEFAULT_FONT); - fontSizeList.setFocusable(false); - } - return fontSizeList; - } - - /** - * Get the family name of the selected font. - * - * @return the font family of the selected font. - * - * @see #setSelectedFontFamily - **/ - public String getSelectedFontFamily() { - return (String) getFontFamilyList().getSelectedValue(); - } - - /** - * Get the style of the selected font. - * - * @return the style of the selected font. Font.PLAIN, - * Font.BOLD, Font.ITALIC, - * Font.BOLD|Font.ITALIC - * - * @see java.awt.Font#PLAIN - * @see java.awt.Font#BOLD - * @see java.awt.Font#ITALIC - * @see #setSelectedFontStyle - **/ - public int getSelectedFontStyle() { - int index = getFontStyleList().getSelectedIndex(); - return FONT_STYLE_CODES[index]; - } - - /** - * Get the size of the selected font. - * - * @return the size of the selected font - * - * @see #setSelectedFontSize - **/ - public int getSelectedFontSize() { - int fontSize; - String fontSizeString = getFontSizeTextField().getText(); - while (true) { - try { - fontSize = Integer.parseInt(fontSizeString); - break; - } catch (NumberFormatException e) { - fontSizeString = (String) getFontSizeList().getSelectedValue(); - getFontSizeTextField().setText(fontSizeString); - } - } - - return fontSize; - } - - /** - * Get the selected font. - * - * @return the selected font - * - * @see #setSelectedFont - * @see java.awt.Font - **/ - public Font getSelectedFont() { - return new Font(getSelectedFontFamily(), getSelectedFontStyle(), getSelectedFontSize()); - } - - /** - * Set the family name of the selected font. - * - * @param name - * the family name of the selected font. - * - * @see #getSelectedFontFamily - **/ - public void setSelectedFontFamily(String name) { - String[] names = getFontFamilies(); - for (int i = 0; i < names.length; i++) { - if (names[i].equalsIgnoreCase(name)) { - getFontFamilyList().setSelectedIndex(i); - break; - } - } - updateSampleFont(); - } - - /** - * Set the style of the selected font. - * - * @param style - * the size of the selected font. Font.PLAIN, - * Font.BOLD, Font.ITALIC, or - * Font.BOLD|Font.ITALIC. - * - * @see java.awt.Font#PLAIN - * @see java.awt.Font#BOLD - * @see java.awt.Font#ITALIC - * @see #getSelectedFontStyle - **/ - public void setSelectedFontStyle(int style) { - for (int i = 0; i < FONT_STYLE_CODES.length; i++) { - if (FONT_STYLE_CODES[i] == style) { - getFontStyleList().setSelectedIndex(i); - break; - } - } - updateSampleFont(); - } - - /** - * Set the size of the selected font. - * - * @param size - * the size of the selected font - * - * @see #getSelectedFontSize - **/ - public void setSelectedFontSize(int size) { - String sizeString = String.valueOf(size); - for (int i = 0; i < this.fontSizeStrings.length; i++) { - if (this.fontSizeStrings[i].equals(sizeString)) { - getFontSizeList().setSelectedIndex(i); - break; - } - } - getFontSizeTextField().setText(sizeString); - updateSampleFont(); - } - - /** - * Set the selected font. - * - * @param font - * the selected font - * - * @see #getSelectedFont - * @see java.awt.Font - **/ - public void setSelectedFont(Font font) { - setSelectedFontFamily(font.getFamily()); - setSelectedFontStyle(font.getStyle()); - setSelectedFontSize(font.getSize()); - } - - public String getVersionString() { - return ("Version"); - } - - /** - * Show font selection dialog. - * - * @param parent - * Dialog's Parent component. - * @return OK_OPTION, CANCEL_OPTION or ERROR_OPTION - * - * @see #OK_OPTION - * @see #CANCEL_OPTION - * @see #ERROR_OPTION - **/ - public int showDialog(Component parent) { - dialogResultValue = ERROR_OPTION; - JDialog dialog = createDialog(parent); - dialog.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - dialogResultValue = CANCEL_OPTION; - } - }); - - dialog.setVisible(true); - dialog.dispose(); - - return dialogResultValue; - } - - protected class ListSelectionHandler implements ListSelectionListener { - private final JTextComponent textComponent; - - ListSelectionHandler(JTextComponent textComponent) { - this.textComponent = textComponent; - } - - public void valueChanged(ListSelectionEvent e) { - if (!e.getValueIsAdjusting()) { - JList list = (JList) e.getSource(); - String selectedValue = (String) list.getSelectedValue(); - - String oldValue = textComponent.getText(); - textComponent.setText(selectedValue); - if (!oldValue.equalsIgnoreCase(selectedValue)) { - textComponent.selectAll(); - textComponent.requestFocus(); - } - - updateSampleFont(); - } - } - } - - protected class TextFieldFocusHandlerForTextSelection extends FocusAdapter { - private final JTextComponent textComponent; - - public TextFieldFocusHandlerForTextSelection(JTextComponent textComponent) { - this.textComponent = textComponent; - } - - public void focusGained(FocusEvent e) { - textComponent.selectAll(); - } - - public void focusLost(FocusEvent e) { - textComponent.select(0, 0); - updateSampleFont(); - } - } - - protected static class TextFieldKeyHandlerForListSelectionUpDown extends KeyAdapter { - private final JList targetList; - - public TextFieldKeyHandlerForListSelectionUpDown(JList list) { - this.targetList = list; - } - - public void keyPressed(KeyEvent e) { - int i; - switch (e.getKeyCode()) { - case KeyEvent.VK_UP: - i = targetList.getSelectedIndex() - 1; - if (i < 0) { - i = 0; - } - targetList.setSelectedIndex(i); - break; - case KeyEvent.VK_DOWN: - int listSize = targetList.getModel().getSize(); - i = targetList.getSelectedIndex() + 1; - if (i >= listSize) { - i = listSize - 1; - } - targetList.setSelectedIndex(i); - break; - default: - break; - } - } - } - - protected static class ListSearchTextFieldDocumentHandler implements DocumentListener { - JList targetList; - - public ListSearchTextFieldDocumentHandler(JList targetList) { - this.targetList = targetList; - } - - public void insertUpdate(DocumentEvent e) { - update(e); - } - - public void removeUpdate(DocumentEvent e) { - update(e); - } - - public void changedUpdate(DocumentEvent e) { - update(e); - } - - private void update(DocumentEvent event) { - String newValue = ""; - try { - Document doc = event.getDocument(); - newValue = doc.getText(0, doc.getLength()); - } catch (BadLocationException e) { - Luyten.showExceptionDialog("Exception!", e); - } - - if (newValue.length() > 0) { - int index = targetList.getNextMatch(newValue, 0, Position.Bias.Forward); - if (index < 0) { - index = 0; - } - targetList.ensureIndexIsVisible(index); - - String matchedName = targetList.getModel().getElementAt(index).toString(); - if (newValue.equalsIgnoreCase(matchedName)) { - if (index != targetList.getSelectedIndex()) { - SwingUtilities.invokeLater(new ListSelector(index)); - } - } - } - } - - public class ListSelector implements Runnable { - private final int index; - - public ListSelector(int index) { - this.index = index; - } - - public void run() { - targetList.setSelectedIndex(this.index); - } - } - } - - protected class DialogOKAction extends AbstractAction { - /** - * - */ - private static final long serialVersionUID = 1618273732543947323L; - protected static final String ACTION_NAME = "OK"; - private final JDialog dialog; - - protected DialogOKAction(JDialog dialog) { - this.dialog = dialog; - putValue(Action.DEFAULT, ACTION_NAME); - putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); - putValue(Action.NAME, (ACTION_NAME)); - } - - public void actionPerformed(ActionEvent e) { - dialogResultValue = OK_OPTION; - dialog.setVisible(false); - } - } - - protected class DialogCancelAction extends AbstractAction { - /** - * - */ - private static final long serialVersionUID = -4941763616565382601L; - protected static final String ACTION_NAME = "Cancel"; - private final JDialog dialog; - - protected DialogCancelAction(JDialog dialog) { - this.dialog = dialog; - putValue(Action.DEFAULT, ACTION_NAME); - putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); - putValue(Action.NAME, (ACTION_NAME)); - } - - public void actionPerformed(ActionEvent e) { - dialogResultValue = CANCEL_OPTION; - dialog.setVisible(false); - } - } - - protected JDialog createDialog(Component parent) { - Frame frame = parent instanceof Frame ? (Frame) parent - : (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); - JDialog dialog = new JDialog(frame, ("Select Font"), true); - - Action okAction = new DialogOKAction(dialog); - Action cancelAction = new DialogCancelAction(dialog); - - JButton okButton = new JButton(okAction); - okButton.setFont(DEFAULT_FONT); - JButton cancelButton = new JButton(cancelAction); - cancelButton.setFont(DEFAULT_FONT); - - JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new GridLayout(2, 1)); - buttonsPanel.add(okButton); - buttonsPanel.add(cancelButton); - buttonsPanel.setBorder(BorderFactory.createEmptyBorder(25, 0, 10, 10)); - - ActionMap actionMap = buttonsPanel.getActionMap(); - actionMap.put(cancelAction.getValue(Action.DEFAULT), cancelAction); - actionMap.put(okAction.getValue(Action.DEFAULT), okAction); - InputMap inputMap = buttonsPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), cancelAction.getValue(Action.DEFAULT)); - inputMap.put(KeyStroke.getKeyStroke("ENTER"), okAction.getValue(Action.DEFAULT)); - - JPanel dialogEastPanel = new JPanel(); - dialogEastPanel.setLayout(new BorderLayout()); - dialogEastPanel.add(buttonsPanel, BorderLayout.NORTH); - - dialog.getContentPane().add(this, BorderLayout.CENTER); - dialog.getContentPane().add(dialogEastPanel, BorderLayout.EAST); - dialog.pack(); - dialog.setLocationRelativeTo(frame); - return dialog; - } - - protected void updateSampleFont() { - Font font = getSelectedFont(); - getSampleTextField().setFont(font); - } - - protected JPanel getFontFamilyPanel() { - if (fontNamePanel == null) { - fontNamePanel = new JPanel(); - fontNamePanel.setLayout(new BorderLayout()); - fontNamePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - fontNamePanel.setPreferredSize(new Dimension(180, 130)); - - JScrollPane scrollPane = new JScrollPane(getFontFamilyList()); - scrollPane.getVerticalScrollBar().setFocusable(false); - scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - - JPanel p = new JPanel(); - p.setLayout(new BorderLayout()); - p.add(getFontFamilyTextField(), BorderLayout.NORTH); - p.add(scrollPane, BorderLayout.CENTER); - - JLabel label = new JLabel(("Font Name")); - label.setHorizontalAlignment(JLabel.LEFT); - label.setHorizontalTextPosition(JLabel.LEFT); - label.setLabelFor(getFontFamilyTextField()); - label.setDisplayedMnemonic('F'); - - fontNamePanel.add(label, BorderLayout.NORTH); - fontNamePanel.add(p, BorderLayout.CENTER); - - } - return fontNamePanel; - } - - protected JPanel getFontStylePanel() { - if (fontStylePanel == null) { - fontStylePanel = new JPanel(); - fontStylePanel.setLayout(new BorderLayout()); - fontStylePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - fontStylePanel.setPreferredSize(new Dimension(140, 130)); - - JScrollPane scrollPane = new JScrollPane(getFontStyleList()); - scrollPane.getVerticalScrollBar().setFocusable(false); - scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - - JPanel p = new JPanel(); - p.setLayout(new BorderLayout()); - p.add(getFontStyleTextField(), BorderLayout.NORTH); - p.add(scrollPane, BorderLayout.CENTER); - - JLabel label = new JLabel(("Font Style")); - label.setHorizontalAlignment(JLabel.LEFT); - label.setHorizontalTextPosition(JLabel.LEFT); - label.setLabelFor(getFontStyleTextField()); - label.setDisplayedMnemonic('Y'); - - fontStylePanel.add(label, BorderLayout.NORTH); - fontStylePanel.add(p, BorderLayout.CENTER); - } - return fontStylePanel; - } - - protected JPanel getFontSizePanel() { - if (fontSizePanel == null) { - fontSizePanel = new JPanel(); - fontSizePanel.setLayout(new BorderLayout()); - fontSizePanel.setPreferredSize(new Dimension(70, 130)); - fontSizePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - - JScrollPane scrollPane = new JScrollPane(getFontSizeList()); - scrollPane.getVerticalScrollBar().setFocusable(false); - scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - - JPanel p = new JPanel(); - p.setLayout(new BorderLayout()); - p.add(getFontSizeTextField(), BorderLayout.NORTH); - p.add(scrollPane, BorderLayout.CENTER); - - JLabel label = new JLabel(("Font Size")); - label.setHorizontalAlignment(JLabel.LEFT); - label.setHorizontalTextPosition(JLabel.LEFT); - label.setLabelFor(getFontSizeTextField()); - label.setDisplayedMnemonic('S'); - - fontSizePanel.add(label, BorderLayout.NORTH); - fontSizePanel.add(p, BorderLayout.CENTER); - } - return fontSizePanel; - } - - protected JPanel getSamplePanel() { - if (samplePanel == null) { - Border titledBorder = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), ("Sample")); - Border empty = BorderFactory.createEmptyBorder(5, 10, 10, 10); - Border border = BorderFactory.createCompoundBorder(titledBorder, empty); - - samplePanel = new JPanel(); - samplePanel.setLayout(new BorderLayout()); - samplePanel.setBorder(border); - - samplePanel.add(getSampleTextField(), BorderLayout.CENTER); - } - return samplePanel; - } - - protected JTextField getSampleTextField() { - if (sampleText == null) { - Border lowered = BorderFactory.createLoweredBevelBorder(); - - sampleText = new JTextField(("AaBbYyZz")); - sampleText.setHorizontalAlignment(JTextField.CENTER); - sampleText.setBorder(lowered); - sampleText.setPreferredSize(new Dimension(300, 100)); - } - return sampleText; - } - - protected String[] getFontFamilies() { - if (fontFamilyNames == null) { - GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - fontFamilyNames = env.getAvailableFontFamilyNames(); - } - return fontFamilyNames; - } - - protected String[] getFontStyleNames() { - if (fontStyleNames == null) { - int i = 0; - fontStyleNames = new String[4]; - fontStyleNames[i++] = ("Plain"); - fontStyleNames[i++] = ("Bold"); - fontStyleNames[i++] = ("Italic"); - fontStyleNames[i++] = ("BoldItalic"); - } - return fontStyleNames; - } -} diff --git a/src/us/deathmarine/luyten/JarEntryFilter.java b/src/us/deathmarine/luyten/JarEntryFilter.java deleted file mode 100644 index 90135df9..00000000 --- a/src/us/deathmarine/luyten/JarEntryFilter.java +++ /dev/null @@ -1,82 +0,0 @@ -package us.deathmarine.luyten; - -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -public class JarEntryFilter { - - private JarFile jfile; - - public JarEntryFilter() { - } - - public JarEntryFilter(JarFile jfile) { - this.jfile = jfile; - } - - public List getAllEntriesFromJar() { - List mass = new ArrayList<>(); - Enumeration entries = jfile.entries(); - while (entries.hasMoreElements()) { - JarEntry e = entries.nextElement(); - if (!e.isDirectory()) { - mass.add(e.getName()); - } - } - return mass; - } - - public List getEntriesWithoutInnerClasses() { - List mass = new ArrayList<>(); - Enumeration entries = jfile.entries(); - Set possibleInnerClasses = new HashSet<>(); - Set baseClasses = new HashSet<>(); - - while (entries.hasMoreElements()) { - JarEntry e = entries.nextElement(); - if (!e.isDirectory()) { - String entryName = e.getName(); - - if (entryName.trim().length() > 0) { - entryName = entryName.trim(); - - if (!entryName.endsWith(".class")) { - mass.add(entryName); - - // com/acme/Model$16.class - } else if (entryName.matches(".*[^(/|\\\\)]+\\$[^(/|\\\\)]+$")) { - possibleInnerClasses.add(entryName); - - } else { - baseClasses.add(entryName); - mass.add(entryName); - } - } - } - } - - // keep Badly$Named but not inner classes - for (String inner : possibleInnerClasses) { - - // com/acme/Connection$Conn$1.class -> com/acme/Connection - String innerWithoutTail = inner.replaceAll("\\$[^(/|\\\\)]+\\.class$", ""); - if (!baseClasses.contains(innerWithoutTail + ".class")) { - mass.add(inner); - } - } - return mass; - } - - public JarFile getJfile() { - return jfile; - } - - public void setJfile(JarFile jfile) { - this.jfile = jfile; - } -} diff --git a/src/us/deathmarine/luyten/LinkProvider.java b/src/us/deathmarine/luyten/LinkProvider.java deleted file mode 100644 index e156a3a0..00000000 --- a/src/us/deathmarine/luyten/LinkProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -package us.deathmarine.luyten; - -import java.util.Map; -import java.util.Set; - -public interface LinkProvider { - - void generateContent(); - - String getTextContent(); - - void processLinks(); - - Map getDefinitionToSelectionMap(); - - Map> getReferenceToSelectionsMap(); - - boolean isLinkNavigable(String uniqueStr); - - String getLinkDescription(String uniqueStr); - -} diff --git a/src/us/deathmarine/luyten/Luyten.java b/src/us/deathmarine/luyten/Luyten.java deleted file mode 100644 index d8d42d91..00000000 --- a/src/us/deathmarine/luyten/Luyten.java +++ /dev/null @@ -1,262 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Cursor; -import java.awt.Desktop; -import java.awt.Font; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.*; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.net.URI; -import java.util.concurrent.atomic.AtomicReference; -import java.util.List; -import java.util.ArrayList; - -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.border.BevelBorder; -import javax.swing.border.CompoundBorder; -import javax.swing.text.DefaultEditorKit; - -/** - * Starter, the main class - */ -public class Luyten { - - private static final AtomicReference mainWindowRef = new AtomicReference<>(); - private static final List pendingFiles = new ArrayList<>(); - private static ServerSocket lockSocket = null; - - public static void main(final String[] args) { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - if (lockSocket != null) { - lockSocket.close(); - } - } catch (IOException ignored) { - } - })); - - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { - e.printStackTrace(); - } - - // for TotalCommander External Viewer setting: - // javaw -jar "c:\Program Files\Luyten\luyten.jar" - // (TC will not complain about temporary file when opening .class from - // .zip or .jar) - final File fileFromCommandLine = getFileFromCommandLine(args); - - try { - launchMainInstance(fileFromCommandLine); - } catch (Exception e) { - // Instance already exists. Open new file in running instance - try { - Socket socket = new Socket("localhost", 3456); - DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); - dos.writeUTF(args[0]); - dos.flush(); - dos.close(); - socket.close(); - } catch (IOException ex) { - showExceptionDialog("Exception", e); - } - } - } - - private static void launchMainInstance(final File fileFromCommandLine) throws IOException { - lockSocket = new ServerSocket(3456); - launchSession(fileFromCommandLine); - new Thread(Luyten::launchServer).start(); - } - - private static void launchSession(final File fileFromCommandLine) { - SwingUtilities.invokeLater(() -> { - if (!mainWindowRef.compareAndSet(null, new MainWindow(fileFromCommandLine))) { - // Already set - so add the files to open - addToPendingFiles(fileFromCommandLine); - } - processPendingFiles(); - mainWindowRef.get().setVisible(true); - }); - } - - private static void launchServer() { - try { // Server - while (true) { - Socket socket = lockSocket.accept(); - DataInputStream dis = new DataInputStream(socket.getInputStream()); - addToPendingFiles(getFileFromCommandLine(dis.readUTF())); - processPendingFiles(); - dis.close(); - socket.close(); - } - } catch (SocketException ignored) { - // Ignore exception on shutdown - } catch (IOException e) { // Client - showExceptionDialog("Exception", e); - } - } - - // Private function which processes all pending files - synchronized on the - // list of pending files - public static void processPendingFiles() { - final MainWindow mainWindow = mainWindowRef.get(); - if (mainWindow != null) { - synchronized (pendingFiles) { - for (File f : pendingFiles) { - mainWindow.loadNewFile(f); - } - pendingFiles.clear(); - } - } - } - - // Function which opens the given file in the instance, if it's running - - // and if not, it processes the files - public static void addToPendingFiles(File fileToOpen) { - synchronized (pendingFiles) { - if (fileToOpen != null) { - pendingFiles.add(fileToOpen); - } - } - } - - // Function which exits the application if it's running - public static void quitInstance() { - final MainWindow mainWindow = mainWindowRef.get(); - if (mainWindow != null) { - mainWindow.onExitMenu(); - } - } - - public static File getFileFromCommandLine(String... args) { - File fileFromCommandLine = null; - try { - if (args.length > 0) { - String realFileName = new File(args[0]).getCanonicalPath(); - fileFromCommandLine = new File(realFileName); - } - } catch (Exception e) { - e.printStackTrace(); - } - return fileFromCommandLine; - } - - public static String getVersion() { - String result = ""; - try { - String line; - BufferedReader br = new BufferedReader(new InputStreamReader( - ClassLoader.getSystemResourceAsStream("META-INF/maven/us.deathmarine/luyten/pom.properties"))); - while ((line = br.readLine()) != null) { - if (line.contains("version")) - result = line.split("=")[1]; - } - br.close(); - } catch (Exception e) { - return result; - } - return result; - - } - - /** - * Method allows for users to copy the stacktrace for reporting any issues. - * Add Cool Hyperlink Enhanced for mouse users. - * - * @param message - * @param e - */ - public static void showExceptionDialog(String message, Exception e) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - String stacktrace = sw.toString(); - try { - sw.close(); - pw.close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - System.out.println(stacktrace); - - JPanel pane = new JPanel(); - pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); - if (message.contains("\n")) { - for (String s : message.split("\n")) { - pane.add(new JLabel(s)); - } - } else { - pane.add(new JLabel(message)); - } - pane.add(new JLabel(" \n")); // Whitespace - final JTextArea exception = new JTextArea(25, 100); - exception.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 10)); - exception.setText(stacktrace); - exception.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { - new JPopupMenu() { - { - JMenuItem menuitem = new JMenuItem("Select All"); - menuitem.addActionListener(e12 -> { - exception.requestFocus(); - exception.selectAll(); - }); - this.add(menuitem); - menuitem = new JMenuItem("Copy"); - menuitem.addActionListener(new DefaultEditorKit.CopyAction()); - this.add(menuitem); - } - - private static final long serialVersionUID = 562054483562666832L; - }.show(e.getComponent(), e.getX(), e.getY()); - } - } - }); - JScrollPane scroll = new JScrollPane(exception); - scroll.setBorder(new CompoundBorder(BorderFactory.createTitledBorder("Stacktrace"), - new BevelBorder(BevelBorder.LOWERED))); - pane.add(scroll); - final String issue = "https://github.com/deathmarine/Luyten/issues"; - final JLabel link = new JLabel("Submit to " + issue + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - try { - Desktop.getDesktop().browse(new URI(issue)); - } catch (Exception e1) { - e1.printStackTrace(); - } - } - - @Override - public void mouseEntered(MouseEvent e) { - link.setText("Submit to " + issue + ""); - } - - @Override - public void mouseExited(MouseEvent e) { - link.setText("Submit to " + issue + ""); - } - }); - pane.add(link); - JOptionPane.showMessageDialog(null, pane, "Error!", JOptionPane.ERROR_MESSAGE); - } -} diff --git a/src/us/deathmarine/luyten/LuytenOsx.java b/src/us/deathmarine/luyten/LuytenOsx.java deleted file mode 100644 index 3ba653d2..00000000 --- a/src/us/deathmarine/luyten/LuytenOsx.java +++ /dev/null @@ -1,31 +0,0 @@ -package us.deathmarine.luyten; - -import java.io.File; -import com.apple.eawt.Application; -import com.apple.eawt.ApplicationAdapter; -import com.apple.eawt.ApplicationEvent; - -/** - * An OS X-specific initialization method for dragging/dropping - */ -public class LuytenOsx extends Luyten { - public static void main(String[] args) { - // Add an adapter as the handler to a new instance of the application - // class - @SuppressWarnings("deprecation") - Application app = new Application(); - app.addApplicationListener(new ApplicationAdapter() { - public void handleOpenFile(ApplicationEvent e) { - Luyten.addToPendingFiles(new File(e.getFilename())); - Luyten.processPendingFiles(); - } - - public void handleQuit(ApplicationEvent e) { - Luyten.quitInstance(); - } - }); - - // Call the superclass's main function - Luyten.main(args); - } -} diff --git a/src/us/deathmarine/luyten/LuytenPreferences.java b/src/us/deathmarine/luyten/LuytenPreferences.java deleted file mode 100644 index 341e4408..00000000 --- a/src/us/deathmarine/luyten/LuytenPreferences.java +++ /dev/null @@ -1,87 +0,0 @@ -package us.deathmarine.luyten; - -/** - * Do not instantiate this class, get the instance from ConfigSaver. All - * not-static fields will be saved automatically named by the field's java - * variable name. (Watch for collisions with existing IDs defined in - * ConfigSaver.) Only String, boolean and int fields are supported. Write - * default values into the field declarations. - */ -public class LuytenPreferences { - public static final String THEME_XML_PATH = "/org/fife/ui/rsyntaxtextarea/themes/"; - public static final String DEFAULT_THEME_XML = "eclipse.xml"; - - private String themeXml = DEFAULT_THEME_XML; - private String fileOpenCurrentDirectory = ""; - private String fileSaveCurrentDirectory = ""; - private int font_size = 10; - - private boolean isPackageExplorerStyle = true; - private boolean isFilterOutInnerClassEntries = true; - private boolean isSingleClickOpenEnabled = true; - private boolean isExitByEscEnabled = false; - - public String getThemeXml() { - return themeXml; - } - - public void setThemeXml(String themeXml) { - this.themeXml = themeXml; - } - - public String getFileOpenCurrentDirectory() { - return fileOpenCurrentDirectory; - } - - public void setFileOpenCurrentDirectory(String fileOpenCurrentDirectory) { - this.fileOpenCurrentDirectory = fileOpenCurrentDirectory; - } - - public String getFileSaveCurrentDirectory() { - return fileSaveCurrentDirectory; - } - - public void setFileSaveCurrentDirectory(String fileSaveCurrentDirectory) { - this.fileSaveCurrentDirectory = fileSaveCurrentDirectory; - } - - public boolean isPackageExplorerStyle() { - return isPackageExplorerStyle; - } - - public void setPackageExplorerStyle(boolean isPackageExplorerStyle) { - this.isPackageExplorerStyle = isPackageExplorerStyle; - } - - public boolean isFilterOutInnerClassEntries() { - return isFilterOutInnerClassEntries; - } - - public void setFilterOutInnerClassEntries(boolean isFilterOutInnerClassEntries) { - this.isFilterOutInnerClassEntries = isFilterOutInnerClassEntries; - } - - public boolean isSingleClickOpenEnabled() { - return isSingleClickOpenEnabled; - } - - public void setSingleClickOpenEnabled(boolean isSingleClickOpenEnabled) { - this.isSingleClickOpenEnabled = isSingleClickOpenEnabled; - } - - public boolean isExitByEscEnabled() { - return isExitByEscEnabled; - } - - public void setExitByEscEnabled(boolean isExitByEscEnabled) { - this.isExitByEscEnabled = isExitByEscEnabled; - } - - public int getFont_size() { - return font_size; - } - - public void setFont_size(int font_size) { - this.font_size = font_size; - } -} diff --git a/src/us/deathmarine/luyten/LuytenTypeLoader.java b/src/us/deathmarine/luyten/LuytenTypeLoader.java deleted file mode 100644 index 35e0569d..00000000 --- a/src/us/deathmarine/luyten/LuytenTypeLoader.java +++ /dev/null @@ -1,34 +0,0 @@ -package us.deathmarine.luyten; - -import com.strobel.assembler.InputTypeLoader; -import com.strobel.assembler.metadata.Buffer; -import com.strobel.assembler.metadata.ITypeLoader; - -import java.util.ArrayList; -import java.util.List; - -public final class LuytenTypeLoader implements ITypeLoader { - private final List _typeLoaders; - - public LuytenTypeLoader() { - _typeLoaders = new ArrayList<>(); - _typeLoaders.add(new InputTypeLoader()); - } - - public List getTypeLoaders() { - return _typeLoaders; - } - - @Override - public boolean tryLoadType(final String internalName, final Buffer buffer) { - for (final ITypeLoader typeLoader : _typeLoaders) { - if (typeLoader.tryLoadType(internalName, buffer)) { - return true; - } - - buffer.reset(); - } - - return false; - } -} diff --git a/src/us/deathmarine/luyten/MainMenuBar.java b/src/us/deathmarine/luyten/MainMenuBar.java deleted file mode 100644 index 86aa7fef..00000000 --- a/src/us/deathmarine/luyten/MainMenuBar.java +++ /dev/null @@ -1,587 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Cursor; -import java.awt.Desktop; -import java.awt.Font; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.ListIterator; -import java.util.Map; - -import javax.swing.AbstractAction; -import javax.swing.AbstractButton; -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.ButtonModel; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRadioButtonMenuItem; -import javax.swing.JTabbedPane; -import javax.swing.KeyStroke; -import javax.swing.text.DefaultEditorKit; - -import com.strobel.Procyon; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.languages.Language; -import com.strobel.decompiler.languages.Languages; - -/** - * Main menu (only MainWindow should be called from here) - */ -public class MainMenuBar extends JMenuBar { - private static final long serialVersionUID = -7949855817172562075L; - private final MainWindow mainWindow; - private final Map languageLookup = new HashMap<>(); - - private JMenu recentFiles; - private JMenuItem clearRecentFiles; - private JCheckBoxMenuItem flattenSwitchBlocks; - private JCheckBoxMenuItem forceExplicitImports; - private JCheckBoxMenuItem forceExplicitTypes; - private JCheckBoxMenuItem showSyntheticMembers; - private JCheckBoxMenuItem excludeNestedTypes; - private JCheckBoxMenuItem retainRedundantCasts; - private JCheckBoxMenuItem unicodeReplacement; - private JCheckBoxMenuItem debugLineNumbers; - private JCheckBoxMenuItem showDebugInfo; - private JCheckBoxMenuItem bytecodeLineNumbers; - private JRadioButtonMenuItem java; - private JRadioButtonMenuItem bytecode; - private JRadioButtonMenuItem bytecodeAST; - private ButtonGroup languagesGroup; - private ButtonGroup themesGroup; - private JCheckBoxMenuItem packageExplorerStyle; - private JCheckBoxMenuItem filterOutInnerClassEntries; - private JCheckBoxMenuItem singleClickOpenEnabled; - private JCheckBoxMenuItem exitByEscEnabled; - private final DecompilerSettings settings; - private final LuytenPreferences luytenPrefs; - - public MainMenuBar(MainWindow mainWnd) { - this.mainWindow = mainWnd; - final ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); - settings = configSaver.getDecompilerSettings(); - luytenPrefs = configSaver.getLuytenPreferences(); - - final JMenu fileMenu = new JMenu("File"); - fileMenu.add(new JMenuItem("...")); - this.add(fileMenu); - final JMenu editMenu = new JMenu("Edit"); - editMenu.add(new JMenuItem("...")); - this.add(editMenu); - final JMenu themesMenu = new JMenu("Themes"); - themesMenu.add(new JMenuItem("...")); - this.add(themesMenu); - final JMenu operationMenu = new JMenu("Operation"); - operationMenu.add(new JMenuItem("...")); - this.add(operationMenu); - final JMenu settingsMenu = new JMenu("Settings"); - settingsMenu.add(new JMenuItem("...")); - this.add(settingsMenu); - final JMenu helpMenu = new JMenu("Help"); - helpMenu.add(new JMenuItem("...")); - this.add(helpMenu); - - // start quicker - new Thread() { - public void run() { - try { - // build menu later - buildFileMenu(fileMenu); - refreshMenuPopup(fileMenu); - - buildEditMenu(editMenu); - refreshMenuPopup(editMenu); - - buildThemesMenu(themesMenu); - refreshMenuPopup(themesMenu); - - buildOperationMenu(operationMenu); - refreshMenuPopup(operationMenu); - - buildSettingsMenu(settingsMenu); - refreshMenuPopup(settingsMenu); - - buildHelpMenu(helpMenu); - refreshMenuPopup(helpMenu); - - updateRecentFiles(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - // refresh currently opened menu - // (if user selected a menu before it was ready) - private void refreshMenuPopup(JMenu menu) { - try { - if (menu.isPopupMenuVisible()) { - menu.getPopupMenu().setVisible(false); - menu.getPopupMenu().setVisible(true); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }.start(); - } - - public void updateRecentFiles() { - if (RecentFiles.paths.isEmpty()) { - recentFiles.setEnabled(false); - clearRecentFiles.setEnabled(false); - return; - } else { - recentFiles.setEnabled(true); - clearRecentFiles.setEnabled(true); - } - - recentFiles.removeAll(); - ListIterator li = RecentFiles.paths.listIterator(RecentFiles.paths.size()); - boolean rfSaveNeeded = false; - - while (li.hasPrevious()) { - String path = li.previous(); - final File file = new File(path); - - if (!file.exists()) { - rfSaveNeeded = true; - continue; - } - - JMenuItem menuItem = new JMenuItem(path); - menuItem.addActionListener(e -> mainWindow.loadNewFile(file)); - recentFiles.add(menuItem); - } - - if (rfSaveNeeded) RecentFiles.save(); - } - - private void buildFileMenu(final JMenu fileMenu) { - fileMenu.removeAll(); - JMenuItem menuItem = new JMenuItem("Open File..."); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(e -> mainWindow.onOpenFileMenu()); - fileMenu.add(menuItem); - fileMenu.addSeparator(); - - menuItem = new JMenuItem("Close File"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(e -> { - JTabbedPane house = mainWindow.getSelectedModel().house; - - if (e.getModifiers() != InputEvent.CTRL_MASK || house.getTabCount() == 0) - mainWindow.onCloseFileMenu(); - else { - mainWindow.getSelectedModel().closeOpenTab(house.getSelectedIndex()); - } - }); - fileMenu.add(menuItem); - fileMenu.addSeparator(); - - menuItem = new JMenuItem("Save As..."); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(e -> mainWindow.onSaveAsMenu()); - fileMenu.add(menuItem); - - menuItem = new JMenuItem("Save All..."); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(e -> mainWindow.onSaveAllMenu()); - fileMenu.add(menuItem); - fileMenu.addSeparator(); - - recentFiles = new JMenu("Recent Files"); - fileMenu.add(recentFiles); - - clearRecentFiles = new JMenuItem("Clear Recent Files"); - clearRecentFiles.addActionListener(e -> { - RecentFiles.paths.clear(); - RecentFiles.save(); - updateRecentFiles(); - }); - fileMenu.add(clearRecentFiles); - - fileMenu.addSeparator(); - - // Only add the exit command for non-OS X. OS X handles its close - // automatically - if (!Boolean.getBoolean("apple.laf.useScreenMenuBar")) { - menuItem = new JMenuItem("Exit"); - menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK)); - menuItem.addActionListener(e -> mainWindow.onExitMenu()); - fileMenu.add(menuItem); - } - } - - private void buildEditMenu(JMenu editMenu) { - editMenu.removeAll(); - JMenuItem menuItem = new JMenuItem("Cut"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.setEnabled(false); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Copy"); - menuItem.addActionListener(new DefaultEditorKit.CopyAction()); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Paste"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.setEnabled(false); - editMenu.add(menuItem); - - editMenu.addSeparator(); - - menuItem = new JMenuItem("Select All"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(e -> mainWindow.onSelectAllMenu()); - editMenu.add(menuItem); - editMenu.addSeparator(); - - menuItem = new JMenuItem("Find..."); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(e -> mainWindow.onFindMenu()); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Find Next"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)); - menuItem.addActionListener(e -> { - if(mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(true); - }); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Find Previous"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK)); - menuItem.addActionListener(e -> { - if(mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(false); - }); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Find All"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(e -> mainWindow.onFindAllMenu()); - editMenu.add(menuItem); - } - - private void buildThemesMenu(JMenu themesMenu) { - themesMenu.removeAll(); - themesGroup = new ButtonGroup(); - JRadioButtonMenuItem a = new JRadioButtonMenuItem(new ThemeAction("Default", "default.xml")); - a.setSelected("default.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("Default-Alt", "default-alt.xml")); - a.setSelected("default-alt.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("Dark", "dark.xml")); - a.setSelected("dark.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("Eclipse", "eclipse.xml")); - a.setSelected("eclipse.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("Visual Studio", "vs.xml")); - a.setSelected("vs.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("IntelliJ", "idea.xml")); - a.setSelected("idea.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - } - - private void buildOperationMenu(JMenu operationMenu) { - operationMenu.removeAll(); - packageExplorerStyle = new JCheckBoxMenuItem("Package Explorer Style"); - packageExplorerStyle.setSelected(luytenPrefs.isPackageExplorerStyle()); - packageExplorerStyle.addActionListener(e -> { - luytenPrefs.setPackageExplorerStyle(packageExplorerStyle.isSelected()); - mainWindow.onTreeSettingsChanged(); - }); - operationMenu.add(packageExplorerStyle); - - filterOutInnerClassEntries = new JCheckBoxMenuItem("Filter Out Inner Class Entries"); - filterOutInnerClassEntries.setSelected(luytenPrefs.isFilterOutInnerClassEntries()); - filterOutInnerClassEntries.addActionListener(e -> { - luytenPrefs.setFilterOutInnerClassEntries(filterOutInnerClassEntries.isSelected()); - mainWindow.onTreeSettingsChanged(); - }); - operationMenu.add(filterOutInnerClassEntries); - - singleClickOpenEnabled = new JCheckBoxMenuItem("Single Click Open"); - singleClickOpenEnabled.setSelected(luytenPrefs.isSingleClickOpenEnabled()); - singleClickOpenEnabled.addActionListener(e -> luytenPrefs.setSingleClickOpenEnabled(singleClickOpenEnabled.isSelected())); - operationMenu.add(singleClickOpenEnabled); - - exitByEscEnabled = new JCheckBoxMenuItem("Exit By Esc"); - exitByEscEnabled.setSelected(luytenPrefs.isExitByEscEnabled()); - exitByEscEnabled.addActionListener(e -> luytenPrefs.setExitByEscEnabled(exitByEscEnabled.isSelected())); - operationMenu.add(exitByEscEnabled); - } - - private void buildSettingsMenu(JMenu settingsMenu) { - settingsMenu.removeAll(); - ActionListener settingsChanged = e -> new Thread(() -> { - populateSettingsFromSettingsMenu(); - mainWindow.onSettingsChanged(); - }).start(); - flattenSwitchBlocks = new JCheckBoxMenuItem("Flatten Switch Blocks"); - flattenSwitchBlocks.setSelected(settings.getFlattenSwitchBlocks()); - flattenSwitchBlocks.addActionListener(settingsChanged); - settingsMenu.add(flattenSwitchBlocks); - - forceExplicitImports = new JCheckBoxMenuItem("Force Explicit Imports"); - forceExplicitImports.setSelected(settings.getForceExplicitImports()); - forceExplicitImports.addActionListener(settingsChanged); - settingsMenu.add(forceExplicitImports); - - forceExplicitTypes = new JCheckBoxMenuItem("Force Explicit Types"); - forceExplicitTypes.setSelected(settings.getForceExplicitTypeArguments()); - forceExplicitTypes.addActionListener(settingsChanged); - settingsMenu.add(forceExplicitTypes); - - showSyntheticMembers = new JCheckBoxMenuItem("Show Synthetic Members"); - showSyntheticMembers.setSelected(settings.getShowSyntheticMembers()); - showSyntheticMembers.addActionListener(settingsChanged); - settingsMenu.add(showSyntheticMembers); - - excludeNestedTypes = new JCheckBoxMenuItem("Exclude Nested Types"); - excludeNestedTypes.setSelected(settings.getExcludeNestedTypes()); - excludeNestedTypes.addActionListener(settingsChanged); - settingsMenu.add(excludeNestedTypes); - - retainRedundantCasts = new JCheckBoxMenuItem("Retain Redundant Casts"); - retainRedundantCasts.setSelected(settings.getRetainRedundantCasts()); - retainRedundantCasts.addActionListener(settingsChanged); - settingsMenu.add(retainRedundantCasts); - - unicodeReplacement = new JCheckBoxMenuItem("Enable Unicode Replacement"); - unicodeReplacement.setSelected(settings.isUnicodeOutputEnabled()); - unicodeReplacement.addActionListener(settingsChanged); - settingsMenu.add(unicodeReplacement); - - debugLineNumbers = new JCheckBoxMenuItem("Show Debug Line Numbers"); - debugLineNumbers.setSelected(settings.getShowDebugLineNumbers()); - debugLineNumbers.addActionListener(settingsChanged); - settingsMenu.add(debugLineNumbers); - - JMenu debugSettingsMenu = new JMenu("Debug Settings"); - showDebugInfo = new JCheckBoxMenuItem("Include Error Diagnostics"); - showDebugInfo.setSelected(settings.getIncludeErrorDiagnostics()); - showDebugInfo.addActionListener(settingsChanged); - - debugSettingsMenu.add(showDebugInfo); - settingsMenu.add(debugSettingsMenu); - settingsMenu.addSeparator(); - - languageLookup.put(Languages.java().getName(), Languages.java()); - languageLookup.put(Languages.bytecode().getName(), Languages.bytecode()); - languageLookup.put(Languages.bytecodeAst().getName(), Languages.bytecodeAst()); - - languagesGroup = new ButtonGroup(); - java = new JRadioButtonMenuItem(Languages.java().getName()); - java.getModel().setActionCommand(Languages.java().getName()); - java.setSelected(Languages.java().getName().equals(settings.getLanguage().getName())); - languagesGroup.add(java); - settingsMenu.add(java); - bytecode = new JRadioButtonMenuItem(Languages.bytecode().getName()); - bytecode.getModel().setActionCommand(Languages.bytecode().getName()); - bytecode.setSelected(Languages.bytecode().getName().equals(settings.getLanguage().getName())); - languagesGroup.add(bytecode); - settingsMenu.add(bytecode); - bytecodeAST = new JRadioButtonMenuItem(Languages.bytecodeAst().getName()); - bytecodeAST.getModel().setActionCommand(Languages.bytecodeAst().getName()); - bytecodeAST.setSelected(Languages.bytecodeAst().getName().equals(settings.getLanguage().getName())); - languagesGroup.add(bytecodeAST); - settingsMenu.add(bytecodeAST); - - JMenu debugLanguagesMenu = new JMenu("Debug Languages"); - for (final Language language : Languages.debug()) { - final JRadioButtonMenuItem m = new JRadioButtonMenuItem(language.getName()); - m.getModel().setActionCommand(language.getName()); - m.setSelected(language.getName().equals(settings.getLanguage().getName())); - languagesGroup.add(m); - debugLanguagesMenu.add(m); - languageLookup.put(language.getName(), language); - } - for (AbstractButton button : Collections.list(languagesGroup.getElements())) { - button.addActionListener(settingsChanged); - } - settingsMenu.add(debugLanguagesMenu); - - bytecodeLineNumbers = new JCheckBoxMenuItem("Show Line Numbers In Bytecode"); - bytecodeLineNumbers.setSelected(settings.getIncludeLineNumbersInBytecode()); - bytecodeLineNumbers.addActionListener(settingsChanged); - settingsMenu.add(bytecodeLineNumbers); - } - - private void buildHelpMenu(JMenu helpMenu) { - helpMenu.removeAll(); - JMenuItem menuItem = new JMenuItem("Legal"); - menuItem.addActionListener(e -> mainWindow.onLegalMenu()); - helpMenu.add(menuItem); - JMenu menuDebug = new JMenu("Debug"); - menuItem = new JMenuItem("List JVM Classes"); - menuItem.addActionListener(e -> mainWindow.onListLoadedClasses()); - menuDebug.add(menuItem); - helpMenu.add(menuDebug); - menuItem = new JMenuItem("About"); - menuItem.addActionListener(event -> { - JPanel pane = new JPanel(); - pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); - JLabel title = new JLabel("Luyten " + Luyten.getVersion()); - title.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18)); - pane.add(title); - pane.add(new JLabel("by Deathmarine")); - String project = "https://github.com/deathmarine/Luyten/"; - JLabel link = new JLabel("" + project + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(project, link)); - pane.add(link); - pane.add(new JLabel("Contributions By:")); - pane.add(new JLabel("zerdei, toonetown, dstmath")); - pane.add(new JLabel("virustotalop, xtrafrancyz,")); - pane.add(new JLabel("mbax, quitten, mstrobel,")); - pane.add(new JLabel("FisheyLP, Syquel, and ThexXTURBOXx")); - pane.add(new JLabel(" ")); - pane.add(new JLabel("Powered By:")); - String procyon = "https://github.com/mstrobel/procyon"; - link = new JLabel("" + procyon + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(procyon, link)); - pane.add(link); - pane.add(new JLabel("Version: " + Procyon.version())); - pane.add(new JLabel("(c) 2021 Mike Strobel")); - String rsyntax = "https://github.com/bobbylight/RSyntaxTextArea"; - link = new JLabel("" + rsyntax + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(rsyntax, link)); - pane.add(link); - pane.add(new JLabel("Version: 3.1.5")); - pane.add(new JLabel("(c) 2021 Robert Futrell")); - pane.add(new JLabel(" ")); - JOptionPane.showMessageDialog(null, pane); - }); - helpMenu.add(menuItem); - } - - private void populateSettingsFromSettingsMenu() { - // synchronized: do not disturb decompiler at work (synchronize every - // time before run decompiler) - synchronized (settings) { - settings.setFlattenSwitchBlocks(flattenSwitchBlocks.isSelected()); - settings.setForceExplicitImports(forceExplicitImports.isSelected()); - settings.setShowSyntheticMembers(showSyntheticMembers.isSelected()); - settings.setExcludeNestedTypes(excludeNestedTypes.isSelected()); - settings.setForceExplicitTypeArguments(forceExplicitTypes.isSelected()); - settings.setRetainRedundantCasts(retainRedundantCasts.isSelected()); - settings.setIncludeErrorDiagnostics(showDebugInfo.isSelected()); - settings.setUnicodeOutputEnabled(unicodeReplacement.isSelected()); - settings.setShowDebugLineNumbers(debugLineNumbers.isSelected()); - // - // Note: You shouldn't ever need to set this. It's only for - // languages that support catch - // blocks without an exception variable. Java doesn't allow this. I - // think Scala does. - // - // settings.setAlwaysGenerateExceptionVariableForCatchBlocks(true); - // - - final ButtonModel selectedLanguage = languagesGroup.getSelection(); - if (selectedLanguage != null) { - final Language language = languageLookup.get(selectedLanguage.getActionCommand()); - - if (language != null) - settings.setLanguage(language); - } - - if (java.isSelected()) { - settings.setLanguage(Languages.java()); - } else if (bytecode.isSelected()) { - settings.setLanguage(Languages.bytecode()); - } else if (bytecodeAST.isSelected()) { - settings.setLanguage(Languages.bytecodeAst()); - } - settings.setIncludeLineNumbersInBytecode(bytecodeLineNumbers.isSelected()); - } - } - - private class ThemeAction extends AbstractAction { - private static final long serialVersionUID = -6618680171943723199L; - private final String xml; - - public ThemeAction(String name, String xml) { - putValue(NAME, name); - this.xml = xml; - } - - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setThemeXml(xml); - mainWindow.onThemesChanged(); - } - } - - private static class LinkListener extends MouseAdapter { - String link; - JLabel label; - - public LinkListener(String link, JLabel label) { - this.link = link; - this.label = label; - } - - @Override - public void mouseClicked(MouseEvent e) { - try { - Desktop.getDesktop().browse(new URI(link)); - } catch (Exception e1) { - e1.printStackTrace(); - } - } - - @Override - public void mouseEntered(MouseEvent e) { - label.setText("" + link + ""); - } - - @Override - public void mouseExited(MouseEvent e) { - label.setText("" + link + ""); - } - - } -} diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java deleted file mode 100644 index 5b25aa44..00000000 --- a/src/us/deathmarine/luyten/MainWindow.java +++ /dev/null @@ -1,505 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Toolkit; -import java.awt.dnd.DropTarget; -import java.awt.event.ActionEvent; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.*; - -import javax.swing.*; -import javax.swing.border.BevelBorder; -import javax.swing.plaf.basic.BasicTabbedPaneUI; - -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; - -/** - * Dispatcher - */ -public class MainWindow extends JFrame { - private static final long serialVersionUID = 5265556630724988013L; - - private static final String TITLE = "Luyten"; - private static final String DEFAULT_TAB = "#DEFAULT"; - - private final JProgressBar bar; - private final JLabel label; - FindBox findBox; - private FindAllBox findAllBox; - private final ConfigSaver configSaver; - private final WindowPosition windowPosition; - private final LuytenPreferences luytenPrefs; - private final FileDialog fileDialog; - private final FileSaver fileSaver; - private final JTabbedPane jarsTabbedPane; - private final Map jarModels; - public MainMenuBar mainMenuBar; - - public MainWindow(File fileFromCommandLine) { - configSaver = ConfigSaver.getLoadedInstance(); - windowPosition = configSaver.getMainWindowPosition(); - luytenPrefs = configSaver.getLuytenPreferences(); - - jarModels = new HashMap<>(); - mainMenuBar = new MainMenuBar(this); - this.setJMenuBar(mainMenuBar); - - this.adjustWindowPositionBySavedState(); - this.setHideFindBoxOnMainWindowFocus(); - this.setShowFindAllBoxOnMainWindowFocus(); - this.setQuitOnWindowClosing(); - this.setTitle(TITLE); - this.setIconImage(new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/Luyten.png"))).getImage()); - - JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.LEFT)); - label = new JLabel(); - label.setHorizontalAlignment(JLabel.LEFT); - panel1.setBorder(new BevelBorder(BevelBorder.LOWERED)); - panel1.setPreferredSize(new Dimension(this.getWidth() / 2, 20)); - panel1.add(label); - - JPanel panel2 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - bar = new JProgressBar(); - - bar.setStringPainted(true); - bar.setOpaque(false); - bar.setVisible(false); - panel2.setPreferredSize(new Dimension(this.getWidth() / 3, 20)); - panel2.add(bar); - - jarsTabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); - jarsTabbedPane.setUI(new BasicTabbedPaneUI() { - @Override - protected int calculateTabAreaHeight(int tab_placement, int run_count, int max_tab_height) { - if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) == -1) - return super.calculateTabAreaHeight(tab_placement, run_count, max_tab_height); - else - return 0; - } - }); - jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); - this.getContentPane().add(jarsTabbedPane); - - JSplitPane spt = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel1, panel2) { - private static final long serialVersionUID = 2189946972124687305L; - private final int location = 400; - - { - setDividerLocation(location); - } - - @Override - public int getDividerLocation() { - return location; - } - - @Override - public int getLastDividerLocation() { - return location; - } - }; - spt.setBorder(new BevelBorder(BevelBorder.LOWERED)); - spt.setPreferredSize(new Dimension(this.getWidth(), 24)); - this.add(spt, BorderLayout.SOUTH); - Model jarModel = null; - if (fileFromCommandLine != null) { - jarModel = loadNewFile(fileFromCommandLine); - } - - try { - DropTarget dt = new DropTarget(); - dt.addDropTargetListener(new DropListener(this)); - this.setDropTarget(dt); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - - fileDialog = new FileDialog(this); - fileSaver = new FileSaver(bar, label); - - if (jarModel != null) { - this.setExitOnEscWhenEnabled(jarModel); - } - - if (jarModel != null && (fileFromCommandLine.getName().toLowerCase().endsWith(".jar") - || fileFromCommandLine.getName().toLowerCase().endsWith(".zip"))) { - jarModel.startWarmUpThread(); - } - - if(RecentFiles.load() > 0) mainMenuBar.updateRecentFiles(); - } - - private void createDefaultTab() { - jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); - } - - private void removeDefaultTab() { - jarsTabbedPane.remove(jarsTabbedPane.indexOfTab(DEFAULT_TAB)); - } - - public void onOpenFileMenu() { - File selectedFile = fileDialog.doOpenDialog(); - if (selectedFile != null) { - System.out.println("[Open]: Opening " + selectedFile.getAbsolutePath()); - this.loadNewFile(selectedFile); - } - } - - public Model loadNewFile(final File file) { - // In case we open the same file again - // we remove the old entry to force a refresh - if (jarModels.containsKey(file.getAbsolutePath())) { - jarModels.remove(file.getAbsolutePath()); - int index = jarsTabbedPane.indexOfTab(file.getName()); - jarsTabbedPane.remove(index); - } - - final Model jarModel = new Model(this); - jarModel.loadFile(file); - jarModels.put(file.getAbsolutePath(), jarModel); - jarsTabbedPane.addTab(file.getName(), jarModel); - jarsTabbedPane.setSelectedComponent(jarModel); - - final String tabName = file.getName(); - int index = jarsTabbedPane.indexOfTab(tabName); - Model.Tab tabUI = new Model.Tab(tabName, () -> { - int index1 = jarsTabbedPane.indexOfTab(tabName); - jarModels.remove(file.getAbsolutePath()); - jarsTabbedPane.remove(index1); - jarModel.closeFile(); - if (jarsTabbedPane.getTabCount() == 0) { - createDefaultTab(); - } - }); - jarsTabbedPane.setTabComponentAt(index, tabUI); - if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) != -1 && jarsTabbedPane.getTabCount() > 1) { - removeDefaultTab(); - } - return jarModel; - } - - public void onCloseFileMenu() { - this.getSelectedModel().closeFile(); - jarModels.remove(getSelectedModel()); - } - - public void onSaveAsMenu() { - RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); - if (pane == null) - return; - String tabTitle = this.getSelectedModel().getCurrentTabTitle(); - if (tabTitle == null) - return; - - String recommendedFileName = tabTitle.replace(".class", ".java"); - File selectedFile = fileDialog.doSaveDialog(recommendedFileName); - if (selectedFile != null) { - fileSaver.saveText(pane.getText(), selectedFile); - } - } - - public void onSaveAllMenu() { - File openedFile = this.getSelectedModel().getOpenedFile(); - if (openedFile == null) - return; - - String fileName = openedFile.getName(); - if (fileName.endsWith(".class")) { - fileName = fileName.replace(".class", ".java"); - } else if (fileName.toLowerCase().endsWith(".jar")) { - fileName = "decompiled-" + fileName.replaceAll("\\.[jJ][aA][rR]", ".zip"); - } else { - fileName = "saved-" + fileName; - } - - File selectedFileToSave = fileDialog.doSaveAllDialog(fileName); - if (selectedFileToSave != null) { - fileSaver.saveAllDecompiled(openedFile, selectedFileToSave); - } - } - - public void onExitMenu() { - quit(); - } - - public void onSelectAllMenu() { - try { - RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); - if (pane != null) { - pane.requestFocusInWindow(); - pane.setSelectionStart(0); - pane.setSelectionEnd(pane.getText().length()); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - public void onFindMenu() { - try { - RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); - if (pane != null) { - if (findBox == null) - findBox = new FindBox(this); - findBox.showFindBox(); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - public void onFindAllMenu() { - try { - if (findAllBox == null) - findAllBox = new FindAllBox(this); - findAllBox.showFindBox(); - - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - public void onLegalMenu() { - new Thread(() -> { - try { - bar.setVisible(true); - bar.setIndeterminate(true); - String legalStr = getLegalStr(); - getSelectedModel().showLegal(legalStr); - } finally { - bar.setIndeterminate(false); - bar.setVisible(false); - } - }).start(); - } - - public void onListLoadedClasses() { - try { - StringBuilder sb = new StringBuilder(); - ClassLoader myCL = Thread.currentThread().getContextClassLoader(); - bar.setVisible(true); - bar.setIndeterminate(true); - while (myCL != null) { - sb.append("ClassLoader: ").append(myCL).append("\n"); - for (Iterator iter = list(myCL); iter.hasNext();) { - sb.append("\t").append(iter.next()).append("\n"); - } - myCL = myCL.getParent(); - } - this.getSelectedModel().show("Debug", sb.toString()); - } finally { - bar.setIndeterminate(false); - bar.setVisible(false); - } - } - - private static Iterator list(ClassLoader CL) { - Class CL_class = CL.getClass(); - while (CL_class != java.lang.ClassLoader.class) { - CL_class = CL_class.getSuperclass(); - } - java.lang.reflect.Field ClassLoader_classes_field; - try { - ClassLoader_classes_field = CL_class.getDeclaredField("classes"); - ClassLoader_classes_field.setAccessible(true); - Vector classes = (Vector) ClassLoader_classes_field.get(CL); - return classes.iterator(); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - Luyten.showExceptionDialog("Exception!", e); - } - return null; - } - - private String getLegalStr() { - StringBuilder sb = new StringBuilder(); - try { - BufferedReader reader = new BufferedReader( - new InputStreamReader(getClass().getResourceAsStream("/distfiles/Procyon.License.txt"))); - String line; - while ((line = reader.readLine()) != null) - sb.append(line).append("\n"); - sb.append("\n\n\n\n\n"); - reader = new BufferedReader( - new InputStreamReader(getClass().getResourceAsStream("/distfiles/RSyntaxTextArea.License.txt"))); - while ((line = reader.readLine()) != null) - sb.append(line).append("\n"); - } catch (IOException e) { - Luyten.showExceptionDialog("Exception!", e); - } - return sb.toString(); - } - - public void onThemesChanged() { - for (Model jarModel : jarModels.values()) { - jarModel.changeTheme(luytenPrefs.getThemeXml()); - luytenPrefs.setFont_size(jarModel.getTheme().baseFont.getSize()); - } - } - - public void onSettingsChanged() { - for (Model jarModel : jarModels.values()) { - jarModel.updateOpenClasses(); - } - } - - public void onTreeSettingsChanged() { - for (Model jarModel : jarModels.values()) { - jarModel.updateTree(); - } - } - - public void onFilesDropped(List files) { - if (files != null) { - for (File file : files) { - this.loadNewFile(file); - } - } - } - - public void onFileDropped(File file) { - if (file != null) { - this.loadNewFile(file); - } - } - - public void onFileLoadEnded() { - try { - Model model = getSelectedModel(); - if (model != null && model.getFileName() != null) { - this.setTitle(TITLE + " - " + model.getFileName()); - } else { - this.setTitle(TITLE); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - public void onNavigationRequest(String uniqueStr) { - this.getSelectedModel().navigateTo(uniqueStr); - } - - private void adjustWindowPositionBySavedState() { - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - if (!windowPosition.isSavedWindowPositionValid()) { - final Dimension center = new Dimension((int) (screenSize.width * 0.75), (int) (screenSize.height * 0.75)); - final int x = (int) (center.width * 0.2); - final int y = (int) (center.height * 0.2); - this.setBounds(x, y, center.width, center.height); - - } else if (windowPosition.isFullScreen()) { - int heightMinusTray = screenSize.height; - if (screenSize.height > 30) - heightMinusTray -= 30; - this.setBounds(0, 0, screenSize.width, heightMinusTray); - this.setExtendedState(JFrame.MAXIMIZED_BOTH); - - this.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - if (MainWindow.this.getExtendedState() != JFrame.MAXIMIZED_BOTH) { - windowPosition.setFullScreen(false); - if (windowPosition.isSavedWindowPositionValid()) { - MainWindow.this.setBounds(windowPosition.getWindowX(), windowPosition.getWindowY(), - windowPosition.getWindowWidth(), windowPosition.getWindowHeight()); - } - MainWindow.this.removeComponentListener(this); - } - } - }); - - } else { - this.setBounds(windowPosition.getWindowX(), windowPosition.getWindowY(), windowPosition.getWindowWidth(), - windowPosition.getWindowHeight()); - } - } - - private void setHideFindBoxOnMainWindowFocus() { - this.addWindowFocusListener(new WindowAdapter() { - @Override - public void windowGainedFocus(WindowEvent e) { - if (findBox != null && findBox.isVisible()) { - findBox.setVisible(false); - } - } - }); - } - - private void setShowFindAllBoxOnMainWindowFocus() { - this.addWindowFocusListener(new WindowAdapter() { - @Override - public void windowGainedFocus(WindowEvent e) { - if (findAllBox != null && findAllBox.isVisible()) { - findAllBox.setVisible(false); - } - } - }); - } - - private void setQuitOnWindowClosing() { - this.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - quit(); - } - }); - } - - private void quit() { - try { - windowPosition.readPositionFromWindow(this); - configSaver.saveConfig(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } finally { - try { - this.dispose(); - } finally { - System.exit(0); - } - } - } - - private void setExitOnEscWhenEnabled(JComponent mainComponent) { - Action escapeAction = new AbstractAction() { - private static final long serialVersionUID = -3460391555954575248L; - - @Override - public void actionPerformed(ActionEvent e) { - if (luytenPrefs.isExitByEscEnabled()) { - quit(); - } - } - }; - KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); - mainComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(escapeKeyStroke, "ESCAPE"); - mainComponent.getActionMap().put("ESCAPE", escapeAction); - } - - public Model getSelectedModel() { - return (Model) jarsTabbedPane.getSelectedComponent(); - } - - public Collection getModels() { - return jarModels.values(); - } - - public JProgressBar getBar() { - return bar; - } - - public JLabel getLabel() { - return label; - } -} diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java deleted file mode 100644 index a83ed372..00000000 --- a/src/us/deathmarine/luyten/Model.java +++ /dev/null @@ -1,1031 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.Toolkit; -import java.awt.event.*; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import javax.swing.AbstractAction; -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTree; -import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.TreeExpansionEvent; -import javax.swing.event.TreeExpansionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rsyntaxtextarea.Theme; -import org.fife.ui.rtextarea.RTextScrollPane; -import com.strobel.assembler.InputTypeLoader; -import com.strobel.assembler.metadata.ITypeLoader; -import com.strobel.assembler.metadata.JarTypeLoader; -import com.strobel.assembler.metadata.MetadataSystem; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.core.StringUtilities; -import com.strobel.core.VerifyArgument; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; - -/** - * Jar-level model - */ -public class Model extends JSplitPane { - private static final long serialVersionUID = 6896857630400910200L; - - private static final long MAX_JAR_FILE_SIZE_BYTES = 10_000_000_000L; - private static final long MAX_UNPACKED_FILE_SIZE_BYTES = 10_000_000L; - - private final LuytenTypeLoader typeLoader = new LuytenTypeLoader(); - private final MetadataSystem metadataSystem = new MetadataSystem(typeLoader); - - private final JTree tree; - public JTabbedPane house; - private File file; - private final DecompilerSettings settings; - private final DecompilationOptions decompilationOptions; - private Theme theme; - private final MainWindow mainWindow; - private final JProgressBar bar; - private JLabel label; - private final HashSet hmap = new HashSet<>(); - private Set treeExpansionState; - private boolean open = false; - private State state; - private final ConfigSaver configSaver; - private final LuytenPreferences luytenPrefs; - - public Model(MainWindow mainWindow) { - this.mainWindow = mainWindow; - this.bar = mainWindow.getBar(); - this.setLabel(mainWindow.getLabel()); - - configSaver = ConfigSaver.getLoadedInstance(); - settings = configSaver.getDecompilerSettings(); - luytenPrefs = configSaver.getLuytenPreferences(); - - try { - String themeXml = luytenPrefs.getThemeXml(); - setTheme(Theme.load(getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + themeXml))); - } catch (Exception e1) { - try { - Luyten.showExceptionDialog("Exception!", e1); - String themeXml = LuytenPreferences.DEFAULT_THEME_XML; - luytenPrefs.setThemeXml(themeXml); - setTheme(Theme.load(getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + themeXml))); - } catch (Exception e2) { - Luyten.showExceptionDialog("Exception!", e2); - } - } - - tree = new JTree(); - tree.setModel(new DefaultTreeModel(null)); - tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - tree.setCellRenderer(new CellRenderer()); - TreeListener tl = new TreeListener(); - tree.addMouseListener(tl); - tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener()); - tree.addKeyListener(new KeyAdapter() { - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - openEntryByTreePath(tree.getSelectionPath()); - } - } - }); - - JPanel panel2 = new JPanel(); - panel2.setLayout(new BoxLayout(panel2, BoxLayout.Y_AXIS)); - panel2.setBorder(BorderFactory.createTitledBorder("Structure")); - panel2.add(new JScrollPane(tree)); - - house = new JTabbedPane(); - house.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - house.addChangeListener(new TabChangeListener()); - house.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isMiddleMouseButton(e)) { - closeOpenTab(house.getSelectedIndex()); - } - } - }); - - KeyStroke sfuncF4 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, Keymap.ctrlDownModifier(), false); - mainWindow.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sfuncF4, "CloseTab"); - - mainWindow.getRootPane().getActionMap().put("CloseTab", new AbstractAction() { - private static final long serialVersionUID = -885398399200419492L; - - @Override - public void actionPerformed(ActionEvent e) { - closeOpenTab(house.getSelectedIndex()); - } - - }); - - JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); - panel.setBorder(BorderFactory.createTitledBorder("Code")); - panel.add(house); - this.setOrientation(JSplitPane.HORIZONTAL_SPLIT); - this.setDividerLocation(250 % mainWindow.getWidth()); - this.setLeftComponent(panel2); - this.setRightComponent(panel); - - decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - } - - public void showLegal(String legalStr) { - show("Legal", legalStr); - } - - public void show(String name, String contents) { - OpenFile open = new OpenFile(name, "*/" + name, getTheme(), mainWindow); - open.setContent(contents); - hmap.add(open); - addOrSwitchToTab(open); - } - - private void addOrSwitchToTab(final OpenFile open) { - SwingUtilities.invokeLater(() -> { - try { - final String title = open.name; - RTextScrollPane rTextScrollPane = open.scrollPane; - int index = house.indexOfTab(title); - if (index > -1 && house.getTabComponentAt(index) != open.scrollPane) { - index = -1; - for (int i = 0; i < house.getTabCount(); i++) { - if (house.getComponentAt(i) == open.scrollPane) { - index = i; - break; - } - } - } - if (index < 0) { - house.addTab(title, rTextScrollPane); - index = house.indexOfComponent(rTextScrollPane); - house.setSelectedIndex(index); - Tab ct = new Tab(title, () -> { - int index1 = house.indexOfTab(title); - closeOpenTab(index1); - }); - house.setTabComponentAt(index, ct); - } else { - house.setSelectedIndex(index); - } - open.onAddedToScreen(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - }); - } - - public void closeOpenTab(int index) { - if (index < 0 || index >= house.getComponentCount()) - return; - - RTextScrollPane co = (RTextScrollPane) house.getComponentAt(index); - RSyntaxTextArea pane = (RSyntaxTextArea) co.getViewport().getView(); - OpenFile open = null; - for (OpenFile file : hmap) - if (pane.equals(file.textArea)) - open = file; - if (open != null) - hmap.remove(open); - house.remove(co); - if (open != null) - open.close(); - } - - private String getName(String path) { - if (path == null) - return ""; - int i = path.lastIndexOf("/"); - if (i == -1) - i = path.lastIndexOf("\\"); - if (i != -1) - return path.substring(i + 1); - return path; - } - - private class TreeListener extends MouseAdapter { - @Override - public void mousePressed(MouseEvent event) { - boolean isClickCountMatches = (event.getClickCount() == 1 && luytenPrefs.isSingleClickOpenEnabled()) - || (event.getClickCount() == 2 && !luytenPrefs.isSingleClickOpenEnabled()); - if (!isClickCountMatches) - return; - - if (!SwingUtilities.isLeftMouseButton(event)) - return; - - final TreePath trp = tree.getPathForLocation(event.getX(), event.getY()); - if (trp == null) - return; - - Object lastPathComponent = trp.getLastPathComponent(); - boolean isLeaf = (lastPathComponent instanceof TreeNode && ((TreeNode) lastPathComponent).isLeaf()); - if (!isLeaf) - return; - - new Thread(() -> openEntryByTreePath(trp)).start(); - } - } - - private class FurtherExpandingTreeExpansionListener implements TreeExpansionListener { - @Override - public void treeExpanded(final TreeExpansionEvent event) { - final TreePath treePath = event.getPath(); - - final Object expandedTreePathObject = treePath.getLastPathComponent(); - if (!(expandedTreePathObject instanceof TreeNode)) { - return; - } - - final TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject; - if (expandedTreeNode.getChildCount() == 1) { - final TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0); - - if (descendantTreeNode.isLeaf()) { - return; - } - - final TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode); - tree.expandPath(nextTreePath); - } - } - - @Override - public void treeCollapsed(final TreeExpansionEvent event) { - - } - } - - public void openEntryByTreePath(TreePath trp) { - String name = ""; - StringBuilder path = new StringBuilder(); - try { - bar.setVisible(true); - if (trp.getPathCount() > 1) { - for (int i = 1; i < trp.getPathCount(); i++) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); - TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); - if (i == trp.getPathCount() - 1) { - name = userObject.getOriginalName(); - } else { - path.append(userObject.getOriginalName()).append("/"); - } - } - path.append(name); - - if (file.getName().endsWith(".jar") || file.getName().endsWith(".zip")) { - if (state == null) { - JarFile jfile = new JarFile(file); - ITypeLoader jarLoader = new JarTypeLoader(jfile); - - typeLoader.getTypeLoaders().add(jarLoader); - state = new State(file.getCanonicalPath(), file, jfile, jarLoader); - } - - JarEntry entry = state.jarFile.getJarEntry(path.toString()); - if (entry == null) { - throw new FileEntryNotFoundException(); - } - if (entry.getSize() > MAX_UNPACKED_FILE_SIZE_BYTES) { - throw new TooLargeFileException(entry.getSize()); - } - String entryName = entry.getName(); - if (entryName.endsWith(".class")) { - getLabel().setText("Extracting: " + name); - String internalName = StringUtilities.removeRight(entryName, ".class"); - TypeReference type = metadataSystem.lookupType(internalName); - extractClassToTextPane(type, name, path.toString(), null); - } else { - getLabel().setText("Opening: " + name); - try (InputStream in = state.jarFile.getInputStream(entry)) { - extractSimpleFileEntryToTextPane(in, name, path.toString()); - } - } - } - } else { - name = file.getName(); - path = new StringBuilder(file.getPath().replaceAll("\\\\", "/")); - if (file.length() > MAX_UNPACKED_FILE_SIZE_BYTES) { - throw new TooLargeFileException(file.length()); - } - if (name.endsWith(".class")) { - getLabel().setText("Extracting: " + name); - TypeReference type = metadataSystem.lookupType(path.toString()); - extractClassToTextPane(type, name, path.toString(), null); - } else { - getLabel().setText("Opening: " + name); - try (InputStream in = new FileInputStream(file)) { - extractSimpleFileEntryToTextPane(in, name, path.toString()); - } - } - } - - getLabel().setText("Complete"); - } catch (FileEntryNotFoundException e) { - getLabel().setText("File not found: " + name); - } catch (FileIsBinaryException e) { - getLabel().setText("Binary resource: " + name); - } catch (TooLargeFileException e) { - getLabel().setText("File is too large: " + name + " - size: " + e.getReadableFileSize()); - } catch (Exception e) { - getLabel().setText("Cannot open: " + name); - Luyten.showExceptionDialog("Unable to open file!", e); - } finally { - bar.setVisible(false); - } - } - - void extractClassToTextPane(TypeReference type, String tabTitle, String path, String navigationLink) - throws Exception { - if (tabTitle == null || tabTitle.trim().length() < 1 || path == null) { - throw new FileEntryNotFoundException(); - } - OpenFile sameTitledOpen = null; - for (OpenFile nextOpen : hmap) { - if (tabTitle.equals(nextOpen.name) && path.equals(nextOpen.path) && type.equals(nextOpen.getType())) { - sameTitledOpen = nextOpen; - break; - } - } - if (sameTitledOpen != null && sameTitledOpen.isContentValid()) { - sameTitledOpen.setInitialNavigationLink(navigationLink); - addOrSwitchToTab(sameTitledOpen); - return; - } - - // resolve TypeDefinition - TypeDefinition resolvedType; - if (type == null || ((resolvedType = type.resolve()) == null)) { - throw new Exception("Unable to resolve type."); - } - - // open tab, store type information, start decompilation - if (sameTitledOpen != null) { - sameTitledOpen.path = path; - sameTitledOpen.invalidateContent(); - sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - sameTitledOpen.setType(resolvedType); - sameTitledOpen.setInitialNavigationLink(navigationLink); - sameTitledOpen.resetScrollPosition(); - sameTitledOpen.decompile(); - addOrSwitchToTab(sameTitledOpen); - } else { - OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); - open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - open.setType(resolvedType); - open.setInitialNavigationLink(navigationLink); - open.decompile(); - hmap.add(open); - addOrSwitchToTab(open); - } - } - - public void extractSimpleFileEntryToTextPane(InputStream inputStream, String tabTitle, String path) - throws Exception { - if (inputStream == null || tabTitle == null || tabTitle.trim().length() < 1 || path == null) { - throw new FileEntryNotFoundException(); - } - OpenFile sameTitledOpen = null; - for (OpenFile nextOpen : hmap) { - if (tabTitle.equals(nextOpen.name) && path.equals(nextOpen.path)) { - sameTitledOpen = nextOpen; - break; - } - } - if (sameTitledOpen != null) { - addOrSwitchToTab(sameTitledOpen); - return; - } - - // build tab content - StringBuilder sb = new StringBuilder(); - long nonprintableCharactersCount = 0; - try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - BufferedReader reader = new BufferedReader(inputStreamReader)) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); - - for (byte nextByte : line.getBytes()) { - if (nextByte <= 0) { - nonprintableCharactersCount++; - } - } - - } - } - - // guess binary or text - String extension = "." + tabTitle.replaceAll("^[^.]*$", "").replaceAll("[^.]*\\.", ""); - boolean isTextFile = (OpenFile.WELL_KNOWN_TEXT_FILE_EXTENSIONS.contains(extension) - || nonprintableCharactersCount < sb.length() / 5); - if (!isTextFile) { - throw new FileIsBinaryException(); - } - - // open tab - if (sameTitledOpen != null) { - sameTitledOpen.path = path; - sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - sameTitledOpen.resetScrollPosition(); - sameTitledOpen.setContent(sb.toString()); - addOrSwitchToTab(sameTitledOpen); - } else { - OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); - open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - open.setContent(sb.toString()); - hmap.add(open); - addOrSwitchToTab(open); - } - } - - private class TabChangeListener implements ChangeListener { - @Override - public void stateChanged(ChangeEvent e) { - int selectedIndex = house.getSelectedIndex(); - if (selectedIndex < 0) { - return; - } - for (OpenFile open : hmap) { - if (house.indexOfTab(open.name) == selectedIndex) { - - if (open.getType() != null && !open.isContentValid()) { - updateOpenClass(open); - break; - } - - } - } - } - } - - public void updateOpenClasses() { - // invalidate all open classes (update will hapen at tab change) - for (OpenFile open : hmap) { - if (open.getType() != null) { - open.invalidateContent(); - } - } - // update the current open tab - if it is a class - for (OpenFile open : hmap) { - if (open.getType() != null && isTabInForeground(open)) { - updateOpenClass(open); - break; - } - } - } - - private void updateOpenClass(final OpenFile open) { - if (open.getType() == null) { - return; - } - new Thread(() -> { - try { - bar.setVisible(true); - getLabel().setText("Extracting: " + open.name); - open.invalidateContent(); - open.decompile(); - getLabel().setText("Complete"); - } catch (Exception e) { - getLabel().setText("Error, cannot update: " + open.name); - } finally { - bar.setVisible(false); - } - }).start(); - } - - private boolean isTabInForeground(OpenFile open) { - String title = open.name; - int selectedIndex = house.getSelectedIndex(); - return (selectedIndex >= 0 && selectedIndex == house.indexOfTab(title)); - } - - final class State implements AutoCloseable { - private final String key; - private final File file; - final JarFile jarFile; - final ITypeLoader typeLoader; - - private State(String key, File file, JarFile jarFile, ITypeLoader typeLoader) { - this.key = VerifyArgument.notNull(key, "key"); - this.file = VerifyArgument.notNull(file, "file"); - this.jarFile = jarFile; - this.typeLoader = typeLoader; - } - - @Override - public void close() { - if (typeLoader != null) { - Model.this.typeLoader.getTypeLoaders().remove(typeLoader); - } - Closer.tryClose(jarFile); - } - - public File getFile() { - return file; - } - - public String getKey() { - return key; - } - } - - public static class Tab extends JPanel { - private final JLabel tabTitle; - private final JLabel closeButton = new JLabel(new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/icon_close.png")))); - - public Tab(String title, final Runnable onCloseTabAction) { - super(new GridBagLayout()); - this.setOpaque(false); - this.tabTitle = new JLabel(title); - this.createTab(); - - // TODO: Disables tab switching... Is there a workaround? - /*addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (onCloseTabAction != null && SwingUtilities.isMiddleMouseButton(e)) { - try { - onCloseTabAction.run(); - } catch (Exception ex) { - Luyten.showExceptionDialog("Exception!", ex); - } - } - } - });*/ - - closeButton.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (onCloseTabAction != null) { - try { - onCloseTabAction.run(); - } catch (Exception ex) { - Luyten.showExceptionDialog("Exception!", ex); - } - } - } - }); - } - - public void createTab() { - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.weightx = 1; - this.add(tabTitle, gbc); - gbc.gridx++; - gbc.insets = new Insets(0, 5, 0, 0); - gbc.anchor = GridBagConstraints.EAST; - this.add(closeButton, gbc); - } - } - - public DefaultMutableTreeNode loadNodesByNames(DefaultMutableTreeNode node, List originalNames) { - List args = new ArrayList<>(); - for (String originalName : originalNames) { - args.add(new TreeNodeUserObject(originalName)); - } - return loadNodesByUserObj(node, args); - } - - public DefaultMutableTreeNode loadNodesByUserObj(DefaultMutableTreeNode node, List args) { - if (args.size() > 0) { - TreeNodeUserObject name = args.remove(0); - DefaultMutableTreeNode nod = getChild(node, name); - if (nod == null) - nod = new DefaultMutableTreeNode(name); - node.add(loadNodesByUserObj(nod, args)); - } - return node; - } - - @SuppressWarnings("unchecked") - public DefaultMutableTreeNode getChild(DefaultMutableTreeNode node, TreeNodeUserObject name) { - Enumeration entry = node.children(); - while (entry.hasMoreElements()) { - DefaultMutableTreeNode nods = (DefaultMutableTreeNode) entry.nextElement(); - if (((TreeNodeUserObject) nods.getUserObject()).getOriginalName().equals(name.getOriginalName())) { - return nods; - } - } - return null; - } - - public void loadFile(File file) { - if (open) - closeFile(); - this.file = file; - - RecentFiles.add(file.getAbsolutePath()); - mainWindow.mainMenuBar.updateRecentFiles(); - loadTree(); - } - - public void updateTree() { - TreeUtil treeUtil = new TreeUtil(tree); - treeExpansionState = treeUtil.getExpansionState(); - loadTree(); - } - - public void loadTree() { - new Thread(() -> { - try { - if (file == null) { - return; - } - tree.setModel(new DefaultTreeModel(null)); - - if (file.length() > MAX_JAR_FILE_SIZE_BYTES) { - throw new TooLargeFileException(file.length()); - } - if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) { - JarFile jfile = new JarFile(file); - getLabel().setText("Loading: " + jfile.getName()); - bar.setVisible(true); - - JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); - List mass; - if (luytenPrefs.isFilterOutInnerClassEntries()) { - mass = jarEntryFilter.getEntriesWithoutInnerClasses(); - } else { - mass = jarEntryFilter.getAllEntriesFromJar(); - } - buildTreeFromMass(mass); - - if (state == null) { - ITypeLoader jarLoader = new JarTypeLoader(jfile); - typeLoader.getTypeLoaders().add(jarLoader); - state = new State(file.getCanonicalPath(), file, jfile, jarLoader); - } - open = true; - getLabel().setText("Complete"); - } else { - TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); - final DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); - tree.setModel(new DefaultTreeModel(top)); - settings.setTypeLoader(new InputTypeLoader()); - open = true; - getLabel().setText("Complete"); - - // open it automatically - new Thread(() -> { - TreePath trp = new TreePath(top.getPath()); - openEntryByTreePath(trp); - }).start(); - } - - if (treeExpansionState != null) { - try { - TreeUtil treeUtil = new TreeUtil(tree); - treeUtil.restoreExpanstionState(treeExpansionState); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - } catch (TooLargeFileException e) { - getLabel().setText("File is too large: " + file.getName() + " - size: " + e.getReadableFileSize()); - closeFile(); - } catch (Exception e1) { - Luyten.showExceptionDialog("Cannot open " + file.getName() + "!", e1); - getLabel().setText("Cannot open: " + file.getName()); - closeFile(); - } finally { - mainWindow.onFileLoadEnded(); - bar.setVisible(false); - } - }).start(); - } - - private void buildTreeFromMass(List mass) { - if (luytenPrefs.isPackageExplorerStyle()) { - buildFlatTreeFromMass(mass); - } else { - buildDirectoryTreeFromMass(mass); - } - } - - private void buildDirectoryTreeFromMass(List mass) { - TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); - DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); - List sort = new ArrayList<>(); - mass.sort(String.CASE_INSENSITIVE_ORDER); - for (String m : mass) - if (m.contains("META-INF") && !sort.contains(m)) - sort.add(m); - Set set = new HashSet<>(); - for (String m : mass) { - if (m.contains("/")) { - set.add(m.substring(0, m.lastIndexOf("/") + 1)); - } - } - List packs = Arrays.asList(set.toArray(new String[] {})); - packs.sort(String.CASE_INSENSITIVE_ORDER); - packs.sort((o1, o2) -> o2.split("/").length - o1.split("/").length); - for (String pack : packs) - for (String m : mass) - if (!m.contains("META-INF") && m.contains(pack) && !m.replace(pack, "").contains("/")) - sort.add(m); - for (String m : mass) - if (!m.contains("META-INF") && !m.contains("/") && !sort.contains(m)) - sort.add(m); - for (String pack : sort) { - LinkedList list = new LinkedList<>(Arrays.asList(pack.split("/"))); - loadNodesByNames(top, list); - } - tree.setModel(new DefaultTreeModel(top)); - } - - private void buildFlatTreeFromMass(List mass) { - TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); - DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); - - TreeMap> packages = new TreeMap<>(); - HashSet classContainingPackageRoots = new HashSet<>(); - - // (assertion: mass does not contain null elements) - Comparator sortByFileExtensionsComparator = Comparator.comparing( - (String o) -> o.replaceAll("[^.]*\\.", "")).thenComparing(o -> o); - - for (String entry : mass) { - String packagePath = ""; - String packageRoot = ""; - if (entry.contains("/")) { - packagePath = entry.replaceAll("/[^/]*$", ""); - packageRoot = entry.replaceAll("/.*$", ""); - } - String packageEntry = entry.replace(packagePath + "/", ""); - if (!packages.containsKey(packagePath)) { - packages.put(packagePath, new TreeSet<>(sortByFileExtensionsComparator)); - } - packages.get(packagePath).add(packageEntry); - if (!entry.startsWith("META-INF") && packageRoot.trim().length() > 0 - && entry.matches(".*\\.(class|java|prop|properties)$")) { - classContainingPackageRoots.add(packageRoot); - } - } - - // META-INF comes first -> not flat - for (String packagePath : packages.keySet()) { - if (packagePath.startsWith("META-INF")) { - List packagePathElements = Arrays.asList(packagePath.split("/")); - for (String entry : packages.get(packagePath)) { - ArrayList list = new ArrayList<>(packagePathElements); - list.add(entry); - loadNodesByNames(top, list); - } - } - } - - // real packages: path starts with a classContainingPackageRoot -> flat - for (String packagePath : packages.keySet()) { - String packageRoot = packagePath.replaceAll("/.*$", ""); - if (classContainingPackageRoots.contains(packageRoot)) { - for (String entry : packages.get(packagePath)) { - ArrayList list = new ArrayList<>(); - list.add(new TreeNodeUserObject(packagePath, packagePath.replaceAll("/", "."))); - list.add(new TreeNodeUserObject(entry)); - loadNodesByUserObj(top, list); - } - } - } - - // the rest, not real packages but directories -> not flat - for (String packagePath : packages.keySet()) { - String packageRoot = packagePath.replaceAll("/.*$", ""); - if (!classContainingPackageRoots.contains(packageRoot) && !packagePath.startsWith("META-INF") - && packagePath.length() > 0) { - List packagePathElements = Arrays.asList(packagePath.split("/")); - for (String entry : packages.get(packagePath)) { - ArrayList list = new ArrayList<>(packagePathElements); - list.add(entry); - loadNodesByNames(top, list); - } - } - } - - // the default package -> not flat - String packagePath = ""; - if (packages.containsKey(packagePath)) { - for (String entry : packages.get(packagePath)) { - ArrayList list = new ArrayList<>(); - list.add(entry); - loadNodesByNames(top, list); - } - } - tree.setModel(new DefaultTreeModel(top)); - } - - public void closeFile() { - for (OpenFile co : hmap) { - int pos = house.indexOfTab(co.name); - if (pos >= 0) - house.remove(pos); - co.close(); - } - - if (state != null) { - Closer.tryClose(state); - } - state = null; - - hmap.clear(); - tree.setModel(new DefaultTreeModel(null)); - file = null; - treeExpansionState = null; - open = false; - mainWindow.onFileLoadEnded(); - } - - public void changeTheme(String xml) { - InputStream in = getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + xml); - try { - if (in != null) { - setTheme(Theme.load(in)); - for (OpenFile f : hmap) { - getTheme().apply(f.textArea); - } - } - } catch (Exception e1) { - Luyten.showExceptionDialog("Exception!", e1); - } - } - - public File getOpenedFile() { - File openedFile = null; - if (file != null && open) { - openedFile = file; - } - if (openedFile == null) { - getLabel().setText("No open file"); - } - return openedFile; - } - - public String getCurrentTabTitle() { - String tabTitle = null; - try { - int pos = house.getSelectedIndex(); - if (pos >= 0) { - tabTitle = house.getTitleAt(pos); - } - } catch (Exception e1) { - Luyten.showExceptionDialog("Exception!", e1); - } - if (tabTitle == null) { - getLabel().setText("No open tab"); - } - return tabTitle; - } - - public RSyntaxTextArea getCurrentTextArea() { - RSyntaxTextArea currentTextArea = null; - try { - int pos = house.getSelectedIndex(); - System.out.println(pos); - if (pos >= 0) { - RTextScrollPane co = (RTextScrollPane) house.getComponentAt(pos); - currentTextArea = (RSyntaxTextArea) co.getViewport().getView(); - } - } catch (Exception e1) { - Luyten.showExceptionDialog("Exception!", e1); - } - if (currentTextArea == null) { - getLabel().setText("No open tab"); - } - return currentTextArea; - } - - public void startWarmUpThread() { - new Thread(() -> { - try { - Thread.sleep(500); - String internalName = FindBox.class.getName(); - TypeReference type = metadataSystem.lookupType(internalName); - TypeDefinition resolvedType; - if ((type == null) || ((resolvedType = type.resolve()) == null)) { - return; - } - StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput - .setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); - String decompiledSource = stringwriter.toString(); - OpenFile open = new OpenFile(internalName, "*/" + internalName, getTheme(), mainWindow); - open.setContent(decompiledSource); - JTabbedPane pane = new JTabbedPane(); - pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - pane.addTab("title", open.scrollPane); - pane.setSelectedIndex(pane.indexOfTab("title")); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - }).start(); - } - - public void navigateTo(final String uniqueStr) { - new Thread(() -> { - if (uniqueStr == null) - return; - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length <= 1) - return; - String destinationTypeStr = linkParts[1]; - try { - bar.setVisible(true); - getLabel().setText("Navigating: " + destinationTypeStr.replaceAll("/", ".")); - - TypeReference type = metadataSystem.lookupType(destinationTypeStr); - if (type == null) - throw new RuntimeException("Cannot lookup type: " + destinationTypeStr); - TypeDefinition typeDef = type.resolve(); - if (typeDef == null) - throw new RuntimeException("Cannot resolve type: " + destinationTypeStr); - - String tabTitle = typeDef.getName() + ".class"; - extractClassToTextPane(typeDef, tabTitle, destinationTypeStr, uniqueStr); - - getLabel().setText("Complete"); - } catch (Exception e) { - getLabel().setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); - Luyten.showExceptionDialog("Cannot Navigate!", e); - } finally { - bar.setVisible(false); - } - }).start(); - } - - public JLabel getLabel() { - return label; - } - - public void setLabel(JLabel label) { - this.label = label; - } - - public State getState() { - return state; - } - - public Theme getTheme() { - return theme; - } - - public void setTheme(Theme theme) { - this.theme = theme; - } - - public MetadataSystem getMetadataSystem() { - return metadataSystem; - } - - public String getFileName() { - return file == null ? null : getName(file.getName()); - } -} diff --git a/src/us/deathmarine/luyten/OpenFile.java b/src/us/deathmarine/luyten/OpenFile.java deleted file mode 100644 index fb535214..00000000 --- a/src/us/deathmarine/luyten/OpenFile.java +++ /dev/null @@ -1,774 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.awt.Cursor; -import java.awt.Font; -import java.awt.Rectangle; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionAdapter; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; -import java.io.File; -import java.io.StringWriter; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.concurrent.ConcurrentHashMap; - -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JScrollBar; -import javax.swing.JViewport; -import javax.swing.ScrollPaneConstants; -import javax.swing.Scrollable; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.event.HyperlinkEvent; -import org.fife.ui.rsyntaxtextarea.FileTypeUtil; -import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rsyntaxtextarea.SyntaxConstants; -import org.fife.ui.rsyntaxtextarea.Theme; -import org.fife.ui.rtextarea.RTextScrollPane; - -import com.strobel.assembler.metadata.MetadataSystem; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; -import com.strobel.decompiler.languages.Languages; - -public class OpenFile { - - public static final HashSet WELL_KNOWN_TEXT_FILE_EXTENSIONS = new HashSet<>( - Arrays.asList(".java", ".xml", ".rss", ".project", ".classpath", ".h", ".c", ".cpp", ".yaml", ".yml", ".ini", ".sql", ".js", ".php", ".php5", - ".phtml", ".html", ".htm", ".xhtm", ".xhtml", ".lua", ".bat", ".pl", ".sh", ".css", ".json", ".txt", - ".rb", ".make", ".mak", ".py", ".properties", ".prop")); - - private static final FileTypeUtil FILE_TYPE_UTIL = FileTypeUtil.get(); - - // navigation links - private TreeMap selectionToUniqueStrTreeMap = new TreeMap<>(); - private final Map isNavigableCache = new ConcurrentHashMap<>(); - private final Map readableLinksCache = new ConcurrentHashMap<>(); - - private volatile boolean isContentValid = false; - private volatile boolean isNavigationLinksValid = false; - private volatile boolean isWaitForLinksCursor = false; - private volatile Double lastScrollPercent = null; - - private LinkProvider linkProvider; - private String initialNavigationLink; - private boolean isFirstTimeRun = true; - - MainWindow mainWindow; - RTextScrollPane scrollPane; - RSyntaxTextArea textArea; - String name; - String path; - - private final ConfigSaver configSaver; - private final LuytenPreferences luytenPrefs; - - // decompiler and type references (not needed for text files) - private MetadataSystem metadataSystem; - private DecompilerSettings settings; - private DecompilationOptions decompilationOptions; - private TypeDefinition type; - - public OpenFile(String name, String path, Theme theme, final MainWindow mainWindow) { - this.name = name; - this.path = path; - this.mainWindow = mainWindow; - - configSaver = ConfigSaver.getLoadedInstance(); - luytenPrefs = configSaver.getLuytenPreferences(); - - textArea = new RSyntaxTextArea(25, 70); - textArea.setCaretPosition(0); - textArea.requestFocusInWindow(); - textArea.setMarkOccurrences(true); - textArea.setClearWhitespaceLinesEnabled(false); - textArea.setEditable(false); - textArea.setAntiAliasingEnabled(true); - textArea.setCodeFoldingEnabled(true); - - setLanguage(textArea, name); - scrollPane = new RTextScrollPane(textArea, true); - - scrollPane.setIconRowHeaderEnabled(true); - textArea.setText(""); - - // Edit RTextArea's PopupMenu - JPopupMenu pop = textArea.getPopupMenu(); - pop.addSeparator(); - JMenuItem item = new JMenuItem("Font"); - item.addActionListener(e -> { - JFontChooser fontChooser = new JFontChooser(); - fontChooser.setSelectedFont(textArea.getFont()); - fontChooser.setSelectedFontSize(textArea.getFont().getSize()); - int result = fontChooser.showDialog(mainWindow); - if (result == JFontChooser.OK_OPTION) { - textArea.setFont(fontChooser.getSelectedFont()); - luytenPrefs.setFont_size(fontChooser.getSelectedFontSize()); - } - }); - pop.add(item); - textArea.setPopupMenu(pop); - - theme.apply(textArea); - - textArea.setFont(new Font(textArea.getFont().getName(), textArea.getFont().getStyle(), luytenPrefs.getFont_size())); - - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - final JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); - if (verticalScrollbar != null) { - verticalScrollbar.addAdjustmentListener(e -> { - String content = textArea.getText(); - if (content == null || content.length() == 0) - return; - int scrollValue = verticalScrollbar.getValue() - verticalScrollbar.getMinimum(); - int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); - if (scrollMax < 1 || scrollValue < 0 || scrollValue > scrollMax) - return; - lastScrollPercent = (((double) scrollValue) / ((double) scrollMax)); - }); - } - - textArea.setHyperlinksEnabled(true); - textArea.setLinkScanningMask(Keymap.ctrlDownModifier()); - - textArea.setLinkGenerator((textArea, offs) -> { - final String uniqueStr = getUniqueStrForOffset(offs); - final Integer selectionFrom = getSelectionFromForOffset(offs); - if (uniqueStr != null && selectionFrom != null) { - return new LinkGeneratorResult() { - @Override - public HyperlinkEvent execute() { - if (isNavigationLinksValid) - onNavigationClicked(uniqueStr); - return null; - } - - @Override - public int getSourceOffset() { - if (isNavigationLinksValid) - return selectionFrom; - return offs; - } - }; - } - return null; - }); - - /* - * Add Ctrl+Wheel Zoom for Text Size Removes all standard listeners and - * writes new listeners for wheelscroll movement. - */ - for (MouseWheelListener listeners : scrollPane.getMouseWheelListeners()) { - scrollPane.removeMouseWheelListener(listeners); - } - - scrollPane.addMouseWheelListener(e -> { - if (e.getWheelRotation() == 0) { - // Nothing to do here. This happens when scroll event is delivered from a touchbar - // or MagicMouse. There's getPreciseWheelRotation, however it looks like there's no - // trivial and consistent way to use that - // See https://github.com/JetBrains/intellij-community/blob/21c99af7c78fc82aefc4d05646389f4991b08b38/bin/idea.properties#L133-L156 - return; - } - - if ((e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0) { - Font font = textArea.getFont(); - int size = font.getSize(); - if (e.getWheelRotation() > 0) { - textArea.setFont(new Font(font.getName(), font.getStyle(), --size >= 8 ? --size : 8)); - } else { - textArea.setFont(new Font(font.getName(), font.getStyle(), ++size)); - } - luytenPrefs.setFont_size(size); - } else { - if (scrollPane.isWheelScrollingEnabled() && e.getWheelRotation() != 0) { - JScrollBar toScroll = scrollPane.getVerticalScrollBar(); - int direction = e.getWheelRotation() < 0 ? -1 : 1; - int orientation = SwingConstants.VERTICAL; - if (toScroll == null || !toScroll.isVisible()) { - toScroll = scrollPane.getHorizontalScrollBar(); - if (toScroll == null || !toScroll.isVisible()) { - return; - } - orientation = SwingConstants.HORIZONTAL; - } - e.consume(); - - if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { - JViewport vp = scrollPane.getViewport(); - if (vp == null) { - return; - } - Component comp = vp.getView(); - int units = Math.abs(e.getUnitsToScroll()); - boolean limitScroll = Math.abs(e.getWheelRotation()) == 1; - Object fastWheelScroll = toScroll.getClientProperty("JScrollBar.fastWheelScrolling"); - if (Boolean.TRUE == fastWheelScroll && comp instanceof Scrollable) { - Scrollable scrollComp = (Scrollable) comp; - Rectangle viewRect = vp.getViewRect(); - int startingX = viewRect.x; - boolean leftToRight = comp.getComponentOrientation().isLeftToRight(); - int scrollMin = toScroll.getMinimum(); - int scrollMax = toScroll.getMaximum() - toScroll.getModel().getExtent(); - - if (limitScroll) { - int blockIncr = scrollComp.getScrollableBlockIncrement(viewRect, orientation, - direction); - if (direction < 0) { - scrollMin = Math.max(scrollMin, toScroll.getValue() - blockIncr); - } else { - scrollMax = Math.min(scrollMax, toScroll.getValue() + blockIncr); - } - } - - for (int i = 0; i < units; i++) { - int unitIncr = scrollComp.getScrollableUnitIncrement(viewRect, orientation, - direction); - if (orientation == SwingConstants.VERTICAL) { - if (direction < 0) { - viewRect.y -= unitIncr; - if (viewRect.y <= scrollMin) { - viewRect.y = scrollMin; - break; - } - } else { // (direction > 0 - viewRect.y += unitIncr; - if (viewRect.y >= scrollMax) { - viewRect.y = scrollMax; - break; - } - } - } else { - if ((leftToRight && direction < 0) || (!leftToRight && direction > 0)) { - viewRect.x -= unitIncr; - if (leftToRight) { - if (viewRect.x < scrollMin) { - viewRect.x = scrollMin; - break; - } - } - } else { - viewRect.x += unitIncr; - if (leftToRight) { - if (viewRect.x > scrollMax) { - viewRect.x = scrollMax; - break; - } - } - } - } - } - if (orientation == SwingConstants.VERTICAL) { - toScroll.setValue(viewRect.y); - } else { - if (leftToRight) { - toScroll.setValue(viewRect.x); - } else { - int newPos = toScroll.getValue() - (viewRect.x - startingX); - if (newPos < scrollMin) { - newPos = scrollMin; - } else if (newPos > scrollMax) { - newPos = scrollMax; - } - toScroll.setValue(newPos); - } - } - } else { - int delta; - int limit = -1; - - if (limitScroll) { - if (direction < 0) { - limit = toScroll.getValue() - toScroll.getBlockIncrement(direction); - } else { - limit = toScroll.getValue() + toScroll.getBlockIncrement(direction); - } - } - - for (int i = 0; i < units; i++) { - if (direction > 0) { - delta = toScroll.getUnitIncrement(direction); - } else { - delta = -toScroll.getUnitIncrement(direction); - } - int oldValue = toScroll.getValue(); - int newValue = oldValue + delta; - if (delta > 0 && newValue < oldValue) { - newValue = toScroll.getMaximum(); - } else if (delta < 0 && newValue > oldValue) { - newValue = toScroll.getMinimum(); - } - if (oldValue == newValue) { - break; - } - if (limitScroll && i > 0) { - assert limit != -1; - if ((direction < 0 && newValue < limit) - || (direction > 0 && newValue > limit)) { - break; - } - } - toScroll.setValue(newValue); - } - - } - } else if (e.getScrollType() == MouseWheelEvent.WHEEL_BLOCK_SCROLL) { - int oldValue = toScroll.getValue(); - int blockIncrement = toScroll.getBlockIncrement(direction); - int delta = blockIncrement * ((direction > 0) ? +1 : -1); - int newValue = oldValue + delta; - if (delta > 0 && newValue < oldValue) { - newValue = toScroll.getMaximum(); - } else if (delta < 0 && newValue > oldValue) { - newValue = toScroll.getMinimum(); - } - toScroll.setValue(newValue); - } - } - } - - e.consume(); - }); - - textArea.addMouseMotionListener(new MouseMotionAdapter() { - private boolean isLinkLabelPrev = false; - private String prevLinkText = null; - - @Override - public synchronized void mouseMoved(MouseEvent e) { - String linkText = null; - boolean isLinkLabel = false; - boolean isCtrlDown = (e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0; - if (isCtrlDown) { - linkText = createLinkLabel(e); - isLinkLabel = linkText != null; - } - if (isCtrlDown && isWaitForLinksCursor) { - textArea.setCursor(new Cursor(Cursor.WAIT_CURSOR)); - } else if (textArea.getCursor().getType() == Cursor.WAIT_CURSOR) { - textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - - JLabel label = OpenFile.this.mainWindow.getLabel(); - - if (isLinkLabel && isLinkLabelPrev) { - if (!linkText.equals(prevLinkText)) { - setLinkLabel(label, linkText); - } - } else if (isLinkLabel) { - setLinkLabel(label, linkText); - - } else if (isLinkLabelPrev) { - setLinkLabel(label, null); - } - isLinkLabelPrev = isLinkLabel; - prevLinkText = linkText; - } - - private void setLinkLabel(JLabel label, String text) { - String current = label.getText(); - if (text == null && current != null) - if (current.startsWith("Navigating:") || current.startsWith("Cannot navigate:")) - return; - label.setText(text != null ? text : "Complete"); - } - - private String createLinkLabel(MouseEvent e) { - int offs = textArea.viewToModel(e.getPoint()); - if (isNavigationLinksValid) { - return getLinkDescriptionForOffset(offs); - } - return null; - } - }); - } - - public static void setLanguage(RSyntaxTextArea area, String fileName) { - // Hard code class -> Java mapping - if (fileName.toLowerCase().endsWith(".class")) { - area.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); - return; - } - - String type = FILE_TYPE_UTIL.guessContentType(new File(fileName)); - if (type == null || type.equals(SyntaxConstants.SYNTAX_STYLE_NONE)) { - type = FILE_TYPE_UTIL.guessContentType(area); - } - area.setSyntaxEditingStyle(type); - } - - public void setContent(String content) { - textArea.setText(content); - setLanguage(textArea, name); - } - - public void decompile() { - this.invalidateContent(); - // synchronized: do not accept changes from menu while running - synchronized (settings) { - if (Languages.java().getName().equals(settings.getLanguage().getName())) { - decompileWithNavigationLinks(); - } else { - decompileWithoutLinks(); - } - } - } - - private void decompileWithoutLinks() { - this.invalidateContent(); - isNavigationLinksValid = false; - textArea.setHyperlinksEnabled(false); - - StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput.setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(type, plainTextOutput, decompilationOptions); - setContentPreserveLastScrollPosition(stringwriter.toString()); - this.isContentValid = true; - } - - private void decompileWithNavigationLinks() { - this.invalidateContent(); - DecompilerLinkProvider newLinkProvider = new DecompilerLinkProvider(); - newLinkProvider.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - newLinkProvider.setType(type); - linkProvider = newLinkProvider; - - linkProvider.generateContent(); - setContentPreserveLastScrollPosition(linkProvider.getTextContent()); - this.isContentValid = true; - enableLinks(); - } - - private void setContentPreserveLastScrollPosition(final String content) { - final Double scrollPercent = lastScrollPercent; - if (scrollPercent != null && initialNavigationLink == null) { - SwingUtilities.invokeLater(() -> { - textArea.setText(content); - restoreScrollPosition(scrollPercent); - }); - } else { - textArea.setText(content); - } - } - - private void restoreScrollPosition(final double position) { - SwingUtilities.invokeLater(() -> { - JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); - if (verticalScrollbar == null) - return; - int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); - long newScrollValue = Math.round(position * scrollMax) + verticalScrollbar.getMinimum(); - if (newScrollValue < verticalScrollbar.getMinimum()) - newScrollValue = verticalScrollbar.getMinimum(); - if (newScrollValue > verticalScrollbar.getMaximum()) - newScrollValue = verticalScrollbar.getMaximum(); - verticalScrollbar.setValue((int) newScrollValue); - }); - } - - private void enableLinks() { - if (initialNavigationLink != null) { - doEnableLinks(); - } else { - new Thread(() -> { - try { - isWaitForLinksCursor = true; - doEnableLinks(); - } finally { - isWaitForLinksCursor = false; - resetCursor(); - } - }).start(); - } - } - - private void resetCursor() { - SwingUtilities.invokeLater(() -> textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR))); - } - - private void doEnableLinks() { - isNavigationLinksValid = false; - linkProvider.processLinks(); - buildSelectionToUniqueStrTreeMap(); - clearLinksCache(); - isNavigationLinksValid = true; - textArea.setHyperlinksEnabled(true); - warmUpWithFirstLink(); - } - - private void warmUpWithFirstLink() { - if (selectionToUniqueStrTreeMap.keySet().size() > 0) { - Selection selection = selectionToUniqueStrTreeMap.keySet().iterator().next(); - getLinkDescriptionForOffset(selection.from); - } - } - - public void clearLinksCache() { - try { - isNavigableCache.clear(); - readableLinksCache.clear(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private void buildSelectionToUniqueStrTreeMap() { - TreeMap treeMap = new TreeMap<>(); - Map definitionToSelectionMap = linkProvider.getDefinitionToSelectionMap(); - Map> referenceToSelectionsMap = linkProvider.getReferenceToSelectionsMap(); - - for (String key : definitionToSelectionMap.keySet()) { - Selection selection = definitionToSelectionMap.get(key); - treeMap.put(selection, key); - } - for (String key : referenceToSelectionsMap.keySet()) { - for (Selection selection : referenceToSelectionsMap.get(key)) { - treeMap.put(selection, key); - } - } - selectionToUniqueStrTreeMap = treeMap; - } - - private Selection getSelectionForOffset(int offset) { - if (isNavigationLinksValid) { - Selection offsetSelection = new Selection(offset, offset); - Selection floorSelection = selectionToUniqueStrTreeMap.floorKey(offsetSelection); - if (floorSelection != null && floorSelection.from <= offset && floorSelection.to > offset) { - return floorSelection; - } - } - return null; - } - - private String getUniqueStrForOffset(int offset) { - Selection selection = getSelectionForOffset(offset); - if (selection != null) { - String uniqueStr = selectionToUniqueStrTreeMap.get(selection); - if (this.isLinkNavigable(uniqueStr) && this.getLinkDescription(uniqueStr) != null) { - return uniqueStr; - } - } - return null; - } - - private Integer getSelectionFromForOffset(int offset) { - Selection selection = getSelectionForOffset(offset); - if (selection != null) { - return selection.from; - } - return null; - } - - private String getLinkDescriptionForOffset(int offset) { - String uniqueStr = getUniqueStrForOffset(offset); - if (uniqueStr != null) { - return this.getLinkDescription(uniqueStr); - } - return null; - } - - private boolean isLinkNavigable(String uniqueStr) { - try { - Boolean isNavigableCached = isNavigableCache.get(uniqueStr); - if (isNavigableCached != null) - return isNavigableCached; - - boolean isNavigable = linkProvider.isLinkNavigable(uniqueStr); - isNavigableCache.put(uniqueStr, isNavigable); - return isNavigable; - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - return false; - } - - private String getLinkDescription(String uniqueStr) { - try { - String descriptionCached = readableLinksCache.get(uniqueStr); - if (descriptionCached != null) - return descriptionCached; - - String description = linkProvider.getLinkDescription(uniqueStr); - if (description != null && description.trim().length() > 0) { - readableLinksCache.put(uniqueStr, description); - return description; - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - return null; - } - - private void onNavigationClicked(String clickedReferenceUniqueStr) { - if (isLocallyNavigable(clickedReferenceUniqueStr)) { - onLocalNavigationRequest(clickedReferenceUniqueStr); - } else if (linkProvider.isLinkNavigable(clickedReferenceUniqueStr)) { - onOutboundNavigationRequest(clickedReferenceUniqueStr); - } else { - JLabel label = this.mainWindow.getLabel(); - if (label == null) - return; - String[] linkParts = clickedReferenceUniqueStr.split("\\|"); - if (linkParts.length <= 1) { - label.setText("Cannot navigate: " + clickedReferenceUniqueStr); - return; - } - String destinationTypeStr = linkParts[1]; - label.setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); - } - } - - private boolean isLocallyNavigable(String uniqueStr) { - return linkProvider.getDefinitionToSelectionMap().containsKey(uniqueStr); - } - - private void onLocalNavigationRequest(String uniqueStr) { - try { - Selection selection = linkProvider.getDefinitionToSelectionMap().get(uniqueStr); - doLocalNavigation(selection); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private void doLocalNavigation(Selection selection) { - try { - textArea.requestFocusInWindow(); - if (selection != null) { - textArea.setSelectionStart(selection.from); - textArea.setSelectionEnd(selection.to); - scrollToSelection(selection.from); - } else { - textArea.setSelectionStart(0); - textArea.setSelectionEnd(0); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private void scrollToSelection(final int selectionBeginningOffset) { - SwingUtilities.invokeLater(() -> { - try { - int fullHeight = textArea.getBounds().height; - int viewportHeight = textArea.getVisibleRect().height; - int viewportLineCount = viewportHeight / textArea.getLineHeight(); - int selectionLineNum = textArea.getLineOfOffset(selectionBeginningOffset); - int upperMarginToScroll = Math.round(viewportLineCount * 0.29f); - int upperLineToSet = selectionLineNum - upperMarginToScroll; - int currentUpperLine = textArea.getVisibleRect().y / textArea.getLineHeight(); - - if (selectionLineNum <= currentUpperLine + 2 - || selectionLineNum >= currentUpperLine + viewportLineCount - 4) { - Rectangle rectToScroll = new Rectangle(); - rectToScroll.x = 0; - rectToScroll.width = 1; - rectToScroll.y = Math.max(upperLineToSet * textArea.getLineHeight(), 0); - rectToScroll.height = Math.min(viewportHeight, fullHeight - rectToScroll.y); - textArea.scrollRectToVisible(rectToScroll); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - }); - } - - private void onOutboundNavigationRequest(String uniqueStr) { - mainWindow.onNavigationRequest(uniqueStr); - } - - public void setDecompilerReferences(MetadataSystem metadataSystem, DecompilerSettings settings, - DecompilationOptions decompilationOptions) { - this.metadataSystem = metadataSystem; - this.settings = settings; - this.decompilationOptions = decompilationOptions; - } - - public TypeDefinition getType() { - return type; - } - - public void setType(TypeDefinition type) { - this.type = type; - } - - public boolean isContentValid() { - return isContentValid; - } - - public void invalidateContent() { - try { - this.setContent(""); - } finally { - this.isContentValid = false; - this.isNavigationLinksValid = false; - } - } - - public void resetScrollPosition() { - lastScrollPercent = null; - } - - public void setInitialNavigationLink(String initialNavigationLink) { - this.initialNavigationLink = initialNavigationLink; - } - - public void onAddedToScreen() { - try { - if (initialNavigationLink != null) { - onLocalNavigationRequest(initialNavigationLink); - } else if (isFirstTimeRun) { - // warm up scrolling - isFirstTimeRun = false; - doLocalNavigation(new Selection(0, 0)); - } - } finally { - initialNavigationLink = null; - } - } - - /** - * sun.swing.CachedPainter holds on OpenFile for a while even after - * JTabbedPane.remove(component) - */ - public void close() { - linkProvider = null; - type = null; - invalidateContent(); - clearLinksCache(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((path == null) ? 0 : path.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - OpenFile other = (OpenFile) obj; - if (path == null) { - return other.path == null; - } else return path.equals(other.path); - } - -} diff --git a/src/us/deathmarine/luyten/RecentFiles.java b/src/us/deathmarine/luyten/RecentFiles.java deleted file mode 100644 index 625650d4..00000000 --- a/src/us/deathmarine/luyten/RecentFiles.java +++ /dev/null @@ -1,67 +0,0 @@ -package us.deathmarine.luyten; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.prefs.Preferences; - -public class RecentFiles { - - public static List paths = new ArrayList<>(); - private static final Preferences prefs = Preferences.userNodeForPackage(RecentFiles.class); - - public static int load() { - boolean saveNeeded = false; - - String serializedPaths = prefs.get("recentFiles", null); - - if (serializedPaths == null) return 0; - - // "test","asdf.txt","text2" - for (String path : serializedPaths.split("\",\"")) { - - path = path.replace("\"", ""); - - if (!path.trim().isEmpty() && !new File(path).exists()) saveNeeded = true; - else paths.add(path); - } - - if (saveNeeded) save(); - - return paths.size(); - } - - public static void add(String path) { - if (paths.contains(path)) { - paths.remove(path); - paths.add(path); - return; - } - - if (paths.size() >= 10) paths.remove(0); - paths.add(path); - - save(); - } - - public static void save() { - if (paths.size() == 0) { - prefs.put("recentFiles", ""); - return; - } - - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < paths.size(); i++) { - if (i != 0) sb.append(','); - - /*if (!new File(paths.get(i)).exists()) { - paths.remove(i); - continue; - }*/ - sb.append("\"").append(paths.get(i)).append("\""); - } - - prefs.put("recentFiles", sb.toString()); - } -} diff --git a/src/us/deathmarine/luyten/Selection.java b/src/us/deathmarine/luyten/Selection.java deleted file mode 100644 index 6af8bb8f..00000000 --- a/src/us/deathmarine/luyten/Selection.java +++ /dev/null @@ -1,38 +0,0 @@ -package us.deathmarine.luyten; - -public class Selection implements Comparable { - public final Integer from; - public final Integer to; - - public Selection(Integer from, Integer to) { - this.from = from; - this.to = to; - } - - @Override - public int compareTo(Selection o) { - return from.compareTo(o.from); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((from == null) ? 0 : from.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Selection other = (Selection) obj; - if (from == null) { - return other.from == null; - } else return from.equals(other.from); - } -} diff --git a/src/us/deathmarine/luyten/TooLargeFileException.java b/src/us/deathmarine/luyten/TooLargeFileException.java deleted file mode 100644 index d4c867e1..00000000 --- a/src/us/deathmarine/luyten/TooLargeFileException.java +++ /dev/null @@ -1,20 +0,0 @@ -package us.deathmarine.luyten; - -import java.text.DecimalFormat; - -public class TooLargeFileException extends Exception { - private static final long serialVersionUID = 6091096838075139962L; - private final long size; - - public TooLargeFileException(long size) { - this.size = size; - } - - public String getReadableFileSize() { - if (size <= 0) - return "0"; - final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" }; - int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); - return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; - } -} diff --git a/src/us/deathmarine/luyten/TreeNodeUserObject.java b/src/us/deathmarine/luyten/TreeNodeUserObject.java deleted file mode 100644 index 7bed250c..00000000 --- a/src/us/deathmarine/luyten/TreeNodeUserObject.java +++ /dev/null @@ -1,37 +0,0 @@ -package us.deathmarine.luyten; - -public class TreeNodeUserObject { - - private String originalName; - private String displayName; - - public TreeNodeUserObject(String name) { - this(name, name); - } - - public TreeNodeUserObject(String originalName, String displayName) { - this.originalName = originalName; - this.displayName = displayName; - } - - public String getOriginalName() { - return originalName; - } - - public void setOriginalName(String originalName) { - this.originalName = originalName; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - - @Override - public String toString() { - return displayName; - } -} diff --git a/src/us/deathmarine/luyten/TreeUtil.java b/src/us/deathmarine/luyten/TreeUtil.java deleted file mode 100644 index 1ae83277..00000000 --- a/src/us/deathmarine/luyten/TreeUtil.java +++ /dev/null @@ -1,81 +0,0 @@ -package us.deathmarine.luyten; - -import java.util.HashSet; -import java.util.Set; -import javax.swing.JTree; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreePath; - -public class TreeUtil { - - private JTree tree; - - public TreeUtil() { - } - - public TreeUtil(JTree tree) { - this.tree = tree; - } - - public Set getExpansionState() { - Set openedSet = new HashSet<>(); - if (tree != null) { - int rowCount = tree.getRowCount(); - for (int i = 0; i < rowCount; i++) { - TreePath path = tree.getPathForRow(i); - if (tree.isExpanded(path)) { - String rowPathStr = getRowPathStr(path); - // for switching Package Explorer on/off - openedSet.addAll(getAllParentPathsStr(rowPathStr)); - } - } - } - return openedSet; - } - - private Set getAllParentPathsStr(String rowPathStr) { - Set parents = new HashSet<>(); - parents.add(rowPathStr); - if (rowPathStr.contains("/")) { - String[] pathElements = rowPathStr.split("/"); - String path = ""; - for (String pathElement : pathElements) { - path = path + pathElement + "/"; - parents.add(path); - } - } - return parents; - } - - public void restoreExpanstionState(Set expansionState) { - if (tree != null && expansionState != null) { - // tree.getRowCount() changes at tree.expandRow() - for (int i = 0; i < tree.getRowCount(); i++) { - TreePath path = tree.getPathForRow(i); - if (expansionState.contains(getRowPathStr(path))) { - tree.expandRow(i); - } - } - } - } - - private String getRowPathStr(TreePath trp) { - StringBuilder pathStr = new StringBuilder(); - if (trp.getPathCount() > 1) { - for (int i = 1; i < trp.getPathCount(); i++) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); - TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); - pathStr.append(userObject.getOriginalName()).append("/"); - } - } - return pathStr.toString(); - } - - public JTree getTree() { - return tree; - } - - public void setTree(JTree tree) { - this.tree = tree; - } -} diff --git a/src/us/deathmarine/luyten/WindowPosition.java b/src/us/deathmarine/luyten/WindowPosition.java deleted file mode 100644 index cfeb18c4..00000000 --- a/src/us/deathmarine/luyten/WindowPosition.java +++ /dev/null @@ -1,90 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Toolkit; -import javax.swing.JDialog; -import javax.swing.JFrame; - -public class WindowPosition { - - private boolean isFullScreen; - private int windowWidth; - private int windowHeight; - private int windowX; - private int windowY; - - public void readPositionFromWindow(JFrame window) { - isFullScreen = (window.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH; - if (!isFullScreen) { - this.readPositionFromComponent(window); - } - } - - public void readPositionFromDialog(JDialog dialog) { - this.readPositionFromComponent(dialog); - } - - private void readPositionFromComponent(Component component) { - isFullScreen = false; - windowWidth = component.getWidth(); - windowHeight = component.getHeight(); - windowX = component.getX(); - windowY = component.getY(); - } - - public boolean isSavedWindowPositionValid() { - if (isFullScreen) { - return true; - } - if (windowWidth < 100 || windowHeight < 100) { - return false; - } - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - if (windowWidth > screenSize.width + 50 || windowHeight > screenSize.height + 50) { - return false; - } - return windowY >= -20 && windowY <= screenSize.height - 50 && windowX >= 50 - windowWidth - && windowX <= screenSize.width - 50; - } - - public boolean isFullScreen() { - return isFullScreen; - } - - public void setFullScreen(boolean isFullScreen) { - this.isFullScreen = isFullScreen; - } - - public int getWindowWidth() { - return windowWidth; - } - - public void setWindowWidth(int windowWidth) { - this.windowWidth = windowWidth; - } - - public int getWindowHeight() { - return windowHeight; - } - - public void setWindowHeight(int windowHeight) { - this.windowHeight = windowHeight; - } - - public int getWindowX() { - return windowX; - } - - public void setWindowX(int windowX) { - this.windowX = windowX; - } - - public int getWindowY() { - return windowY; - } - - public void setWindowY(int windowY) { - this.windowY = windowY; - } -} From ad144286442c83745c4ac5dd3942f5b01c31245e Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 7 Jan 2022 14:38:57 +0100 Subject: [PATCH 23/26] Add File Util --- .../java/us/deathmarine/luyten/FileUtil.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/us/deathmarine/luyten/FileUtil.java diff --git a/src/main/java/us/deathmarine/luyten/FileUtil.java b/src/main/java/us/deathmarine/luyten/FileUtil.java new file mode 100644 index 00000000..ac12275e --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FileUtil.java @@ -0,0 +1,33 @@ +package us.deathmarine.luyten; + +import java.io.File; +import org.fife.ui.rsyntaxtextarea.FileTypeUtil; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; + +public final class FileUtil { + + private static final FileTypeUtil FILE_TYPE_UTIL = FileTypeUtil.get(); + + private FileUtil() { + throw new UnsupportedOperationException(); + } + + public static String getLanguage(String fileName) { + // Hard code .class -> Java mapping + if (fileName.toLowerCase().endsWith(".class")) { + return SyntaxConstants.SYNTAX_STYLE_JAVA; + } + // Otherwise, let RSTA do the job + return FILE_TYPE_UTIL.guessContentType(new File(fileName)); + } + + public static void setLanguage(RSyntaxTextArea area, String fileName) { + String type = getLanguage(fileName); + if (type == null || type.equals(SyntaxConstants.SYNTAX_STYLE_NONE)) { + type = FILE_TYPE_UTIL.guessContentType(area); + } + area.setSyntaxEditingStyle(type); + } + +} From cbc8611f333db9d4337456b2ef1292cb349d54b9 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 7 Jan 2022 14:50:21 +0100 Subject: [PATCH 24/26] Update links --- license.txt => LICENSE | 64 +++++++++---------- ReadMe.md => README.md | 24 ++++--- .../us/deathmarine/luyten/FindAllBox.java | 3 +- 3 files changed, 49 insertions(+), 42 deletions(-) rename license.txt => LICENSE (99%) rename ReadMe.md => README.md (58%) diff --git a/license.txt b/LICENSE similarity index 99% rename from license.txt rename to LICENSE index a8fb69f3..e6145367 100644 --- a/license.txt +++ b/LICENSE @@ -1,32 +1,32 @@ -Luyten -Copyright (c) 2019, Deathmarine - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -1. Definitions. -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: -You must give any other recipients of the Work or Derivative Works a copy of this License; and -You must cause any modified files to carry prominent notices stating that You changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file +Luyten +Copyright (c) 2021, Deathmarine + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +1. Definitions. +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. diff --git a/ReadMe.md b/README.md similarity index 58% rename from ReadMe.md rename to README.md index 11f299c3..41ceaa93 100644 --- a/ReadMe.md +++ b/README.md @@ -5,34 +5,40 @@ Java Decompiler Gui for Procyon Apache License, Version 2.0 [![Releases](https://img.shields.io/github/downloads/deathmarine/luyten/total.svg)](https://github.com/deathmarine/Luyten/releases) + ## Compilation + ***** We use maven to handle our dependencies. -* Install [Maven 3](https://maven.apache.org/download.html) +* Install [Maven 3](https://maven.apache.org/download.cgi) * Clone this repo and: `mvn clean install` ## Screenshot + ![Screen](https://i.imgur.com/phc59W6.png) ### Downloads -[Releases](https://github.com/deathmarine/Luyten/releases/latest) + +[Releases](https://github.com/deathmarine/Luyten/releases/latest) ### Bugs/Suggestions -[Issues](https://github.com/deathmarine/Luyten/issues) +[Issues](https://github.com/deathmarine/Luyten/issues) + +## Powered by -## Powered by ***** ### Procyon -© 2015 Mike Strobel -[https://bitbucket.org/mstrobel/procyon/overview](https://bitbucket.org/mstrobel/procyon/overview) -[Apache License](https://github.com/deathmarine/Luyten/blob/master/src/distfiles/Procyon.License.txt) +© 2021 Mike Strobel +[https://github.com/mstrobel/procyon](https://github.com/mstrobel/procyon) +[Apache License](https://github.com/deathmarine/Luyten/blob/fixes/src/main/resources/distfiles/Procyon.License.txt) ### RSyntaxTextArea -© 2012 Robert Futrell + +© 2021 Robert Futrell [https://bobbylight.github.io/RSyntaxTextArea/](https://bobbylight.github.io/RSyntaxTextArea/) -[All Rights Reserved](https://github.com/deathmarine/Luyten/blob/master/src/distfiles/RSyntaxTextArea.License.txt) +[All Rights Reserved](https://github.com/deathmarine/Luyten/blob/fixes/src/main/resources/distfiles/RSyntaxTextArea.License.txt) diff --git a/src/main/java/us/deathmarine/luyten/FindAllBox.java b/src/main/java/us/deathmarine/luyten/FindAllBox.java index 0f131a4c..8ba55251 100644 --- a/src/main/java/us/deathmarine/luyten/FindAllBox.java +++ b/src/main/java/us/deathmarine/luyten/FindAllBox.java @@ -255,7 +255,8 @@ public void actionPerformed(ActionEvent event) { + "2 additional arguments required for method " + "java/lang/invoke/StringConcatFactory.makeConcatWithConstants, " + "but only 1 specified.")) { - // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to https://bitbucket.org/mstrobel/procyon/issues/336/ + // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to + // https://web.archive.org/web/20200722211732/https://bitbucket.org/mstrobel/procyon/issues/336/ // Searching in a WAR or JAR file could pop-up a lot of error dialogs // for a lot of class files, we simply say nothing here addClassName(entry.getName() + " (search failed due to known " From a0e44a9cb64e188af0f1a61e25fe4139de05f881 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 7 Jan 2022 14:52:22 +0100 Subject: [PATCH 25/26] Forgot it was 2022 already :) --- LICENSE | 2 +- README.md | 4 ++-- pom.xml | 4 ++-- src/main/java/us/deathmarine/luyten/MainMenuBar.java | 4 ++-- src/main/resources/distfiles/Procyon.License.txt | 4 ++-- src/main/resources/distfiles/RSyntaxTextArea.License.txt | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/LICENSE b/LICENSE index e6145367..6a32be67 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ Luyten -Copyright (c) 2021, Deathmarine +Copyright (c) 2022, Deathmarine Apache License Version 2.0, January 2004 diff --git a/README.md b/README.md index 41ceaa93..5acd8759 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,12 @@ We use maven to handle our dependencies. ### Procyon -© 2021 Mike Strobel +© 2022 Mike Strobel [https://github.com/mstrobel/procyon](https://github.com/mstrobel/procyon) [Apache License](https://github.com/deathmarine/Luyten/blob/fixes/src/main/resources/distfiles/Procyon.License.txt) ### RSyntaxTextArea -© 2021 Robert Futrell +© 2022 Robert Futrell [https://bobbylight.github.io/RSyntaxTextArea/](https://bobbylight.github.io/RSyntaxTextArea/) [All Rights Reserved](https://github.com/deathmarine/Luyten/blob/fixes/src/main/resources/distfiles/RSyntaxTextArea.License.txt) diff --git a/pom.xml b/pom.xml index fa983b37..f7c966d9 100644 --- a/pom.xml +++ b/pom.xml @@ -133,7 +133,7 @@ 0.${project.version} 0.${project.version} Java Decompiler - 2021 + 2022 0.${project.version} 0.${project.version} ${project.artifactId} @@ -189,7 +189,7 @@ useJavaXKey="true" workingdirectory="$JAVAROOT" bundleid="${project.groupId}.${project.artifactId}" mainclass="${project.groupId}.${project.artifactId}.LuytenOsx" - version="${project.version}" copyright="2021" + version="${project.version}" copyright="2022" icon="${project.basedir}/src/main/resources/luyten.icns" jvmversion="1.8+" screenmenu="true" antialiasedgraphics="true" highresolutioncapable="true"> diff --git a/src/main/java/us/deathmarine/luyten/MainMenuBar.java b/src/main/java/us/deathmarine/luyten/MainMenuBar.java index 62070a68..7ffd928c 100644 --- a/src/main/java/us/deathmarine/luyten/MainMenuBar.java +++ b/src/main/java/us/deathmarine/luyten/MainMenuBar.java @@ -483,14 +483,14 @@ private void buildHelpMenu(JMenu helpMenu) { link.addMouseListener(new LinkListener(procyon, link)); pane.add(link); pane.add(new JLabel("Version: " + Procyon.version())); - pane.add(new JLabel("(c) 2021 Mike Strobel")); + pane.add(new JLabel("(c) 2022 Mike Strobel")); String rsyntax = "https://github.com/bobbylight/RSyntaxTextArea"; link = new JLabel("" + rsyntax + ""); link.setCursor(new Cursor(Cursor.HAND_CURSOR)); link.addMouseListener(new LinkListener(rsyntax, link)); pane.add(link); pane.add(new JLabel("Version: 3.1.5")); - pane.add(new JLabel("(c) 2021 Robert Futrell")); + pane.add(new JLabel("(c) 2022 Robert Futrell")); pane.add(new JLabel(" ")); JOptionPane.showMessageDialog(null, pane); }); diff --git a/src/main/resources/distfiles/Procyon.License.txt b/src/main/resources/distfiles/Procyon.License.txt index 01a08e08..bb69a347 100644 --- a/src/main/resources/distfiles/Procyon.License.txt +++ b/src/main/resources/distfiles/Procyon.License.txt @@ -1,5 +1,5 @@ Procyon -Copyright (c) 2013, Mike Strobel +Copyright (c) 2022, Mike Strobel Apache License Version 2.0, January 2004 @@ -29,4 +29,4 @@ You may add Your own copyright statement to Your modifications and may provide a 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. diff --git a/src/main/resources/distfiles/RSyntaxTextArea.License.txt b/src/main/resources/distfiles/RSyntaxTextArea.License.txt index 75344190..d1f01ea3 100644 --- a/src/main/resources/distfiles/RSyntaxTextArea.License.txt +++ b/src/main/resources/distfiles/RSyntaxTextArea.License.txt @@ -1,5 +1,5 @@ RSyntaxTextArea -Copyright (c) 2012, Robert Futrell +Copyright (c) 2022, Robert Futrell All rights reserved. Redistribution and use in source and binary forms, with or without @@ -22,4 +22,4 @@ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From be544958e4bf17afab7b8b1590cbdd8f59ff83a8 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Fri, 7 Jan 2022 15:13:58 +0100 Subject: [PATCH 26/26] Fix bug in some Java implementations This doesn't fix Java 17, though --- src/main/java/us/deathmarine/luyten/MainWindow.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/us/deathmarine/luyten/MainWindow.java b/src/main/java/us/deathmarine/luyten/MainWindow.java index 9f7456f5..db4a9961 100644 --- a/src/main/java/us/deathmarine/luyten/MainWindow.java +++ b/src/main/java/us/deathmarine/luyten/MainWindow.java @@ -20,7 +20,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Vector; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; @@ -328,7 +327,7 @@ private static Iterator list(ClassLoader CL) { try { ClassLoader_classes_field = CL_class.getDeclaredField("classes"); ClassLoader_classes_field.setAccessible(true); - Vector classes = (Vector) ClassLoader_classes_field.get(CL); + Iterable classes = (Iterable) ClassLoader_classes_field.get(CL); return classes.iterator(); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { Luyten.showExceptionDialog("Exception!", e);