Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
J
jadx
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open-source
jadx
Commits
bd4d4f49
Commit
bd4d4f49
authored
Jul 13, 2015
by
Skylot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
gui: add full text search (#74)
parent
5a24eac3
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
253 additions
and
98 deletions
+253
-98
build.gradle
jadx-gui/build.gradle
+1
-0
CodeNode.java
jadx-gui/src/main/java/jadx/gui/treemodel/CodeNode.java
+46
-0
MainWindow.java
jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
+9
-2
SearchDialog.java
jadx-gui/src/main/java/jadx/gui/ui/SearchDialog.java
+93
-68
CacheObject.java
jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java
+21
-0
NameIndex.java
jadx-gui/src/main/java/jadx/gui/utils/NameIndex.java
+0
-28
TextSearchIndex.java
jadx-gui/src/main/java/jadx/gui/utils/TextSearchIndex.java
+83
-0
No files found.
jadx-gui/build.gradle
View file @
bd4d4f49
...
...
@@ -8,6 +8,7 @@ dependencies {
compile
'com.fifesoft:rsyntaxtextarea:2.5.6'
compile
'com.google.code.gson:gson:2.3.1'
compile
files
(
'libs/jfontchooser-1.0.5.jar'
)
compile
'com.googlecode.concurrent-trees:concurrent-trees:2.4.0'
}
applicationDistribution
.
with
{
...
...
jadx-gui/src/main/java/jadx/gui/treemodel/CodeNode.java
0 → 100644
View file @
bd4d4f49
package
jadx
.
gui
.
treemodel
;
import
jadx.api.JavaClass
;
import
jadx.gui.utils.Utils
;
import
javax.swing.Icon
;
import
javax.swing.ImageIcon
;
public
class
CodeNode
extends
JClass
{
private
static
final
ImageIcon
ICON
=
Utils
.
openIcon
(
"file_obj"
);
private
final
String
line
;
private
final
int
lineNum
;
public
CodeNode
(
JavaClass
javaClass
,
int
lineNum
,
String
line
)
{
super
(
javaClass
,
(
JClass
)
makeFrom
(
javaClass
.
getDeclaringClass
()));
this
.
line
=
line
;
this
.
lineNum
=
lineNum
;
}
@Override
public
Icon
getIcon
()
{
return
ICON
;
}
@Override
public
int
getLine
()
{
return
lineNum
;
}
@Override
public
String
makeString
()
{
return
getCls
().
getFullName
()
+
":"
+
lineNum
+
" "
+
line
;
}
@Override
public
String
makeLongString
()
{
return
makeString
();
}
@Override
public
String
toString
()
{
return
makeString
();
}
}
jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
View file @
bd4d4f49
...
...
@@ -10,6 +10,7 @@ import jadx.gui.treemodel.JRoot;
import
jadx.gui.update.JadxUpdate
;
import
jadx.gui.update.JadxUpdate.IUpdateCallback
;
import
jadx.gui.update.data.Release
;
import
jadx.gui.utils.CacheObject
;
import
jadx.gui.utils.Link
;
import
jadx.gui.utils.NLS
;
import
jadx.gui.utils.Position
;
...
...
@@ -88,6 +89,7 @@ public class MainWindow extends JFrame {
private
final
JadxWrapper
wrapper
;
private
final
JadxSettings
settings
;
private
final
CacheObject
cacheObject
;
private
JPanel
mainPanel
;
...
...
@@ -105,6 +107,7 @@ public class MainWindow extends JFrame {
public
MainWindow
(
JadxSettings
settings
)
{
this
.
wrapper
=
new
JadxWrapper
(
settings
);
this
.
settings
=
settings
;
this
.
cacheObject
=
new
CacheObject
();
initUI
();
initMenuAndToolbar
();
...
...
@@ -162,6 +165,7 @@ public class MainWindow extends JFrame {
}
public
void
openFile
(
File
file
)
{
cacheObject
.
reset
();
wrapper
.
openFile
(
file
);
deobfToggleBtn
.
setSelected
(
settings
.
isDeobfuscationOn
());
settings
.
addRecentFile
(
file
.
getAbsolutePath
());
...
...
@@ -355,10 +359,9 @@ public class MainWindow extends JFrame {
nav
.
add
(
search
);
ActionListener
searchAction
=
new
ActionListener
()
{
public
void
actionPerformed
(
ActionEvent
event
)
{
final
SearchDialog
dialog
=
new
SearchDialog
(
MainWindow
.
this
,
tabbedPane
,
wrapper
);
dialog
.
prepare
();
SwingUtilities
.
invokeLater
(
new
Runnable
()
{
public
void
run
()
{
SearchDialog
dialog
=
new
SearchDialog
(
MainWindow
.
this
,
tabbedPane
,
wrapper
);
dialog
.
setVisible
(
true
);
}
});
...
...
@@ -606,6 +609,10 @@ public class MainWindow extends JFrame {
return
settings
;
}
public
CacheObject
getCacheObject
()
{
return
cacheObject
;
}
private
class
OpenListener
implements
ActionListener
{
public
void
actionPerformed
(
ActionEvent
event
)
{
openFile
();
...
...
jadx-gui/src/main/java/jadx/gui/ui/SearchDialog.java
View file @
bd4d4f49
package
jadx
.
gui
.
ui
;
import
jadx.api.JavaClass
;
import
jadx.api.JavaField
;
import
jadx.api.JavaMethod
;
import
jadx.api.JavaNode
;
import
jadx.gui.JadxWrapper
;
import
jadx.gui.treemodel.JNode
;
import
jadx.gui.treemodel.TextNode
;
import
jadx.gui.utils.CacheObject
;
import
jadx.gui.utils.NLS
;
import
jadx.gui.utils.NameIndex
;
import
jadx.gui.utils.Position
;
import
jadx.gui.utils.TextSearchIndex
;
import
jadx.gui.utils.TextStandardActions
;
import
javax.swing.BorderFactory
;
...
...
@@ -39,118 +38,133 @@ import java.awt.Container;
import
java.awt.Cursor
;
import
java.awt.Dimension
;
import
java.awt.FlowLayout
;
import
java.awt.Frame
;
import
java.awt.event.ActionEvent
;
import
java.awt.event.ActionListener
;
import
java.awt.event.ItemEvent
;
import
java.awt.event.ItemListener
;
import
java.awt.event.KeyAdapter
;
import
java.awt.event.KeyEvent
;
import
java.awt.event.MouseAdapter
;
import
java.awt.event.MouseEvent
;
import
java.util.Collections
;
import
java.awt.event.WindowAdapter
;
import
java.awt.event.WindowEvent
;
import
java.util.EnumSet
;
import
java.util.List
;
import
java.util.Set
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
public
class
SearchDialog
extends
JDialog
{
private
static
final
long
serialVersionUID
=
-
5105405456969134105L
;
private
static
final
int
MAX_RESULTS_COUNT
=
100
;
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
SearchDialog
.
class
);
private
static
final
int
MAX_RESULTS_COUNT
=
500
;
private
static
enum
SearchOptions
{
private
enum
SearchOptions
{
CLASS
,
METHOD
,
FIELD
,
CODE
}
private
static
final
Set
<
SearchOptions
>
OPTIONS
=
EnumSet
.
of
(
SearchOptions
.
CLASS
,
SearchOptions
.
METHOD
,
SearchOptions
.
FIELD
);
private
static
final
Set
<
SearchOptions
>
OPTIONS
=
EnumSet
.
allOf
(
SearchOptions
.
class
);
private
final
TabbedPane
tabbedPane
;
private
final
JadxWrapper
wrapper
;
private
NameIndex
<
JavaNode
>
index
;
private
final
CacheObject
cache
;
private
JTextField
searchField
;
private
ResultsModel
resultsModel
;
private
JList
resultsList
;
private
JProgressBar
busyBar
;
public
SearchDialog
(
Frame
owner
,
TabbedPane
tabbedPane
,
JadxWrapper
wrapper
)
{
super
(
owner
);
public
SearchDialog
(
MainWindow
mainWindow
,
TabbedPane
tabbedPane
,
JadxWrapper
wrapper
)
{
super
(
mainWindow
);
this
.
tabbedPane
=
tabbedPane
;
this
.
wrapper
=
wrapper
;
this
.
cache
=
mainWindow
.
getCacheObject
();
initUI
();
addWindowListener
(
new
WindowAdapter
()
{
@Override
public
void
windowActivated
(
WindowEvent
e
)
{
SwingUtilities
.
invokeLater
(
new
Runnable
()
{
@Override
public
void
run
()
{
prepare
();
}
});
}
});
}
public
void
prepare
()
{
TextSearchIndex
index
=
cache
.
getTextIndex
();
if
(
index
!=
null
)
{
return
;
}
LoadTask
task
=
new
LoadTask
();
task
.
init
();
task
.
execute
();
}
private
void
loadData
()
{
index
=
new
NameIndex
<
JavaNode
>();
TextSearchIndex
index
=
cache
.
getTextIndex
();
if
(
index
!=
null
)
{
return
;
}
index
=
new
TextSearchIndex
();
for
(
JavaClass
cls
:
wrapper
.
getClasses
())
{
index
Clas
s
(
cls
);
index
.
indexName
s
(
cls
);
}
for
(
JavaClass
cls
:
wrapper
.
getClasses
())
{
index
.
indexCode
(
cls
);
}
cache
.
setTextIndex
(
index
);
}
private
synchronized
void
performSearch
()
{
resultsModel
.
removeAllElements
();
String
text
=
searchField
.
getText
();
List
<
JavaNode
>
results
;
if
(
text
==
null
||
text
.
isEmpty
()
||
index
==
null
)
{
results
=
Collections
.
emptyList
();
}
else
{
results
=
index
.
search
(
text
);
}
resultsModel
.
setResults
(
results
);
}
private
void
openSelectedItem
()
{
int
selectedId
=
resultsList
.
getSelectedIndex
();
if
(
selectedId
==
-
1
)
{
if
(
text
==
null
||
text
.
isEmpty
()
||
OPTIONS
.
isEmpty
())
{
return
;
}
JNode
node
=
(
JNode
)
resultsModel
.
get
(
selectedId
);
tabbedPane
.
showCode
(
new
Position
(
node
.
getRootClass
(),
node
.
getLine
()));
dispose
();
TextSearchIndex
index
=
cache
.
getTextIndex
();
if
(
index
==
null
)
{
return
;
}
private
void
indexClass
(
JavaClass
cls
)
{
if
(
OPTIONS
.
contains
(
SearchOptions
.
CLASS
))
{
index
.
add
(
cls
.
getFullName
(),
cls
);
resultsModel
.
addAll
(
index
.
searchClsName
(
text
)
);
}
if
(
OPTIONS
.
contains
(
SearchOptions
.
METHOD
))
{
for
(
JavaMethod
mth
:
cls
.
getMethods
())
{
index
.
add
(
mth
.
getFullName
(),
mth
);
}
resultsModel
.
addAll
(
index
.
searchMthName
(
text
));
}
if
(
OPTIONS
.
contains
(
SearchOptions
.
FIELD
))
{
for
(
JavaField
fld
:
cls
.
getFields
())
{
index
.
add
(
fld
.
getFullName
(),
fld
);
}
resultsModel
.
addAll
(
index
.
searchFldName
(
text
));
}
if
(
OPTIONS
.
contains
(
SearchOptions
.
CODE
))
{
String
code
=
cls
.
getCode
();
index
.
add
(
code
,
cls
);
resultsModel
.
addAll
(
index
.
searchCode
(
text
));
}
LOG
.
info
(
"Search returned {} results"
,
resultsModel
.
size
());
}
for
(
JavaClass
innerCls
:
cls
.
getInnerClasses
())
{
indexClass
(
innerCls
);
private
void
openSelectedItem
()
{
int
selectedId
=
resultsList
.
getSelectedIndex
();
if
(
selectedId
==
-
1
)
{
return
;
}
JNode
node
=
(
JNode
)
resultsModel
.
get
(
selectedId
);
tabbedPane
.
showCode
(
new
Position
(
node
.
getRootClass
(),
node
.
getLine
()));
dispose
();
}
private
class
LoadTask
extends
SwingWorker
<
Void
,
Void
>
{
public
void
init
()
{
SwingUtilities
.
invokeLater
(
new
Runnable
()
{
public
void
run
()
{
public
LoadTask
()
{
setCursor
(
Cursor
.
getPredefinedCursor
(
Cursor
.
WAIT_CURSOR
));
busyBar
.
setVisible
(
true
);
searchField
.
setEnabled
(
false
);
resultsList
.
setEnabled
(
false
);
setCursor
(
Cursor
.
getPredefinedCursor
(
Cursor
.
WAIT_CURSOR
));
}
});
}
@Override
...
...
@@ -175,14 +189,15 @@ public class SearchDialog extends JDialog {
private
static
class
ResultsModel
extends
DefaultListModel
{
private
static
final
long
serialVersionUID
=
-
7821286846923903208L
;
private
void
setResults
(
List
<
JavaNode
>
results
)
{
removeAllElements
();
if
(
results
.
isEmpty
())
{
private
void
addAll
(
Iterable
<?
extends
JNode
>
nodes
)
{
for
(
JNode
node
:
nodes
)
{
if
(
size
()
>=
MAX_RESULTS_COUNT
)
{
if
(
size
()
==
MAX_RESULTS_COUNT
)
{
addElement
(
new
TextNode
(
"Search results truncated (limit: "
+
MAX_RESULTS_COUNT
+
")"
));
}
return
;
}
int
count
=
Math
.
min
(
results
.
size
(),
MAX_RESULTS_COUNT
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
addElement
(
JNode
.
makeFrom
(
results
.
get
(
i
)));
addElement
(
node
);
}
}
}
...
...
@@ -243,7 +258,6 @@ public class SearchDialog extends JDialog {
JCheckBox
mthChBox
=
makeOptionsCheckBox
(
NLS
.
str
(
"search_dialog.method"
),
SearchOptions
.
METHOD
);
JCheckBox
fldChBox
=
makeOptionsCheckBox
(
NLS
.
str
(
"search_dialog.field"
),
SearchOptions
.
FIELD
);
JCheckBox
codeChBox
=
makeOptionsCheckBox
(
NLS
.
str
(
"search_dialog.code"
),
SearchOptions
.
CODE
);
codeChBox
.
setEnabled
(
false
);
resultsModel
=
new
ResultsModel
();
resultsList
=
new
JList
(
resultsModel
);
...
...
@@ -307,12 +321,24 @@ public class SearchDialog extends JDialog {
buttonPane
.
add
(
Box
.
createRigidArea
(
new
Dimension
(
10
,
0
)));
buttonPane
.
add
(
cancelButton
);
Container
contentPane
=
getContentPane
();
final
Container
contentPane
=
getContentPane
();
contentPane
.
add
(
searchPane
,
BorderLayout
.
PAGE_START
);
contentPane
.
add
(
listPane
,
BorderLayout
.
CENTER
);
contentPane
.
add
(
buttonPane
,
BorderLayout
.
PAGE_END
);
getRootPane
().
setDefaultButton
(
openBtn
);
searchField
.
addKeyListener
(
new
KeyAdapter
()
{
@Override
public
void
keyReleased
(
KeyEvent
e
)
{
if
(
e
.
getKeyCode
()
==
KeyEvent
.
VK_ENTER
)
{
resultsList
.
requestFocus
();
if
(!
resultsModel
.
isEmpty
())
{
resultsList
.
setSelectedIndex
(
0
);
}
}
}
});
setTitle
(
NLS
.
str
(
"menu.search"
));
pack
();
setSize
(
700
,
500
);
...
...
@@ -322,17 +348,16 @@ public class SearchDialog extends JDialog {
}
private
JCheckBox
makeOptionsCheckBox
(
String
name
,
final
SearchOptions
opt
)
{
JCheckBox
chBox
=
new
JCheckBox
(
name
);
final
JCheckBox
chBox
=
new
JCheckBox
(
name
);
chBox
.
setAlignmentX
(
LEFT_ALIGNMENT
);
chBox
.
setSelected
(
OPTIONS
.
contains
(
opt
));
chBox
.
addItemListener
(
new
ItemListener
()
{
public
void
itemStateChanged
(
ItemEvent
e
)
{
if
(
e
.
getStateChange
()
==
ItemEvent
.
SELECTED
)
{
if
(
chBox
.
isSelected
()
)
{
OPTIONS
.
add
(
opt
);
}
else
{
OPTIONS
.
remove
(
opt
);
}
loadData
();
performSearch
();
}
});
...
...
jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java
0 → 100644
View file @
bd4d4f49
package
jadx
.
gui
.
utils
;
import
org.jetbrains.annotations.Nullable
;
public
class
CacheObject
{
@Nullable
private
TextSearchIndex
textIndex
;
public
void
reset
()
{
textIndex
=
null
;
}
@Nullable
public
TextSearchIndex
getTextIndex
()
{
return
textIndex
;
}
public
void
setTextIndex
(
@Nullable
TextSearchIndex
textIndex
)
{
this
.
textIndex
=
textIndex
;
}
}
jadx-gui/src/main/java/jadx/gui/utils/NameIndex.java
deleted
100644 → 0
View file @
5a24eac3
package
jadx
.
gui
.
utils
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
public
class
NameIndex
<
T
>
{
private
final
List
<
String
>
strings
=
new
ArrayList
<
String
>();
private
final
List
<
T
>
objects
=
new
ArrayList
<
T
>();
public
void
add
(
String
name
,
T
obj
)
{
strings
.
add
(
name
);
objects
.
add
(
obj
);
}
public
List
<
T
>
search
(
String
text
)
{
List
<
T
>
results
=
new
ArrayList
<
T
>();
int
count
=
strings
.
size
();
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
String
name
=
strings
.
get
(
i
);
if
(
name
.
contains
(
text
))
{
results
.
add
(
objects
.
get
(
i
));
}
}
return
results
.
isEmpty
()
?
Collections
.<
T
>
emptyList
()
:
results
;
}
}
jadx-gui/src/main/java/jadx/gui/utils/TextSearchIndex.java
0 → 100644
View file @
bd4d4f49
package
jadx
.
gui
.
utils
;
import
jadx.api.JavaClass
;
import
jadx.api.JavaField
;
import
jadx.api.JavaMethod
;
import
jadx.gui.treemodel.CodeNode
;
import
jadx.gui.treemodel.JNode
;
import
java.io.BufferedReader
;
import
java.io.StringReader
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory
;
import
com.googlecode.concurrenttrees.suffix.ConcurrentSuffixTree
;
import
com.googlecode.concurrenttrees.suffix.SuffixTree
;
public
class
TextSearchIndex
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
TextSearchIndex
.
class
);
private
SuffixTree
<
JNode
>
clsNamesTree
;
private
SuffixTree
<
JNode
>
mthNamesTree
;
private
SuffixTree
<
JNode
>
fldNamesTree
;
private
SuffixTree
<
CodeNode
>
codeTree
;
public
TextSearchIndex
()
{
clsNamesTree
=
new
ConcurrentSuffixTree
<
JNode
>(
new
DefaultCharArrayNodeFactory
());
mthNamesTree
=
new
ConcurrentSuffixTree
<
JNode
>(
new
DefaultCharArrayNodeFactory
());
fldNamesTree
=
new
ConcurrentSuffixTree
<
JNode
>(
new
DefaultCharArrayNodeFactory
());
codeTree
=
new
ConcurrentSuffixTree
<
CodeNode
>(
new
DefaultCharArrayNodeFactory
());
}
public
void
indexNames
(
JavaClass
cls
)
{
cls
.
decompile
();
clsNamesTree
.
put
(
cls
.
getFullName
(),
JNode
.
makeFrom
(
cls
));
for
(
JavaMethod
mth
:
cls
.
getMethods
())
{
mthNamesTree
.
put
(
mth
.
getFullName
(),
JNode
.
makeFrom
(
mth
));
}
for
(
JavaField
fld
:
cls
.
getFields
())
{
fldNamesTree
.
put
(
fld
.
getFullName
(),
JNode
.
makeFrom
(
fld
));
}
for
(
JavaClass
innerCls
:
cls
.
getInnerClasses
())
{
indexNames
(
innerCls
);
}
}
public
void
indexCode
(
JavaClass
cls
)
{
try
{
String
code
=
cls
.
getCode
();
BufferedReader
bufReader
=
new
BufferedReader
(
new
StringReader
(
code
));
String
line
;
int
lineNum
=
0
;
while
((
line
=
bufReader
.
readLine
())
!=
null
)
{
lineNum
++;
line
=
line
.
trim
();
if
(!
line
.
isEmpty
())
{
CodeNode
node
=
new
CodeNode
(
cls
,
lineNum
,
line
);
codeTree
.
put
(
line
,
node
);
}
}
}
catch
(
Exception
e
)
{
LOG
.
warn
(
"Failed to index class: {}"
,
cls
,
e
);
}
}
public
Iterable
<
JNode
>
searchClsName
(
String
text
)
{
return
clsNamesTree
.
getValuesForKeysContaining
(
text
);
}
public
Iterable
<
JNode
>
searchMthName
(
String
text
)
{
return
mthNamesTree
.
getValuesForKeysContaining
(
text
);
}
public
Iterable
<
JNode
>
searchFldName
(
String
text
)
{
return
fldNamesTree
.
getValuesForKeysContaining
(
text
);
}
public
Iterable
<
CodeNode
>
searchCode
(
String
text
)
{
return
codeTree
.
getValuesForKeysContaining
(
text
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment