diff --git a/.gitignore b/.gitignore index 362852ac..30abf426 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.class # JD-GUI +src-generated/ jd-gui.cfg # Idea @@ -20,6 +21,9 @@ classes/ # Mac .DS_Store +#Windows +Thumbs.db + # Maven log/ target/ @@ -30,4 +34,3 @@ build/ # WinMerge *.bak - diff --git a/api/src/main/java/jd/gui/api/feature/UriOpenable.java b/api/src/main/java/jd/gui/api/feature/UriOpenable.java index a17ce4a8..153e317d 100644 --- a/api/src/main/java/jd/gui/api/feature/UriOpenable.java +++ b/api/src/main/java/jd/gui/api/feature/UriOpenable.java @@ -8,14 +8,34 @@ import java.net.URI; /** - * uri = [scheme:][//authority][path][?query][#fragment]
- * scheme = generic | jar | war | ear | dex | ...
- * authority = ''
- * path = path/to/dir/ | path/to/file
- * query = '' | highlight=text
- * fragment = '' | type | innertype | lineNumber=... | highlightPattern=...&highlightFlags=[drtcmfs]&highlightScope=...
+ * uri : scheme '://' path ('?' query)? ('#' fragment)?
+ * scheme : 'generic' | 'jar' | 'war' | 'ear' | 'dex' | ...
+ * path : singlePath('!' singlePath)*
+ * singlePath : [path/to/dir/] | [path/to/file]
+ * query : queryLineNumber | queryPosition | querySearch
+ * queryLineNumber : 'lineNumber=' [numeric]
+ * queryPosition : 'position=' [numeric]
+ * querySearch : 'highlightPattern=' queryPattern '&highlightFlags=' queryFlags ('&highlightScope=' typeName)?
+ * queryPattern : [start of string] | [start of type name] | [start of field name] | [start of method name]
+ * queryFlags : 'd'? // Match declarations
+ * 'r'? // Match references
+ * 't'? // Match types
+ * 'c'? // Match constructors
+ * 'm'? // Match methods
+ * 'f'? // Match fields
+ * 's'? // Match strings
+ * fragment : fragmentType | fragmentField | fragmentMethod
+ * fragmentType : typeName
+ * fragmentField : typeName '-' [field name] '-' descriptor
+ * fragmentMethod : typeName '-' [method name] '-' methodDescriptor
+ * methodDescriptor : '(*)?' | // Match all method descriptors
+ * '(' descriptor* ')' descriptor
+ * descriptor : '?' | // Match a primitive or a type name
+ * '['* primitiveOrTypeName
+ * primitiveOrTypeName : 'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'L' typeName ';' | 'S' | 'Z'
+ * typeName : [internal qualified name] | '*\/' [name]
*
- * Examples: + * Examples:
* */ public interface UriOpenable { diff --git a/app/src/main/groovy/jd/gui/controller/SearchInConstantPoolsController.groovy b/app/src/main/groovy/jd/gui/controller/SearchInConstantPoolsController.groovy index ece8374b..8feeea9d 100644 --- a/app/src/main/groovy/jd/gui/controller/SearchInConstantPoolsController.groovy +++ b/app/src/main/groovy/jd/gui/controller/SearchInConstantPoolsController.groovy @@ -14,7 +14,6 @@ import jd.gui.api.model.Indexes import jd.gui.model.configuration.Configuration import jd.gui.model.container.FilteredContainerWrapper import jd.gui.service.type.TypeFactoryService -import jd.gui.util.UriUtil import jd.gui.view.SearchInConstantPoolsView import java.awt.Cursor diff --git a/build.gradle b/build.gradle index a2641c2d..ff843c55 100644 --- a/build.gradle +++ b/build.gradle @@ -31,11 +31,10 @@ allprojects { } // 'cleanIdea' task extension // -task fullCleanIdea { +cleanIdea { file(project.name + '.iws').delete() ant.delete(dir: 'out') } -fullCleanIdea.mustRunAfter cleanIdea // All in one JAR // subprojects.each { subproject -> @@ -116,22 +115,18 @@ task installOsxDistConfig(type: Copy) { } distributions { - osx { - contents { - into('JD-GUI.app/Contents') { - from 'build/distributions/osx' - } - into('JD-GUI.app/Contents/Resources/Java') { - from jar.archivePath - } - from 'LICENSE', 'NOTICE', 'README.md' + osx.contents { + into('JD-GUI.app/Contents') { + from 'build/distributions/osx' } - } - windows { - contents { - from 'build/launch4j/jd-gui.exe' - from 'LICENSE', 'NOTICE', 'README.md' + into('JD-GUI.app/Contents/Resources/Java') { + from jar.archivePath } + from 'LICENSE', 'NOTICE', 'README.md' + } + windows.contents { + from 'build/launch4j/jd-gui.exe' + from 'LICENSE', 'NOTICE', 'README.md' } } installOsxDist.dependsOn build diff --git a/services/build.gradle b/services/build.gradle index 885b7492..8f643818 100644 --- a/services/build.gradle +++ b/services/build.gradle @@ -8,4 +8,5 @@ dependencies { compile 'com.fifesoft:rsyntaxtextarea:2.5.6' compile 'jd:jd-core:0.7.1' compile project(':api') + testCompile 'org.codehaus.groovy:groovy-test:2.4.0' } 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 d294fc9f..8e39d835 100644 --- a/services/src/main/groovy/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy +++ b/services/src/main/groovy/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy @@ -97,7 +97,7 @@ abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeF if (!initialized) { removeAllChildren() // Create type node - def type = api.getTypeFactory(entry).make(api, entry, null) + 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)) } 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 a5f0e184..60fac359 100644 --- a/services/src/main/groovy/jd/gui/view/component/ClassFilePage.groovy +++ b/services/src/main/groovy/jd/gui/view/component/ClassFilePage.groovy @@ -17,6 +17,7 @@ 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 @@ -29,9 +30,9 @@ class ClassFilePage extends CustomLineNumbersPage implements UriGettable, IndexesChangeListener, LineNumberNavigable, FocusedTypeGettable, PreferencesChangeListener { - protected static final String ESCAPE_UNICODE_CHARACTERS = 'ClassFileViewerPreferences.escapeUnicodeCharacters' - protected static final String OMIT_THIS_PREFIX = 'ClassFileViewerPreferences.omitThisPrefix' - protected static final String REALIGN_LINE_NUMBERS = 'ClassFileViewerPreferences.realignLineNumbers' + protected static final String ESCAPE_UNICODE_CHARACTERS = 'ClassFileViewerPreferences.escapeUnicodeCharacters' + protected static final String OMIT_THIS_PREFIX = 'ClassFileViewerPreferences.omitThisPrefix' + protected static final String REALIGN_LINE_NUMBERS = 'ClassFileViewerPreferences.realignLineNumbers' protected static final String DISPLAY_DEFAULT_CONSTRUCTOR = 'ClassFileViewerPreferences.displayDefaultConstructor' protected static final Decompiler DECOMPILER = new DecompilerImpl() @@ -253,6 +254,9 @@ 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 @@ -261,29 +265,7 @@ class ClassFilePage textArea.highlighter.clearMarkAllHighlights() if (fragment) { - int index = fragment.indexOf('?') - - if (index == -1) { - // Known descriptor ==> Search and high light item - def data = declarations.get(fragment) - if (data) { - ranges.add(new DocumentRange(data.startPosition, data.endPosition)) - } - } else { - // Unknown descriptor ==> Select all and scroll to the first one - def prefix = fragment.substring(0, fragment.lastIndexOf('-') + 1) - boolean method = (fragment.charAt(index - 1) == '(') - int prefixLength = prefix.size() - - for (def entry : declarations.entrySet()) { - if (entry.key.startsWith(prefix)) { - def flag = (entry.key.charAt(prefixLength) == '(') - if (method == flag) { - ranges.add(new DocumentRange(entry.value.startPosition, entry.value.endPosition)) - } - } - } - } + matchFragmentAndAddDocumentRange(fragment, declarations, ranges) } if (query) { @@ -304,87 +286,174 @@ class ClassFilePage } } } else { - 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 (!highlightScope || data.owner.equals(highlightScope)) { - def matcher = patternForString.matcher(data.text) - int offset = data.startPosition - - while(matcher.find()) { - ranges.add(new DocumentRange(offset + matcher.start(), offset + matcher.end())) - } - } + 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)) } } - - 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 (!highlightScope || declaration.type.equals(highlightScope)) { - 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) - } - } + } 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 (highlightFlags.indexOf('r') != -1) { - // Highlight references - for (def entry : hyperlinks.entrySet()) { - def hyperlink = entry.value - def reference = hyperlink.reference as ReferenceData - - if (!highlightScope || reference.owner.equals(highlightScope)) { - 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) - } - } + 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)) + } } + } - if (ranges) { - textArea.markAllHighlightColor = SELECT_HIGHLIGHT_COLOR - textArea.markAll(ranges) - setCaretPositionAndCenter(ranges.sort().get(0)) + @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 - void matchAndAddDocumentRange(Pattern pattern, String text, int start, int end, List ranges) { + 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 - String getMostInnerTypeName(String typeName) { + static String getMostInnerTypeName(String typeName) { int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1 int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1 int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex) diff --git a/services/src/main/groovy/jd/gui/view/component/LogPage.groovy b/services/src/main/groovy/jd/gui/view/component/LogPage.groovy index d992e9e9..26a29ffb 100644 --- a/services/src/main/groovy/jd/gui/view/component/LogPage.groovy +++ b/services/src/main/groovy/jd/gui/view/component/LogPage.groovy @@ -74,7 +74,7 @@ class LogPage extends HyperlinkPage implements UriGettable, IndexesChangeListene // Example: at java.security.AccessController.doPrivileged(Native Method) lastDotIndex = internalTypeName.lastIndexOf('/') def shortTypeName = internalTypeName.substring(lastDotIndex+1) - api.openURI(x, y, entries, null, shortTypeName + '-' + methodName + '-(?)?') + api.openURI(x, y, entries, null, shortTypeName + '-' + methodName + '-(*)?') } else { // Example: at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294) int colonIndex = lineNumberOrNativeMethodFlag.indexOf(':') diff --git a/services/src/main/groovy/jd/gui/view/component/ManifestFilePage.groovy b/services/src/main/groovy/jd/gui/view/component/ManifestFilePage.groovy index 92308ce7..2b72768e 100644 --- a/services/src/main/groovy/jd/gui/view/component/ManifestFilePage.groovy +++ b/services/src/main/groovy/jd/gui/view/component/ManifestFilePage.groovy @@ -46,7 +46,7 @@ class ManifestFilePage extends HyperlinkPage implements UriGettable, IndexesChan def internalTypeName = typeName.replace('.', '/') // Undefined parameters : 2 candidate methods // http://docs.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html - addHyperlink(new ManifestHyperlinkData(startIndex, endIndex, internalTypeName + '-premain-(?)?')) + addHyperlink(new ManifestHyperlinkData(startIndex, endIndex, internalTypeName + '-premain-(*)?')) } // Display setText(text) diff --git a/services/src/main/java/jd/gui/util/matcher/DescriptorMatcher.java b/services/src/main/java/jd/gui/util/matcher/DescriptorMatcher.java new file mode 100644 index 00000000..50175b7b --- /dev/null +++ b/services/src/main/java/jd/gui/util/matcher/DescriptorMatcher.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.matcher; + +/* + * Descriptor format : @see jd.gui.api.feature.UriOpenable + */ +public class DescriptorMatcher { + + public static boolean matchFieldDescriptors(String d1, String d2) { + return matchDescriptors(new CharBuffer(d1), new CharBuffer(d2)); + } + + protected static boolean matchDescriptors(CharBuffer cb1, CharBuffer cb2) { + if (cb1.read() == '?') { + if (cb2.read() == '?') { + return true; + } else { + cb2.unread(); + return cb2.skipType(); + } + } else { + cb1.unread(); + + if (cb2.read() == '?') { + return cb1.skipType(); + } else { + cb2.unread(); + return cb1.compareTypeWith(cb2); + } + } + } + + public static boolean matchMethodDescriptors(String d1, String d2) { + CharBuffer cb1 = new CharBuffer(d1); + CharBuffer cb2 = new CharBuffer(d2); + + if ((cb1.read() != '(') || (cb2.read() != '(')) + return false; + + if (cb1.read() == '*') { + return true; + } + if (cb2.read() == '*') { + return true; + } + + cb1.unread(); + cb2.unread(); + + // Check parameter descriptors + while (cb2.get() != ')') { + if (matchDescriptors(cb1, cb2) == false) + return false; + } + + if ((cb1.read() != ')') || (cb2.read() != ')')) + return false; + + // Check return descriptor + return matchDescriptors(cb1, cb2); + } + + protected static class CharBuffer { + protected char[] buffer; + protected int length; + protected int offset; + + public CharBuffer(String s) { + this.buffer = s.toCharArray(); + this.length = buffer.length; + this.offset = 0; + } + + public char read() { + if (offset < length) + return buffer[offset++]; + else + return (char)0; + } + + public boolean unread() { + if (offset > 0) { + offset--; + return true; + } else { + return false; + } + } + + public char get() { + if (offset < length) + return buffer[offset]; + else + return (char)0; + } + + public boolean skipType() { + if (offset < length) { + char c = buffer[offset++]; + + while ((c == '[') && (offset < length)) { + c = buffer[offset++]; + } + + if (c == 'L') { + while (offset < length) { + if (buffer[offset++] == ';') + return true; + } + } else if (c != '[') { + return true; + } + } + return false; + } + + public boolean compareTypeWith(CharBuffer other) { + if (offset >= length) + return false; + + char c = buffer[offset++]; + + if (c != other.read()) + return false; + + if (c == 'L') { + if ((offset >= length) || (other.offset >= other.length)) + return false; + + char[] otherBuffer = other.buffer; + + if ((buffer[offset] == '*') || (otherBuffer[other.offset] == '*')) { + int start = offset; + int otherStart = other.offset; + + // Search ';' + if ((searchEndOfType() == false) || (other.searchEndOfType() == false)) + return false; + + int current = offset - 1; + int otherCurrent = other.offset - 1; + + // Backward comparison + while ((start < current) && (otherStart < otherCurrent)) { + c = buffer[--current]; + if (c == '*') + return true; + + char otherC = otherBuffer[--otherCurrent]; + if (otherC == '*') + return true; + if (c != otherC) + return false; + } + } else { + // Forward comparison + while (offset < length) { + c = buffer[offset++]; + if (c != other.read()) + return false; + if (c == ';') + return true; + } + return false; + } + } + + return true; + } + + protected boolean searchEndOfType() { + while (offset < length) { + if (buffer[offset++] == ';') + return true; + } + return false; + } + + public String toString() { + return new String(buffer, offset, length-offset); + } + } +} diff --git a/services/src/test/groovy/jd/gui/util/matcher/DescriptorMatcherTest.groovy b/services/src/test/groovy/jd/gui/util/matcher/DescriptorMatcherTest.groovy new file mode 100644 index 00000000..499a1d43 --- /dev/null +++ b/services/src/test/groovy/jd/gui/util/matcher/DescriptorMatcherTest.groovy @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.util.matcher + +class DescriptorMatcherTest extends GroovyTestCase { + void testMatchFieldDescriptors() { + assertTrue DescriptorMatcher.matchFieldDescriptors("?", "?") + + assertTrue DescriptorMatcher.matchFieldDescriptors("I", "I") + assertTrue DescriptorMatcher.matchFieldDescriptors("?", "I") + assertTrue DescriptorMatcher.matchFieldDescriptors("I", "?") + + assertTrue DescriptorMatcher.matchFieldDescriptors("Ltest/Test;", "Ltest/Test;") + assertTrue DescriptorMatcher.matchFieldDescriptors("?", "Ltest/Test;") + assertTrue DescriptorMatcher.matchFieldDescriptors("Ltest/Test;", "?") + assertTrue DescriptorMatcher.matchFieldDescriptors("L*/Test;", "Ltest/Test;") + assertTrue DescriptorMatcher.matchFieldDescriptors("Ltest/Test;", "L*/Test;") + + assertTrue DescriptorMatcher.matchFieldDescriptors("L*/Test;", "L*/Test;") + assertTrue DescriptorMatcher.matchFieldDescriptors("?", "L*/Test;") + assertTrue DescriptorMatcher.matchFieldDescriptors("L*/Test;", "?") + + assertTrue DescriptorMatcher.matchFieldDescriptors("[Z", "[Z") + assertTrue DescriptorMatcher.matchFieldDescriptors("[Z", "?") + assertTrue DescriptorMatcher.matchFieldDescriptors("?", "[Z") + + assertTrue DescriptorMatcher.matchFieldDescriptors("Ltest/Test;", "Ltest/Test;") + assertTrue DescriptorMatcher.matchFieldDescriptors("Ltest/Test;", "?") + assertTrue DescriptorMatcher.matchFieldDescriptors("?", "Ltest/Test;") + + assertTrue DescriptorMatcher.matchFieldDescriptors("[[[Ltest/Test;", "[[[Ltest/Test;") + assertTrue DescriptorMatcher.matchFieldDescriptors("[[[Ltest/Test;", "?") + assertTrue DescriptorMatcher.matchFieldDescriptors("?", "[[[Ltest/Test;") + + assertTrue DescriptorMatcher.matchFieldDescriptors("[[[L*/Test;", "[[[L*/Test;") + assertTrue DescriptorMatcher.matchFieldDescriptors("[[[L*/Test;", "?") + assertTrue DescriptorMatcher.matchFieldDescriptors("?", "[[[L*/Test;") + } + + void testMatchMethodDescriptors() { + assertFalse DescriptorMatcher.matchMethodDescriptors("I", "I") + + assertTrue DescriptorMatcher.matchMethodDescriptors("()I", "()I") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "()I") + assertTrue DescriptorMatcher.matchMethodDescriptors("()I", "(*)?") + + assertTrue DescriptorMatcher.matchMethodDescriptors("(I)I", "(I)I") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "(I)I") + assertTrue DescriptorMatcher.matchMethodDescriptors("(I)I", "(*)?") + + assertTrue DescriptorMatcher.matchMethodDescriptors("(IJ)I", "(IJ)I") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "(IJ)I") + assertTrue DescriptorMatcher.matchMethodDescriptors("(IJ)I", "(*)?") + + assertTrue DescriptorMatcher.matchMethodDescriptors("(Ltest/Test;)Ltest/Test;", "(Ltest/Test;)Ltest/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "(Ltest/Test;)Ltest/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(Ltest/Test;)Ltest/Test;", "(*)?") + assertTrue DescriptorMatcher.matchMethodDescriptors("([[Ltest/Test;[[Ltest/Test;)Ltest/Test;", "([[L*/Test;[[L*/Test;)L*/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("([[L*/Test;[[L*/Test;)L*/Test;", "([[Ltest/Test;[[Ltest/Test;)Ltest/Test;") + + assertTrue DescriptorMatcher.matchMethodDescriptors("(Ltest/Test;Ltest/Test;)Ltest/Test;", "(Ltest/Test;Ltest/Test;)Ltest/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "(Ltest/Test;Ltest/Test;)Ltest/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(Ltest/Test;Ltest/Test;)Ltest/Test;", "(*)?") + + assertTrue DescriptorMatcher.matchMethodDescriptors("([[Ltest/Test;[[Ltest/Test;)Ltest/Test;", "([[Ltest/Test;[[Ltest/Test;)Ltest/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "([[Ltest/Test;[[Ltest/Test;)Ltest/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("([[Ltest/Test;[[Ltest/Test;)Ltest/Test;", "(*)?") + assertTrue DescriptorMatcher.matchMethodDescriptors("([[L*/Test;[[L*/Test;)L*/Test;", "([[Ltest/Test;[[Ltest/Test;)Ltest/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("([[Ltest/Test;[[Ltest/Test;)Ltest/Test;", "([[L*/Test;[[L*/Test;)L*/Test;") + + assertTrue DescriptorMatcher.matchMethodDescriptors("(L*/Test;)L*/Test;", "(L*/Test;)L*/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "(L*/Test;)L*/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(L*/Test;)L*/Test;", "(*)?") + + assertTrue DescriptorMatcher.matchMethodDescriptors("(L*/Test;L*/Test;)L*/Test;", "(L*/Test;L*/Test;)L*/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "(L*/Test;L*/Test;)L*/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(Ltest/Test;Ltest/Test;)Ltest/Test;", "(*)?") + + assertTrue DescriptorMatcher.matchMethodDescriptors("([[L*/Test;[[L*/Test;)L*/Test;", "([[L*/Test;[[L*/Test;)L*/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("(*)?", "([[L*/Test;[[L*/Test;)L*/Test;") + assertTrue DescriptorMatcher.matchMethodDescriptors("([[L*/Test;[[L*/Test;)L*/Test;", "(*)?") + } +} diff --git a/services/src/test/groovy/jd/gui/view/component/ClassFilePageTest.groovy b/services/src/test/groovy/jd/gui/view/component/ClassFilePageTest.groovy new file mode 100644 index 00000000..71774d02 --- /dev/null +++ b/services/src/test/groovy/jd/gui/view/component/ClassFilePageTest.groovy @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2008-2015 Emmanuel Dupuy + * This program is made available under the terms of the GPLv3 License. + */ + +package jd.gui.view.component + +class ClassFilePageTest extends GroovyTestCase { + + HashMap initDeclarations() { + def data = new ClassFilePage.DeclarationData(0, 1, "Test", "test", "I") + HashMap declarations = [:] + + // Init type declarations + declarations.put("Test", data) + declarations.put("test/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) + + // 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-()I", data) + declarations.put("test/Test-getString-()Ljava/lang/String;", data) + declarations.put("test/Test-add-(JJ)J", data) + declarations.put("test/Test-createBuffer-(I)[C", data) + + return declarations + } + + 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"))) + + return hyperlinks + } + + ArrayList initStrings() { + def strings = new ArrayList() + + strings.add(new ClassFilePage.StringData(0, 3, "abc", "Test")) + + return strings + } + + void testMatchFragmentAndAddDocumentRange() { + def declarations = initDeclarations() + def ranges = [] + + ranges.clear() + ClassFilePage.matchFragmentAndAddDocumentRange("Test-attributeBoolean-Z", declarations, ranges) + assertTrue ranges.size() == 1 + + ranges.clear() + ClassFilePage.matchFragmentAndAddDocumentRange("test/Test-attributeBoolean-Z", declarations, ranges) + assertTrue ranges.size() == 1 + + ranges.clear() + ClassFilePage.matchFragmentAndAddDocumentRange("*/Test-attributeBoolean-Z", declarations, ranges) + assertTrue ranges.size() == 2 + + ranges.clear() + ClassFilePage.matchFragmentAndAddDocumentRange("Test-createBuffer-(I)[C", declarations, ranges) + assertTrue ranges.size() == 1 + + ranges.clear() + ClassFilePage.matchFragmentAndAddDocumentRange("test/Test-createBuffer-(I)[C", declarations, ranges) + assertTrue ranges.size() == 1 + + ranges.clear() + ClassFilePage.matchFragmentAndAddDocumentRange("*/Test-getString-(*)?", declarations, ranges) + assertTrue ranges.size() == 2 + + ranges.clear() + ClassFilePage.matchFragmentAndAddDocumentRange("test/Test-add-(?J)?", declarations, ranges) + assertTrue ranges.size() == 1 + } + + void testMatchQueryAndAddDocumentRange() { + def declarations = initDeclarations() + def hyperlinks = initHyperlinks() + def strings = initStrings() + def ranges = [] + + ranges.clear() + ClassFilePage.matchQueryAndAddDocumentRange([highlightPattern:"ab", highlightFlags:"s", highlightScope:null], declarations, hyperlinks, strings, ranges) + assertTrue ranges.size() == 1 + + ranges.clear() + ClassFilePage.matchQueryAndAddDocumentRange([highlightPattern:"ab", highlightFlags:"s", highlightScope:""], declarations, hyperlinks, strings, ranges) + assertTrue ranges.size() == 1 + + ranges.clear() + ClassFilePage.matchQueryAndAddDocumentRange([highlightPattern:"ab", highlightFlags:"s", highlightScope:"Test"], declarations, hyperlinks, strings, ranges) + assertTrue ranges.size() == 1 + } + + void testMatchScope() { + assertTrue ClassFilePage.matchScope(null, "java/lang/String") + assertTrue ClassFilePage.matchScope("", "java/lang/String") + + assertTrue ClassFilePage.matchScope("java/lang/String", "java/lang/String") + assertTrue ClassFilePage.matchScope("*/lang/String", "java/lang/String") + assertTrue ClassFilePage.matchScope("*/String", "java/lang/String") + + assertTrue ClassFilePage.matchScope(null, "Test") + assertTrue ClassFilePage.matchScope("", "Test") + + assertTrue ClassFilePage.matchScope("Test", "Test") + assertTrue ClassFilePage.matchScope("*/Test", "Test") + } +}