From e8a49c4e75933a34adaedcc1670d8069a98743fd Mon Sep 17 00:00:00 2001 From: emmanue1 Date: Sun, 14 Jun 2015 11:53:48 +0200 Subject: [PATCH] Adds support for JAVA files --- services/build.gradle | 56 + services/src/main/antlr/Java.g4 | 1020 +++++++++++++++++ .../AbstractTypeFileLoaderProvider.groovy | 57 + .../fileloader/ClassFileLoaderProvider.groovy | 49 +- .../fileloader/JavaFileLoaderProvider.groovy | 35 + ...ractTypeFileTreeNodeFactoryProvider.groovy | 77 +- .../ClassFileTreeNodeFactoryProvider.groovy | 46 +- .../JavaFileTreeNodeFactoryProvider.groovy | 48 + .../gui/view/component/ClassFilePage.groovy | 499 +------- .../view/component/EjbJarXmlFilePage.groovy | 4 +- .../jd/gui/view/component/JavaFilePage.groovy | 735 ++++++++++++ .../jd/gui/view/component/TextPage.groovy | 2 +- .../jd/gui/view/component/TypePage.groovy | 457 ++++++++ ...nkPage.groovy => TypeReferencePage.groovy} | 5 +- .../gui/view/component/WebXmlFilePage.groovy | 6 +- .../indexer/AbstractIndexerProvider.java | 17 +- .../indexer/ClassFileIndexerProvider.java | 80 +- .../indexer/JavaFileIndexerProvider.java | 321 ++++++ .../type/AbstractTypeFactoryProvider.java | 155 ++- .../type/ClassFileTypeFactoryProvider.java | 198 +--- .../type/JavaFileTypeFactoryProvider.java | 387 +++++++ .../jd/gui/util/parser/antlr/ANTLRParser.java | 30 + .../parser/antlr/AbstractJavaListener.java | 187 +++ .../META-INF/services/jd.gui.spi.FileLoader | 1 + .../META-INF/services/jd.gui.spi.Indexer | 1 + .../services/jd.gui.spi.TreeNodeFactory | 2 +- .../META-INF/services/jd.gui.spi.TypeFactory | 1 + .../src/main/resources/images/jcu_obj.png | Bin 0 -> 738 bytes .../view/component/ClassFilePageTest.groovy | 16 +- .../view/component/JavaFilePageTest.groovy | 61 + src/osx/resources/Info.plist | 10 + 31 files changed, 3754 insertions(+), 809 deletions(-) create mode 100644 services/src/main/antlr/Java.g4 create mode 100644 services/src/main/groovy/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.groovy create mode 100644 services/src/main/groovy/jd/gui/service/fileloader/JavaFileLoaderProvider.groovy create mode 100644 services/src/main/groovy/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.groovy create mode 100644 services/src/main/groovy/jd/gui/view/component/JavaFilePage.groovy create mode 100644 services/src/main/groovy/jd/gui/view/component/TypePage.groovy rename services/src/main/groovy/jd/gui/view/component/{TypeHyperlinkPage.groovy => TypeReferencePage.groovy} (96%) create mode 100644 services/src/main/java/jd/gui/service/indexer/JavaFileIndexerProvider.java create mode 100644 services/src/main/java/jd/gui/service/type/JavaFileTypeFactoryProvider.java create mode 100644 services/src/main/java/jd/gui/util/parser/antlr/ANTLRParser.java create mode 100644 services/src/main/java/jd/gui/util/parser/antlr/AbstractJavaListener.java create mode 100644 services/src/main/resources/images/jcu_obj.png create mode 100644 services/src/test/groovy/jd/gui/view/component/JavaFilePageTest.groovy diff --git a/services/build.gradle b/services/build.gradle index 8f64381..2e33c42 100644 --- a/services/build.gradle +++ b/services/build.gradle @@ -10,3 +10,59 @@ dependencies { compile project(':api') testCompile 'org.codehaus.groovy:groovy-test:2.4.0' } + +// ANTLR // +ext.antlr4 = [ + antlrSource: 'src/main/antlr', + destinationDir: 'src-generated/antlr/java', + grammarPackage: 'jd.gui.util.parser.antlr' +] + +configurations { + antlr4 { + description = "ANTLR4" + } +} + +dependencies { + compile 'org.antlr:antlr4-runtime:4.5' + antlr4 'org.antlr:antlr4:4.5' +} + +task antlr4OutputDir() { + mkdir antlr4.destinationDir +} + +task antlr4GenerateGrammarSource(dependsOn: antlr4OutputDir, type: JavaExec) { + description = 'Generates Java sources from ANTLR4 grammars.' + + inputs.dir file(antlr4.antlrSource) + outputs.dir file(antlr4.destinationDir) + + def grammars = fileTree(antlr4.antlrSource).include('**/*.g4') + def pkg = antlr4.grammarPackage.replaceAll("\\.", "/") + + main = 'org.antlr.v4.Tool' + classpath = configurations.antlr4 + args = ['-o', "${antlr4.destinationDir}/${pkg}", '-atn', '-package', antlr4.grammarPackage, grammars.files].flatten() +} + +compileJava { + dependsOn antlr4GenerateGrammarSource + source antlr4.destinationDir +} + +clean { + delete 'src-generated' +} + +idea.module { + sourceDirs += file(antlr4.destinationDir) +} +ideaModule.dependsOn antlr4GenerateGrammarSource + +eclipse.classpath.file.withXml { xml -> + def node = xml.asNode() + node.appendNode( 'classpathentry', [ kind: 'src', path: antlr4.destinationDir]) +} +eclipseClasspath.dependsOn antlr4GenerateGrammarSource diff --git a/services/src/main/antlr/Java.g4 b/services/src/main/antlr/Java.g4 new file mode 100644 index 0000000..2997ff2 --- /dev/null +++ b/services/src/main/antlr/Java.g4 @@ -0,0 +1,1020 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr, Sam Harwell + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +*/ + +/** A Java 1.7 grammar for ANTLR v4 derived from ANTLR v3 Java grammar. + * Uses ANTLR v4's left-recursive expression notation. + * It parses ECJ, Netbeans, JDK etc... + * + * Sam Harwell cleaned this up significantly and updated to 1.7! + * + * You can test with + * + * $ antlr4 Java.g4 + * $ javac *.java + * $ grun Java compilationUnit *.java + */ +grammar Java; + +// starting point for parsing a java file +compilationUnit + : packageDeclaration? importDeclaration* typeDeclaration* EOF + ; + +packageDeclaration + : annotation* 'package' qualifiedName ';' + ; + +importDeclaration + : 'import' 'static'? qualifiedName ('.' '*')? ';' + ; + +typeDeclaration + : classOrInterfaceModifier* classDeclaration + | classOrInterfaceModifier* enumDeclaration + | classOrInterfaceModifier* interfaceDeclaration + | classOrInterfaceModifier* annotationTypeDeclaration + | ';' + ; + +modifier + : classOrInterfaceModifier + | ( 'native' + | 'synchronized' + | 'transient' + | 'volatile' + ) + ; + +classOrInterfaceModifier + : annotation // class or interface + | ( 'public' // class or interface + | 'protected' // class or interface + | 'private' // class or interface + | 'static' // class or interface + | 'abstract' // class or interface + | 'final' // class only -- does not apply to interfaces + | 'strictfp' // class or interface + ) + ; + +variableModifier + : 'final' + | annotation + ; + +classDeclaration + : 'class' Identifier typeParameters? + ('extends' type)? + ('implements' typeList)? + classBody + ; + +typeParameters + : '<' typeParameter (',' typeParameter)* '>' + ; + +typeParameter + : Identifier ('extends' typeBound)? + ; + +typeBound + : type ('&' type)* + ; + +enumDeclaration + : ENUM Identifier ('implements' typeList)? + '{' enumConstants? ','? enumBodyDeclarations? '}' + ; + +enumConstants + : enumConstant (',' enumConstant)* + ; + +enumConstant + : annotation* Identifier arguments? classBody? + ; + +enumBodyDeclarations + : ';' classBodyDeclaration* + ; + +interfaceDeclaration + : 'interface' Identifier typeParameters? ('extends' typeList)? interfaceBody + ; + +typeList + : type (',' type)* + ; + +classBody + : '{' classBodyDeclaration* '}' + ; + +interfaceBody + : '{' interfaceBodyDeclaration* '}' + ; + +classBodyDeclaration + : ';' + | 'static'? block + | modifier* memberDeclaration + ; + +memberDeclaration + : methodDeclaration + | genericMethodDeclaration + | fieldDeclaration + | constructorDeclaration + | genericConstructorDeclaration + | interfaceDeclaration + | annotationTypeDeclaration + | classDeclaration + | enumDeclaration + ; + +/* We use rule this even for void methods which cannot have [] after parameters. + This simplifies grammar and we can consider void to be a type, which + renders the [] matching as a context-sensitive issue or a semantic check + for invalid return type after parsing. + */ +methodDeclaration + : (type|'void') Identifier formalParameters ('[' ']')* + ('throws' qualifiedNameList)? + ( methodBody + | ';' + ) + ; + +genericMethodDeclaration + : typeParameters methodDeclaration + ; + +constructorDeclaration + : Identifier formalParameters ('throws' qualifiedNameList)? + constructorBody + ; + +genericConstructorDeclaration + : typeParameters constructorDeclaration + ; + +fieldDeclaration + : type variableDeclarators ';' + ; + +interfaceBodyDeclaration + : modifier* interfaceMemberDeclaration + | ';' + ; + +interfaceMemberDeclaration + : constDeclaration + | interfaceMethodDeclaration + | genericInterfaceMethodDeclaration + | interfaceDeclaration + | annotationTypeDeclaration + | classDeclaration + | enumDeclaration + ; + +constDeclaration + : type constantDeclarator (',' constantDeclarator)* ';' + ; + +constantDeclarator + : Identifier ('[' ']')* '=' variableInitializer + ; + +// see matching of [] comment in methodDeclaratorRest +interfaceMethodDeclaration + : (type|'void') Identifier formalParameters ('[' ']')* + ('throws' qualifiedNameList)? + ';' + ; + +genericInterfaceMethodDeclaration + : typeParameters interfaceMethodDeclaration + ; + +variableDeclarators + : variableDeclarator (',' variableDeclarator)* + ; + +variableDeclarator + : variableDeclaratorId ('=' variableInitializer)? + ; + +variableDeclaratorId + : Identifier ('[' ']')* + ; + +variableInitializer + : arrayInitializer + | expression + ; + +arrayInitializer + : '{' (variableInitializer (',' variableInitializer)* (',')? )? '}' + ; + +enumConstantName + : Identifier + ; + +type + : classOrInterfaceType ('[' ']')* + | primitiveType ('[' ']')* + ; + +classOrInterfaceType + : Identifier typeArguments? ('.' Identifier typeArguments? )* + ; + +primitiveType + : 'boolean' + | 'char' + | 'byte' + | 'short' + | 'int' + | 'long' + | 'float' + | 'double' + ; + +typeArguments + : '<' typeArgument (',' typeArgument)* '>' + ; + +typeArgument + : type + | '?' (('extends' | 'super') type)? + ; + +qualifiedNameList + : qualifiedName (',' qualifiedName)* + ; + +formalParameters + : '(' formalParameterList? ')' + ; + +formalParameterList + : formalParameter (',' formalParameter)* (',' lastFormalParameter)? + | lastFormalParameter + ; + +formalParameter + : variableModifier* type variableDeclaratorId + ; + +lastFormalParameter + : variableModifier* type '...' variableDeclaratorId + ; + +methodBody + : block + ; + +constructorBody + : block + ; + +qualifiedName + : Identifier ('.' Identifier)* + ; + +literal + : IntegerLiteral + | FloatingPointLiteral + | CharacterLiteral + | StringLiteral + | BooleanLiteral + | 'null' + ; + +// ANNOTATIONS + +annotation + : '@' annotationName ( '(' ( elementValuePairs | elementValue )? ')' )? + ; + +annotationName : qualifiedName ; + +elementValuePairs + : elementValuePair (',' elementValuePair)* + ; + +elementValuePair + : Identifier '=' elementValue + ; + +elementValue + : expression + | annotation + | elementValueArrayInitializer + ; + +elementValueArrayInitializer + : '{' (elementValue (',' elementValue)*)? (',')? '}' + ; + +annotationTypeDeclaration + : '@' 'interface' Identifier annotationTypeBody + ; + +annotationTypeBody + : '{' (annotationTypeElementDeclaration)* '}' + ; + +annotationTypeElementDeclaration + : modifier* annotationTypeElementRest + | ';' // this is not allowed by the grammar, but apparently allowed by the actual compiler + ; + +annotationTypeElementRest + : type annotationMethodOrConstantRest ';' + | classDeclaration ';'? + | interfaceDeclaration ';'? + | enumDeclaration ';'? + | annotationTypeDeclaration ';'? + ; + +annotationMethodOrConstantRest + : annotationMethodRest + | annotationConstantRest + ; + +annotationMethodRest + : Identifier '(' ')' defaultValue? + ; + +annotationConstantRest + : variableDeclarators + ; + +defaultValue + : 'default' elementValue + ; + +// STATEMENTS / BLOCKS + +block + : '{' blockStatement* '}' + ; + +blockStatement + : localVariableDeclarationStatement + | statement + | typeDeclaration + ; + +localVariableDeclarationStatement + : localVariableDeclaration ';' + ; + +localVariableDeclaration + : variableModifier* type variableDeclarators + ; + +statement + : block + | ASSERT expression (':' expression)? ';' + | 'if' parExpression statement ('else' statement)? + | 'for' '(' forControl ')' statement + | 'while' parExpression statement + | 'do' statement 'while' parExpression ';' + | 'try' block (catchClause+ finallyBlock? | finallyBlock) + | 'try' resourceSpecification block catchClause* finallyBlock? + | 'switch' parExpression '{' switchBlockStatementGroup* switchLabel* '}' + | 'synchronized' parExpression block + | 'return' expression? ';' + | 'throw' expression ';' + | 'break' Identifier? ';' + | 'continue' Identifier? ';' + | ';' + | statementExpression ';' + | Identifier ':' statement + ; + +catchClause + : 'catch' '(' variableModifier* catchType Identifier ')' block + ; + +catchType + : qualifiedName ('|' qualifiedName)* + ; + +finallyBlock + : 'finally' block + ; + +resourceSpecification + : '(' resources ';'? ')' + ; + +resources + : resource (';' resource)* + ; + +resource + : variableModifier* classOrInterfaceType variableDeclaratorId '=' expression + ; + +/** Matches cases then statements, both of which are mandatory. + * To handle empty cases at the end, we add switchLabel* to statement. + */ +switchBlockStatementGroup + : switchLabel+ blockStatement+ + ; + +switchLabel + : 'case' constantExpression ':' + | 'case' enumConstantName ':' + | 'default' ':' + ; + +forControl + : enhancedForControl + | forInit? ';' expression? ';' forUpdate? + ; + +forInit + : localVariableDeclaration + | expressionList + ; + +enhancedForControl + : variableModifier* type variableDeclaratorId ':' expression + ; + +forUpdate + : expressionList + ; + +// EXPRESSIONS + +parExpression + : '(' expression ')' + ; + +expressionList + : expression (',' expression)* + ; + +statementExpression + : expression + ; + +constantExpression + : expression + ; + +expression + : primary + | expression '.' Identifier + | expression '.' 'this' + | expression '.' 'new' nonWildcardTypeArguments? innerCreator + | expression '.' 'super' superSuffix + | expression '.' explicitGenericInvocation + | expression '[' expression ']' + | expression '(' expressionList? ')' + | 'new' creator + | '(' type ')' expression + | expression ('++' | '--') + | ('+'|'-'|'++'|'--') expression + | ('~'|'!') expression + | expression ('*'|'/'|'%') expression + | expression ('+'|'-') expression + | expression ('<' '<' | '>' '>' '>' | '>' '>') expression + | expression ('<=' | '>=' | '>' | '<') expression + | expression 'instanceof' type + | expression ('==' | '!=') expression + | expression '&' expression + | expression '^' expression + | expression '|' expression + | expression '&&' expression + | expression '||' expression + | expression '?' expression ':' expression + | expression + ( '=' + | '+=' + | '-=' + | '*=' + | '/=' + | '&=' + | '|=' + | '^=' + | '>>=' + | '>>>=' + | '<<=' + | '%=' + ) + expression + ; + +primary + : '(' expression ')' + | 'this' + | 'super' + | literal + | Identifier + | type '.' 'class' + | 'void' '.' 'class' + | nonWildcardTypeArguments (explicitGenericInvocationSuffix | 'this' arguments) + ; + +creator + : nonWildcardTypeArguments createdName classCreatorRest + | createdName (arrayCreatorRest | classCreatorRest) + ; + +createdName + : Identifier typeArgumentsOrDiamond? ('.' Identifier typeArgumentsOrDiamond?)* + | primitiveType + ; + +innerCreator + : Identifier nonWildcardTypeArgumentsOrDiamond? classCreatorRest + ; + +arrayCreatorRest + : '[' + ( ']' ('[' ']')* arrayInitializer + | expression ']' ('[' expression ']')* ('[' ']')* + ) + ; + +classCreatorRest + : arguments classBody? + ; + +explicitGenericInvocation + : nonWildcardTypeArguments explicitGenericInvocationSuffix + ; + +nonWildcardTypeArguments + : '<' typeList '>' + ; + +typeArgumentsOrDiamond + : '<' '>' + | typeArguments + ; + +nonWildcardTypeArgumentsOrDiamond + : '<' '>' + | nonWildcardTypeArguments + ; + +superSuffix + : arguments + | '.' Identifier arguments? + ; + +explicitGenericInvocationSuffix + : 'super' superSuffix + | Identifier arguments + ; + +arguments + : '(' expressionList? ')' + ; + +// LEXER + +// §3.9 Keywords + +ABSTRACT : 'abstract'; +ASSERT : 'assert'; +BOOLEAN : 'boolean'; +BREAK : 'break'; +BYTE : 'byte'; +CASE : 'case'; +CATCH : 'catch'; +CHAR : 'char'; +CLASS : 'class'; +CONST : 'const'; +CONTINUE : 'continue'; +DEFAULT : 'default'; +DO : 'do'; +DOUBLE : 'double'; +ELSE : 'else'; +ENUM : 'enum'; +EXTENDS : 'extends'; +FINAL : 'final'; +FINALLY : 'finally'; +FLOAT : 'float'; +FOR : 'for'; +IF : 'if'; +GOTO : 'goto'; +IMPLEMENTS : 'implements'; +IMPORT : 'import'; +INSTANCEOF : 'instanceof'; +INT : 'int'; +INTERFACE : 'interface'; +LONG : 'long'; +NATIVE : 'native'; +NEW : 'new'; +PACKAGE : 'package'; +PRIVATE : 'private'; +PROTECTED : 'protected'; +PUBLIC : 'public'; +RETURN : 'return'; +SHORT : 'short'; +STATIC : 'static'; +STRICTFP : 'strictfp'; +SUPER : 'super'; +SWITCH : 'switch'; +SYNCHRONIZED : 'synchronized'; +THIS : 'this'; +THROW : 'throw'; +THROWS : 'throws'; +TRANSIENT : 'transient'; +TRY : 'try'; +VOID : 'void'; +VOLATILE : 'volatile'; +WHILE : 'while'; + +// §3.10.1 Integer Literals + +IntegerLiteral + : DecimalIntegerLiteral + | HexIntegerLiteral + | OctalIntegerLiteral + | BinaryIntegerLiteral + ; + +fragment +DecimalIntegerLiteral + : DecimalNumeral IntegerTypeSuffix? + ; + +fragment +HexIntegerLiteral + : HexNumeral IntegerTypeSuffix? + ; + +fragment +OctalIntegerLiteral + : OctalNumeral IntegerTypeSuffix? + ; + +fragment +BinaryIntegerLiteral + : BinaryNumeral IntegerTypeSuffix? + ; + +fragment +IntegerTypeSuffix + : [lL] + ; + +fragment +DecimalNumeral + : '0' + | NonZeroDigit (Digits? | Underscores Digits) + ; + +fragment +Digits + : Digit (DigitOrUnderscore* Digit)? + ; + +fragment +Digit + : '0' + | NonZeroDigit + ; + +fragment +NonZeroDigit + : [1-9] + ; + +fragment +DigitOrUnderscore + : Digit + | '_' + ; + +fragment +Underscores + : '_'+ + ; + +fragment +HexNumeral + : '0' [xX] HexDigits + ; + +fragment +HexDigits + : HexDigit (HexDigitOrUnderscore* HexDigit)? + ; + +fragment +HexDigit + : [0-9a-fA-F] + ; + +fragment +HexDigitOrUnderscore + : HexDigit + | '_' + ; + +fragment +OctalNumeral + : '0' Underscores? OctalDigits + ; + +fragment +OctalDigits + : OctalDigit (OctalDigitOrUnderscore* OctalDigit)? + ; + +fragment +OctalDigit + : [0-7] + ; + +fragment +OctalDigitOrUnderscore + : OctalDigit + | '_' + ; + +fragment +BinaryNumeral + : '0' [bB] BinaryDigits + ; + +fragment +BinaryDigits + : BinaryDigit (BinaryDigitOrUnderscore* BinaryDigit)? + ; + +fragment +BinaryDigit + : [01] + ; + +fragment +BinaryDigitOrUnderscore + : BinaryDigit + | '_' + ; + +// §3.10.2 Floating-Point Literals + +FloatingPointLiteral + : DecimalFloatingPointLiteral + | HexadecimalFloatingPointLiteral + ; + +fragment +DecimalFloatingPointLiteral + : Digits '.' Digits? ExponentPart? FloatTypeSuffix? + | '.' Digits ExponentPart? FloatTypeSuffix? + | Digits ExponentPart FloatTypeSuffix? + | Digits FloatTypeSuffix + ; + +fragment +ExponentPart + : ExponentIndicator SignedInteger + ; + +fragment +ExponentIndicator + : [eE] + ; + +fragment +SignedInteger + : Sign? Digits + ; + +fragment +Sign + : [+-] + ; + +fragment +FloatTypeSuffix + : [fFdD] + ; + +fragment +HexadecimalFloatingPointLiteral + : HexSignificand BinaryExponent FloatTypeSuffix? + ; + +fragment +HexSignificand + : HexNumeral '.'? + | '0' [xX] HexDigits? '.' HexDigits + ; + +fragment +BinaryExponent + : BinaryExponentIndicator SignedInteger + ; + +fragment +BinaryExponentIndicator + : [pP] + ; + +// §3.10.3 Boolean Literals + +BooleanLiteral + : 'true' + | 'false' + ; + +// §3.10.4 Character Literals + +CharacterLiteral + : '\'' SingleCharacter '\'' + | '\'' EscapeSequence '\'' + ; + +fragment +SingleCharacter + : ~['\\] + ; + +// §3.10.5 String Literals + +StringLiteral + : '"' StringCharacters? '"' + ; + +fragment +StringCharacters + : StringCharacter+ + ; + +fragment +StringCharacter + : ~["\\] + | EscapeSequence + ; + +// §3.10.6 Escape Sequences for Character and String Literals + +fragment +EscapeSequence + : '\\' [btnfr"'\\] + | OctalEscape + | UnicodeEscape + ; + +fragment +OctalEscape + : '\\' OctalDigit + | '\\' OctalDigit OctalDigit + | '\\' ZeroToThree OctalDigit OctalDigit + ; + +fragment +UnicodeEscape + : '\\' 'u' HexDigit HexDigit HexDigit HexDigit + ; + +fragment +ZeroToThree + : [0-3] + ; + +// §3.10.7 The Null Literal + +NullLiteral + : 'null' + ; + +// §3.11 Separators + +LPAREN : '('; +RPAREN : ')'; +LBRACE : '{'; +RBRACE : '}'; +LBRACK : '['; +RBRACK : ']'; +SEMI : ';'; +COMMA : ','; +DOT : '.'; + +// §3.12 Operators + +ASSIGN : '='; +GT : '>'; +LT : '<'; +BANG : '!'; +TILDE : '~'; +QUESTION : '?'; +COLON : ':'; +EQUAL : '=='; +LE : '<='; +GE : '>='; +NOTEQUAL : '!='; +AND : '&&'; +OR : '||'; +INC : '++'; +DEC : '--'; +ADD : '+'; +SUB : '-'; +MUL : '*'; +DIV : '/'; +BITAND : '&'; +BITOR : '|'; +CARET : '^'; +MOD : '%'; + +ADD_ASSIGN : '+='; +SUB_ASSIGN : '-='; +MUL_ASSIGN : '*='; +DIV_ASSIGN : '/='; +AND_ASSIGN : '&='; +OR_ASSIGN : '|='; +XOR_ASSIGN : '^='; +MOD_ASSIGN : '%='; +LSHIFT_ASSIGN : '<<='; +RSHIFT_ASSIGN : '>>='; +URSHIFT_ASSIGN : '>>>='; + +// §3.8 Identifiers (must appear after all keywords in the grammar) + +Identifier + : JavaLetter JavaLetterOrDigit* + ; + +fragment +JavaLetter + : [a-zA-Z$_] // these are the "java letters" below 0xFF + | // covers all characters above 0xFF which are not a surrogate + ~[\u0000-\u00FF\uD800-\uDBFF] + {Character.isJavaIdentifierStart(_input.LA(-1))}? + | // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF + [\uD800-\uDBFF] [\uDC00-\uDFFF] + {Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}? + ; + +fragment +JavaLetterOrDigit + : [a-zA-Z0-9$_] // these are the "java letters or digits" below 0xFF + | // covers all characters above 0xFF which are not a surrogate + ~[\u0000-\u00FF\uD800-\uDBFF] + {Character.isJavaIdentifierPart(_input.LA(-1))}? + | // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF + [\uD800-\uDBFF] [\uDC00-\uDFFF] + {Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}? + ; + +// +// Additional symbols not defined in the lexical specification +// + +AT : '@'; +ELLIPSIS : '...'; + +// +// Whitespace and comments +// + +WS : [ \t\r\n\u000C]+ -> skip + ; + +COMMENT + : '/*' .*? '*/' -> skip + ; + +LINE_COMMENT + : '//' ~[\r\n]* -> skip + ; diff --git a/services/src/main/groovy/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.groovy b/services/src/main/groovy/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.groovy new file mode 100644 index 0000000..e660ce8 --- /dev/null +++ b/services/src/main/groovy/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.groovy @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.service.fileloader + +import jd.gui.api.API +import jd.gui.api.feature.UriOpenable + +import java.nio.file.Paths + +abstract class AbstractTypeFileLoaderProvider extends AbstractFileLoaderProvider { + + protected boolean load(API api, File file, String pathInFile) { + // Search root path + String pathSuffix = pathInFile + String path = file.path + + while (! path.endsWith(pathSuffix)) { + int index = pathSuffix.indexOf(File.separator) + + if (index == -1) { + pathSuffix = '' + } else { + pathSuffix = pathSuffix.substring(index+1) + } + } + + if (pathSuffix) { + // Init root file + File rootFile = file + int index = pathSuffix.indexOf(File.separator) + + while (index != -1) { + rootFile = rootFile.parentFile + pathSuffix = pathSuffix.substring(index+1) + index = pathSuffix.indexOf(File.separator) + } + rootFile = rootFile.parentFile + + // Create panel + def mainPanel = load(api, rootFile, Paths.get(rootFile.toURI())) + + if (mainPanel instanceof UriOpenable) { + // Open page + pathSuffix = file.absolutePath.substring(rootFile.absolutePath.length()).replace(File.separator, '/') + def rootUri = rootFile.toURI() + def uri = new URI(rootUri.scheme, rootUri.host, rootUri.path + '!' + pathSuffix, null) + mainPanel.openUri(uri) + return true + } else { + return mainPanel != null + } + } + } +} diff --git a/services/src/main/groovy/jd/gui/service/fileloader/ClassFileLoaderProvider.groovy b/services/src/main/groovy/jd/gui/service/fileloader/ClassFileLoaderProvider.groovy index 8fd4579..c1b672a 100644 --- a/services/src/main/groovy/jd/gui/service/fileloader/ClassFileLoaderProvider.groovy +++ b/services/src/main/groovy/jd/gui/service/fileloader/ClassFileLoaderProvider.groovy @@ -7,11 +7,8 @@ package jd.gui.service.fileloader import groovyjarjarasm.asm.ClassReader import jd.gui.api.API -import jd.gui.api.feature.UriOpenable -import java.nio.file.Paths - -class ClassFileLoaderProvider extends AbstractFileLoaderProvider { +class ClassFileLoaderProvider extends AbstractTypeFileLoaderProvider { String[] getExtensions() { ['class'] } String getDescription() { 'Class files (*.class)' } @@ -22,48 +19,10 @@ class ClassFileLoaderProvider extends AbstractFileLoaderProvider { boolean load(API api, File file) { file.withInputStream { is -> - ClassReader classReader = new ClassReader(is) + def classReader = new ClassReader(is) + def pathInFile = classReader.className.replace('/', File.separator) + '.class' - // Search root path - def pathSuffix = classReader.className.replace('/', File.separator) + '.class' - def path = file.path - - while (! path.endsWith(pathSuffix)) { - int index = pathSuffix.indexOf(File.separator) - - if (index == -1) { - pathSuffix = '' - } else { - pathSuffix = pathSuffix.substring(index+1) - } - } - - if (pathSuffix) { - // Init root file - File rootFile = file - int index = pathSuffix.indexOf(File.separator) - - while (index != -1) { - rootFile = rootFile.parentFile - pathSuffix = pathSuffix.substring(index+1) - index = pathSuffix.indexOf(File.separator) - } - rootFile = rootFile.parentFile - - // Create panel - def mainPanel = load(api, rootFile, Paths.get(rootFile.toURI())) - - if (mainPanel instanceof UriOpenable) { - // Open page - pathSuffix = file.absolutePath.substring(rootFile.absolutePath.length()).replace(File.separator, '/') - def rootUri = rootFile.toURI() - def uri = new URI(rootUri.scheme, rootUri.host, rootUri.path + '!' + pathSuffix, null) - mainPanel.openUri(uri) - return true - } else { - return mainPanel != null - } - } + return load(api, file, pathInFile) } } } diff --git a/services/src/main/groovy/jd/gui/service/fileloader/JavaFileLoaderProvider.groovy b/services/src/main/groovy/jd/gui/service/fileloader/JavaFileLoaderProvider.groovy new file mode 100644 index 0000000..fb4dfbc --- /dev/null +++ b/services/src/main/groovy/jd/gui/service/fileloader/JavaFileLoaderProvider.groovy @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.service.fileloader + +import jd.gui.api.API + +import java.util.regex.Pattern + +class JavaFileLoaderProvider extends AbstractTypeFileLoaderProvider { + + String[] getExtensions() { ['java'] } + String getDescription() { 'Java files (*.java)' } + + boolean accept(API api, File file) { + return file.exists() && file.canRead() && file.name.toLowerCase().endsWith('.java') + } + + boolean load(API api, File file) { + def pattern = Pattern.compile('(?s)(.*\\s)?package\\s+(\\S+)\\s*;.*') + def matcher = file.text =~ pattern + + if (matcher.matches()) { + // Package name found + def pathInFile = matcher[0][2].replace('.', File.separator) + File.separator + file.name + + return load(api, file, pathInFile) + } else { + // Package name not found + return load(api, file, file.name) + } + } +} diff --git a/services/src/main/groovy/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy index 8e39d83..c7ddab7 100644 --- a/services/src/main/groovy/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy +++ b/services/src/main/groovy/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy @@ -5,7 +5,6 @@ package jd.gui.service.treenode -import groovy.transform.CompileStatic import jd.gui.api.API import jd.gui.api.feature.PageCreator import jd.gui.api.feature.TreeNodeExpandable @@ -21,13 +20,13 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF static class BaseTreeNode extends DefaultMutableTreeNode implements UriGettable, PageCreator { Container.Entry entry - PageFactory pageFactory; + PageAndTipFactory factory; URI uri - BaseTreeNode(Container.Entry entry, String fragment, Object userObject, PageFactory pageFactory) { + BaseTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { super(userObject) this.entry = entry - this.pageFactory = pageFactory + this.factory = factory if (fragment) { def uri = entry.uri @@ -43,50 +42,20 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF // --- PageCreator --- // public T createPage(API api) { // Lazy 'tip' initialization - def file = new File(entry.container.root.uri) - def tip = "Location: $file.path" - - entry.inputStream.withStream { is -> - is.skip(4) // Skip magic number - int minorVersion = readUnsignedShort(is) - int majorVersion = readUnsignedShort(is) - - if (majorVersion >= 49) { - tip += "
Java compiler version: ${majorVersion - (49-5)} ($majorVersion.$minorVersion)" - } else if (majorVersion >= 45) { - tip += "
Java compiler version: 1.${majorVersion - (45-1)} ($majorVersion.$minorVersion)" - } - } - - tip += "" - - userObject.tip = tip - - return pageFactory.make(api, entry) - } - - /** - * @see java.io.DataInputStream#readUnsignedShort() - */ - @CompileStatic - int readUnsignedShort(InputStream is) throws IOException { - int ch1 = is.read() - int ch2 = is.read() - if ((ch1 | ch2) < 0) - throw new EOFException() - return (ch1 << 8) + (ch2 << 0) + userObject.tip = factory.makeTip(api, entry) + return factory.makePage(api, entry) } } static class FileTreeNode extends BaseTreeNode implements TreeNodeExpandable { boolean initialized - FileTreeNode(Container.Entry entry, Object userObject, PageFactory pageFactory) { - this(entry, null, userObject, pageFactory) + FileTreeNode(Container.Entry entry, Object userObject, PageAndTipFactory pageAndTipFactory) { + this(entry, null, userObject, pageAndTipFactory) } - FileTreeNode(Container.Entry entry, String fragment, Object userObject, PageFactory pageFactory) { - super(entry, fragment, userObject, pageFactory) + FileTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { + super(entry, fragment, userObject, factory) initialized = false // Add dummy node add(new DefaultMutableTreeNode()) @@ -97,9 +66,10 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF if (!initialized) { removeAllChildren() // Create type node - def type = api.getTypeFactory(entry)?.make(api, entry, null) - if (type) { - add(new TypeTreeNode(entry, type, new TreeNodeBean(label: type.displayTypeName, icon: type.icon), pageFactory)) + def types = api.getTypeFactory(entry)?.make(api, entry) + + for (def type : types) { + add(new TypeTreeNode(entry, type, new TreeNodeBean(label: type.displayTypeName, icon: type.icon), factory)) } initialized = true @@ -111,8 +81,8 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF boolean initialized Type type - TypeTreeNode(Container.Entry entry, Type type, Object userObject, PageFactory pageFactory) { - super(entry, type.name, userObject, pageFactory) + TypeTreeNode(Container.Entry entry, Type type, Object userObject, PageAndTipFactory factory) { + super(entry, type.name, userObject, factory) this.initialized = false this.type = type // Add dummy node @@ -134,7 +104,7 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF type.innerTypes.sort { t1, t2 -> t1.name.compareTo(t2.name) }.each { - add(new TypeTreeNode(entry, it, new TreeNodeBean(label: it.displayInnerTypeName, icon: it.icon), pageFactory)) + add(new TypeTreeNode(entry, it, new TreeNodeBean(label: it.displayInnerTypeName, icon: it.icon), factory)) } // Create fields @@ -148,7 +118,7 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF }.sort { f1, f2 -> f1.label.compareTo(f2.label) }.each { - add(new FieldOrMethodTreeNode(entry, it.fragment, new TreeNodeBean(label: it.label, icon: it.icon), pageFactory)) + add(new FieldOrMethodTreeNode(entry, it.fragment, new TreeNodeBean(label: it.label, icon: it.icon), factory)) } // Create methods @@ -161,7 +131,7 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF }.sort { m1, m2 -> m1.label.compareTo(m2.label) }.each { - add(new FieldOrMethodTreeNode(entry, it.fragment, new TreeNodeBean(label: it.label, icon: it.icon), pageFactory)) + add(new FieldOrMethodTreeNode(entry, it.fragment, new TreeNodeBean(label: it.label, icon: it.icon), factory)) } initialized = true @@ -400,8 +370,8 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF } static class FieldOrMethodTreeNode extends BaseTreeNode { - FieldOrMethodTreeNode(Container.Entry entry, String fragment, Object userObject, PageFactory pageFactory) { - super(entry, fragment, userObject, pageFactory) + FieldOrMethodTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { + super(entry, fragment, userObject, factory) } } @@ -409,8 +379,9 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF String fragment, label Icon icon } - - interface PageFactory { - public T make(API api, Container.Entry entry); + + interface PageAndTipFactory { + public T makePage(API api, Container.Entry entry); + public String makeTip(API api, Container.Entry entry); } } diff --git a/services/src/main/groovy/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.groovy index 28beac1..195a79d 100644 --- a/services/src/main/groovy/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.groovy +++ b/services/src/main/groovy/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.groovy @@ -5,6 +5,7 @@ package jd.gui.service.treenode +import groovy.transform.CompileStatic import jd.gui.api.API import jd.gui.api.feature.UriGettable import jd.gui.api.model.Container @@ -17,6 +18,8 @@ import javax.swing.tree.DefaultMutableTreeNode class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider { static final ImageIcon CLASS_FILE_ICON = new ImageIcon(ClassFileTreeNodeFactoryProvider.class.classLoader.getResource('images/classf_obj.png')) + static final Factory FACTORY = new Factory(); + static { // Early class loading try { @@ -36,9 +39,46 @@ class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryPr return new FileTreeNode( entry, new TreeNodeBean(label:name, icon:CLASS_FILE_ICON), - new AbstractTypeFileTreeNodeFactoryProvider.PageFactory() { - public T make(API a, Container.Entry e) { new ClassFilePage(a, e) } - } + FACTORY ) } + + static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory { + public T makePage(API a, Container.Entry e) { + return new ClassFilePage(a, e) + } + + public String makeTip(API api, Container.Entry entry) { + def file = new File(entry.container.root.uri) + def tip = "Location: $file.path" + + entry.inputStream.withStream { is -> + is.skip(4) // Skip magic number + int minorVersion = readUnsignedShort(is) + int majorVersion = readUnsignedShort(is) + + if (majorVersion >= 49) { + tip += "
Java compiler version: ${majorVersion - (49-5)} ($majorVersion.$minorVersion)" + } else if (majorVersion >= 45) { + tip += "
Java compiler version: 1.${majorVersion - (45-1)} ($majorVersion.$minorVersion)" + } + } + + tip += "" + + return tip + } + + /** + * @see java.io.DataInputStream#readUnsignedShort() + */ + @CompileStatic + int readUnsignedShort(InputStream is) throws IOException { + int ch1 = is.read() + int ch2 = is.read() + if ((ch1 | ch2) < 0) + throw new EOFException() + return (ch1 << 8) + (ch2 << 0) + } + } } diff --git a/services/src/main/groovy/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.groovy new file mode 100644 index 0000000..836533e --- /dev/null +++ b/services/src/main/groovy/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.groovy @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.service.treenode + +import jd.gui.api.API +import jd.gui.api.feature.UriGettable +import jd.gui.api.model.Container +import jd.gui.view.component.JavaFilePage +import jd.gui.view.data.TreeNodeBean + +import javax.swing.* +import javax.swing.tree.DefaultMutableTreeNode + +class JavaFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider { + static final ImageIcon JAVA_FILE_ICON = new ImageIcon(JavaFileTreeNodeFactoryProvider.class.classLoader.getResource('images/jcu_obj.png')) + + static final Factory FACTORY = new Factory(); + + /** + * @return local + optional external selectors + */ + String[] getSelectors() { ['*:file:*.java'] + externalSelectors } + + public T make(API api, Container.Entry entry) { + int lastSlashIndex = entry.path.lastIndexOf('/') + def name = entry.path.substring(lastSlashIndex+1) + + return new FileTreeNode( + entry, + new TreeNodeBean(label:name, icon:JAVA_FILE_ICON), + FACTORY + ) + } + + static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory { + public T makePage(API a, Container.Entry e) { + return new JavaFilePage(a, e) + } + + public String makeTip(API api, Container.Entry entry) { + def file = new File(entry.container.root.uri) + return "Location: $file.path" + } + } +} diff --git a/services/src/main/groovy/jd/gui/view/component/ClassFilePage.groovy b/services/src/main/groovy/jd/gui/view/component/ClassFilePage.groovy index 60fac35..af7586b 100644 --- a/services/src/main/groovy/jd/gui/view/component/ClassFilePage.groovy +++ b/services/src/main/groovy/jd/gui/view/component/ClassFilePage.groovy @@ -13,22 +13,16 @@ import jd.core.process.DecompilerImpl import jd.gui.api.API import jd.gui.api.feature.* import jd.gui.api.model.Container -import jd.gui.api.model.Indexes import jd.gui.util.decompiler.ClassFileSourcePrinter import jd.gui.util.decompiler.ContainerLoader import jd.gui.util.decompiler.GuiPreferences -import jd.gui.util.matcher.DescriptorMatcher import org.fife.ui.rsyntaxtextarea.DocumentRange import org.fife.ui.rsyntaxtextarea.SyntaxConstants import javax.swing.text.DefaultCaret import java.awt.Color -import java.awt.Point -import java.util.regex.Pattern -class ClassFilePage - extends CustomLineNumbersPage - implements UriGettable, IndexesChangeListener, LineNumberNavigable, FocusedTypeGettable, PreferencesChangeListener { +class ClassFilePage extends TypePage implements PreferencesChangeListener { protected static final String ESCAPE_UNICODE_CHARACTERS = 'ClassFileViewerPreferences.escapeUnicodeCharacters' protected static final String OMIT_THIS_PREFIX = 'ClassFileViewerPreferences.omitThisPrefix' @@ -37,15 +31,6 @@ class ClassFilePage protected static final Decompiler DECOMPILER = new DecompilerImpl() - protected API api - protected Container.Entry entry - protected Collection collectionOfIndexes - - protected ArrayList references = new ArrayList<>() - protected HashMap declarations = new HashMap<>() - protected TreeMap typeDeclarations = new TreeMap<>() - protected ArrayList strings = new ArrayList<>() - protected int maximumLineNumber = -1 static { @@ -69,9 +54,7 @@ class ClassFilePage } ClassFilePage(API api, Container.Entry entry) { - // Init attributes - this.api = api - this.entry = entry + super(api, entry) // Init view errorForeground = Color.decode(api.preferences.get('JdGuiPreferences.errorBackgroundColor')) // Display source @@ -111,43 +94,18 @@ class ClassFilePage maximumLineNumber = getMaximumSourceLineNumber() } - String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_JAVA } + @CompileStatic + protected static boolean getPreferenceValue(Map preferences, String key, boolean defaultValue) { + String v = preferences.get(key); - protected boolean isHyperlinkEnabled(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.reference.enabled } - - protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { - if (hyperlinkData.reference.enabled) { - // Save current position in history - def location = textArea.getLocationOnScreen() - int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) - def uri = entry.uri - api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) - - // Open link - def reference = hyperlinkData.reference - def typeName = reference.type - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(typeName) }.flatten().grep { it!=null } - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) } - def fragment = typeName - - if (reference.name) { - fragment += '-' + reference.name - } - if (reference.descriptor) { - fragment += '-' + reference.descriptor - } - - if (sameContainerEntries) { - api.openURI(x, y, sameContainerEntries, null, fragment) - } else if (entries) { - api.openURI(x, y, entries, null, fragment) - } + if (v == null) { + return defaultValue; + } else { + return Boolean.valueOf(v); } } - // --- UriGettable --- // - URI getUri() { entry.uri } + String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_JAVA } // --- ContentSavable --- // String getFileName() { @@ -156,89 +114,6 @@ class ClassFilePage return path.substring(0, index) + '.java' } - // --- IndexesChangeListener --- // - @CompileStatic - void indexesChanged(Collection collectionOfIndexes) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // Refresh links - boolean refresh = false - - for (def reference : references) { - def typeName = reference.type - boolean enabled - - if (reference.name) { - typeName = searchTypeHavingMember(typeName, reference.name, reference.descriptor, entry) - if (typeName) { - // Replace type with the real type containing the referenced member - reference.type = typeName - enabled = true - } else { - enabled = false - } - } else { - enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(typeName) } != null - } - - if (reference.enabled != enabled) { - reference.enabled = enabled - refresh = true - } - } - - if (refresh) { - textArea.repaint() - } - } - - protected String searchTypeHavingMember(String typeName, String name, String descriptor, Container.Entry entry) { - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(typeName) }.flatten().grep { it!=null } - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { Container.Entry e -> e.uri.toString().startsWith(rootUri) } - - if (sameContainerEntries) { - return searchTypeHavingMember(typeName, name, descriptor, sameContainerEntries) - } else { - return searchTypeHavingMember(typeName, name, descriptor, entries) - } - } - - @CompileStatic - protected String searchTypeHavingMember(String typeName, String name, String descriptor, List entries) { - for (def entry : entries) { - def type = api.getTypeFactory(entry).make(api, entry, null) - - if (type) { - if (descriptor.indexOf('(') == -1) { - // Search a field - for (def field : type.fields) { - if (field.name.equals(name) && field.descriptor.equals(descriptor)) { - // Field found - return typeName - } - } - } else { - // Search a method - for (def method : type.methods) { - if (method.name.equals(name) && method.descriptor.equals(descriptor)) { - // Method found - return typeName - } - } - } - - // Not found -> Search in super type - def typeOwnerName = searchTypeHavingMember(type.superName, name, descriptor, entry) - if (typeOwnerName) { - return typeOwnerName - } - } - } - - return null - } - // --- LineNumberNavigable --- // int getMaximumLineNumber() { maximumLineNumber } @@ -253,218 +128,6 @@ class ClassFilePage boolean checkLineNumber(int lineNumber) { lineNumber <= maximumLineNumber } - // --- UriOpenable --- // - /** - * @param uri for URI format, @see jd.gui.api.feature.UriOpenable - */ - boolean openUri(URI uri) { - List ranges = [] - def fragment = uri.fragment - def query = uri.query - - textArea.highlighter.clearMarkAllHighlights() - - if (fragment) { - matchFragmentAndAddDocumentRange(fragment, declarations, ranges) - } - - if (query) { - Map parameters = parseQuery(query) - - if (parameters.containsKey('lineNumber')) { - def lineNumber = parameters.get('lineNumber') - if (lineNumber.isNumber()) { - goToLineNumber(lineNumber.toInteger()) - return true - } - } else if (parameters.containsKey('position')) { - def position = parameters.get('position') - if (position.isNumber()) { - int pos = position.toInteger() - if (textArea.document.length > pos) { - ranges.add(new DocumentRange(pos, pos)) - } - } - } else { - matchQueryAndAddDocumentRange(parameters, declarations, hyperlinks, strings, ranges) - } - } - - if (ranges) { - textArea.markAllHighlightColor = SELECT_HIGHLIGHT_COLOR - textArea.markAll(ranges) - setCaretPositionAndCenter(ranges.sort().get(0)) - } - - return true - } - - @CompileStatic - static void matchFragmentAndAddDocumentRange( - String fragment, HashMap declarations, List ranges) { - - if ((fragment.indexOf('?') != -1) || (fragment.indexOf('*') != -1)) { - // Unknown type and/or descriptor ==> Select all and scroll to the first one - int lastDash = fragment.lastIndexOf('-') - - if (lastDash == -1) { - // Search types - String slashAndTypeName = fragment.substring(1) - String typeName = fragment.substring(2) - - for (def entry : declarations.entrySet()) { - if (entry.key.endsWith(slashAndTypeName) || entry.key.equals(typeName)) { - ranges.add(new DocumentRange(entry.value.startPosition, entry.value.endPosition)) - } - } - } else { - def prefix = fragment.substring(0, lastDash+1) - def suffix = fragment.substring(lastDash+1) - def addRangeClosure - - if (suffix.charAt(0) == '(') { - addRangeClosure = { String key, DeclarationData value -> - int index = key.lastIndexOf('-') + 1 - if (DescriptorMatcher.matchMethodDescriptors(suffix, key.substring(index))) { - ranges.add(new DocumentRange(value.startPosition, value.endPosition)) - } - } - } else { - addRangeClosure = { String key, DeclarationData value -> - int index = key.lastIndexOf('-') + 1 - if (DescriptorMatcher.matchFieldDescriptors(suffix, key.substring(index))) { - ranges.add(new DocumentRange(value.startPosition, value.endPosition)) - } - } - } - - if (fragment.charAt(0) == '*') { - // Unknown type - String slashAndTypeNameAndName = prefix.substring(1) - String typeNameAndName = prefix.substring(2) - - for (def entry : declarations.entrySet()) { - if ((entry.key.indexOf(slashAndTypeNameAndName) != -1) || (entry.key.startsWith(typeNameAndName))) { - addRangeClosure(entry.key, entry.value) - } - } - } else { - // Known type - for (def entry : declarations.entrySet()) { - if (entry.key.startsWith(prefix)) { - addRangeClosure(entry.key, entry.value) - } - } - } - } - } else { - // Known type and descriptor ==> Search and high light item - def data = declarations.get(fragment) - if (data) { - ranges.add(new DocumentRange(data.startPosition, data.endPosition)) - } - } - } - - @CompileStatic - static void matchQueryAndAddDocumentRange( - Map parameters, - HashMap declarations, TreeMap hyperlinks, ArrayList strings, - List ranges) { - - def highlightFlags = parameters.get('highlightFlags') - def highlightPattern = parameters.get('highlightPattern') - - if (highlightFlags && highlightPattern) { - def highlightScope = parameters.get('highlightScope') - def regexp = createRegExp(highlightPattern) - def pattern = Pattern.compile(regexp + '.*') - - if (highlightFlags.indexOf('s') != -1) { - // Highlight strings - def patternForString = Pattern.compile(regexp) - - for (def data : strings) { - if (matchScope(highlightScope, data.owner)) { - def matcher = patternForString.matcher(data.text) - int offset = data.startPosition - - while(matcher.find()) { - ranges.add(new DocumentRange(offset + matcher.start(), offset + matcher.end())) - } - } - } - } - - boolean t = (highlightFlags.indexOf('t') != -1) // Highlight types - boolean f = (highlightFlags.indexOf('f') != -1) // Highlight fields - boolean m = (highlightFlags.indexOf('m') != -1) // Highlight methods - boolean c = (highlightFlags.indexOf('c') != -1) // Highlight constructors - - if (highlightFlags.indexOf('d') != -1) { - // Highlight declarations - for (def entry : declarations.entrySet()) { - def declaration = entry.value - - if (matchScope(highlightScope, declaration.type)) { - if ((t && declaration.isAType()) || (c && declaration.isAConstructor())) { - matchAndAddDocumentRange(pattern, getMostInnerTypeName(declaration.type), declaration.startPosition, declaration.endPosition, ranges) - } - if ((f && declaration.isAField()) || (m && declaration.isAMethod())) { - matchAndAddDocumentRange(pattern, declaration.name, declaration.startPosition, declaration.endPosition, ranges) - } - } - } - } - - if (highlightFlags.indexOf('r') != -1) { - // Highlight references - for (def entry : hyperlinks.entrySet()) { - def hyperlink = entry.value - def reference = ((HyperlinkReferenceData)hyperlink).reference - - if (matchScope(highlightScope, reference.owner)) { - if ((t && reference.isAType()) || (c && reference.isAConstructor())) { - matchAndAddDocumentRange(pattern, getMostInnerTypeName(reference.type), hyperlink.startPosition, hyperlink.endPosition, ranges) - } - if ((f && reference.isAField()) || (m && reference.isAMethod())) { - matchAndAddDocumentRange(pattern, reference.name, hyperlink.startPosition, hyperlink.endPosition, ranges) - } - } - } - } - } - } - - @CompileStatic - static boolean matchScope(String scope, String type) { - if (!scope) - return true - if (scope.charAt(0) == '*') - return type.endsWith(scope.substring(1)) || type.equals(scope.substring(2)) - return type.equals(scope) - } - - @CompileStatic - static void matchAndAddDocumentRange(Pattern pattern, String text, int start, int end, List ranges) { - if (pattern.matcher(text).matches()) { - ranges.add(new DocumentRange(start, end)) - } - } - - @CompileStatic - static String getMostInnerTypeName(String typeName) { - int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1 - int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1 - int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex) - return typeName.substring(lastIndex) - } - - // --- FocusedTypeGettable --- // - String getFocusedTypeName() { typeDeclarations.floorEntry(textArea.caretPosition)?.value?.type } - - Container.Entry getEntry() { entry } - // --- PreferencesChangeListener --- // void preferencesChanged(Map preferences) { def caret = textArea.caret @@ -477,18 +140,16 @@ class ClassFilePage @CompileStatic class Printer extends ClassFileSourcePrinter { - protected StringBuffer stringBuffer + protected StringBuffer stringBuffer = new StringBuffer(10*1024) protected boolean realignmentLineNumber protected boolean showPrefixThis protected boolean unicodeEscape - protected HashMap referencesCache + protected HashMap referencesCache = new HashMap<>() Printer(GuiPreferences preferences) { - this.stringBuffer = new StringBuffer(10*1024) this.realignmentLineNumber = preferences.getRealignmentLineNumber() this.showPrefixThis = preferences.isShowPrefixThis() this.unicodeEscape = preferences.isUnicodeEscape() - this.referencesCache = new HashMap<>() } boolean getRealignmentLineNumber() { realignmentLineNumber } @@ -496,8 +157,7 @@ class ClassFilePage boolean isUnicodeEscape() { unicodeEscape } void append(char c) { stringBuffer.append(c) } - void append(String s) { - stringBuffer.append(s) } + void append(String s) { stringBuffer.append(s) } // Manage line number and misalignment int textAreaLineNumber = 1 @@ -526,52 +186,52 @@ class ClassFilePage } } - // --- Manage strings --- // + // --- Add strings --- // void printString(String s, String scopeInternalName) { - strings.add(new StringData(stringBuffer.length(), s.length(), s, scopeInternalName)) + strings.add(new TypePage.StringData(stringBuffer.length(), s.length(), s, scopeInternalName)) super.printString(s, scopeInternalName) } - // --- Manage references --- // + // --- Add references --- // void printTypeImport(String internalName, String name) { - addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, null, null, null))) + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, null, null, null))) super.printTypeImport(internalName, name) } void printType(String internalName, String name, String scopeInternalName) { - addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, null, null, scopeInternalName))) + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, null, null, scopeInternalName))) super.printType(internalName, name, scopeInternalName) } void printField(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) super.printField(internalName, name, descriptor, scopeInternalName) } void printStaticField(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) super.printStaticField(internalName, name, descriptor, scopeInternalName) } void printConstructor(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, "", descriptor, scopeInternalName))) + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, "", descriptor, scopeInternalName))) super.printConstructor(internalName, name, descriptor, scopeInternalName) } void printMethod(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) super.printMethod(internalName, name, descriptor, scopeInternalName) } void printStaticMethod(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) super.printStaticMethod(internalName, name, descriptor, scopeInternalName) } - ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) { + TypePage.ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) { def key = internalName + '-' + name + '-'+ descriptor + '-' + scopeInternalName def reference = referencesCache.get(key) if (reference == null) { - reference = new ReferenceData(internalName, name, descriptor, scopeInternalName) + reference = new TypePage.ReferenceData(internalName, name, descriptor, scopeInternalName) referencesCache.put(key, reference) references.add(reference) } @@ -579,132 +239,37 @@ class ClassFilePage return reference } - // --- Manage declarations --- // + // --- Add declarations --- // void printTypeDeclaration(String internalName, String name) { - def data = new DeclarationData(stringBuffer.length(), name.length(), internalName, null, null) + def data = new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, null, null) declarations.put(internalName, data) typeDeclarations.put(stringBuffer.length(), data) super.printTypeDeclaration(internalName, name) } void printFieldDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '-' + name + '-' + descriptor, new DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) + declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) super.printFieldDeclaration(internalName, name, descriptor) } void printStaticFieldDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '-' + name + '-' + descriptor, new DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) + declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) super.printStaticFieldDeclaration(internalName, name, descriptor) } void printConstructorDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '--' + descriptor, new DeclarationData(stringBuffer.length(), name.length(), internalName, "", descriptor)) + declarations.put(internalName + '--' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, "", descriptor)) super.printConstructorDeclaration(internalName, name, descriptor) } void printMethodDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '-' + name + '-' + descriptor, new DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) + declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) super.printMethodDeclaration(internalName, name, descriptor) } void printStaticMethodDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '-' + name + '-' + descriptor, new DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) + declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) super.printStaticMethodDeclaration(internalName, name, descriptor) } String toString() { stringBuffer.toString() } } - - @CompileStatic - static class StringData { - int startPosition - int endPosition - String text - String owner - - StringData(int startPosition, int length, String text, String owner) { - this.startPosition = startPosition - this.endPosition = startPosition + length - this.text = text - this.owner = owner - } - } - - @CompileStatic - static class DeclarationData { - int startPosition - int endPosition - String type - /** - * Field or method name or null for type - */ - String name - String descriptor - - DeclarationData(int startPosition, int length, String type, String name, String descriptor) { - this.startPosition = startPosition - this.endPosition = startPosition + length - this.type = type - this.name = name - this.descriptor = descriptor - } - - boolean isAType() { name == null } - boolean isAField() { descriptor && descriptor.charAt(0) != '('} - boolean isAMethod() { descriptor && descriptor.charAt(0) == '('} - boolean isAConstructor() { "".equals(name) } - } - - @CompileStatic - static class HyperlinkReferenceData extends HyperlinkPage.HyperlinkData { - ReferenceData reference - - HyperlinkReferenceData(int startPosition, int length, ReferenceData reference) { - super(startPosition, startPosition+length) - this.reference = reference - } - } - - @CompileStatic - static class ReferenceData { - String type - /** - * Field or method name or null for type - */ - String name - /** - * Field or method descriptor or null for type - */ - String descriptor - /** - * Internal type name containing reference or null for "import" statement. - * Used to high light items matching with URI like "file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=type". - */ - String owner - /** - * "Enabled" flag for link of reference - */ - boolean enabled = false - - ReferenceData(String type, String name, String descriptor, String owner) { - this.type = type - this.name = name - this.descriptor = descriptor - this.owner = owner - } - - boolean isAType() { name == null } - boolean isAField() { descriptor && descriptor.charAt(0) != '('} - boolean isAMethod() { descriptor && descriptor.charAt(0) == '('} - boolean isAConstructor() { "".equals(name) } - } - - @CompileStatic - protected static boolean getPreferenceValue(Map preferences, String key, boolean defaultValue) { - String v = preferences.get(key); - - if (v == null) { - return defaultValue; - } else { - return Boolean.valueOf(v); - } - } } diff --git a/services/src/main/groovy/jd/gui/view/component/EjbJarXmlFilePage.groovy b/services/src/main/groovy/jd/gui/view/component/EjbJarXmlFilePage.groovy index e9351fc..9a0eb21 100644 --- a/services/src/main/groovy/jd/gui/view/component/EjbJarXmlFilePage.groovy +++ b/services/src/main/groovy/jd/gui/view/component/EjbJarXmlFilePage.groovy @@ -15,7 +15,7 @@ import org.fife.ui.rsyntaxtextarea.SyntaxConstants import java.awt.* -class EjbJarXmlFilePage extends TypeHyperlinkPage implements UriGettable, IndexesChangeListener { +class EjbJarXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { protected API api protected Container.Entry entry protected Collection collectionOfIndexes @@ -133,7 +133,7 @@ class EjbJarXmlFilePage extends TypeHyperlinkPage implements UriGettable, Indexe int startIndex = position + text.indexOf(trim) int endIndex = startIndex + trim.length() def internalTypeName = trim.replace('.', '/') - addHyperlink(new TypeHyperlinkPage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)) + addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)) } } } diff --git a/services/src/main/groovy/jd/gui/view/component/JavaFilePage.groovy b/services/src/main/groovy/jd/gui/view/component/JavaFilePage.groovy new file mode 100644 index 0000000..5619b1f --- /dev/null +++ b/services/src/main/groovy/jd/gui/view/component/JavaFilePage.groovy @@ -0,0 +1,735 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.view.component + +import groovy.transform.CompileStatic +import jd.gui.api.API +import jd.gui.api.model.Container +import jd.gui.util.parser.antlr.ANTLRParser +import jd.gui.util.parser.antlr.AbstractJavaListener +import jd.gui.util.parser.antlr.JavaParser +import org.antlr.v4.runtime.ANTLRInputStream +import org.antlr.v4.runtime.ParserRuleContext +import org.antlr.v4.runtime.tree.ParseTree +import org.antlr.v4.runtime.tree.TerminalNode +import org.fife.ui.rsyntaxtextarea.SyntaxConstants + +class JavaFilePage extends TypePage { + + JavaFilePage(API api, Container.Entry entry) { + super(api, entry) + // Load content file + def text = entry.inputStream.text.replace('\r\n', '\n').replace('\r', '\n') + // Parse + def declarationListener = new DeclarationListener(entry) + def referenceListener = new ReferenceListener(entry) + + ANTLRParser.parse(new ANTLRInputStream(text), declarationListener) + referenceListener.init(declarationListener) + ANTLRParser.parse(new ANTLRInputStream(text), referenceListener) + // Display + setText(text) + // Show hyperlinks + indexesChanged(api.collectionOfIndexes) + } + + String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_JAVA } + + // --- ContentSavable --- // + String getFileName() { + def path = entry.path + int index = path.lastIndexOf('/') + return path.substring(index+1) + } + + @CompileStatic + class DeclarationListener extends AbstractJavaListener { + + protected StringBuffer sbTypeDeclaration = new StringBuffer() + protected String currentInternalTypeName + + DeclarationListener(Container.Entry entry) { super(entry) } + + HashMap getNameToInternalTypeName() { super.nameToInternalTypeName } + + // --- Add declarations --- // + void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { + super.enterPackageDeclaration(ctx); + + if (! packageName.isEmpty()) { + sbTypeDeclaration.append(packageName).append('/'); + } + } + + void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { + List identifiers = ctx.qualifiedName().Identifier() + String internalTypeName = concatIdentifiers(identifiers) + String typeName = identifiers.get(identifiers.size()-1).symbol.text + + nameToInternalTypeName.put(typeName, internalTypeName) + } + + void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); } + void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } + + void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); } + void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } + + void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); } + void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } + + void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); } + void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } + + void enterTypeDeclaration(ParserRuleContext ctx) { + // Type declaration + def identifier = ctx.getToken(JavaParser.Identifier, 0); + def typeName = identifier.text + int position = identifier.symbol.startIndex + int length = sbTypeDeclaration.length(); + + if ((length == 0) || (sbTypeDeclaration.charAt(length-1) == '/')) { + sbTypeDeclaration.append(typeName); + } else { + sbTypeDeclaration.append('$').append(typeName); + } + + currentInternalTypeName = sbTypeDeclaration.toString() + nameToInternalTypeName.put(typeName, currentInternalTypeName); + + // Super type reference + JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0) + String superInternalTypeName = superType ? resolveInternalTypeName(superType.classOrInterfaceType().Identifier()) : null + + def data = new TypeDeclarationData(position, typeName.length(), currentInternalTypeName, null, null, superInternalTypeName) + + declarations.put(currentInternalTypeName, data) + typeDeclarations.put(position, data) + } + + void exitTypeDeclaration() { + int index = sbTypeDeclaration.lastIndexOf('$'); + + if (index == -1) { + index = sbTypeDeclaration.lastIndexOf('/') + 1; + } + + if (index == -1) { + sbTypeDeclaration.setLength(0); + } else { + sbTypeDeclaration.setLength(index); + } + + currentInternalTypeName = sbTypeDeclaration.toString() + } + + public void enterClassBodyDeclaration(JavaParser.ClassBodyDeclarationContext ctx) { + if (ctx.getChildCount() == 2) { + def first = ctx.getChild(0); + + if (first instanceof TerminalNode) { + if (first.getSymbol().type == JavaParser.STATIC) { + String name = first.text + int position = first.getSymbol().startIndex + declarations.put(currentInternalTypeName + '--()V', new TypePage.DeclarationData(position, 5, currentInternalTypeName, name, '()V')) + } + } + } + } + + void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) { + def typeContext = ctx.type(); + + for (def constantDeclaratorContext : ctx.constantDeclarator()) { + def identifier = constantDeclaratorContext.Identifier() + def name = identifier.text + int dimensionOnVariable = countDimension(constantDeclaratorContext.children) + def descriptor = createDescriptor(typeContext, dimensionOnVariable) + int position = identifier.symbol.startIndex + + declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)) + } + } + + void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) { + def typeContext = ctx.type(); + + for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) { + def variableDeclaratorId = declaration.variableDeclaratorId() + def identifier = variableDeclaratorId.Identifier() + def name = identifier.text + int dimensionOnVariable = countDimension(variableDeclaratorId.children) + def descriptor = createDescriptor(typeContext, dimensionOnVariable) + int position = identifier.symbol.startIndex + def data = new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor) + + declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, data) + } + } + + void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { + enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); + } + + void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) { + enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); + } + + void enterMethodDeclaration( + ParserRuleContext ctx, TerminalNode identifier, + JavaParser.FormalParametersContext formalParameters, JavaParser.TypeContext returnType) { + + def name = identifier.text + def paramDescriptors = createParamDescriptors(formalParameters.formalParameterList()) + def returnDescriptor = createDescriptor(returnType, 0) + def descriptor = paramDescriptors + returnDescriptor + int position = identifier.symbol.startIndex + + declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)) + } + + void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) { + def identifier = ctx.Identifier() + def name = identifier.text + def paramDescriptors = createParamDescriptors(ctx.formalParameters().formalParameterList()) + def descriptor = paramDescriptors + "V" + int position = identifier.symbol.startIndex + + declarations.put(currentInternalTypeName + '--' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)) + } + + String createParamDescriptors(JavaParser.FormalParameterListContext formalParameterList) { + StringBuffer paramDescriptors = null + + if (formalParameterList != null) { + def formalParameters = formalParameterList.formalParameter() + paramDescriptors = new StringBuffer("(") + + for (def formalParameter : formalParameters) { + int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children) + def descriptor = createDescriptor(formalParameter.type(), dimensionOnParameter) + + paramDescriptors.append(descriptor) + } + } + + return (paramDescriptors == null) ? "()" : paramDescriptors.append(')').toString(); + } + } + + @CompileStatic + class ReferenceListener extends AbstractJavaListener { + + protected StringBuffer sbTypeDeclaration = new StringBuffer() + protected HashMap referencesCache = new HashMap<>() + protected String currentInternalTypeName + protected Context currentContext = null + + ReferenceListener(Container.Entry entry) { super(entry) } + + void init(DeclarationListener declarationListener) { + this.nameToInternalTypeName.putAll(declarationListener.nameToInternalTypeName) + } + + // --- Add declarations --- // + void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { + super.enterPackageDeclaration(ctx); + + if (! packageName.isEmpty()) { + sbTypeDeclaration.append(packageName).append('/'); + } + } + + void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { + List identifiers = ctx.qualifiedName().Identifier() + int position = identifiers.get(0).symbol.startIndex + String internalTypeName = concatIdentifiers(identifiers) + + addHyperlink(new TypePage.HyperlinkReferenceData(position, internalTypeName.length(), newReferenceData(internalTypeName, null, null, null))) + } + + void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); } + void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } + + void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); } + void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } + + void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); } + void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } + + void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); } + void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } + + void enterTypeDeclaration(ParserRuleContext ctx) { + // Type declaration + def identifier = ctx.getToken(JavaParser.Identifier, 0); + def typeName = identifier.text + int length = sbTypeDeclaration.length(); + + if ((length == 0) || (sbTypeDeclaration.charAt(length-1) == '/')) { + sbTypeDeclaration.append(typeName); + } else { + sbTypeDeclaration.append('$').append(typeName); + } + + currentInternalTypeName = sbTypeDeclaration.toString() + currentContext = new Context(currentContext) + } + + void exitTypeDeclaration() { + int index = sbTypeDeclaration.lastIndexOf('$'); + + if (index == -1) { + index = sbTypeDeclaration.lastIndexOf('/') + 1; + } + + if (index == -1) { + sbTypeDeclaration.setLength(0); + } else { + sbTypeDeclaration.setLength(index); + } + + currentInternalTypeName = sbTypeDeclaration.toString() + } + + void enterFormalParameters(JavaParser.FormalParametersContext ctx) { + def formalParameterList = ctx.formalParameterList() + + if (formalParameterList != null) { + def formalParameters = formalParameterList.formalParameter() + + for (def formalParameter : formalParameters) { + int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children) + def descriptor = createDescriptor(formalParameter.type(), dimensionOnParameter) + def name = formalParameter.variableDeclaratorId().Identifier().symbol.text + + currentContext.nameToDescriptor.put(name, descriptor) + } + } + } + + // --- Add references --- // + void enterType(JavaParser.TypeContext ctx) { + // Add type reference + def classOrInterfaceType = ctx.classOrInterfaceType() + + if (classOrInterfaceType != null) { + def identifiers = classOrInterfaceType.Identifier() + def name = concatIdentifiers(identifiers) + def internalTypeName = resolveInternalTypeName(identifiers) + int position = identifiers.get(0).symbol.startIndex + + addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName))) + } + } + + void enterLocalVariableDeclaration(JavaParser.LocalVariableDeclarationContext ctx) { + def typeContext = ctx.type() + + for (def variableDeclarator : ctx.variableDeclarators().variableDeclarator()) { + def variableDeclaratorId = variableDeclarator.variableDeclaratorId() + int dimensionOnVariable = countDimension(variableDeclaratorId.children) + def descriptor = createDescriptor(typeContext, dimensionOnVariable) + def name = variableDeclarator.variableDeclaratorId().Identifier().getSymbol().getText() + + currentContext.nameToDescriptor.put(name, descriptor) + } + } + + void enterCreator(JavaParser.CreatorContext ctx) { + enterNewExpression(ctx.createdName().Identifier(), ctx.classCreatorRest()) + } + + void enterInnerCreator(JavaParser.InnerCreatorContext ctx) { + enterNewExpression(Collections.singletonList(ctx.Identifier()), ctx.classCreatorRest()) + } + + void enterNewExpression(List identifiers, JavaParser.ClassCreatorRestContext classCreatorRest) { + if (identifiers.size() > 0) { + def name = concatIdentifiers(identifiers) + def internalTypeName = resolveInternalTypeName(identifiers) + int position = identifiers.get(0).symbol.startIndex + + if (classCreatorRest) { + // Constructor call -> Add a link to the constructor declaration + def expressionList = classCreatorRest.arguments().expressionList() + def descriptor = expressionList ? getParametersDescriptor(expressionList).append('V').toString() : '()V' + + addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, '', descriptor, currentInternalTypeName))) + } else { + // New type array -> Add a link to the type declaration + addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName))) + } + } + } + + void enterExpression(JavaParser.ExpressionContext ctx) { + switch (ctx.getChildCount()) { + case 1: + TerminalNode identifier0 = getToken(ctx.children, JavaParser.Identifier, 0); + + if (identifier0 != null) { + if (isAField(ctx)) { + def primaryContext = ctx.primary() + + if (primaryContext) { + String fieldName = primaryContext.literal().StringLiteral(); + + if (!currentContext.getDescriptor(fieldName) != null) { + // Not a local variable or a method parameter + def fieldTypeName = searchInternalTypeNameForThisFieldName(currentInternalTypeName, fieldName) + int position = ctx.Identifier().getSymbol().startIndex + + addHyperlink(new TypePage.HyperlinkReferenceData(position, fieldName.length(), newReferenceData(fieldTypeName, fieldName, '?', currentInternalTypeName))) + } + } + } + } else { + def identifier = ctx.primary().Identifier() + + if (identifier) { + def symbol = identifier.getSymbol() + def name = symbol.text + def internalTypeName = nameToInternalTypeName.get(name) + + if (internalTypeName) { + int position = symbol.startIndex + + addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName))) + } + } + } + break; + case 3: + if (getToken(ctx.children, JavaParser.DOT, 1) != null) { + // Search "expression '.' Identifier" : field reference + def identifier3 = getToken(ctx.children, JavaParser.Identifier, 2); + + if ((identifier3 != null) && isAField(ctx)) { + def fieldTypeName = getInternalTypeName(ctx.getChild(0)) + + if (fieldTypeName) { + int position = identifier3.symbol.startIndex + def fieldName = identifier3.getText() + + addHyperlink(new TypePage.HyperlinkReferenceData(position, fieldName.length(), newReferenceData(fieldTypeName, fieldName, '?', currentInternalTypeName))) + } + } + } else if (getToken(ctx.children, JavaParser.LPAREN, 1) != null) { + // Search "expression '(' ')'" : method reference + if (getToken(ctx.children, JavaParser.RPAREN, 2) != null) { + enterCallMethodExpression(ctx, null) + } + } + break; + case 4: + if (getToken(ctx.children, JavaParser.LPAREN, 1) != null) { + // Search "expression '(' expressionList ')'" : method reference + if (getToken(ctx.children, JavaParser.RPAREN, 3) != null) { + def expressionListContext = ctx.expressionList(); + + if ((expressionListContext != null) && (expressionListContext == ctx.children.get(2))) { + enterCallMethodExpression(ctx, expressionListContext) + } + } + } + break; + } + } + + void enterCallMethodExpression(JavaParser.ExpressionContext ctx, JavaParser.ExpressionListContext expressionListContext) { + ParseTree first = ctx.children.get(0) + + if (first instanceof JavaParser.ExpressionContext) { + switch (first.getChildCount()) { + case 1: + def primary = first.primary() + def identifier = primary.Identifier() + + if (identifier) { + def symbol = identifier.getSymbol() + + if (symbol) { + String methodName = symbol.text + String methodTypeName = searchInternalTypeNameForThisMethodName(currentInternalTypeName, methodName) + + if (methodTypeName) { + int position = symbol.startIndex + def methodDescriptor = expressionListContext ? getParametersDescriptor(expressionListContext).append('?').toString() : '()?' + + addHyperlink(new TypePage.HyperlinkReferenceData(position, methodName.length(), newReferenceData(methodTypeName, methodName, methodDescriptor, currentInternalTypeName))) + } + } + } else { + def symbol = primary.getChild(TerminalNode.class, 0).getSymbol() + + if (symbol) { + switch (symbol.type) { + case JavaParser.THIS: + int position = symbol.startIndex + def methodDescriptor = expressionListContext ? getParametersDescriptor(expressionListContext).append('?').toString() : '()?' + + addHyperlink(new TypePage.HyperlinkReferenceData(position, 4, newReferenceData(currentInternalTypeName, '', methodDescriptor, currentInternalTypeName))) + break + case JavaParser.SUPER: + def data = declarations.get(currentInternalTypeName) + + if (data instanceof TypeDeclarationData) { + int position = symbol.startIndex + def methodTypeName = ((TypeDeclarationData) data).superTypeName + def methodDescriptor = expressionListContext ? getParametersDescriptor(expressionListContext).append('?').toString() : '()?' + + addHyperlink(new TypePage.HyperlinkReferenceData(position, 5, newReferenceData(methodTypeName, '', methodDescriptor, currentInternalTypeName))) + } + break + } + } + } + break + case 3: + // Search "expression '.' Identifier" + ParseTree dot = first.getChild(1) + + if ((dot instanceof TerminalNode) && (dot.getSymbol().getType() == JavaParser.DOT)) { + ParseTree identifier3 = first.getChild(2) + + if ((identifier3 instanceof TerminalNode) && (identifier3.getSymbol().type == JavaParser.Identifier)) { + String methodTypeName = getInternalTypeName(first.getChild(0)) + + if (methodTypeName) { + int position = identifier3.getSymbol().startIndex + def methodName = identifier3.getText() + def methodDescriptor = expressionListContext ? getParametersDescriptor(expressionListContext).append('?').toString() : '()?' + + addHyperlink(new TypePage.HyperlinkReferenceData(position, methodName.length(), newReferenceData(methodTypeName, methodName, methodDescriptor, currentInternalTypeName))) + } + } + } + break + } + } + } + + StringBuffer getParametersDescriptor(JavaParser.ExpressionListContext expressionListContext) { + def sb = new StringBuffer('(') + for (def exp : expressionListContext.expression()) sb.append('?') + sb.append(')') + return sb + } + + boolean isAField(JavaParser.ExpressionContext ctx) { + def parent = ctx.parent + + if (parent instanceof JavaParser.ExpressionContext) { + int size = parent.getChildCount(); + + if (parent.getChild(size - 1) != ctx) { + for (int i=0; i children, int type, int i) { + ParseTree pt = children.get(i); + + if (pt instanceof TerminalNode) { + if (((TerminalNode)pt).getSymbol().getType() == type) { + return (TerminalNode)pt; + } + } + + return null; + } + + void enterBlock(JavaParser.BlockContext ctx) { + currentContext = new Context(currentContext) + } + + void exitBlock(JavaParser.BlockContext ctx) { + currentContext = currentContext.outerContext + } + + TypePage.ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) { + def key = internalName + '-' + name + '-'+ descriptor + '-' + scopeInternalName + def reference = referencesCache.get(key) + + if (reference == null) { + reference = new TypePage.ReferenceData(internalName, name, descriptor, scopeInternalName) + referencesCache.put(key, reference) + references.add(reference) + } + + return reference + } + + // --- Add strings --- // + void enterLiteral(JavaParser.LiteralContext ctx) { + def stringLiteral = ctx.StringLiteral() + + if (stringLiteral != null) { + String str = stringLiteral.getSymbol().getText() + int position = stringLiteral.getSymbol().getStartIndex() + + strings.add(new TypePage.StringData(position, str.length(), str, currentInternalTypeName)) + } + } + } + + @CompileStatic + static class Context { + Context outerContext + + HashMap nameToDescriptor = new HashMap<>() + + Context(Context outerContext) { + this.outerContext = outerContext + } + + /** + * @param name Parameter or variable name + * @return Qualified type name + */ + String getDescriptor(String name) { + String descriptor = nameToDescriptor.get(name) + + if ((descriptor == null) && (outerContext != null)) { + descriptor = outerContext.getDescriptor(name) + } + + return descriptor + } + } + + @CompileStatic + static class TypeDeclarationData extends TypePage.DeclarationData { + String superTypeName + + TypeDeclarationData(int startPosition, int length, String type, String name, String descriptor, String superTypeName) { + super(startPosition, length, type, name, descriptor) + + this.superTypeName = superTypeName + } + } +} diff --git a/services/src/main/groovy/jd/gui/view/component/TextPage.groovy b/services/src/main/groovy/jd/gui/view/component/TextPage.groovy index bac1a93..5306afc 100644 --- a/services/src/main/groovy/jd/gui/view/component/TextPage.groovy +++ b/services/src/main/groovy/jd/gui/view/component/TextPage.groovy @@ -213,7 +213,7 @@ class TextPage extends JPanel implements ContentCopyable, ContentSelectable, Lin // --- LineNumberNavigable --- // int getMaximumLineNumber() { - return textArea.getLineOfOffset(textArea.document.length) + return textArea.getLineOfOffset(textArea.document.length) + 1 } void goToLineNumber(int lineNumber) { diff --git a/services/src/main/groovy/jd/gui/view/component/TypePage.groovy b/services/src/main/groovy/jd/gui/view/component/TypePage.groovy new file mode 100644 index 0000000..ac1835d --- /dev/null +++ b/services/src/main/groovy/jd/gui/view/component/TypePage.groovy @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ +package jd.gui.view.component + +import groovy.transform.CompileStatic +import jd.gui.api.API +import jd.gui.api.feature.FocusedTypeGettable +import jd.gui.api.feature.IndexesChangeListener +import jd.gui.api.feature.UriGettable +import jd.gui.api.feature.UriOpenable +import jd.gui.api.model.Container +import jd.gui.api.model.Indexes +import jd.gui.util.matcher.DescriptorMatcher +import org.fife.ui.rsyntaxtextarea.DocumentRange + +import java.awt.Point +import java.util.regex.Pattern + +abstract class TypePage extends CustomLineNumbersPage implements UriGettable, UriOpenable, IndexesChangeListener, FocusedTypeGettable { + + protected API api + protected Container.Entry entry + protected Collection collectionOfIndexes + + protected HashMap declarations = new HashMap<>() + protected TreeMap typeDeclarations = new TreeMap<>() + protected ArrayList references = new ArrayList<>() + protected ArrayList strings = new ArrayList<>() + + TypePage(API api, Container.Entry entry) { + // Init attributes + this.api = api + this.entry = entry + } + + protected boolean isHyperlinkEnabled(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.reference.enabled } + + protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { + if (hyperlinkData.reference.enabled) { + // Save current position in history + def location = textArea.getLocationOnScreen() + int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) + def uri = entry.uri + api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) + + // Open link + ReferenceData reference = hyperlinkData.reference + def typeName = reference.typeName + def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(typeName) }.flatten().grep { it != null } + def fragment = typeName + + if (reference.name) { + fragment += '-' + reference.name + } + if (reference.descriptor) { + fragment += '-' + reference.descriptor + } + + if (entries.contains(entry)) { + api.openURI(new URI(uri.scheme, uri.authority, uri.path, fragment)) + } else { + def rootUri = entry.container.root.uri.toString() + def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) } + + if (sameContainerEntries) { + api.openURI(x, y, sameContainerEntries, null, fragment) + } else if (entries) { + api.openURI(x, y, entries, null, fragment) + } + } + } + } + + // --- UriGettable --- // + URI getUri() { entry.uri } + + // --- UriOpenable --- // + /** + * @param uri for URI format, @see jd.gui.api.feature.UriOpenable + */ + boolean openUri(URI uri) { + List ranges = [] + def fragment = uri.fragment + def query = uri.query + + textArea.highlighter.clearMarkAllHighlights() + + if (fragment) { + matchFragmentAndAddDocumentRange(fragment, declarations, ranges) + } + + if (query) { + Map parameters = parseQuery(query) + + if (parameters.containsKey('lineNumber')) { + def lineNumber = parameters.get('lineNumber') + if (lineNumber.isNumber()) { + goToLineNumber(lineNumber.toInteger()) + return true + } + } else if (parameters.containsKey('position')) { + def position = parameters.get('position') + if (position.isNumber()) { + int pos = position.toInteger() + if (textArea.document.length > pos) { + ranges.add(new DocumentRange(pos, pos)) + } + } + } else { + matchQueryAndAddDocumentRange(parameters, declarations, hyperlinks, strings, ranges) + } + } + + if (ranges) { + textArea.markAllHighlightColor = SELECT_HIGHLIGHT_COLOR + textArea.markAll(ranges) + setCaretPositionAndCenter(ranges.sort().get(0)) + } + + return true + } + + @CompileStatic + static void matchFragmentAndAddDocumentRange( + String fragment, HashMap declarations, List ranges) { + + if ((fragment.indexOf('?') != -1) || (fragment.indexOf('*') != -1)) { + // Unknown type and/or descriptor ==> Select all and scroll to the first one + int lastDash = fragment.lastIndexOf('-') + + if (lastDash == -1) { + // Search types + String slashAndTypeName = fragment.substring(1) + String typeName = fragment.substring(2) + + for (def entry : declarations.entrySet()) { + if (entry.key.endsWith(slashAndTypeName) || entry.key.equals(typeName)) { + ranges.add(new DocumentRange(entry.value.startPosition, entry.value.endPosition)) + } + } + } else { + def prefix = fragment.substring(0, lastDash+1) + def suffix = fragment.substring(lastDash+1) + def addRangeClosure + + if (suffix.charAt(0) == '(') { + addRangeClosure = { String key, DeclarationData value -> + int index = key.lastIndexOf('-') + 1 + if (DescriptorMatcher.matchMethodDescriptors(suffix, key.substring(index))) { + ranges.add(new DocumentRange(value.startPosition, value.endPosition)) + } + } + } else { + addRangeClosure = { String key, DeclarationData value -> + int index = key.lastIndexOf('-') + 1 + if (DescriptorMatcher.matchFieldDescriptors(suffix, key.substring(index))) { + ranges.add(new DocumentRange(value.startPosition, value.endPosition)) + } + } + } + + if (fragment.charAt(0) == '*') { + // Unknown type + String slashAndTypeNameAndName = prefix.substring(1) + String typeNameAndName = prefix.substring(2) + + for (def entry : declarations.entrySet()) { + if ((entry.key.indexOf(slashAndTypeNameAndName) != -1) || (entry.key.startsWith(typeNameAndName))) { + addRangeClosure(entry.key, entry.value) + } + } + } else { + // Known type + for (def entry : declarations.entrySet()) { + if (entry.key.startsWith(prefix)) { + addRangeClosure(entry.key, entry.value) + } + } + } + } + } else { + // Known type and descriptor ==> Search and high light item + def data = declarations.get(fragment) + if (data) { + ranges.add(new DocumentRange(data.startPosition, data.endPosition)) + } + } + } + + @CompileStatic + static void matchQueryAndAddDocumentRange( + Map parameters, + HashMap declarations, TreeMap hyperlinks, ArrayList strings, + List ranges) { + + def highlightFlags = parameters.get('highlightFlags') + def highlightPattern = parameters.get('highlightPattern') + + if (highlightFlags && highlightPattern) { + def highlightScope = parameters.get('highlightScope') + def regexp = createRegExp(highlightPattern) + def pattern = Pattern.compile(regexp + '.*') + + if (highlightFlags.indexOf('s') != -1) { + // Highlight strings + def patternForString = Pattern.compile(regexp) + + for (def data : strings) { + if (matchScope(highlightScope, data.owner)) { + def matcher = patternForString.matcher(data.text) + int offset = data.startPosition + + while(matcher.find()) { + ranges.add(new DocumentRange(offset + matcher.start(), offset + matcher.end())) + } + } + } + } + + boolean t = (highlightFlags.indexOf('t') != -1) // Highlight types + boolean f = (highlightFlags.indexOf('f') != -1) // Highlight fields + boolean m = (highlightFlags.indexOf('m') != -1) // Highlight methods + boolean c = (highlightFlags.indexOf('c') != -1) // Highlight constructors + + if (highlightFlags.indexOf('d') != -1) { + // Highlight declarations + for (def entry : declarations.entrySet()) { + def declaration = entry.value + + if (matchScope(highlightScope, declaration.typeName)) { + if ((t && declaration.isAType()) || (c && declaration.isAConstructor())) { + matchAndAddDocumentRange(pattern, getMostInnerTypeName(declaration.typeName), declaration.startPosition, declaration.endPosition, ranges) + } + if ((f && declaration.isAField()) || (m && declaration.isAMethod())) { + matchAndAddDocumentRange(pattern, declaration.name, declaration.startPosition, declaration.endPosition, ranges) + } + } + } + } + + if (highlightFlags.indexOf('r') != -1) { + // Highlight references + for (def entry : hyperlinks.entrySet()) { + def hyperlink = entry.value + def reference = ((HyperlinkReferenceData)hyperlink).reference + + if (matchScope(highlightScope, reference.owner)) { + if ((t && reference.isAType()) || (c && reference.isAConstructor())) { + matchAndAddDocumentRange(pattern, getMostInnerTypeName(reference.typeName), hyperlink.startPosition, hyperlink.endPosition, ranges) + } + if ((f && reference.isAField()) || (m && reference.isAMethod())) { + matchAndAddDocumentRange(pattern, reference.name, hyperlink.startPosition, hyperlink.endPosition, ranges) + } + } + } + } + } + } + + @CompileStatic + static boolean matchScope(String scope, String type) { + if (!scope) + return true + if (scope.charAt(0) == '*') + return type.endsWith(scope.substring(1)) || type.equals(scope.substring(2)) + return type.equals(scope) + } + + @CompileStatic + static void matchAndAddDocumentRange(Pattern pattern, String text, int start, int end, List ranges) { + if (pattern.matcher(text).matches()) { + ranges.add(new DocumentRange(start, end)) + } + } + + @CompileStatic + static String getMostInnerTypeName(String typeName) { + int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1 + int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1 + int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex) + return typeName.substring(lastIndex) + } + + // --- FocusedTypeGettable --- // + String getFocusedTypeName() { typeDeclarations.floorEntry(textArea.caretPosition)?.value?.typeName } + + Container.Entry getEntry() { entry } + + // --- IndexesChangeListener --- // + @CompileStatic + void indexesChanged(Collection collectionOfIndexes) { + // Update the list of containers + this.collectionOfIndexes = collectionOfIndexes + // Refresh links + boolean refresh = false + + for (def reference : references) { + def typeName = reference.typeName + boolean enabled + + if (reference.name) { + typeName = searchTypeHavingMember(typeName, reference.name, reference.descriptor, entry) + if (typeName) { + // Replace type with the real type containing the referenced member + reference.typeName = typeName + enabled = true + } else { + enabled = false + } + } else { + enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(typeName) } != null + } + + if (reference.enabled != enabled) { + reference.enabled = enabled + refresh = true + } + } + + if (refresh) { + textArea.repaint() + } + } + + protected String searchTypeHavingMember(String typeName, String name, String descriptor, Container.Entry entry) { + def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(typeName) }.flatten().grep { it!=null } + def rootUri = entry.container.root.uri.toString() + def sameContainerEntries = entries?.grep { Container.Entry e -> e.uri.toString().startsWith(rootUri) } + + if (sameContainerEntries) { + return searchTypeHavingMember(typeName, name, descriptor, sameContainerEntries) + } else { + return searchTypeHavingMember(typeName, name, descriptor, entries) + } + } + + @CompileStatic + protected String searchTypeHavingMember(String typeName, String name, String descriptor, List entries) { + for (def entry : entries) { + def type = api.getTypeFactory(entry).make(api, entry, typeName) + + if (type) { + if (descriptor.indexOf('(') == -1) { + // Search a field + for (def field : type.fields) { + if (field.name.equals(name) && DescriptorMatcher.matchFieldDescriptors(field.descriptor, descriptor)) { + // Field found + return typeName + } + } + } else { + // Search a method + for (def method : type.methods) { + if (method.name.equals(name) && DescriptorMatcher.matchMethodDescriptors(method.descriptor, descriptor)) { + // Method found + return typeName + } + } + } + + // Not found -> Search in super type + def typeOwnerName = searchTypeHavingMember(type.superName, name, descriptor, entry) + if (typeOwnerName) { + return typeOwnerName + } + } + } + + return null + } + + @CompileStatic + static class StringData { + int startPosition + int endPosition + String text + String owner + + StringData(int startPosition, int length, String text, String owner) { + this.startPosition = startPosition + this.endPosition = startPosition + length + this.text = text + this.owner = owner + } + } + + @CompileStatic + static class DeclarationData { + int startPosition + int endPosition + String typeName + /** + * Field or method name or null for type + */ + String name + String descriptor + + DeclarationData(int startPosition, int length, String typeName, String name, String descriptor) { + this.startPosition = startPosition + this.endPosition = startPosition + length + this.typeName = typeName + this.name = name + this.descriptor = descriptor + } + + boolean isAType() { name == null } + boolean isAField() { descriptor && descriptor.charAt(0) != '('} + boolean isAMethod() { descriptor && descriptor.charAt(0) == '('} + boolean isAConstructor() { "".equals(name) } + } + + @CompileStatic + static class HyperlinkReferenceData extends HyperlinkPage.HyperlinkData { + ReferenceData reference + + HyperlinkReferenceData(int startPosition, int length, ReferenceData reference) { + super(startPosition, startPosition+length) + this.reference = reference + } + } + + @CompileStatic + static class ReferenceData { + String typeName + /** + * Field or method name or null for type + */ + String name + /** + * Field or method descriptor or null for type + */ + String descriptor + /** + * Internal type name containing reference or null for "import" statement. + * Used to high light items matching with URI like "file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=type". + */ + String owner + /** + * "Enabled" flag for link of reference + */ + boolean enabled = false + + ReferenceData(String typeName, String name, String descriptor, String owner) { + this.typeName = typeName + this.name = name + this.descriptor = descriptor + this.owner = owner + } + + boolean isAType() { name == null } + boolean isAField() { descriptor && descriptor.charAt(0) != '('} + boolean isAMethod() { descriptor && descriptor.charAt(0) == '('} + boolean isAConstructor() { "".equals(name) } + } +} diff --git a/services/src/main/groovy/jd/gui/view/component/TypeHyperlinkPage.groovy b/services/src/main/groovy/jd/gui/view/component/TypeReferencePage.groovy similarity index 96% rename from services/src/main/groovy/jd/gui/view/component/TypeHyperlinkPage.groovy rename to services/src/main/groovy/jd/gui/view/component/TypeReferencePage.groovy index 360c7f8..9176c76 100644 --- a/services/src/main/groovy/jd/gui/view/component/TypeHyperlinkPage.groovy +++ b/services/src/main/groovy/jd/gui/view/component/TypeReferencePage.groovy @@ -10,7 +10,10 @@ import org.fife.ui.rsyntaxtextarea.DocumentRange import java.util.regex.Pattern -abstract class TypeHyperlinkPage extends HyperlinkPage { +/** + * Page containing type references (Hyperlinks to pages of type) + */ +abstract class TypeReferencePage extends HyperlinkPage { // --- UriOpenable --- // boolean openUri(URI uri) { diff --git a/services/src/main/groovy/jd/gui/view/component/WebXmlFilePage.groovy b/services/src/main/groovy/jd/gui/view/component/WebXmlFilePage.groovy index 01f7de1..8c5f5ac 100644 --- a/services/src/main/groovy/jd/gui/view/component/WebXmlFilePage.groovy +++ b/services/src/main/groovy/jd/gui/view/component/WebXmlFilePage.groovy @@ -16,7 +16,7 @@ import org.fife.ui.rsyntaxtextarea.SyntaxConstants import java.awt.Point -class WebXmlFilePage extends TypeHyperlinkPage implements UriGettable, IndexesChangeListener { +class WebXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { protected API api protected Container.Entry entry protected Collection collectionOfIndexes @@ -47,7 +47,7 @@ class WebXmlFilePage extends TypeHyperlinkPage implements UriGettable, IndexesCh api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) // Open link - if (hyperlinkData instanceof TypeHyperlinkPage.TypeHyperlinkData) { + if (hyperlinkData instanceof TypeReferencePage.TypeHyperlinkData) { def internalTypeName = hyperlinkData.internalTypeName def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(internalTypeName) }.flatten().grep { it!=null } def rootUri = entry.container.root.uri.toString() @@ -163,7 +163,7 @@ class WebXmlFilePage extends TypeHyperlinkPage implements UriGettable, IndexesCh addHyperlink(new PathHyperlinkData(startIndex, endIndex, trim)) } else { def internalTypeName = trim.replace('.', '/') - addHyperlink(new TypeHyperlinkPage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)) + addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)) } } } diff --git a/services/src/main/java/jd/gui/service/indexer/AbstractIndexerProvider.java b/services/src/main/java/jd/gui/service/indexer/AbstractIndexerProvider.java index 53dfb23..72195e3 100644 --- a/services/src/main/java/jd/gui/service/indexer/AbstractIndexerProvider.java +++ b/services/src/main/java/jd/gui/service/indexer/AbstractIndexerProvider.java @@ -5,13 +5,13 @@ package jd.gui.service.indexer; +import jd.gui.api.model.Container; +import jd.gui.api.model.Indexes; import jd.gui.spi.Indexer; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; +import java.util.*; import java.util.regex.Pattern; public abstract class AbstractIndexerProvider implements Indexer { @@ -51,4 +51,15 @@ public abstract class AbstractIndexerProvider implements Indexer { return (externalSelectors==null) ? null : externalSelectors.toArray(new String[externalSelectors.size()]); } public Pattern getPathPattern() { return externalPathPattern; } + + @SuppressWarnings("unchecked") + protected static void addToIndex(Indexes indexes, String indexName, Set set, Container.Entry entry) { + if (set.size() > 0) { + Map index = indexes.getIndex(indexName); + + for (String key : set) { + index.get(key).add(entry); + } + } + } } diff --git a/services/src/main/java/jd/gui/service/indexer/ClassFileIndexerProvider.java b/services/src/main/java/jd/gui/service/indexer/ClassFileIndexerProvider.java index e923aa8..0bcfc8d 100644 --- a/services/src/main/java/jd/gui/service/indexer/ClassFileIndexerProvider.java +++ b/services/src/main/java/jd/gui/service/indexer/ClassFileIndexerProvider.java @@ -12,7 +12,6 @@ import jd.gui.api.API; import jd.gui.api.model.Container; import jd.gui.api.model.Indexes; -import java.io.IOException; import java.io.InputStream; import java.util.*; @@ -20,17 +19,17 @@ import java.util.*; * Unsafe thread implementation of class file indexer. */ public class ClassFileIndexerProvider extends AbstractIndexerProvider { - protected Set typeDeclarationSet = new HashSet<>(); - protected Set constructorDeclarationSet = new HashSet<>(); - protected Set methodDeclarationSet = new HashSet<>(); - protected Set fieldDeclarationSet = new HashSet<>(); - protected Set typeReferenceSet = new HashSet<>(); - protected Set constructorReferenceSet = new HashSet<>(); - protected Set methodReferenceSet = new HashSet<>(); - protected Set fieldReferenceSet = new HashSet<>(); - protected Set stringSet = new HashSet<>(); - protected Set superTypeNameSet = new HashSet<>(); - protected Set descriptorSet = new HashSet<>(); + protected HashSet typeDeclarationSet = new HashSet<>(); + protected HashSet constructorDeclarationSet = new HashSet<>(); + protected HashSet methodDeclarationSet = new HashSet<>(); + protected HashSet fieldDeclarationSet = new HashSet<>(); + protected HashSet typeReferenceSet = new HashSet<>(); + protected HashSet constructorReferenceSet = new HashSet<>(); + protected HashSet methodReferenceSet = new HashSet<>(); + protected HashSet fieldReferenceSet = new HashSet<>(); + protected HashSet stringSet = new HashSet<>(); + protected HashSet superTypeNameSet = new HashSet<>(); + protected HashSet descriptorSet = new HashSet<>(); protected ClassIndexer classIndexer = new ClassIndexer( typeDeclarationSet, constructorDeclarationSet, methodDeclarationSet, @@ -72,11 +71,9 @@ public class ClassFileIndexerProvider extends AbstractIndexerProvider { superTypeNameSet.clear(); descriptorSet.clear(); - InputStream inputStream = null; - - try { + try (InputStream inputStream = entry.getInputStream()) { // Index field, method, interfaces & super type - ClassReader classReader = new ClassReader(inputStream = entry.getInputStream()); + ClassReader classReader = new ClassReader(inputStream); classReader.accept(classIndexer, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); // Index descriptors @@ -159,32 +156,17 @@ public class ClassFileIndexerProvider extends AbstractIndexerProvider { } } catch (Exception ignore) { - } finally { - if (inputStream != null) { - try { inputStream.close(); } catch (IOException ignore) {} - } - } - } - - @SuppressWarnings("unchecked") - protected static void addToIndex(Indexes indexes, String indexName, Set set, Container.Entry entry) { - if (set.size() > 0) { - Map index = indexes.getIndex(indexName); - - for (String key : set) { - index.get(key).add(entry); - } } } protected static class ClassIndexer extends ClassVisitor { - protected Set typeDeclarationSet; - protected Set constructorDeclarationSet; - protected Set methodDeclarationSet; - protected Set fieldDeclarationSet; - protected Set typeReferenceSet; - protected Set superTypeNameSet; - protected Set descriptorSet; + protected HashSet typeDeclarationSet; + protected HashSet constructorDeclarationSet; + protected HashSet methodDeclarationSet; + protected HashSet fieldDeclarationSet; + protected HashSet typeReferenceSet; + protected HashSet superTypeNameSet; + protected HashSet descriptorSet; protected AnnotationIndexer annotationIndexer; protected FieldIndexer fieldIndexer; @@ -193,9 +175,9 @@ public class ClassFileIndexerProvider extends AbstractIndexerProvider { protected String name; public ClassIndexer( - Set typeDeclarationSet, Set constructorDeclarationSet, - Set methodDeclarationSet, Set fieldDeclarationSet, - Set typeReferenceSet, Set superTypeNameSet, Set descriptorSet) { + HashSet typeDeclarationSet, HashSet constructorDeclarationSet, + HashSet methodDeclarationSet, HashSet fieldDeclarationSet, + HashSet typeReferenceSet, HashSet superTypeNameSet, HashSet descriptorSet) { super(Opcodes.ASM5); this.typeDeclarationSet = typeDeclarationSet; @@ -258,9 +240,9 @@ public class ClassFileIndexerProvider extends AbstractIndexerProvider { } protected static class SignatureIndexer extends SignatureVisitor { - protected Set typeReferenceSet; + protected HashSet typeReferenceSet; - SignatureIndexer(Set typeReferenceSet) { + SignatureIndexer(HashSet typeReferenceSet) { super(Opcodes.ASM5); this.typeReferenceSet = typeReferenceSet; } @@ -271,9 +253,9 @@ public class ClassFileIndexerProvider extends AbstractIndexerProvider { } protected static class AnnotationIndexer extends AnnotationVisitor { - protected Set descriptorSet; + protected HashSet descriptorSet; - public AnnotationIndexer(Set descriptorSet) { + public AnnotationIndexer(HashSet descriptorSet) { super(Opcodes.ASM5); this.descriptorSet = descriptorSet; } @@ -289,10 +271,10 @@ public class ClassFileIndexerProvider extends AbstractIndexerProvider { } protected static class FieldIndexer extends FieldVisitor { - protected Set descriptorSet; + protected HashSet descriptorSet; protected AnnotationIndexer annotationIndexer; - public FieldIndexer(Set descriptorSet, AnnotationIndexer annotationInexer) { + public FieldIndexer(HashSet descriptorSet, AnnotationIndexer annotationInexer) { super(Opcodes.ASM5); this.descriptorSet = descriptorSet; this.annotationIndexer = annotationInexer; @@ -310,10 +292,10 @@ public class ClassFileIndexerProvider extends AbstractIndexerProvider { } protected static class MethodIndexer extends MethodVisitor { - protected Set descriptorSet; + protected HashSet descriptorSet; protected AnnotationIndexer annotationIndexer; - public MethodIndexer(Set descriptorSet, AnnotationIndexer annotationIndexer) { + public MethodIndexer(HashSet descriptorSet, AnnotationIndexer annotationIndexer) { super(Opcodes.ASM5); this.descriptorSet = descriptorSet; this.annotationIndexer = annotationIndexer; diff --git a/services/src/main/java/jd/gui/service/indexer/JavaFileIndexerProvider.java b/services/src/main/java/jd/gui/service/indexer/JavaFileIndexerProvider.java new file mode 100644 index 0000000..1675799 --- /dev/null +++ b/services/src/main/java/jd/gui/service/indexer/JavaFileIndexerProvider.java @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.service.indexer; + +import jd.gui.api.API; +import jd.gui.api.model.Container; +import jd.gui.api.model.Indexes; +import jd.gui.util.parser.antlr.ANTLRParser; +import jd.gui.util.parser.antlr.AbstractJavaListener; +import jd.gui.util.parser.antlr.JavaParser; +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +/** + * Unsafe thread implementation of java file indexer. + */ +public class JavaFileIndexerProvider extends AbstractIndexerProvider { + + static { + // Early class loading + ANTLRParser.parse(new ANTLRInputStream("class EarlyLoading{}"), new Listener(null)); + } + + /** + * @return local + optional external selectors + */ + public String[] getSelectors() { + List externalSelectors = getExternalSelectors(); + + if (externalSelectors == null) { + return new String[] { "*:file:*.java" }; + } else { + int size = externalSelectors.size(); + String[] selectors = new String[size+1]; + externalSelectors.toArray(selectors); + selectors[size] = "*:file:*.java"; + return selectors; + } + } + + /** + * Index format : @see jd.gui.spi.Indexer + */ + @SuppressWarnings("unchecked") + public void index(API api, Container.Entry entry, Indexes indexes) { + try (InputStream inputStream = entry.getInputStream()) { + Listener listener = new Listener(entry); + ANTLRParser.parse(new ANTLRInputStream(inputStream), listener); + + // Append sets to indexes + addToIndex(indexes, "typeDeclarations", listener.getTypeDeclarationSet(), entry); + addToIndex(indexes, "constructorDeclarations", listener.getConstructorDeclarationSet(), entry); + addToIndex(indexes, "methodDeclarations", listener.getMethodDeclarationSet(), entry); + addToIndex(indexes, "fieldDeclarations", listener.getFieldDeclarationSet(), entry); + addToIndex(indexes, "typeReferences", listener.getTypeReferenceSet(), entry); + addToIndex(indexes, "constructorReferences", listener.getConstructorReferenceSet(), entry); + addToIndex(indexes, "methodReferences", listener.getMethodReferenceSet(), entry); + addToIndex(indexes, "fieldReferences", listener.getFieldReferenceSet(), entry); + addToIndex(indexes, "strings", listener.getStringSet(), entry); + + // Populate map [super type name : [sub type name]] + Map index = indexes.getIndex("subTypeNames"); + + for (Map.Entry> e : listener.getSuperTypeNamesMap().entrySet()) { + String typeName = e.getKey(); + + for (String superTypeName : e.getValue()) { + index.get(superTypeName).add(typeName); + } + } + } catch (IOException ignore) { + } + } + + protected static class Listener extends AbstractJavaListener { + + protected HashSet typeDeclarationSet = new HashSet<>(); + protected HashSet constructorDeclarationSet = new HashSet<>(); + protected HashSet methodDeclarationSet = new HashSet<>(); + protected HashSet fieldDeclarationSet = new HashSet<>(); + protected HashSet typeReferenceSet = new HashSet<>(); + protected HashSet constructorReferenceSet = new HashSet<>(); + protected HashSet methodReferenceSet = new HashSet<>(); + protected HashSet fieldReferenceSet = new HashSet<>(); + protected HashSet stringSet = new HashSet<>(); + protected HashMap> superTypeNamesMap = new HashMap<>(); + + protected StringBuffer sbTypeDeclaration = new StringBuffer(); + + public Listener(Container.Entry entry) { + super(entry); + } + + public HashSet getTypeDeclarationSet() { return typeDeclarationSet; } + public HashSet getConstructorDeclarationSet() { return constructorDeclarationSet; } + public HashSet getMethodDeclarationSet() { return methodDeclarationSet; } + public HashSet getFieldDeclarationSet() { return fieldDeclarationSet; } + public HashSet getTypeReferenceSet() { return typeReferenceSet; } + public HashSet getConstructorReferenceSet() { return constructorReferenceSet; } + public HashSet getMethodReferenceSet() { return methodReferenceSet; } + public HashSet getFieldReferenceSet() { return fieldReferenceSet; } + public HashSet getStringSet() { return stringSet; } + public HashMap> getSuperTypeNamesMap() { return superTypeNamesMap; } + + // --- ANTLR Listener --- // + + public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { + super.enterPackageDeclaration(ctx); + + if (! packageName.isEmpty()) { + sbTypeDeclaration.append(packageName).append('/'); + } + } + + public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); } + public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } + + public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); } + public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } + + public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); } + public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } + + public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); } + public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } + + protected void enterTypeDeclaration(ParserRuleContext ctx) { + // Add type declaration + String typeName = ctx.getToken(JavaParser.Identifier, 0).getText(); + int length = sbTypeDeclaration.length(); + + if ((length == 0) || (sbTypeDeclaration.charAt(length-1) == '/')) { + sbTypeDeclaration.append(typeName); + } else { + sbTypeDeclaration.append('$').append(typeName); + } + + String internalTypeName = sbTypeDeclaration.toString(); + typeDeclarationSet.add(internalTypeName); + nameToInternalTypeName.put(typeName, internalTypeName); + + HashSet 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); + } + } + + protected void exitTypeDeclaration() { + int index = sbTypeDeclaration.lastIndexOf("$"); + + if (index == -1) { + index = sbTypeDeclaration.lastIndexOf("/") + 1; + } + + if (index == -1) { + sbTypeDeclaration.setLength(0); + } else { + sbTypeDeclaration.setLength(index); + } + } + + public void enterType(JavaParser.TypeContext ctx) { + // Add type reference + JavaParser.ClassOrInterfaceTypeContext classOrInterfaceType = ctx.classOrInterfaceType(); + + if (classOrInterfaceType != null) { + String internalTypeName = resolveInternalTypeName(classOrInterfaceType.Identifier()); + + if (internalTypeName.charAt(0) != '*') + typeReferenceSet.add(internalTypeName); + } + } + + public void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) { + for (JavaParser.ConstantDeclaratorContext constantDeclaratorContext : ctx.constantDeclarator()) { + String name = constantDeclaratorContext.Identifier().getText(); + fieldDeclarationSet.add(name); + } + } + + public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) { + for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) { + String name = declaration.variableDeclaratorId().Identifier().getText(); + fieldDeclarationSet.add(name); + } + } + + public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { + String name = ctx.Identifier().getText(); + methodDeclarationSet.add(name); + } + + public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) { + String name = ctx.Identifier().getText(); + methodDeclarationSet.add(name); + } + + public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) { + String name = ctx.Identifier().getText(); + constructorDeclarationSet.add(name); + } + + public void enterCreatedName(JavaParser.CreatedNameContext ctx) { + String internalTypeName = resolveInternalTypeName(ctx.Identifier()); + + if ((internalTypeName != null) && (internalTypeName.charAt(0) != '*')) + constructorReferenceSet.add(internalTypeName); + } + + public void enterExpression(JavaParser.ExpressionContext ctx) { + switch (ctx.getChildCount()) { + case 3: + if (getToken(ctx.children, 1, JavaParser.DOT) != null) { + // Search "expression '.' Identifier" : field + TerminalNode identifier3 = getToken(ctx.children, 2, JavaParser.Identifier); + + if (identifier3 != null) { + String fieldName = identifier3.getText(); + fieldReferenceSet.add(fieldName); + } + } else if (getToken(ctx.children, 1, JavaParser.LPAREN) != null) { + // Search "expression '(' ')'" : method + if (getToken(ctx.children, 2, JavaParser.RPAREN) != null) { + TerminalNode identifier0 = getRightTerminalNode(ctx.children.get(0)); + + if (identifier0 != null) { + String methodName = identifier0.getText(); + methodReferenceSet.add(methodName); + } + } + } + break; + case 4: + if (getToken(ctx.children, 1, JavaParser.LPAREN) != null) { + // Search "expression '(' expressionList ')'" : method + if (getToken(ctx.children, 3, JavaParser.RPAREN) != null) { + JavaParser.ExpressionListContext expressionListContext = ctx.expressionList(); + + if ((expressionListContext != null) && (expressionListContext == ctx.children.get(2))) { + TerminalNode identifier0 = getRightTerminalNode(ctx.children.get(0)); + + if (identifier0 != null) { + String methodName = identifier0.getText(); + methodReferenceSet.add(methodName); + } + } + } + } + break; + } + } + + protected TerminalNode getToken(List children, int i, int type) { + ParseTree pt = children.get(i); + + if (pt instanceof TerminalNode) { + if (((TerminalNode)pt).getSymbol().getType() == type) { + return (TerminalNode)pt; + } + } + + return null; + } + + protected TerminalNode getRightTerminalNode(ParseTree pt) { + if (pt instanceof ParserRuleContext) { + List children = ((ParserRuleContext)pt).children; + int size = children.size(); + + if (size > 0) { + ParseTree last = children.get(size - 1); + + if (last instanceof TerminalNode) { + return (TerminalNode) last; + } else { + return getRightTerminalNode(last); + } + } + } + + return null; + } + + public void enterLiteral(JavaParser.LiteralContext ctx) { + TerminalNode stringLiteral = ctx.StringLiteral(); + if (stringLiteral != null) { + stringSet.add(stringLiteral.getSymbol().getText()); + } + } + } +} diff --git a/services/src/main/java/jd/gui/service/type/AbstractTypeFactoryProvider.java b/services/src/main/java/jd/gui/service/type/AbstractTypeFactoryProvider.java index 315571a..31ad791 100644 --- a/services/src/main/java/jd/gui/service/type/AbstractTypeFactoryProvider.java +++ b/services/src/main/java/jd/gui/service/type/AbstractTypeFactoryProvider.java @@ -5,8 +5,12 @@ package jd.gui.service.type; +import jd.gui.api.model.Type; import jd.gui.spi.TypeFactory; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; @@ -22,7 +26,7 @@ public abstract class AbstractTypeFactoryProvider implements TypeFactory { /** * Initialize "selectors" and "pathPattern" with optional external properties file */ - AbstractTypeFactoryProvider() { + public AbstractTypeFactoryProvider() { Properties properties = new Properties(); Class clazz = this.getClass(); @@ -51,4 +55,153 @@ public abstract class AbstractTypeFactoryProvider implements TypeFactory { return (externalSelectors==null) ? null : externalSelectors.toArray(new String[externalSelectors.size()]); } public Pattern getPathPattern() { return externalPathPattern; } + + protected static ImageIcon getTypeIcon(int access) { + if ((access & Type.FLAG_ANNOTATION) != 0) + return ANNOTATION_ICON; + else if ((access & Type.FLAG_INTERFACE) != 0) + return INTERFACE_ICONS[accessToIndex(access)]; + else if ((access & Type.FLAG_ENUM) != 0) + return ENUM_ICON; + else + return CLASS_ICONS[accessToIndex(access)]; + } + + protected static ImageIcon getFieldIcon(int access) { + return FIELD_ICONS[accessToIndex(access)]; + } + + protected static ImageIcon getMethodIcon(int access) { + return METHOD_ICONS[accessToIndex(access)]; + } + + protected static int accessToIndex(int access) { + int index = 0; + + if ((access & Type.FLAG_STATIC) != 0) + index += 4; + + if ((access & Type.FLAG_FINAL) != 0) + index += 8; + + if ((access & Type.FLAG_ABSTRACT) != 0) + index += 16; + + if ((access & Type.FLAG_PUBLIC) != 0) + return index + 1; + else if ((access & Type.FLAG_PROTECTED) != 0) + return index + 2; + else if ((access & Type.FLAG_PRIVATE) != 0) + return index + 3; + else + return index; + } + + // Graphic stuff ... + protected static ImageIcon mergeIcons(ImageIcon background, ImageIcon overlay, int x, int y) { + int w = background.getIconWidth(); + int h = background.getIconHeight(); + BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + + if (x + overlay.getIconWidth() > w) + x = w - overlay.getIconWidth(); + if (y + overlay.getIconHeight() > h) + y = h - overlay.getIconHeight(); + + Graphics2D g2 = image.createGraphics(); + g2.drawImage(background.getImage(), 0, 0, null); + g2.drawImage(overlay.getImage(), x, y, null); + g2.dispose(); + + return new ImageIcon(image); + } + + protected static ImageIcon[] mergeIcons(ImageIcon[] backgrounds, ImageIcon overlay, int x, int y) { + int length = backgrounds.length; + ImageIcon[] result = new ImageIcon[length*2]; + + // Copy source icons + System.arraycopy(backgrounds, 0, result, 0, length); + + // Add overlays + for (int i=0; i make(API api, Container.Entry entry) { + return Collections.singletonList(make(api, entry, null)); + } + public Type make(API api, Container.Entry entry, String fragment) { try (InputStream is = entry.getInputStream()) { ClassReader classReader = new ClassReader(is); @@ -110,24 +114,24 @@ public class ClassFileTypeFactoryProvider extends AbstractTypeFactoryProvider { } static class JavaType implements Type { - ClassNode classNode; - Container.Entry entry; - int access; - String name; - String shortName; - String superName; - String outerName; - String displayTypeName; - String displayInnerTypeName; - String displayPackageName; + protected ClassNode classNode; + protected Container.Entry entry; + protected int access; + protected String name; + protected String superName; + protected String outerName; - List innerTypes; - List fields; - List methods; + protected String displayTypeName; + protected String displayInnerTypeName; + protected String displayPackageName; + + protected List innerTypes = null; + protected List fields = null; + protected List methods = null; @SuppressWarnings("unchecked") - JavaType(Container.Entry entry, ClassReader classReader) { + protected JavaType(Container.Entry entry, ClassReader classReader) { this.classNode = new ClassNode(); this.entry = entry; @@ -161,27 +165,17 @@ public class ClassFileTypeFactoryProvider extends AbstractTypeFactoryProvider { if (lastPackageSeparatorIndex == -1) { this.displayPackageName = ""; - this.shortName = this.name; - this.displayTypeName = (this.outerName != null) ? null : this.shortName; + this.displayTypeName = (this.outerName == null) ? this.name : null; } else { this.displayPackageName = this.name.substring(0, lastPackageSeparatorIndex).replace('/', '.'); - this.shortName = this.name.substring(lastPackageSeparatorIndex+1); - this.displayTypeName = (this.outerName != null) ? null : this.shortName; + this.displayTypeName = (this.outerName == null) ? this.name.substring(lastPackageSeparatorIndex+1) : null; } - - this.innerTypes = null; - this.fields = null; - this.methods = null; } public int getFlags() { return access; } public String getName() { return name; } - public String getShortName() { return shortName; } public String getSuperName() { return superName; } public String getOuterName() { return outerName; } - - public Container.Entry getOuterEntry() { return (outerName==null) ? null : getEntry(outerName); } - public String getDisplayPackageName() { return displayPackageName; } public String getDisplayTypeName() { @@ -219,7 +213,7 @@ public class ClassFileTypeFactoryProvider extends AbstractTypeFactoryProvider { } public String getDisplayInnerTypeName() { return displayInnerTypeName; } - public Icon getIcon() { return getIcon(access); } + public Icon getIcon() { return getTypeIcon(access); } @SuppressWarnings("unchecked") public List getInnerTypes() { @@ -266,7 +260,7 @@ public class ClassFileTypeFactoryProvider extends AbstractTypeFactoryProvider { public int getFlags() { return fieldNode.access; } public String getName() { return fieldNode.name; } public String getDescriptor() { return fieldNode.desc; } - public Icon getIcon() { return FIELD_ICONS[accessToIndex(fieldNode.access)]; } + public Icon getIcon() { return getFieldIcon(fieldNode.access); } }); } } @@ -285,153 +279,13 @@ public class ClassFileTypeFactoryProvider extends AbstractTypeFactoryProvider { public int getFlags() { return methodNode.access; } public String getName() { return methodNode.name; } public String getDescriptor() { return methodNode.desc; } - public Icon getIcon() { return METHOD_ICONS[accessToIndex(methodNode.access)]; } + public Icon getIcon() { return getMethodIcon(methodNode.access); } }); } } } + return methods; } - - protected static ImageIcon getIcon(int access) { - if ((access & Opcodes.ACC_ANNOTATION) != 0) - return ANNOTATION_ICON; - else if ((access & Opcodes.ACC_INTERFACE) != 0) - return INTERFACE_ICONS[accessToIndex(access)]; - else if ((access & Opcodes.ACC_ENUM) != 0) - return ENUM_ICON; - else - return CLASS_ICONS[accessToIndex(access)]; - } - - protected static int accessToIndex(int access) { - int index = 0; - - if ((access & Opcodes.ACC_STATIC) != 0) - index += 4; - - if ((access & Opcodes.ACC_FINAL) != 0) - index += 8; - - if ((access & Opcodes.ACC_ABSTRACT) != 0) - index += 16; - - if ((access & Opcodes.ACC_PUBLIC) != 0) - return index + 1; - else if ((access & Opcodes.ACC_PROTECTED) != 0) - return index + 2; - else if ((access & Opcodes.ACC_PRIVATE) != 0) - return index + 3; - else - return index; - } - - // Graphic stuff ... - protected static ImageIcon mergeIcons(ImageIcon background, ImageIcon overlay, int x, int y) { - int w = background.getIconWidth(); - int h = background.getIconHeight(); - BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - - if (x + overlay.getIconWidth() > w) - x = w - overlay.getIconWidth(); - if (y + overlay.getIconHeight() > h) - y = h - overlay.getIconHeight(); - - Graphics2D g2 = image.createGraphics(); - g2.drawImage(background.getImage(), 0, 0, null); - g2.drawImage(overlay.getImage(), x, y, null); - g2.dispose(); - - return new ImageIcon(image); - } - - protected static ImageIcon[] mergeIcons(ImageIcon[] backgrounds, ImageIcon overlay, int x, int y) { - int length = backgrounds.length; - ImageIcon[] result = new ImageIcon[length*2]; - - // Copy source icons - System.arraycopy(backgrounds, 0, result, 0, length); - - // Add overlays - for (int i=0; i externalSelectors = getExternalSelectors(); + + if (externalSelectors == null) { + return new String[] { "*:file:*.java" }; + } else { + int size = externalSelectors.size(); + String[] selectors = new String[size+1]; + externalSelectors.toArray(selectors); + selectors[size] = "*:file:*.java"; + return selectors; + } + } + + public Collection make(API api, Container.Entry entry) { + try (InputStream inputStream = entry.getInputStream()) { + Listener listener = new Listener(entry); + ANTLRParser.parse(new ANTLRInputStream(inputStream), listener); + + return listener.getRootTypes(); + } catch (IOException ignore) { + return Collections.emptyList(); + } + } + + public Type make(API api, Container.Entry entry, String fragment) { + try (InputStream inputStream = entry.getInputStream()) { + Listener listener = new Listener(entry); + ANTLRParser.parse(new ANTLRInputStream(inputStream), listener); + + if ((fragment != null) && (fragment.length() > 0)) { + // Search type name in fragment. URI format : see jd.gui.api.feature.UriOpener + int index = fragment.indexOf('-'); + + if (index != -1) { + // Keep type name only + fragment = fragment.substring(0, index); + } + + return listener.getType(fragment); + } else { + return listener.getMainType(); + } + } catch (IOException ignore) { + return null; + } + } + + protected static class JavaType implements Type { + protected int access; + protected String name; + protected String superName; + protected String outerName; + + protected String displayTypeName; + protected String displayInnerTypeName; + protected String displayPackageName; + + protected List innerTypes = new ArrayList<>(); + protected List fields = new ArrayList<>(); + protected List methods = new ArrayList<>(); + + protected JavaType outerType; + + public JavaType( + int access, String name, String superName, String outerName, + String displayTypeName, String displayInnerTypeName, String displayPackageName, + JavaType outerType) { + + this.access = access; + this.name = name; + this.superName = superName; + this.outerName = outerName; + this.displayTypeName = displayTypeName; + this.displayInnerTypeName = displayInnerTypeName; + this.displayPackageName = displayPackageName; + this.outerType = outerType; + } + + public int getFlags() { return access; } + public String getName() { return name; } + public String getSuperName() { return superName; } + public String getOuterName() { return outerName; } + public String getDisplayTypeName() { return displayTypeName; } + public String getDisplayInnerTypeName() { return displayInnerTypeName; } + public String getDisplayPackageName() { return displayPackageName; } + public Icon getIcon() { return getTypeIcon(access); } + public JavaType getOuterType() { return outerType; } + public Collection getInnerTypes() { return innerTypes; } + public Collection getFields() { return fields; } + public Collection getMethods() { return methods; } + } + + protected static class JavaField implements Type.Field { + protected int access; + protected String name; + protected String descriptor; + + public JavaField(int access, String name, String descriptor) { + this.access = access; + this.name = name; + this.descriptor = descriptor; + } + + public int getFlags() { return access; } + public String getName() { return name; } + public String getDescriptor() { return descriptor; } + public Icon getIcon() { return getFieldIcon(access); } + } + + protected static class JavaMethod implements Type.Method { + protected int access; + protected String name; + protected String descriptor; + + public JavaMethod(int access, String name, String descriptor) { + this.access = access; + this.name = name; + this.descriptor = descriptor; + } + + public int getFlags() { return access; } + public String getName() { return name; } + public String getDescriptor() { return descriptor; } + public Icon getIcon() { return getMethodIcon(access); } + } + + protected static class Listener extends AbstractJavaListener { + + protected String displayPackageName = ""; + + protected JavaType mainType = null; + protected JavaType currentType = null; + protected ArrayList rootTypes = new ArrayList<>(); + protected HashMap types = new HashMap<>(); + + public Listener(Container.Entry entry) { + super(entry); + } + + public Type getMainType() { + return mainType; + } + public Type getType(String typeName) { + return types.get(typeName); + } + public ArrayList getRootTypes() { + return rootTypes; + } + + // --- ANTLR Listener --- // + + public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { + super.enterPackageDeclaration(ctx); + displayPackageName = packageName.replace('/', '.'); + } + + public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx, 0); } + public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } + + public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_ENUM); } + public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } + + public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_INTERFACE); } + public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } + + public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_ANNOTATION); } + public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } + + protected void enterTypeDeclaration(ParserRuleContext ctx, int access) { + String name = ctx.getToken(JavaParser.Identifier, 0).getText(); + + JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0); + String superQualifiedTypeName; + + if (superType == null) { + superQualifiedTypeName = ((access & JavaType.FLAG_INTERFACE) == 0) ? "java/lang/Object" : ""; + } else { + superQualifiedTypeName = resolveInternalTypeName(superType.classOrInterfaceType().Identifier()); + } + + access += getTypeDeclarationContextAccessFlag(ctx.getParent()); + + if (currentType == null) { + String internalTypeName = packageName.isEmpty() ? name : packageName + "/" + name; + String outerName = null; + String displayTypeName = name; + String displayInnerTypeName = null; + + currentType = new JavaType(access, internalTypeName, superQualifiedTypeName, outerName, displayTypeName, displayInnerTypeName, displayPackageName, null); + types.put(internalTypeName, currentType); + rootTypes.add(currentType); + nameToInternalTypeName.put(name, internalTypeName); + + if (mainType == null) { + mainType = currentType; + } else { + // Multi class definitions in the same file + String path = entry.getPath(); + int index = path.lastIndexOf('/') + 1; + + if (path.substring(index).startsWith(name + '.')) { + // Select the correct root type + mainType = currentType; + } + } + } else { + String internalTypeName = currentType.getName() + '$' + name; + String outerName = currentType.getName(); + String displayTypeName = currentType.getDisplayTypeName() + '.' + name; + String displayInnerTypeName = name; + JavaType subType = new JavaType(access, internalTypeName, superQualifiedTypeName, outerName, displayTypeName, displayInnerTypeName, displayPackageName, currentType); + + currentType.getInnerTypes().add(subType); + currentType = subType; + types.put(internalTypeName, currentType); + nameToInternalTypeName.put(name, internalTypeName); + } + } + + protected void exitTypeDeclaration() { + currentType = currentType.getOuterType(); + } + + public void enterClassBodyDeclaration(JavaParser.ClassBodyDeclarationContext ctx) { + if (ctx.getChildCount() == 2) { + ParseTree first = ctx.getChild(0); + + if (first instanceof TerminalNode) { + if (((TerminalNode)first).getSymbol().getType() == JavaParser.STATIC) { + currentType.getMethods().add(new JavaMethod(JavaType.FLAG_STATIC, "", "()V")); + } + } + } + } + + public void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) { + JavaParser.TypeContext typeContext = ctx.type(); + int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent()); + + for (JavaParser.ConstantDeclaratorContext constantDeclaratorContext : ctx.constantDeclarator()) { + TerminalNode identifier = constantDeclaratorContext.Identifier(); + String name = identifier.getText(); + int dimensionOnVariable = countDimension(constantDeclaratorContext.children); + String descriptor = createDescriptor(typeContext, dimensionOnVariable); + + currentType.getFields().add(new JavaField(access, name, descriptor)); + } + } + + public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) { + JavaParser.TypeContext typeContext = ctx.type(); + int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent()); + + for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) { + JavaParser.VariableDeclaratorIdContext variableDeclaratorId = declaration.variableDeclaratorId(); + TerminalNode identifier = variableDeclaratorId.Identifier(); + String name = identifier.getText(); + int dimensionOnVariable = countDimension(variableDeclaratorId.children); + String descriptor = createDescriptor(typeContext, dimensionOnVariable); + + currentType.getFields().add(new JavaField(access, name, descriptor)); + } + } + + public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { + enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); + } + + public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) { + enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); + } + + public void enterMethodDeclaration( + ParserRuleContext ctx, TerminalNode identifier, + JavaParser.FormalParametersContext formalParameters, JavaParser.TypeContext returnType) { + + int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent()); + String name = identifier.getText(); + String paramDescriptors = createParamDescriptors(formalParameters.formalParameterList()); + String returnDescriptor = createDescriptor(returnType, 0); + String descriptor = paramDescriptors + returnDescriptor; + + currentType.getMethods().add(new JavaMethod(access, name, descriptor)); + } + + public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) { + int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent()); + TerminalNode identifier = ctx.Identifier(); + String name = identifier.getText(); + String paramDescriptors = createParamDescriptors(ctx.formalParameters().formalParameterList()); + String descriptor = paramDescriptors + "V"; + + currentType.getMethods().add(new JavaMethod(access, "", descriptor)); + } + + protected String createParamDescriptors(JavaParser.FormalParameterListContext formalParameterList) { + StringBuffer paramDescriptors = null; + + if (formalParameterList != null) { + List formalParameters = formalParameterList.formalParameter(); + paramDescriptors = new StringBuffer("("); + + for (JavaParser.FormalParameterContext formalParameter : formalParameters) { + int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children); + paramDescriptors.append(createDescriptor(formalParameter.type(), dimensionOnParameter)); + } + } + + return (paramDescriptors == null) ? "()" : paramDescriptors.append(')').toString(); + } + + protected int getTypeDeclarationContextAccessFlag(ParserRuleContext ctx) { + int access = 0; + + for (JavaParser.ClassOrInterfaceModifierContext modifierContext : ctx.getRuleContexts(JavaParser.ClassOrInterfaceModifierContext.class)) { + access += getAccessFlag(modifierContext); + } + + return access; + } + + protected int getClassBodyDeclarationAccessFlag(ParserRuleContext ctx) { + int access = 0; + + for (JavaParser.ModifierContext modifierContext : ctx.getRuleContexts(JavaParser.ModifierContext.class)) { + JavaParser.ClassOrInterfaceModifierContext coimc = modifierContext.classOrInterfaceModifier(); + + if (coimc != null) { + access += getAccessFlag(coimc); + } + } + + return access; + } + + protected int getAccessFlag(JavaParser.ClassOrInterfaceModifierContext ctx) { + if (ctx.getChildCount() == 1) { + ParseTree first = ctx.getChild(0); + + if (first instanceof TerminalNode) { + switch (((TerminalNode)first).getSymbol().getType()) { + case JavaParser.STATIC: return JavaType.FLAG_STATIC; + case JavaParser.FINAL: return JavaType.FLAG_FINAL; + case JavaParser.ABSTRACT: return JavaType.FLAG_ABSTRACT; + case JavaParser.PUBLIC: return JavaType.FLAG_PUBLIC; + case JavaParser.PROTECTED: return JavaType.FLAG_PROTECTED; + case JavaParser.PRIVATE: return JavaType.FLAG_PRIVATE; + } + } + } + + return 0; + } + } +} diff --git a/services/src/main/java/jd/gui/util/parser/antlr/ANTLRParser.java b/services/src/main/java/jd/gui/util/parser/antlr/ANTLRParser.java new file mode 100644 index 0000000..2840614 --- /dev/null +++ b/services/src/main/java/jd/gui/util/parser/antlr/ANTLRParser.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.util.parser.antlr; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeWalker; + +public class ANTLRParser { + + public static void parse(CharStream input, JavaListener listener) { + try { + JavaLexer lexer = new JavaLexer(input); + CommonTokenStream tokens = new CommonTokenStream(lexer); + JavaParser parser = new JavaParser(tokens); + + ParseTree tree = parser.compilationUnit(); + ParseTreeWalker walker = new ParseTreeWalker(); + + walker.walk(listener, tree); + } catch (StackOverflowError ignore) { + // Too complex source file, probably not written by a human. + // This error may happen on Java file generated by ANTLR for example. + } + } +} diff --git a/services/src/main/java/jd/gui/util/parser/antlr/AbstractJavaListener.java b/services/src/main/java/jd/gui/util/parser/antlr/AbstractJavaListener.java new file mode 100644 index 0000000..269ad81 --- /dev/null +++ b/services/src/main/java/jd/gui/util/parser/antlr/AbstractJavaListener.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.util.parser.antlr; + +import jd.gui.api.model.Container; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.antlr.v4.runtime.tree.TerminalNodeImpl; + +import java.util.HashMap; +import java.util.List; + +public abstract class AbstractJavaListener extends JavaBaseListener { + + protected Container.Entry entry; + protected String packageName = ""; + protected HashMap nameToInternalTypeName = new HashMap<>(); + protected StringBuffer sb = new StringBuffer(); + protected HashMap typeNameCache = new HashMap<>(); + + public AbstractJavaListener(Container.Entry entry) { + this.entry = entry; + } + + public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { + packageName = concatIdentifiers(ctx.qualifiedName().Identifier()); + } + + public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { + List identifiers = ctx.qualifiedName().Identifier(); + int size = identifiers.size(); + + if (size > 1) { + nameToInternalTypeName.put(identifiers.get(size - 1).getText(), concatIdentifiers(identifiers)); + } + } + + protected String concatIdentifiers(List identifiers) { + switch (identifiers.size()) { + case 0: + return ""; + case 1: + return identifiers.get(0).getText(); + default: + sb.setLength(0); + + for (TerminalNode identifier : identifiers) { + sb.append(identifier.getText()).append('/'); + } + + // Remove last separator + sb.setLength(sb.length() - 1); + + return sb.toString(); + } + } + + protected String resolveInternalTypeName(List identifiers) { + switch (identifiers.size()) { + case 0: + return null; + + case 1: + // Search in cache + String name = identifiers.get(0).getText(); + String qualifiedName = typeNameCache.get(name); + + if (qualifiedName != null) { + return qualifiedName; + } + + // Search in imports + String imp = nameToInternalTypeName.get(name); + + if (imp != null) { + // Import found + return imp; + } + + // Search type in same package + String prefix = name + '.'; + + if (entry.getPath().indexOf('/') != -1) { + // Not in root package + Container.Entry parent = entry.getParent(); + int packageLength = parent.getPath().length() + 1; + + for (Container.Entry child : parent.getChildren()) { + if (!child.isDirectory() && child.getPath().substring(packageLength).startsWith(prefix)) { + qualifiedName = packageName + '/' + name; + typeNameCache.put(name, qualifiedName); + return qualifiedName; + } + } + } + + // Search type in root package + for (Container.Entry child : entry.getContainer().getRoot().getChildren()) { + if (!child.isDirectory() && child.getPath().startsWith(prefix)) { + typeNameCache.put(name, name); + return name; + } + } + + // Search type in 'java.lang' + try { + if (Class.forName("java.lang." + name) != null) { + qualifiedName = "java/lang/" + name; + typeNameCache.put(name, qualifiedName); + return qualifiedName; + } + } catch (ClassNotFoundException ignore) { + } + + // Type not found + qualifiedName = "*/" + name; + typeNameCache.put(name, qualifiedName); + return qualifiedName; + + default: + // Qualified type name -> Nothing to do + return concatIdentifiers(identifiers); + } + } + + protected String createDescriptor(JavaParser.TypeContext typeContext, int dimension) { + if (typeContext == null) { + return "V"; + } else { + dimension += countDimension(typeContext.children); + JavaParser.PrimitiveTypeContext primitive = typeContext.primitiveType(); + String name; + + if (primitive == null) { + JavaParser.ClassOrInterfaceTypeContext type = typeContext.classOrInterfaceType(); + List typeArgumentsContexts = type.typeArguments(); + + if (typeArgumentsContexts.size() == 1) { + JavaParser.TypeArgumentsContext typeArgumentsContext = typeArgumentsContexts.get(0); + List typeArguments = typeArgumentsContext.typeArgument(); + } else if (typeArgumentsContexts.size() > 1) { + throw new RuntimeException("UNEXPECTED"); + } + + name = "L" + resolveInternalTypeName(type.Identifier()) + ";"; + } else { + // Search primitive + switch (primitive.getText()) { + case "boolean": name = "Z"; break; + case "byte": name = "B"; break; + case "char": name = "C"; break; + case "double": name = "D"; break; + case "float": name = "F"; break; + case "int": name = "I"; break; + case "long": name = "J"; break; + case "short": name = "S"; break; + case "void": name = "V"; break; + default: + throw new RuntimeException("UNEXPECTED PRIMITIVE"); + } + } + + switch (dimension) { + case 0: return name; + case 1: return "[" + name; + case 2: return "[[" + name; + default: return new String(new char[dimension]).replace('\0', '[') + name; + } + } + } + + protected int countDimension(List children) { + int dimension = 0; + + for (ParseTree child : children) { + if (child instanceof TerminalNodeImpl) { + if (((TerminalNodeImpl)child).getSymbol().getType() == JavaParser.LBRACK) + dimension++; + } + } + + return dimension; + } +} diff --git a/services/src/main/resources/META-INF/services/jd.gui.spi.FileLoader b/services/src/main/resources/META-INF/services/jd.gui.spi.FileLoader index bac0cea..67a1960 100644 --- a/services/src/main/resources/META-INF/services/jd.gui.spi.FileLoader +++ b/services/src/main/resources/META-INF/services/jd.gui.spi.FileLoader @@ -1,6 +1,7 @@ jd.gui.service.fileloader.ClassFileLoaderProvider jd.gui.service.fileloader.EarFileLoaderProvider jd.gui.service.fileloader.JarFileLoaderProvider +jd.gui.service.fileloader.JavaFileLoaderProvider jd.gui.service.fileloader.LogFileLoaderProvider jd.gui.service.fileloader.WarFileLoaderProvider jd.gui.service.fileloader.ZipFileLoaderProvider diff --git a/services/src/main/resources/META-INF/services/jd.gui.spi.Indexer b/services/src/main/resources/META-INF/services/jd.gui.spi.Indexer index deab060..217151b 100644 --- a/services/src/main/resources/META-INF/services/jd.gui.spi.Indexer +++ b/services/src/main/resources/META-INF/services/jd.gui.spi.Indexer @@ -1,6 +1,7 @@ jd.gui.service.indexer.DirectoryIndexerProvider jd.gui.service.indexer.ClassFileIndexerProvider jd.gui.service.indexer.EjbJarXmlFileIndexerProvider +jd.gui.service.indexer.JavaFileIndexerProvider jd.gui.service.indexer.MetainfServiceFileIndexerProvider jd.gui.service.indexer.TextFileIndexerProvider jd.gui.service.indexer.WebXmlFileIndexerProvider diff --git a/services/src/main/resources/META-INF/services/jd.gui.spi.TreeNodeFactory b/services/src/main/resources/META-INF/services/jd.gui.spi.TreeNodeFactory index aeb19c7..5b4602f 100644 --- a/services/src/main/resources/META-INF/services/jd.gui.spi.TreeNodeFactory +++ b/services/src/main/resources/META-INF/services/jd.gui.spi.TreeNodeFactory @@ -7,7 +7,7 @@ jd.gui.service.treenode.EjbJarXmlFileTreeNodeFactoryProvider jd.gui.service.treenode.FileTreeNodeFactoryProvider jd.gui.service.treenode.HtmlFileTreeNodeFactoryProvider jd.gui.service.treenode.JarFileTreeNodeFactoryProvider -#jd.gui.service.treenode.JavaFileTreeNodeFactoryProvider +jd.gui.service.treenode.JavaFileTreeNodeFactoryProvider jd.gui.service.treenode.JavascriptFileTreeNodeFactoryProvider jd.gui.service.treenode.JspFileTreeNodeFactoryProvider jd.gui.service.treenode.ManifestFileTreeNodeFactoryProvider diff --git a/services/src/main/resources/META-INF/services/jd.gui.spi.TypeFactory b/services/src/main/resources/META-INF/services/jd.gui.spi.TypeFactory index d2563e7..23ada34 100644 --- a/services/src/main/resources/META-INF/services/jd.gui.spi.TypeFactory +++ b/services/src/main/resources/META-INF/services/jd.gui.spi.TypeFactory @@ -1 +1,2 @@ jd.gui.service.type.ClassFileTypeFactoryProvider +jd.gui.service.type.JavaFileTypeFactoryProvider diff --git a/services/src/main/resources/images/jcu_obj.png b/services/src/main/resources/images/jcu_obj.png new file mode 100644 index 0000000000000000000000000000000000000000..dceae72198d63ea160d4a81e76d62e981479b90f GIT binary patch literal 738 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47#jk7LR>$5{{8vu@3$X+efa$A z>-XP3fBm`t;>W|6KOVpS`Q-JF=WlPK>KYaQ1{pX)=KYl-W_2coIA8$VWdjIA3+fP5UC+vCl?#IgyKRY{#TTGTTn=EKDo!?|SryhvRrq!4~IMKhU%V2$n z;hJ`%m955CHf3DdlzD4!&DBkr|Ns97`g(AJ`=R@G0bS->666;QbSKpS!{sc&2B5)= zN#5=*^&a~Rp8`3Y1s;*bK-vS0-A-oP0U3)tT^vIsF2|mDDb^Su(fUw$;zyO`0n500 zPfV$trupuF)tsl@TPwcbs+arnhx;OXV7!I;y}Ql)F${;?1rB;mOFbUq|L`r>hQ!F$ zriBvO4YG>5Ts+R(7^+3i2d-Ffj4>gqf2(H7?4!yDd<06ksv_qz>zEwhNE!D zdoO2X&04>djX~LOQ?BQ#qNR2RSe_ROCF^u0Gwgr8%hu}rXU4iJ#>e$Q_lbZ literal 0 HcmV?d00001 diff --git a/services/src/test/groovy/jd/gui/view/component/ClassFilePageTest.groovy b/services/src/test/groovy/jd/gui/view/component/ClassFilePageTest.groovy index 71774d0..408ad77 100644 --- a/services/src/test/groovy/jd/gui/view/component/ClassFilePageTest.groovy +++ b/services/src/test/groovy/jd/gui/view/component/ClassFilePageTest.groovy @@ -7,9 +7,9 @@ package jd.gui.view.component class ClassFilePageTest extends GroovyTestCase { - HashMap initDeclarations() { - def data = new ClassFilePage.DeclarationData(0, 1, "Test", "test", "I") - HashMap declarations = [:] + HashMap initDeclarations() { + def data = new TypePage.DeclarationData(0, 1, "Test", "test", "I") + HashMap declarations = [:] // Init type declarations declarations.put("Test", data) @@ -43,16 +43,16 @@ class ClassFilePageTest extends GroovyTestCase { TreeMap initHyperlinks() { def hyperlinks = new TreeMap() - hyperlinks.put(0, new ClassFilePage.HyperlinkReferenceData(0, 1, new ClassFilePage.ReferenceData("java/lang/Integer", "MAX_VALUE", "I", "Test"))) - hyperlinks.put(0, new ClassFilePage.HyperlinkReferenceData(0, 1, new ClassFilePage.ReferenceData("java/lang/Integer", "toString", "()Ljava/lang/String;", "Test"))) + hyperlinks.put(0, new TypePage.HyperlinkReferenceData(0, 1, new TypePage.ReferenceData("java/lang/Integer", "MAX_VALUE", "I", "Test"))) + hyperlinks.put(0, new TypePage.HyperlinkReferenceData(0, 1, new TypePage.ReferenceData("java/lang/Integer", "toString", "()Ljava/lang/String;", "Test"))) return hyperlinks } - ArrayList initStrings() { - def strings = new ArrayList() + ArrayList initStrings() { + def strings = new ArrayList() - strings.add(new ClassFilePage.StringData(0, 3, "abc", "Test")) + strings.add(new TypePage.StringData(0, 3, "abc", "Test")) return strings } diff --git a/services/src/test/groovy/jd/gui/view/component/JavaFilePageTest.groovy b/services/src/test/groovy/jd/gui/view/component/JavaFilePageTest.groovy new file mode 100644 index 0000000..9de9d48 --- /dev/null +++ b/services/src/test/groovy/jd/gui/view/component/JavaFilePageTest.groovy @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.view.component + +class JavaFilePageTest extends GroovyTestCase { + + HashMap initDeclarations() { + def data = new TypePage.DeclarationData(0, 1, "Test", "test", "I") + HashMap declarations = [:] + + // Init type declarations + declarations.put("Test", data) + declarations.put("test/Test", data) + declarations.put("*/Test", data) + + // Init field declarations + declarations.put("Test-attributeInt-I", data) + declarations.put("Test-attributeBoolean-Z", data) + declarations.put("Test-attributeArrayBoolean-[[Z", data) + declarations.put("Test-attributeString-Ljava/lang/String;", data) + + declarations.put("test/Test-attributeInt-I", data) + declarations.put("test/Test-attributeBoolean-Z", data) + declarations.put("test/Test-attributeArrayBoolean-[[Z", data) + declarations.put("test/Test-attributeString-Ljava/lang/String;", data) + + declarations.put("*/Test-attributeBoolean-?", data) + declarations.put("*/Test-attributeBoolean-Z", data) + declarations.put("test/Test-attributeBoolean-?", data) + + // Init method declarations + declarations.put("*/Test-getInt-()I", data) + declarations.put("*/Test-getString-()Ljava/lang/String;", data) + declarations.put("*/Test-add-(JJ)J", data) + declarations.put("*/Test-createBuffer-(I)[C", data) + + declarations.put("test/Test-getInt-(*)?", data) + declarations.put("test/Test-getString-(*)?", data) + declarations.put("test/Test-add-(*)?", data) + declarations.put("test/Test-createBuffer-(*)?", data) + + declarations.put("*/Test-getInt-(*)?", data) + declarations.put("*/Test-getString-(*)?", data) + declarations.put("*/Test-add-(*)?", data) + declarations.put("*/Test-createBuffer-(*)?", data) + + return declarations + } + + void testMatchFragmentAndAddDocumentRange() { + } + + void testMatchQueryAndAddDocumentRange() { + } + + void testMatchScope() { + } +} diff --git a/src/osx/resources/Info.plist b/src/osx/resources/Info.plist index b5b52d3..5c79065 100644 --- a/src/osx/resources/Info.plist +++ b/src/osx/resources/Info.plist @@ -29,6 +29,16 @@ LSIsAppleDefaultForType LSTypeIsPackage + + CFBundleTypeExtensions + + java + + CFBundleTypeRole Viewer + CFBundleTypeName Java File + LSIsAppleDefaultForType + LSTypeIsPackage + CFBundleTypeExtensions