package jadx.gui.utils.search; import jadx.api.JavaClass; import jadx.api.JavaField; import jadx.api.JavaMethod; import jadx.api.JavaNode; import jadx.core.codegen.CodeWriter; import jadx.gui.treemodel.CodeNode; import jadx.gui.treemodel.JNode; import jadx.gui.ui.CommonSearchDialog; import jadx.gui.utils.CodeLinesInfo; import jadx.gui.utils.JNodeCache; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TextSearchIndex { private static final Logger LOG = LoggerFactory.getLogger(TextSearchIndex.class); private final JNodeCache nodeCache; private SearchIndex<JNode> clsNamesIndex; private SearchIndex<JNode> mthNamesIndex; private SearchIndex<JNode> fldNamesIndex; private SearchIndex<CodeNode> codeIndex; private List<JavaClass> skippedClasses = new ArrayList<JavaClass>(); public TextSearchIndex(JNodeCache nodeCache) { this.nodeCache = nodeCache; this.clsNamesIndex = new SimpleIndex<JNode>(); this.mthNamesIndex = new SimpleIndex<JNode>(); this.fldNamesIndex = new SimpleIndex<JNode>(); this.codeIndex = new CodeIndex<CodeNode>(); } public void indexNames(JavaClass cls) { clsNamesIndex.put(cls.getFullName(), nodeCache.makeFrom(cls)); for (JavaMethod mth : cls.getMethods()) { mthNamesIndex.put(mth.getFullName(), this.nodeCache.makeFrom(mth)); } for (JavaField fld : cls.getFields()) { fldNamesIndex.put(fld.getFullName(), nodeCache.makeFrom(fld)); } for (JavaClass innerCls : cls.getInnerClasses()) { indexNames(innerCls); } } public void indexCode(JavaClass cls, CodeLinesInfo linesInfo, List<StringRef> lines) { try { boolean strRefSupported = codeIndex.isStringRefSupported(); int count = lines.size(); for (int i = 0; i < count; i++) { StringRef line = lines.get(i); if (line.length() != 0 && line.charAt(0) != '}') { int lineNum = i + 1; JavaNode node = linesInfo.getJavaNodeByLine(lineNum); CodeNode codeNode = new CodeNode(nodeCache.makeFrom(node == null ? cls : node), lineNum, line); if (strRefSupported) { codeIndex.put(line, codeNode); } else { codeIndex.put(line.toString(), codeNode); } } } } catch (Exception e) { LOG.warn("Failed to index class: {}", cls, e); } } public List<JNode> searchClsName(String text) { return clsNamesIndex.getValuesForKeysContaining(text); } public List<JNode> searchMthName(String text) { return mthNamesIndex.getValuesForKeysContaining(text); } public List<JNode> searchFldName(String text) { return fldNamesIndex.getValuesForKeysContaining(text); } public List<CodeNode> searchCode(String text) { List<CodeNode> items; if (codeIndex.size() > 0) { items = codeIndex.getValuesForKeysContaining(text); if (skippedClasses.isEmpty()) { return items; } } else { items = new ArrayList<CodeNode>(); } addSkippedClasses(items, text); return items; } private void addSkippedClasses(List<CodeNode> list, String text) { for (JavaClass javaClass : skippedClasses) { String code = javaClass.getCode(); int pos = 0; while (pos != -1) { pos = searchNext(list, text, javaClass, code, pos); } if (list.size() > CommonSearchDialog.MAX_RESULTS_COUNT) { return; } } } private int searchNext(List<CodeNode> list, String text, JavaNode javaClass, String code, int startPos) { int pos = code.indexOf(text, startPos); if (pos == -1) { return -1; } int lineStart = 1 + code.lastIndexOf(CodeWriter.NL, pos); int lineEnd = code.indexOf(CodeWriter.NL, pos + text.length()); StringRef line = StringRef.subString(code, lineStart, lineEnd == -1 ? code.length() : lineEnd); list.add(new CodeNode(nodeCache.makeFrom(javaClass), -pos, line.trim())); return lineEnd; } public void classCodeIndexSkipped(JavaClass cls) { this.skippedClasses.add(cls); } public List<JavaClass> getSkippedClasses() { return skippedClasses; } public int getSkippedCount() { return skippedClasses.size(); } }