mirror of
https://github.com/java-decompiler/jd-gui.git
synced 2024-11-23 12:39:52 +00:00
Add capability to search source code on maven.org
This commit is contained in:
parent
6d3a04a8b5
commit
d3d556b39b
@ -50,4 +50,14 @@ public interface API {
|
||||
Map<String, String> getPreferences();
|
||||
|
||||
Collection<Future<Indexes>> getCollectionOfFutureIndexes();
|
||||
|
||||
interface LoadSourceListener {
|
||||
void sourceLoaded(String source);
|
||||
}
|
||||
|
||||
String getSource(Container.Entry entry);
|
||||
|
||||
void loadSource(Container.Entry entry, LoadSourceListener listener);
|
||||
|
||||
File loadSourceFile(Container.Entry entry);
|
||||
}
|
||||
|
21
api/src/main/java/org/jd/gui/spi/SourceLoader.java
Normal file
21
api/src/main/java/org/jd/gui/spi/SourceLoader.java
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2019 Emmanuel Dupuy.
|
||||
* This project is distributed under the GPLv3 license.
|
||||
* This is a Copyleft license that gives the user the right to use,
|
||||
* copy and modify the code freely for non-commercial purposes.
|
||||
*/
|
||||
|
||||
package org.jd.gui.spi;
|
||||
|
||||
import org.jd.gui.api.API;
|
||||
import org.jd.gui.api.model.Container;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface SourceLoader {
|
||||
String getSource(API api, Container.Entry entry);
|
||||
|
||||
String loadSource(API api, Container.Entry entry);
|
||||
|
||||
File loadSourceFile(API api, Container.Entry entry);
|
||||
}
|
@ -21,6 +21,7 @@ import org.jd.gui.service.mainpanel.PanelFactoryService;
|
||||
import org.jd.gui.service.pastehandler.PasteHandlerService;
|
||||
import org.jd.gui.service.platform.PlatformService;
|
||||
import org.jd.gui.service.preferencespanel.PreferencesPanelService;
|
||||
import org.jd.gui.service.sourceloader.SourceLoaderService;
|
||||
import org.jd.gui.service.sourcesaver.SourceSaverService;
|
||||
import org.jd.gui.service.treenode.TreeNodeFactoryService;
|
||||
import org.jd.gui.service.type.TypeFactoryService;
|
||||
@ -64,6 +65,7 @@ public class MainController implements API {
|
||||
protected SaveAllSourcesController saveAllSourcesController;
|
||||
protected SelectLocationController selectLocationController;
|
||||
protected AboutController aboutController;
|
||||
protected SourceLoaderService sourceLoaderService;
|
||||
|
||||
protected History history = new History();
|
||||
protected JComponent currentPage = null;
|
||||
@ -147,6 +149,7 @@ public class MainController implements API {
|
||||
preferencesController = new PreferencesController(configuration, mainFrame, PreferencesPanelService.getInstance().getProviders());
|
||||
selectLocationController = new SelectLocationController(MainController.this, mainFrame);
|
||||
aboutController = new AboutController(mainFrame);
|
||||
sourceLoaderService = new SourceLoaderService();
|
||||
// Add listeners
|
||||
mainFrame.addComponentListener(new MainFrameListener(configuration));
|
||||
// Set drop files transfer handler
|
||||
@ -677,4 +680,25 @@ public class MainController implements API {
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSource(Container.Entry entry) {
|
||||
return sourceLoaderService.getSource(this, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadSource(Container.Entry entry, LoadSourceListener listener) {
|
||||
executor.execute(() -> {
|
||||
String source = sourceLoaderService.loadSource(this, entry);
|
||||
|
||||
if ((source != null) && !source.isEmpty()) {
|
||||
listener.sourceLoaded(source);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public File loadSourceFile(Container.Entry entry) {
|
||||
return sourceLoaderService.getSourceFile(this, entry);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2019 Emmanuel Dupuy.
|
||||
* This project is distributed under the GPLv3 license.
|
||||
* This is a Copyleft license that gives the user the right to use,
|
||||
* copy and modify the code freely for non-commercial purposes.
|
||||
*/
|
||||
|
||||
package org.jd.gui.service.sourceloader;
|
||||
|
||||
import org.jd.gui.api.API;
|
||||
import org.jd.gui.api.model.Container;
|
||||
import org.jd.gui.service.extension.ExtensionService;
|
||||
import org.jd.gui.spi.SourceLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
|
||||
public class SourceLoaderService {
|
||||
protected static final SourceLoaderService SOURCE_LOADER_SERVICE = new SourceLoaderService();
|
||||
|
||||
public static SourceLoaderService getInstance() { return SOURCE_LOADER_SERVICE; }
|
||||
|
||||
protected Collection<SourceLoader> providers = ExtensionService.getInstance().load(SourceLoader.class);
|
||||
|
||||
public String getSource(API api, Container.Entry entry) {
|
||||
for (SourceLoader provider : providers) {
|
||||
String source = provider.getSource(api, entry);
|
||||
|
||||
if ((source != null) && !source.isEmpty()) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String loadSource(API api, Container.Entry entry) {
|
||||
for (SourceLoader provider : providers) {
|
||||
String source = provider.loadSource(api, entry);
|
||||
|
||||
if ((source != null) && !source.isEmpty()) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public File getSourceFile(API api, Container.Entry entry) {
|
||||
for (SourceLoader provider : providers) {
|
||||
File file = provider.loadSourceFile(api, entry);
|
||||
|
||||
if (file != null) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2019 Emmanuel Dupuy.
|
||||
* This project is distributed under the GPLv3 license.
|
||||
* This is a Copyleft license that gives the user the right to use,
|
||||
* copy and modify the code freely for non-commercial purposes.
|
||||
*/
|
||||
|
||||
package org.jd.gui.service.preferencespanel;
|
||||
|
||||
import org.jd.gui.spi.PreferencesPanel;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MavenOrgSourceLoaderPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener, ActionListener {
|
||||
public static final String ACTIVATED = "MavenOrgSourceLoaderPreferencesProvider.activated";
|
||||
public static final String FILTERS = "MavenOrgSourceLoaderPreferencesProvider.filters";
|
||||
|
||||
public static final String DEFAULT_FILTERS_VALUE =
|
||||
"+org +com.google +com.ibm +com.jcraft +com.springsource +com.sun -com +java +javax +sun +sunw " +
|
||||
"+spring +springframework +springmodules +tomcat +maven";
|
||||
|
||||
protected static final Pattern CONTROL_PATTERN = Pattern.compile("([+-][a-zA-Z_0-9$_.]+(\\s+[+-][a-zA-Z_0-9$_.]+)*)?\\s*");
|
||||
|
||||
protected JCheckBox enableCheckBox;
|
||||
protected JTextArea filtersTextArea;
|
||||
protected JButton resetButton;
|
||||
protected Color errorBackgroundColor = Color.RED;
|
||||
protected Color defaultBackgroundColor;
|
||||
|
||||
protected PreferencesPanel.PreferencesPanelChangeListener listener;
|
||||
|
||||
public MavenOrgSourceLoaderPreferencesProvider() {
|
||||
super(new BorderLayout());
|
||||
|
||||
enableCheckBox = new JCheckBox("Search source code on maven.org for:");
|
||||
enableCheckBox.addActionListener(this);
|
||||
|
||||
filtersTextArea = new JTextArea();
|
||||
filtersTextArea.setFont(getFont());
|
||||
filtersTextArea.setLineWrap(true);
|
||||
filtersTextArea.getDocument().addDocumentListener(this);
|
||||
defaultBackgroundColor = filtersTextArea.getBackground();
|
||||
|
||||
JComponent spacer = new JComponent() {};
|
||||
JScrollPane scrollPane = new JScrollPane(filtersTextArea);
|
||||
|
||||
String osName = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if (osName.contains("windows")) {
|
||||
spacer.setPreferredSize(new Dimension(22, -1));
|
||||
scrollPane.setPreferredSize(new Dimension(-1, 50));
|
||||
} else if (osName.contains("mac os")) {
|
||||
spacer.setPreferredSize(new Dimension(28, -1));
|
||||
scrollPane.setPreferredSize(new Dimension(-1, 56));
|
||||
} else {
|
||||
spacer.setPreferredSize(new Dimension(22, -1));
|
||||
scrollPane.setPreferredSize(new Dimension(-1, 56));
|
||||
}
|
||||
|
||||
resetButton = new JButton("Reset");
|
||||
resetButton.addActionListener(this);
|
||||
|
||||
JPanel southPanel = new JPanel(new BorderLayout());
|
||||
southPanel.add(resetButton, BorderLayout.EAST);
|
||||
|
||||
add(enableCheckBox, BorderLayout.NORTH);
|
||||
add(spacer, BorderLayout.WEST);
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
add(southPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
// --- PreferencesPanel --- //
|
||||
@Override public String getPreferencesGroupTitle() { return "Source loader"; }
|
||||
@Override public String getPreferencesPanelTitle() { return "maven.org"; }
|
||||
@Override public JComponent getPanel() { return this; }
|
||||
|
||||
@Override
|
||||
public void init(Color errorBackgroundColor) {
|
||||
this.errorBackgroundColor = errorBackgroundColor;
|
||||
}
|
||||
|
||||
@Override public boolean isActivated() { return true; }
|
||||
|
||||
@Override
|
||||
public void loadPreferences(Map<String, String> preferences) {
|
||||
boolean enabled = !"false".equals(preferences.get(ACTIVATED));
|
||||
|
||||
enableCheckBox.setSelected(enabled);
|
||||
filtersTextArea.setEnabled(enabled);
|
||||
resetButton.setEnabled(enabled);
|
||||
|
||||
String filters = preferences.get(FILTERS);
|
||||
|
||||
if ((filters == null) || filters.isEmpty()) {
|
||||
filtersTextArea.setText(DEFAULT_FILTERS_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePreferences(Map<String, String> preferences) {
|
||||
preferences.put(ACTIVATED, Boolean.toString(enableCheckBox.isSelected()));
|
||||
preferences.put(FILTERS, filtersTextArea.getText().trim());
|
||||
}
|
||||
|
||||
@Override public boolean arePreferencesValid() {
|
||||
return CONTROL_PATTERN.matcher(filtersTextArea.getText()).matches();
|
||||
}
|
||||
|
||||
@Override public void addPreferencesChangeListener(PreferencesPanelChangeListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
||||
// --- DocumentListener --- //
|
||||
@Override public void insertUpdate(DocumentEvent e) { onTextChange(); }
|
||||
@Override public void removeUpdate(DocumentEvent e) { onTextChange(); }
|
||||
@Override public void changedUpdate(DocumentEvent e) { onTextChange(); }
|
||||
|
||||
protected void onTextChange() {
|
||||
filtersTextArea.setBackground(arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor);
|
||||
|
||||
if (listener != null) {
|
||||
listener.preferencesPanelChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
// --- ActionListener --- //
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (e.getSource() == enableCheckBox) {
|
||||
boolean enabled = enableCheckBox.isSelected();
|
||||
filtersTextArea.setEnabled(enabled);
|
||||
resetButton.setEnabled(enabled);
|
||||
} else {
|
||||
// Reset button
|
||||
filtersTextArea.setText(DEFAULT_FILTERS_VALUE);
|
||||
filtersTextArea.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2019 Emmanuel Dupuy.
|
||||
* This project is distributed under the GPLv3 license.
|
||||
* This is a Copyleft license that gives the user the right to use,
|
||||
* copy and modify the code freely for non-commercial purposes.
|
||||
*/
|
||||
|
||||
package org.jd.gui.service.sourceloader;
|
||||
|
||||
import org.jd.gui.api.API;
|
||||
import org.jd.gui.api.model.Container;
|
||||
import org.jd.gui.service.preferencespanel.MavenOrgSourceLoaderPreferencesProvider;
|
||||
import org.jd.gui.spi.PreferencesPanel;
|
||||
import org.jd.gui.spi.SourceLoader;
|
||||
import org.jd.gui.util.exception.ExceptionUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLStreamConstants;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.transform.Source;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class MavenOrgSourceLoaderProvider implements SourceLoader {
|
||||
protected static final String MAVENORG_SEARCH_URL_PREFIX = "https://search.maven.org/solrsearch/select?q=1:%22";
|
||||
protected static final String MAVENORG_SEARCH_URL_SUFFIX = "%22&rows=20&wt=xml";
|
||||
|
||||
protected static final String MAVENORG_LOAD_URL_PREFIX = "https://search.maven.org/classic/remotecontent?filepath=";
|
||||
protected static final String MAVENORG_LOAD_URL_SUFFIX = "-sources.jar";
|
||||
|
||||
protected HashSet<Container.Entry> failed = new HashSet<>();
|
||||
protected HashMap<Container.Entry, File> cache = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public String getSource(API api, Container.Entry entry) {
|
||||
if (isActivated(api)) {
|
||||
String filters = api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.FILTERS);
|
||||
|
||||
if ((filters == null) || filters.isEmpty()) {
|
||||
filters = MavenOrgSourceLoaderPreferencesProvider.DEFAULT_FILTERS_VALUE;
|
||||
}
|
||||
|
||||
if (accepted(filters, entry.getPath())) {
|
||||
return search(entry, cache.get(entry.getContainer().getRoot().getParent()));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String loadSource(API api, Container.Entry entry) {
|
||||
boolean activated = !"false".equals(api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.ACTIVATED));
|
||||
|
||||
if (isActivated(api)) {
|
||||
String filters = api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.FILTERS);
|
||||
|
||||
if ((filters == null) || filters.isEmpty()) {
|
||||
filters = MavenOrgSourceLoaderPreferencesProvider.DEFAULT_FILTERS_VALUE;
|
||||
}
|
||||
|
||||
if (accepted(filters, entry.getPath())) {
|
||||
return search(entry, load(entry.getContainer().getRoot().getParent()));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File loadSourceFile(API api, Container.Entry entry) {
|
||||
return isActivated(api) ? load(entry) : null;
|
||||
}
|
||||
|
||||
private static boolean isActivated(API api) {
|
||||
return !"false".equals(api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.ACTIVATED));
|
||||
}
|
||||
|
||||
protected String search(Container.Entry entry, File file) {
|
||||
if (file != null) {
|
||||
String fileName = file.toString().toLowerCase();
|
||||
|
||||
if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) {
|
||||
byte[] buffer = new byte[1024 * 2];
|
||||
|
||||
try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(file)))) {
|
||||
ZipEntry ze = zis.getNextEntry();
|
||||
String name = entry.getPath();
|
||||
|
||||
name = name.substring(0, name.length()-6) + ".java"; // 6 = ".class".length()
|
||||
|
||||
while (ze != null) {
|
||||
if (ze.getName().equals(name)) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int read = zis.read(buffer);
|
||||
|
||||
while (read > 0) {
|
||||
out.write(buffer, 0, read);
|
||||
read = zis.read(buffer);
|
||||
}
|
||||
|
||||
return new String(out.toByteArray(), "UTF-8");
|
||||
}
|
||||
|
||||
ze = zis.getNextEntry();
|
||||
}
|
||||
|
||||
zis.closeEntry();
|
||||
} catch (IOException e) {
|
||||
assert ExceptionUtil.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected File load(Container.Entry entry) {
|
||||
if (cache.containsKey(entry)) {
|
||||
return cache.get(entry);
|
||||
}
|
||||
|
||||
if (!entry.isDirectory() && !failed.contains(entry)) {
|
||||
try {
|
||||
// SHA-1
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
|
||||
byte[] buffer = new byte[1024 * 2];
|
||||
|
||||
try (DigestInputStream is = new DigestInputStream(entry.getInputStream(), messageDigest)) {
|
||||
while (is.read(buffer) > -1);
|
||||
}
|
||||
|
||||
byte[] array = messageDigest.digest();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (byte b : array) {
|
||||
sb.append(hexa((b & 255) >> 4));
|
||||
sb.append(hexa(b & 15));
|
||||
}
|
||||
|
||||
String sha1 = sb.toString();
|
||||
|
||||
// Search artifact on maven.org
|
||||
URL searchUrl = new URL(MAVENORG_SEARCH_URL_PREFIX + sha1 + MAVENORG_SEARCH_URL_SUFFIX);
|
||||
boolean sourceAvailable = false;
|
||||
String id = null;
|
||||
|
||||
try (InputStream is = searchUrl.openStream()) {
|
||||
XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(is);
|
||||
String name = "";
|
||||
|
||||
while (reader.hasNext()) {
|
||||
switch (reader.next()) {
|
||||
case XMLStreamConstants.START_ELEMENT:
|
||||
if ("str".equals(reader.getLocalName())) {
|
||||
if ("id".equals(reader.getAttributeValue(null, "name"))) {
|
||||
name = "id";
|
||||
} else {
|
||||
name = "str";
|
||||
}
|
||||
} else {
|
||||
name = "";
|
||||
}
|
||||
break;
|
||||
case XMLStreamConstants.CHARACTERS:
|
||||
switch (name) {
|
||||
case "id":
|
||||
id = reader.getText().trim();
|
||||
break;
|
||||
case "str":
|
||||
sourceAvailable |= "-sources.jar".equals(reader.getText().trim());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reader.close();
|
||||
}
|
||||
|
||||
if (sourceAvailable == false) {
|
||||
failed.add(entry);
|
||||
} else {
|
||||
// Load source
|
||||
int index1 = id.indexOf(':');
|
||||
int index2 = id.lastIndexOf(':');
|
||||
String groupId = id.substring(0, index1);
|
||||
String artifactId = id.substring(index1+1, index2);
|
||||
String version = id.substring(index2+1);
|
||||
String filePath = groupId.replace('.', '/') + '/' + artifactId + '/' + version + '/' + artifactId + '-' + version;
|
||||
URL loadUrl = new URL(MAVENORG_LOAD_URL_PREFIX + filePath + MAVENORG_LOAD_URL_SUFFIX);
|
||||
File tmpFile = File.createTempFile("jd-gui.tmp.", '.' + artifactId + '-' + version + "-sources.jar");
|
||||
|
||||
tmpFile.delete();
|
||||
tmpFile.deleteOnExit();
|
||||
|
||||
try (InputStream is = new BufferedInputStream(loadUrl.openStream()); OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile))) {
|
||||
int readed = is.read(buffer);
|
||||
while (readed > 0) {
|
||||
os.write(buffer, 0, readed);
|
||||
readed = is.read(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
cache.put(entry, tmpFile);
|
||||
return tmpFile;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
assert ExceptionUtil.printStackTrace(e);
|
||||
failed.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static char hexa(int i) { return (char)( (i <= 9) ? ('0' + i) : (('a' - 10) + i) ); }
|
||||
|
||||
protected boolean accepted(String filters, String path) {
|
||||
// 'filters' example : '+org +com.google +com.ibm +com.jcraft +com.springsource +com.sun -com +java +javax +sun +sunw'
|
||||
StringTokenizer tokenizer = new StringTokenizer(filters);
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String filter = tokenizer.nextToken();
|
||||
|
||||
if (filter.length() > 1) {
|
||||
String prefix = filter.substring(1).replace('.', '/');
|
||||
|
||||
if (prefix.charAt(prefix.length() - 1) != '/') {
|
||||
prefix += '/';
|
||||
}
|
||||
|
||||
if (path.startsWith(prefix)) {
|
||||
return (filter.charAt(0) == '+');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -14,7 +14,10 @@ import org.jd.gui.util.exception.ExceptionUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ZipFileSourceSaverProvider extends DirectorySourceSaverProvider {
|
||||
@ -32,25 +35,31 @@ public class ZipFileSourceSaverProvider extends DirectorySourceSaverProvider {
|
||||
Files.createDirectories(parentPath);
|
||||
}
|
||||
|
||||
File tmpFile = File.createTempFile("jd-gui.", ".tmp.zip");
|
||||
tmpFile.delete();
|
||||
File tmpSourceFile = api.loadSourceFile(entry);
|
||||
|
||||
URI tmpFileUri = tmpFile.toURI();
|
||||
URI tmpArchiveUri = new URI("jar:" + tmpFileUri.getScheme(), tmpFileUri.getHost(), tmpFileUri.getPath() + "!/", null);
|
||||
if (tmpSourceFile != null) {
|
||||
Files.copy(tmpSourceFile.toPath(), path);
|
||||
} else {
|
||||
File tmpFile = File.createTempFile("jd-gui.", ".tmp.zip");
|
||||
|
||||
HashMap<String,String> env = new HashMap<>();
|
||||
env.put("create", "true");
|
||||
tmpFile.delete();
|
||||
tmpFile.deleteOnExit();
|
||||
|
||||
FileSystem tmpArchiveFs = FileSystems.newFileSystem(tmpArchiveUri, env);
|
||||
Path tmpArchiveRootPath = tmpArchiveFs.getPath("/");
|
||||
URI tmpFileUri = tmpFile.toURI();
|
||||
URI tmpArchiveUri = new URI("jar:" + tmpFileUri.getScheme(), tmpFileUri.getHost(), tmpFileUri.getPath() + "!/", null);
|
||||
|
||||
saveContent(api, controller, listener, tmpArchiveRootPath, tmpArchiveRootPath, entry);
|
||||
HashMap<String, String> env = new HashMap<>();
|
||||
env.put("create", "true");
|
||||
|
||||
tmpArchiveFs.close();
|
||||
FileSystem tmpArchiveFs = FileSystems.newFileSystem(tmpArchiveUri, env);
|
||||
Path tmpArchiveRootPath = tmpArchiveFs.getPath("/");
|
||||
|
||||
Path tmpPath = Paths.get(tmpFile.getAbsolutePath());
|
||||
saveContent(api, controller, listener, tmpArchiveRootPath, tmpArchiveRootPath, entry);
|
||||
|
||||
Files.move(tmpPath, path);
|
||||
tmpArchiveFs.close();
|
||||
|
||||
Files.move(tmpFile.toPath(), path);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
assert ExceptionUtil.printStackTrace(e);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import org.jd.gui.api.feature.ContainerEntryGettable;
|
||||
import org.jd.gui.api.feature.UriGettable;
|
||||
import org.jd.gui.api.model.Container;
|
||||
import org.jd.gui.util.exception.ExceptionUtil;
|
||||
import org.jd.gui.view.component.ClassFilePage;
|
||||
import org.jd.gui.view.component.DynamicPage;
|
||||
import org.jd.gui.view.data.TreeNodeBean;
|
||||
|
||||
import javax.swing.*;
|
||||
@ -29,7 +29,7 @@ public class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFa
|
||||
static {
|
||||
// Early class loading
|
||||
try {
|
||||
Class.forName(ClassFilePage.class.getName());
|
||||
Class.forName(DynamicPage.class.getName());
|
||||
} catch (Exception e) {
|
||||
assert ExceptionUtil.printStackTrace(e);
|
||||
}
|
||||
@ -49,7 +49,7 @@ public class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFa
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends JComponent & UriGettable> T makePage(API a, Container.Entry e) {
|
||||
return (T)new ClassFilePage(a, e);
|
||||
return (T)new DynamicPage(a, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -143,6 +143,10 @@ public class AbstractTextPage extends JPanel implements LineNumberNavigable, Con
|
||||
|
||||
public String getText() { return textArea.getText(); }
|
||||
|
||||
public JScrollPane getScrollPane() {
|
||||
return scrollPane;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
textArea.setText(text);
|
||||
textArea.setCaretPosition(0);
|
||||
|
@ -7,7 +7,10 @@
|
||||
|
||||
package org.jd.gui.view.component;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaUI;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
|
||||
import org.fife.ui.rsyntaxtextarea.folding.Fold;
|
||||
import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
|
||||
import org.fife.ui.rtextarea.Gutter;
|
||||
@ -58,6 +61,14 @@ public abstract class CustomLineNumbersPage extends HyperlinkPage {
|
||||
}
|
||||
}
|
||||
|
||||
public void initLineNumbers(int maxLineNumber) {
|
||||
setMaxLineNumber(maxLineNumber);
|
||||
|
||||
for (int i=1; i<=maxLineNumber; i++) {
|
||||
lineNumberMap[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
public void setLineNumber(int textAreaLineNumber, int originalLineNumber) {
|
||||
if (originalLineNumber > 0) {
|
||||
setMaxLineNumber(textAreaLineNumber);
|
||||
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2019 Emmanuel Dupuy.
|
||||
* This project is distributed under the GPLv3 license.
|
||||
* This is a Copyleft license that gives the user the right to use,
|
||||
* copy and modify the code freely for non-commercial purposes.
|
||||
*/
|
||||
|
||||
package org.jd.gui.view.component;
|
||||
|
||||
import org.jd.gui.api.API;
|
||||
import org.jd.gui.api.feature.*;
|
||||
import org.jd.gui.api.model.Container;
|
||||
import org.jd.gui.api.model.Indexes;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class DynamicPage
|
||||
extends JPanel
|
||||
implements ContentCopyable, ContentSavable, ContentSearchable, ContentSelectable, FocusedTypeGettable,
|
||||
IndexesChangeListener, LineNumberNavigable, PreferencesChangeListener, UriGettable, UriOpenable,
|
||||
API.LoadSourceListener
|
||||
{
|
||||
protected API api;
|
||||
protected Container.Entry entry;
|
||||
protected TypePage page;
|
||||
protected URI lastOpenedUri;
|
||||
protected Collection<Future<Indexes>> collectionOfFutureIndexes;
|
||||
|
||||
public DynamicPage(API api, Container.Entry entry) {
|
||||
super(new BorderLayout());
|
||||
this.api = api;
|
||||
this.entry = entry;
|
||||
|
||||
String source = api.getSource(entry);
|
||||
|
||||
if (source == null) {
|
||||
// Display the decompiled source code
|
||||
add(page = new ClassFilePage(api, entry));
|
||||
// Try to load source in background
|
||||
api.loadSource(entry, this);
|
||||
} else {
|
||||
// Display original source code
|
||||
add(page = new JavaFilePage(api, new DelegatedEntry(entry, source)));
|
||||
}
|
||||
}
|
||||
|
||||
// --- ContentCopyable --- //
|
||||
@Override public void copy() { page.copy(); }
|
||||
|
||||
// --- ContentSavable --- //
|
||||
@Override public String getFileName() { return page.getFileName(); }
|
||||
@Override public void save(API api, OutputStream outputStream) { page.save(api, outputStream); }
|
||||
|
||||
// --- ContentSearchable --- //
|
||||
@Override public boolean highlightText(String text, boolean caseSensitive) { return page.highlightText(text, caseSensitive); }
|
||||
@Override public void findNext(String text, boolean caseSensitive) { page.findNext(text, caseSensitive); }
|
||||
@Override public void findPrevious(String text, boolean caseSensitive) { page.findPrevious(text, caseSensitive); }
|
||||
|
||||
// --- ContentSelectable --- //
|
||||
@Override public void selectAll() { page.selectAll(); }
|
||||
|
||||
// --- FocusedTypeGettable --- //
|
||||
@Override public String getFocusedTypeName() { return page.getFocusedTypeName(); }
|
||||
|
||||
// --- ContainerEntryGettable --- //
|
||||
@Override public Container.Entry getEntry() { return entry; }
|
||||
|
||||
// --- IndexesChangeListener --- //
|
||||
@Override public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {
|
||||
this.collectionOfFutureIndexes = collectionOfFutureIndexes;
|
||||
page.indexesChanged(collectionOfFutureIndexes);
|
||||
}
|
||||
|
||||
// --- LineNumberNavigable --- //
|
||||
@Override public int getMaximumLineNumber() { return page.getMaximumLineNumber(); }
|
||||
@Override public void goToLineNumber(int lineNumber) { page.goToLineNumber(lineNumber); }
|
||||
@Override public boolean checkLineNumber(int lineNumber) { return page.checkLineNumber(lineNumber); }
|
||||
|
||||
// --- PreferencesChangeListener --- //
|
||||
@Override public void preferencesChanged(Map<String, String> preferences) { page.preferencesChanged(preferences); }
|
||||
|
||||
// --- UriGettable --- //
|
||||
@Override public URI getUri() { return entry.getUri(); }
|
||||
|
||||
// --- UriOpenable --- //
|
||||
@Override public boolean openUri(URI uri) { return page.openUri(lastOpenedUri = uri); }
|
||||
|
||||
// --- LoadSourceListener --- //
|
||||
@Override public void sourceLoaded(String source) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
// Replace the decompiled source code by the original
|
||||
Point viewPosition = page.getScrollPane().getViewport().getViewPosition();
|
||||
|
||||
removeAll();
|
||||
add(page = new JavaFilePage(api, new DelegatedEntry(entry, source)));
|
||||
page.getScrollPane().getViewport().setViewPosition(viewPosition);
|
||||
|
||||
if (lastOpenedUri != null) {
|
||||
page.openUri(lastOpenedUri);
|
||||
}
|
||||
|
||||
if (collectionOfFutureIndexes != null) {
|
||||
page.indexesChanged(collectionOfFutureIndexes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected static class DelegatedEntry implements Container.Entry {
|
||||
protected Container.Entry entry;
|
||||
protected String source;
|
||||
|
||||
DelegatedEntry(Container.Entry entry, String source) {
|
||||
this.entry = entry;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override public Container getContainer() { return entry.getContainer(); }
|
||||
@Override public Container.Entry getParent() { return entry.getParent(); }
|
||||
@Override public URI getUri() { return entry.getUri(); }
|
||||
@Override public String getPath() { return entry.getPath(); }
|
||||
@Override public boolean isDirectory() { return entry.isDirectory(); }
|
||||
@Override public long length() { return entry.length(); }
|
||||
@Override public InputStream getInputStream() { return new ByteArrayInputStream(source.getBytes()); }
|
||||
@Override public Collection<Container.Entry> getChildren() { return entry.getChildren(); }
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ public class JavaFilePage extends TypePage {
|
||||
public JavaFilePage(API api, Container.Entry entry) {
|
||||
super(api, entry);
|
||||
// Load content file
|
||||
String text = TextReader.getText(entry.getInputStream()).replace("\r\n", "\n").replace("\r", "\n");
|
||||
String text = TextReader.getText(entry.getInputStream()).replace("\r\n", "\n").replace('\r', '\n');
|
||||
// Parse
|
||||
DeclarationListener declarationListener = new DeclarationListener(entry);
|
||||
ReferenceListener referenceListener = new ReferenceListener(entry);
|
||||
@ -40,9 +40,14 @@ public class JavaFilePage extends TypePage {
|
||||
referenceListener.init(declarationListener);
|
||||
ANTLRJavaParser.parse(new ANTLRInputStream(text), referenceListener);
|
||||
// Display
|
||||
initLineNumbers(getMaxLineNumber(text));
|
||||
setText(text);
|
||||
}
|
||||
|
||||
private static int getMaxLineNumber(String text) {
|
||||
return text.length() - text.replace("\n", "").length();
|
||||
}
|
||||
|
||||
public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVA; }
|
||||
|
||||
// --- ContentSavable --- //
|
||||
|
@ -2,3 +2,4 @@ org.jd.gui.service.preferencespanel.DirectoryIndexerPreferencesProvider
|
||||
org.jd.gui.service.preferencespanel.ClassFileSaverPreferencesProvider
|
||||
org.jd.gui.service.preferencespanel.ClassFileDecompilerPreferencesProvider
|
||||
org.jd.gui.service.preferencespanel.ViewerPreferencesProvider
|
||||
org.jd.gui.service.preferencespanel.MavenOrgSourceLoaderPreferencesProvider
|
||||
|
@ -0,0 +1 @@
|
||||
org.jd.gui.service.sourceloader.MavenOrgSourceLoaderProvider
|
Loading…
Reference in New Issue
Block a user