Add Java 9+ module support

This commit is contained in:
emmanue1 2019-06-01 18:40:10 +02:00
parent 25b7e53c02
commit fbc5b14d66
14 changed files with 355 additions and 89 deletions

View File

@ -43,7 +43,7 @@ public class TreeNodeFactoryService {
}
protected TreeNodeFactory get(String containerType, Container.Entry entry) {
String path = entry.getPath();;
String path = entry.getPath();
String type = entry.isDirectory() ? "dir" : "file";
String prefix = containerType + ':' + type + ':';
TreeNodeFactory factory = null;

View File

@ -26,7 +26,7 @@ public class JmodContainerFactoryProvider implements ContainerFactory {
if (rootPath.toUri().toString().toLowerCase().endsWith(".jmod!/")) {
return true;
} else {
// Extension: accept uncompressed WAR file containing a folder 'WEB-INF'
// Extension: accept uncompressed JMOD file containing a folder 'classes'
try {
return rootPath.getFileSystem().provider().getScheme().equals("file") && Files.exists(rootPath.resolve("classes"));
} catch (InvalidPathException e) {

View File

@ -44,8 +44,9 @@ public abstract class AbstractFileLoaderProvider implements FileLoader {
TreeNodeFactory treeNodeFactory = api.getTreeNodeFactory(parentEntry);
Object data = (treeNodeFactory != null) ? treeNodeFactory.make(api, parentEntry).getUserObject() : null;
Icon icon = (data instanceof TreeNodeData) ? ((TreeNodeData)data).getIcon() : null;
String location = file.getPath();
api.addPanel(file.getName(), icon, "Location: " + file.getAbsolutePath(), mainPanel);
api.addPanel(file.getName(), icon, "Location: " + location, mainPanel);
return mainPanel;
}
}

View File

@ -122,43 +122,47 @@ public class JavaFileIndexerProvider extends AbstractIndexerProvider {
protected void enterTypeDeclaration(ParserRuleContext ctx) {
// Add type declaration
String typeName = ctx.getToken(JavaParser.Identifier, 0).getText();
int length = sbTypeDeclaration.length();
TerminalNode identifier = ctx.getToken(JavaParser.Identifier, 0);
if ((length == 0) || (sbTypeDeclaration.charAt(length-1) == '/')) {
sbTypeDeclaration.append(typeName);
} else {
sbTypeDeclaration.append('$').append(typeName);
}
if (identifier != null) {
String typeName = identifier.getText();
int length = sbTypeDeclaration.length();
String internalTypeName = sbTypeDeclaration.toString();
typeDeclarationSet.add(internalTypeName);
nameToInternalTypeName.put(typeName, internalTypeName);
HashSet<String> superInternalTypeNameSet = new HashSet<>();
// Add super type reference
JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0);
if (superType != null) {
String superQualifiedTypeName = resolveInternalTypeName(superType.classOrInterfaceType().Identifier());
if (superQualifiedTypeName.charAt(0) != '*')
superInternalTypeNameSet.add(superQualifiedTypeName);
}
// Add implementation references
JavaParser.TypeListContext superInterfaces = ctx.getRuleContext(JavaParser.TypeListContext.class, 0);
if (superInterfaces != null) {
for (JavaParser.TypeContext superInterface : superInterfaces.type()) {
String superQualifiedInterfaceName = resolveInternalTypeName(superInterface.classOrInterfaceType().Identifier());
if (superQualifiedInterfaceName.charAt(0) != '*')
superInternalTypeNameSet.add(superQualifiedInterfaceName);
if ((length == 0) || (sbTypeDeclaration.charAt(length - 1) == '/')) {
sbTypeDeclaration.append(typeName);
} else {
sbTypeDeclaration.append('$').append(typeName);
}
}
if (! superInternalTypeNameSet.isEmpty()) {
superTypeNamesMap.put(internalTypeName, superInternalTypeNameSet);
String internalTypeName = sbTypeDeclaration.toString();
typeDeclarationSet.add(internalTypeName);
nameToInternalTypeName.put(typeName, internalTypeName);
HashSet<String> superInternalTypeNameSet = new HashSet<>();
// Add super type reference
JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0);
if (superType != null) {
String superQualifiedTypeName = resolveInternalTypeName(superType.classOrInterfaceType().Identifier());
if (superQualifiedTypeName.charAt(0) != '*')
superInternalTypeNameSet.add(superQualifiedTypeName);
}
// Add implementation references
JavaParser.TypeListContext superInterfaces = ctx.getRuleContext(JavaParser.TypeListContext.class, 0);
if (superInterfaces != null) {
for (JavaParser.TypeContext superInterface : superInterfaces.type()) {
String superQualifiedInterfaceName = resolveInternalTypeName(superInterface.classOrInterfaceType().Identifier());
if (superQualifiedInterfaceName.charAt(0) != '*')
superInternalTypeNameSet.add(superQualifiedInterfaceName);
}
}
if (!superInternalTypeNameSet.isEmpty()) {
superTypeNamesMap.put(internalTypeName, superInternalTypeNameSet);
}
}
}
@ -197,19 +201,31 @@ public class JavaFileIndexerProvider extends AbstractIndexerProvider {
public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) {
for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) {
String name = declaration.variableDeclaratorId().Identifier().getText();
fieldDeclarationSet.add(name);
TerminalNode identifier = declaration.variableDeclaratorId().Identifier();
if (identifier != null) {
String name = identifier.getText();
fieldDeclarationSet.add(name);
}
}
}
public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) {
String name = ctx.Identifier().getText();
methodDeclarationSet.add(name);
TerminalNode identifier = ctx.Identifier();
if (identifier != null) {
String name = identifier.getText();
methodDeclarationSet.add(name);
}
}
public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) {
String name = ctx.Identifier().getText();
methodDeclarationSet.add(name);
TerminalNode identifier = ctx.Identifier();
if (identifier != null) {
String name = identifier.getText();
methodDeclarationSet.add(name);
}
}
public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) {
@ -282,15 +298,18 @@ public class JavaFileIndexerProvider extends AbstractIndexerProvider {
protected TerminalNode getRightTerminalNode(ParseTree pt) {
if (pt instanceof ParserRuleContext) {
List<ParseTree> children = ((ParserRuleContext)pt).children;
int size = children.size();
if (size > 0) {
ParseTree last = children.get(size - 1);
if (children != null) {
int size = children.size();
if (last instanceof TerminalNode) {
return (TerminalNode) last;
} else {
return getRightTerminalNode(last);
if (size > 0) {
ParseTree last = children.get(size - 1);
if (last instanceof TerminalNode) {
return (TerminalNode) last;
} else {
return getRightTerminalNode(last);
}
}
}
}

View File

@ -21,6 +21,7 @@ import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Pattern;
public class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider {
protected static final ImageIcon CLASS_FILE_ICON = new ImageIcon(ClassFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/classf_obj.png"));
@ -37,6 +38,15 @@ public class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFa
@Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); }
@Override
public Pattern getPathPattern() {
if (externalPathPattern == null) {
return Pattern.compile("^((?!module-info\\.class).)*$");
} else {
return externalPathPattern;
}
}
@Override
@SuppressWarnings("unchecked")
public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {

View File

@ -43,10 +43,10 @@ public class JavaFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFac
@Override
public String makeTip(API api, Container.Entry entry) {
File file = new File(entry.getContainer().getRoot().getUri());
String location = new File(entry.getUri()).getPath();
StringBuilder tip = new StringBuilder("<html>Location: ");
tip.append(file.getPath());
tip.append(location);
tip.append("</html>");
return tip.toString();

View File

@ -0,0 +1,66 @@
/*
* 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.treenode;
import org.jd.gui.api.API;
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.ModuleInfoFilePage;
import org.jd.gui.view.data.TreeNodeBean;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.io.File;
import java.util.regex.Pattern;
public class ModuleInfoFileTreeNodeFactoryProvider extends ClassFileTreeNodeFactoryProvider {
protected static final Factory FACTORY = new Factory();
static {
// Early class loading
try {
Class.forName(ModuleInfoFilePage.class.getName());
} catch (Exception e) {
assert ExceptionUtil.printStackTrace(e);
}
}
@Override public String[] getSelectors() { return appendSelectors("*:file:*/module-info.class"); }
@Override public Pattern getPathPattern() { return externalPathPattern; }
@Override
@SuppressWarnings("unchecked")
public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {
int lastSlashIndex = entry.getPath().lastIndexOf('/');
String label = entry.getPath().substring(lastSlashIndex+1);
return (T)new FileTreeNode(entry, new TreeNodeBean(label, CLASS_FILE_ICON), FACTORY);
}
protected static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory {
// --- PageAndTipFactory --- //
@Override
@SuppressWarnings("unchecked")
public <T extends JComponent & UriGettable> T makePage(API a, Container.Entry e) {
return (T)new ModuleInfoFilePage(a, e);
}
@Override
public String makeTip(API api, Container.Entry entry) {
String location = new File(entry.getUri()).getPath();
StringBuilder tip = new StringBuilder("<html>Location: ");
tip.append(location);
tip.append("</html>");
return tip.toString();
}
}
}

View File

@ -114,8 +114,8 @@ public abstract class AbstractJavaListener extends JavaBaseListener {
typeNameCache.put(name, qualifiedName);
return qualifiedName;
}
} catch (ClassNotFoundException e) {
assert ExceptionUtil.printStackTrace(e);
} catch (ClassNotFoundException ignore) {
// Ignore class loading error
}
// Type not found

View File

@ -171,23 +171,21 @@ public class AbstractTextPage extends JPanel implements LineNumberNavigable, Con
if (!foldsExpanded) {
try {
Rectangle r = textArea.modelToView(start);
Rectangle rec = textArea.modelToView(start);
if (r != null) {
if (rec != null) {
// Visible
setCaretPositionAndCenter(start, end, r);
setCaretPositionAndCenter(start, end, rec);
} else {
// Not visible yet
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
Rectangle r = textArea.modelToView(start);
if (r != null) {
setCaretPositionAndCenter(start, end, r);
}
} catch (BadLocationException e) {
assert ExceptionUtil.printStackTrace(e);
SwingUtilities.invokeLater(() -> {
try {
Rectangle r = textArea.modelToView(start);
if (r != null) {
setCaretPositionAndCenter(start, end, r);
}
} catch (BadLocationException e) {
assert ExceptionUtil.printStackTrace(e);
}
});
}

View File

@ -84,7 +84,6 @@ public class ClassFilePage extends TypePage {
// Decompile class file
DECOMPILER.decompile(loader, printer, entryInternalName, configuration);
setText(printer.getStringBuffer().toString());
} catch (Throwable t) {
assert ExceptionUtil.printStackTrace(t);
setText("// INTERNAL ERROR //");
@ -154,6 +153,11 @@ public class ClassFilePage extends TypePage {
}
}
@Override
public void end() {
setText(stringBuffer.toString());
}
// --- Add strings --- //
@Override
public void printStringConstant(String constant, String ownerInternalName) {

View File

@ -45,46 +45,57 @@ public abstract class CustomLineNumbersPage extends HyperlinkPage {
protected int[] lineNumberMap = null;
protected int maxLineNumber = 0;
public void setMaxLineNumber(int maxLineNumber) {
protected void setMaxLineNumber(int maxLineNumber) {
if (maxLineNumber > 0) {
if (lineNumberMap == null) {
lineNumberMap = new int[maxLineNumber * 3 / 2];
lineNumberMap = new int[maxLineNumber+1];
} else if (lineNumberMap.length <= maxLineNumber) {
int[] tmp = new int[maxLineNumber * 3 / 2];
int[] tmp = new int[maxLineNumber+1];
System.arraycopy(lineNumberMap, 0, tmp, 0, lineNumberMap.length);
lineNumberMap = tmp;
}
if (this.maxLineNumber < maxLineNumber) {
this.maxLineNumber = maxLineNumber;
this.maxLineNumber = maxLineNumber;
}
}
protected void initLineNumbers() {
String text = getText();
int len = text.length();
if (len == 0) {
setMaxLineNumber(0);
} else {
int mln = len - text.replace("\n", "").length();
if (text.charAt(len-1) != '\n') {
mln++;
}
setMaxLineNumber(mln);
for (int i=1; i<=maxLineNumber; i++) {
lineNumberMap[i] = i;
}
}
}
public void initLineNumbers(int maxLineNumber) {
setMaxLineNumber(maxLineNumber);
for (int i=1; i<=maxLineNumber; i++) {
lineNumberMap[i] = i;
}
}
public void setLineNumber(int textAreaLineNumber, int originalLineNumber) {
protected void setLineNumber(int textAreaLineNumber, int originalLineNumber) {
if (originalLineNumber > 0) {
setMaxLineNumber(textAreaLineNumber);
lineNumberMap[textAreaLineNumber] = originalLineNumber;
}
}
public void clearLineNumbers() {
protected void clearLineNumbers() {
if (lineNumberMap != null) {
Arrays.fill(lineNumberMap, 0);
}
}
public int getMaximumSourceLineNumber() { return maxLineNumber; }
protected int getMaximumSourceLineNumber() { return maxLineNumber; }
public int getTextAreaLineNumber(int originalLineNumber) {
protected int getTextAreaLineNumber(int originalLineNumber) {
int textAreaLineNumber = 1;
int greatestLowerSourceLineNumber = 0;
int i = lineNumberMap.length;

View File

@ -40,12 +40,8 @@ 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();
initLineNumbers();
}
public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVA; }
@ -396,7 +392,7 @@ public class JavaFilePage extends TypePage {
}
}
}
} else {
} else if (ctx.primary() != null) {
TerminalNode identifier = ctx.primary().Identifier();
if (identifier != null) {

View File

@ -0,0 +1,160 @@
/*
* 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.fife.ui.rsyntaxtextarea.*;
import org.jd.gui.api.API;
import org.jd.gui.api.model.Container;
import org.jd.gui.util.decompiler.ContainerLoader;
import org.jd.gui.util.exception.ExceptionUtil;
import javax.swing.text.Segment;
import java.util.Map;
public class ModuleInfoFilePage extends ClassFilePage {
public static final String SYNTAX_STYLE_JAVA_MODULE = "text/java-module";
static {
// Add a new token maker for Java 9+ module
AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory)TokenMakerFactory.getDefaultInstance();
atmf.putMapping(SYNTAX_STYLE_JAVA_MODULE, ModuleInfoTokenMaker.class.getName());
}
public ModuleInfoFilePage(API api, Container.Entry entry) {
super(api, entry);
}
public void decompile(Map<String, String> preferences) {
try {
// Clear ...
clearHyperlinks();
clearLineNumbers();
typeDeclarations.clear();
// Init preferences
boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false);
// Init loader
ContainerLoader loader = new ContainerLoader(entry);
// Init printer
ModuleInfoFilePrinter printer = new ModuleInfoFilePrinter();
printer.setUnicodeEscape(unicodeEscape);
// Format internal name
String entryPath = entry.getPath();
assert entryPath.endsWith(".class");
String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = ".class".length()
// Decompile class file
DECOMPILER.decompile(loader, printer, entryInternalName);
} catch (Throwable t) {
assert ExceptionUtil.printStackTrace(t);
setText("// INTERNAL ERROR //");
}
}
public String getSyntaxStyle() { return SYNTAX_STYLE_JAVA_MODULE; }
public class ModuleInfoFilePrinter extends ClassFilePrinter {
@Override
public void start(int maxLineNumber, int majorVersion, int minorVersion) {}
@Override
public void end() {
setText(stringBuffer.toString());
initLineNumbers();
}
}
// https://github.com/bobbylight/RSyntaxTextArea/wiki/Adding-Syntax-Highlighting-for-a-new-Language
public static class ModuleInfoTokenMaker extends AbstractTokenMaker {
@Override
public TokenMap getWordsToHighlight() {
TokenMap tokenMap = new TokenMap();
tokenMap.put("exports", Token.RESERVED_WORD);
tokenMap.put("module", Token.RESERVED_WORD);
tokenMap.put("open", Token.RESERVED_WORD);
tokenMap.put("opens", Token.RESERVED_WORD);
tokenMap.put("provides", Token.RESERVED_WORD);
tokenMap.put("requires", Token.RESERVED_WORD);
tokenMap.put("to", Token.RESERVED_WORD);
tokenMap.put("transitive", Token.RESERVED_WORD);
tokenMap.put("uses", Token.RESERVED_WORD);
tokenMap.put("with", Token.RESERVED_WORD);
return tokenMap;
}
@Override
public void addToken(Segment segment, int start, int end, int tokenType, int startOffset) {
// This assumes all keywords, etc. were parsed as "identifiers."
if (tokenType==Token.IDENTIFIER) {
int value = wordsToHighlight.get(segment, start, end);
if (value != -1) {
tokenType = value;
}
}
super.addToken(segment, start, end, tokenType, startOffset);
}
@Override
public Token getTokenList(Segment text, int startTokenType, int startOffset) {
resetTokenList();
char[] array = text.array;
int offset = text.offset;
int end = offset + text.count;
int newStartOffset = startOffset - offset;
int currentTokenStart = offset;
int currentTokenType = startTokenType;
for (int i=offset; i<end; i++) {
char c = array[i];
switch (currentTokenType) {
case Token.NULL:
currentTokenStart = i; // Starting a new token here.
if (RSyntaxUtilities.isLetter(c) || c=='_') {
currentTokenType = Token.IDENTIFIER;
} else {
currentTokenType = Token.WHITESPACE;
}
break;
default: // Should never happen
case Token.WHITESPACE:
if (RSyntaxUtilities.isLetterOrDigit(c) || c=='_') {
addToken(text, currentTokenStart, i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
currentTokenStart = i;
currentTokenType = Token.IDENTIFIER;
}
break;
case Token.IDENTIFIER:
if (!RSyntaxUtilities.isLetterOrDigit(c) && c!='_') {
addToken(text, currentTokenStart, i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
currentTokenStart = i;
currentTokenType = Token.WHITESPACE;
}
break;
}
}
if (currentTokenType == Token.NULL) {
addNullToken();
}else {
addToken(text, currentTokenStart,end-1, currentTokenType, newStartOffset+currentTokenStart);
addNullToken();
}
return firstToken;
}
}
}

View File

@ -17,6 +17,7 @@ org.jd.gui.service.treenode.JspFileTreeNodeFactoryProvider
org.jd.gui.service.treenode.ManifestFileTreeNodeFactoryProvider
org.jd.gui.service.treenode.MetainfDirectoryTreeNodeFactoryProvider
org.jd.gui.service.treenode.MetainfServiceFileTreeNodeFactoryProvider
org.jd.gui.service.treenode.ModuleInfoFileTreeNodeFactoryProvider
org.jd.gui.service.treenode.PackageTreeNodeFactoryProvider
org.jd.gui.service.treenode.PropertiesFileTreeNodeFactoryProvider
org.jd.gui.service.treenode.SqlFileTreeNodeFactoryProvider