11package jadx .gui .search .providers ;
22
3- import java .io . File ;
4- import java .io . IOException ;
5- import java .util .ArrayList ;
3+ import java .util . ArrayDeque ;
4+ import java .util . Deque ;
5+ import java .util .Enumeration ;
66import java .util .HashSet ;
7- import java .util .List ;
87import java .util .Set ;
9- import java .util .zip .ZipEntry ;
10- import java .util .zip .ZipFile ;
118
129import javax .swing .tree .TreeNode ;
1310
1815import jadx .api .ICodeWriter ;
1916import jadx .api .ResourceFile ;
2017import jadx .api .ResourceType ;
21- import jadx .core . utils .files . FileUtils ;
18+ import jadx .api . plugins . utils .CommonFileUtils ;
2219import jadx .gui .jobs .Cancelable ;
2320import jadx .gui .search .ISearchProvider ;
2421import jadx .gui .search .SearchSettings ;
2522import jadx .gui .treemodel .JNode ;
2623import jadx .gui .treemodel .JResSearchNode ;
2724import jadx .gui .treemodel .JResource ;
25+ import jadx .gui .treemodel .JRoot ;
2826import jadx .gui .ui .MainWindow ;
29- import jadx .gui .utils .CacheObject ;
3027
3128public class ResourceSearchProvider implements ISearchProvider {
3229 private static final Logger LOG = LoggerFactory .getLogger (ResourceSearchProvider .class );
3330
34- private final CacheObject cache ;
3531 private final SearchSettings searchSettings ;
36- private final Set <String > extSet = new HashSet <>();
37-
38- private List <JResource > resNodes ;
39- private String fileExts ;
32+ private final Set <String > extSet ;
33+ private final int sizeLimit ;
4034 private boolean anyExt ;
41- private int sizeLimit ;
4235
43- private int progress ;
36+ /**
37+ * Resources queue for process. Using UI nodes to reuse loading cache
38+ */
39+ private final Deque <JResource > resQueue ;
4440 private int pos ;
4541
4642 public ResourceSearchProvider (MainWindow mw , SearchSettings searchSettings ) {
47- this .cache = mw .getCacheObject ();
4843 this .searchSettings = searchSettings ;
44+ this .sizeLimit = mw .getSettings ().getSrhResourceSkipSize () * 1048576 ;
45+ this .extSet = buildAllowedFilesExtensions (mw .getSettings ().getSrhResourceFileExt ());
46+ this .resQueue = initResQueue (mw );
4947 }
5048
5149 @ Override
5250 public @ Nullable JNode next (Cancelable cancelable ) {
53- if (resNodes == null ) {
54- load ();
55- }
56- if (resNodes .isEmpty ()) {
57- return null ;
58- }
5951 while (true ) {
6052 if (cancelable .isCanceled ()) {
6153 return null ;
6254 }
63- JResource resNode = resNodes .get (progress );
55+ JResource resNode = getNextNode ();
56+ if (resNode == null ) {
57+ return null ;
58+ }
6459 JNode newResult = search (resNode );
6560 if (newResult != null ) {
6661 return newResult ;
6762 }
68- progress ++;
6963 pos = 0 ;
70- if (progress >= resNodes .size ()) {
64+ resQueue .removeLast ();
65+ addChildren (resQueue , resNode );
66+ if (resQueue .isEmpty ()) {
7167 return null ;
7268 }
7369 }
@@ -94,133 +90,110 @@ private JNode search(JResource resNode) {
9490 return new JResSearchNode (resNode , line .trim (), newPos );
9591 }
9692
97- private synchronized void load () {
98- resNodes = new ArrayList <>();
99- sizeLimit = cache .getJadxSettings ().getSrhResourceSkipSize () * 1048576 ;
100- fileExts = cache .getJadxSettings ().getSrhResourceFileExt ();
101- for (String extStr : fileExts .split ("\\ |" )) {
102- String ext = extStr .trim ();
103- if (!ext .isEmpty ()) {
104- anyExt = ext .equals ("*" );
105- if (anyExt ) {
106- break ;
107- }
108- extSet .add (ext );
109- }
93+ private @ Nullable JResource getNextNode () {
94+ JResource node = resQueue .peekLast ();
95+ if (node == null ) {
96+ return null ;
11097 }
111- try ( ZipFile zipFile = getZipFile ( cache . getJRoot ())) {
112- traverseTree ( cache . getJRoot (), zipFile ); // reindex
98+ try {
99+ node . loadNode ();
113100 } catch (Exception e ) {
114- LOG .error ("Failed to apply settings to resource index" , e );
101+ LOG .error ("Error load resource node: {}" , node , e );
102+ resQueue .removeLast ();
103+ return getNextNode ();
115104 }
105+ if (node .getType () == JResource .JResType .FILE ) {
106+ if (shouldProcess (node )) {
107+ return node ;
108+ }
109+ resQueue .removeLast ();
110+ return getNextNode ();
111+ }
112+ // dit or root
113+ resQueue .removeLast ();
114+ addChildren (resQueue , node );
115+ return getNextNode ();
116116 }
117117
118- private void traverseTree (TreeNode root , @ Nullable ZipFile zip ) {
119- for (int i = 0 ; i < root .getChildCount (); i ++) {
120- TreeNode node = root .getChildAt (i );
118+ private void addChildren (Deque <JResource > deque , JResource resNode ) {
119+ Enumeration <TreeNode > children = resNode .children ();
120+ while (children .hasMoreElements ()) {
121+ TreeNode node = children .nextElement ();
121122 if (node instanceof JResource ) {
122- JResource resNode = (JResource ) node ;
123- try {
124- resNode .loadNode ();
125- } catch (Exception e ) {
126- LOG .error ("Error load resource node: {}" , resNode , e );
127- return ;
128- }
129- ResourceFile resFile = resNode .getResFile ();
130- if (resFile == null ) {
131- traverseTree (node , zip );
132- } else {
133- if (resFile .getType () == ResourceType .ARSC && shouldSearchXML ()) {
134- resFile .loadContent ();
135- resNode .getFiles ().forEach (t -> traverseTree (t , null ));
136- } else {
137- filter (resNode , zip );
138- }
139- }
123+ deque .add ((JResource ) node );
140124 }
141125 }
142126 }
143127
144- private boolean shouldSearchXML () {
145- return anyExt || fileExts .contains (".xml" );
146- }
147-
148- @ Nullable
149- private ZipFile getZipFile (TreeNode res ) {
150- for (int i = 0 ; i < res .getChildCount (); i ++) {
151- TreeNode node = res .getChildAt (i );
128+ private static Deque <JResource > initResQueue (MainWindow mw ) {
129+ JRoot jRoot = mw .getTreeRoot ();
130+ Deque <JResource > deque = new ArrayDeque <>(jRoot .getChildCount ());
131+ Enumeration <TreeNode > children = jRoot .children ();
132+ while (children .hasMoreElements ()) {
133+ TreeNode node = children .nextElement ();
152134 if (node instanceof JResource ) {
153135 JResource resNode = (JResource ) node ;
154- try {
155- resNode .loadNode ();
156- } catch (Exception e ) {
157- LOG .error ("Error load resource node: {}" , resNode , e );
158- return null ;
159- }
160- ResourceFile file = resNode .getResFile ();
161- if (file == null ) {
162- ZipFile zip = getZipFile (resNode );
163- if (zip != null ) {
164- return zip ;
165- }
166- } else {
167- ResourceFile .ZipRef zipRef = file .getZipRef ();
168- if (zipRef != null ) {
169- File zfile = zipRef .getZipFile ();
170- if (FileUtils .isZipFile (zfile )) {
171- try {
172- return new ZipFile (zfile );
173- } catch (IOException ignore ) {
174- }
175- }
176- }
177- }
136+ deque .add (resNode );
178137 }
179138 }
180- return null ;
139+ return deque ;
181140 }
182141
183- private void filter ( JResource resNode , ZipFile zip ) {
184- ResourceFile resFile = resNode . getResFile ();
185- if ( JResource . isSupportedForView ( resFile . getType () )) {
186- long size = - 1 ;
187- if (zip != null ) {
188- ZipEntry entry = zip . getEntry ( resFile . getOriginalName () );
189- if (entry != null ) {
190- size = entry . getSize () ;
142+ private Set < String > buildAllowedFilesExtensions ( String srhResourceFileExt ) {
143+ Set < String > set = new HashSet <> ();
144+ for ( String extStr : srhResourceFileExt . split ( "[|.]" )) {
145+ String ext = extStr . trim () ;
146+ if (! ext . isEmpty () ) {
147+ anyExt = ext . equals ( "*" );
148+ if (anyExt ) {
149+ break ;
191150 }
151+ set .add (ext );
192152 }
193- if (size == -1 ) { // resource from ARSC is unknown size
194- try {
195- size = resNode .getCodeInfo ().getCodeStr ().length ();
196- } catch (Exception ignore ) {
197- return ;
153+ }
154+ return set ;
155+ }
156+
157+ private boolean shouldProcess (JResource resNode ) {
158+ if (!anyExt ) {
159+ String fileExt ;
160+ ResourceFile resFile = resNode .getResFile ();
161+ if (resFile .getType () == ResourceType .ARSC ) {
162+ fileExt = "xml" ;
163+ } else {
164+ fileExt = CommonFileUtils .getFileExtension (resFile .getOriginalName ());
165+ if (fileExt == null ) {
166+ return false ;
198167 }
199168 }
200- if (size <= sizeLimit ) {
201- if (!anyExt ) {
202- for (String ext : extSet ) {
203- if (resFile .getOriginalName ().endsWith (ext )) {
204- resNodes .add (resNode );
205- break ;
206- }
207- }
208- } else {
209- resNodes .add (resNode );
210- }
211- } else {
212- LOG .debug ("Resource index skipped because of size limit: {} res size {} bytes" , resNode , size );
169+ if (!extSet .contains (fileExt )) {
170+ return false ;
213171 }
214172 }
173+ if (sizeLimit == 0 ) {
174+ return true ;
175+ }
176+ try {
177+ int charsCount = resNode .getCodeInfo ().getCodeStr ().length ();
178+ long size = charsCount * 8L ;
179+ if (size > sizeLimit ) {
180+ LOG .debug ("Resource search skipped because of size limit: {} res size {} bytes" , resNode , size );
181+ return false ;
182+ }
183+ return true ;
184+ } catch (Exception e ) {
185+ LOG .warn ("Resource load error: {}" , resNode , e );
186+ return false ;
187+ }
215188 }
216189
217190 @ Override
218191 public int progress () {
219- return progress ;
192+ return 0 ;
220193 }
221194
222195 @ Override
223196 public int total () {
224- return resNodes == null ? 0 : resNodes . size () ;
197+ return 0 ;
225198 }
226199}
0 commit comments