package jadx.gui.treemodel; import jadx.api.JavaPackage; import jadx.gui.JadxWrapper; import jadx.gui.utils.Utils; import javax.swing.Icon; import javax.swing.ImageIcon; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public class JSources extends JNode { private static final long serialVersionUID = 8962924556824862801L; private static final ImageIcon ROOT_ICON = Utils.openIcon("packagefolder_obj"); private final JadxWrapper wrapper; private final boolean flatPackages; public JSources(JRoot jRoot, JadxWrapper wrapper) { this.flatPackages = jRoot.isFlatPackages(); this.wrapper = wrapper; update(); } public final void update() { removeAllChildren(); if (flatPackages) { for (JavaPackage pkg : wrapper.getPackages()) { add(new JPackage(pkg)); } } else { // build packages hierarchy List<JPackage> rootPkgs = getHierarchyPackages(wrapper.getPackages()); for (JPackage jPackage : rootPkgs) { jPackage.update(); add(jPackage); } } } /** * Convert packages list to hierarchical packages representation * * @param packages input packages list * @return root packages */ List<JPackage> getHierarchyPackages(List<JavaPackage> packages) { Map<String, JPackage> pkgMap = new HashMap<String, JPackage>(); for (JavaPackage pkg : packages) { addPackage(pkgMap, new JPackage(pkg)); } // merge packages without classes boolean repeat; do { repeat = false; for (JPackage pkg : pkgMap.values()) { if (pkg.getInnerPackages().size() == 1 && pkg.getClasses().isEmpty()) { JPackage innerPkg = pkg.getInnerPackages().get(0); pkg.getInnerPackages().clear(); pkg.getInnerPackages().addAll(innerPkg.getInnerPackages()); pkg.getClasses().addAll(innerPkg.getClasses()); pkg.setName(pkg.getName() + "." + innerPkg.getName()); innerPkg.getInnerPackages().clear(); innerPkg.getClasses().clear(); repeat = true; break; } } } while (repeat); // remove empty packages for (Iterator<Map.Entry<String, JPackage>> it = pkgMap.entrySet().iterator(); it.hasNext(); ) { JPackage pkg = it.next().getValue(); if (pkg.getInnerPackages().isEmpty() && pkg.getClasses().isEmpty()) { it.remove(); } } // use identity set for collect inner packages Set<JPackage> innerPackages = Collections.newSetFromMap(new IdentityHashMap<JPackage, Boolean>()); for (JPackage pkg : pkgMap.values()) { innerPackages.addAll(pkg.getInnerPackages()); } // find root packages List<JPackage> rootPkgs = new ArrayList<JPackage>(); for (JPackage pkg : pkgMap.values()) { if (!innerPackages.contains(pkg)) { rootPkgs.add(pkg); } } Collections.sort(rootPkgs); return rootPkgs; } private void addPackage(Map<String, JPackage> pkgs, JPackage pkg) { String pkgName = pkg.getName(); JPackage replaced = pkgs.put(pkgName, pkg); if (replaced != null) { pkg.getInnerPackages().addAll(replaced.getInnerPackages()); pkg.getClasses().addAll(replaced.getClasses()); } int dot = pkgName.lastIndexOf('.'); if (dot > 0) { String prevPart = pkgName.substring(0, dot); String shortName = pkgName.substring(dot + 1); pkg.setName(shortName); JPackage prevPkg = pkgs.get(prevPart); if (prevPkg == null) { prevPkg = new JPackage(prevPart); addPackage(pkgs, prevPkg); } prevPkg.getInnerPackages().add(pkg); } } @Override public Icon getIcon() { return ROOT_ICON; } @Override public JClass getJParent() { return null; } @Override public String makeString() { return "Source code"; } }