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:
*
* - file://dir1/dir2/
* - file://dir1/dir2/file
@@ -28,13 +48,15 @@
* - jar://dir1/dir2/javafile#type-methodName-descriptor
* - jar://dir1/dir2/javafile#innertype
* - jar://dir1/dir2/javafile#innertype-fieldName-?
- * - jar://dir1/dir2/javafile#innertype-methodName-(?)?
+ * - jar://dir1/dir2/javafile#innertype-methodName-(*)?
+ * - jar://dir1/dir2/javafile#innertype-methodName-(?JZLjava/lang/Sting;C)I
* - jar://dir1/dir2/javafile#innertype-fieldName-descriptor
* - jar://dir1/dir2/javafile#innertype-methodName-descriptor
*
- * - file://dir1/dir2/file?lineNumber=number
+ * - file://dir1/dir2/file?lineNumber=numeric
* - file://dir1/dir2/file?position=numeric
- * - file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=internalTypeName
+ * - file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=java/lang/String
+ * - file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=*\/String
*
*/
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")
+ }
+}