in profilers-ui/src/com/android/tools/profilers/memory/MemoryClassifierView.java [758:941]
ColoredTreeCellRenderer getNameColumnRenderer() {
return new ColoredTreeCellRenderer() {
// A helper class to define a "problem" to check for.
private static class ProblemDescriptor {
final CaptureObjectInstanceFilter filter;
final String singularName;
final String pluralName;
ProblemDescriptor(CaptureObjectInstanceFilter filter, String singularName, String pluralName) {
this.filter = filter;
this.singularName = singularName;
this.pluralName = pluralName;
}
}
// Use a LinkedHashMap to store counts for different problems, preserving insertion order.
private final Map<ProblemDescriptor, Long> myProblemCounts = new LinkedHashMap<>();
private long myTotalProblemCount = 0;
@Override
protected void paintComponent(Graphics g) {
if (myTotalProblemCount > 0) {
int width = getWidth();
int height = getHeight();
String text = String.valueOf(myTotalProblemCount);
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
int textWidth = g.getFontMetrics().stringWidth(text);
Icon i = mySelected && isFocused() && !ExperimentalUI.isNewUI()
? ColoredIconGenerator.generateWhiteIcon(StudioIcons.Common.WARNING)
: StudioIcons.Common.WARNING;
int iconWidth = i.getIconWidth();
int iconHeight = i.getIconHeight();
i.paintIcon(this, g, width - iconWidth - textWidth - 6, (height - iconHeight) / 2);
g.drawString(text, width - textWidth - 4, (height + iconHeight) / 2 - 2);
}
// paint real content last
super.paintComponent(g);
}
private void setIconColorized(Icon icon) {
setIcon(mySelected && isFocused() && !ExperimentalUI.isNewUI() ? ColoredIconGenerator.generateWhiteIcon(icon) : icon);
}
@Override
public void customizeCellRenderer(@NotNull JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
if (!(value instanceof MemoryObjectTreeNode)) {
return;
}
MemoryObjectTreeNode node = (MemoryObjectTreeNode)value;
if (node.getAdapter() instanceof ClassSet) {
ClassSet classSet = (ClassSet)node.getAdapter();
setIconColorized(((ClassSet)node.getAdapter()).hasStackInfo()
? StudioIcons.Profiler.Overlays.CLASS_STACK
: IconManager.getInstance().getPlatformIcon(PlatformIcons.Class));
String className = classSet.getClassEntry().getSimpleClassName();
String packageName = classSet.getClassEntry().getPackageName();
append(className, SimpleTextAttributes.REGULAR_ATTRIBUTES, className);
if (mySelection.getClassGrouping() == ARRANGE_BY_CLASS) {
if (!packageName.isEmpty()) {
String packageText = " (" + packageName + ")";
append(packageText, SimpleTextAttributes.GRAY_ATTRIBUTES, packageText);
}
}
}
else if (node.getAdapter() instanceof PackageSet) {
ClassifierSet set = (ClassifierSet)node.getAdapter();
setIconColorized(set.hasStackInfo()
? StudioIcons.Profiler.Overlays.PACKAGE_STACK
: IconManager.getInstance().getPlatformIcon(PlatformIcons.Package));
String name = set.getName();
append(name, SimpleTextAttributes.REGULAR_ATTRIBUTES, name);
}
else if (node.getAdapter() instanceof MethodSet) {
setIconColorized(IconManager.getInstance().getPlatformIcon(PlatformIcons.Method));
MethodSet methodObject = (MethodSet)node.getAdapter();
String name = methodObject.getMethodName();
String className = methodObject.getClassName();
String nameAndLine = name + "()";
append(nameAndLine, SimpleTextAttributes.REGULAR_ATTRIBUTES, nameAndLine);
if (!Strings.isNullOrEmpty(className)) {
String classNameText = " (" + className + ")";
append(classNameText, SimpleTextAttributes.GRAY_ATTRIBUTES, classNameText);
}
}
else if (node.getAdapter() instanceof ThreadSet) {
setIconColorized(AllIcons.Debugger.ThreadSuspended);
String threadName = node.getAdapter().getName();
append(threadName, SimpleTextAttributes.REGULAR_ATTRIBUTES, threadName);
}
else if (node.getAdapter() instanceof HeapSet) {
ClassifierSet set = (ClassifierSet)node.getAdapter();
setIconColorized(set.hasStackInfo()
? StudioIcons.Profiler.Overlays.PACKAGE_STACK
: IconManager.getInstance().getPlatformIcon(PlatformIcons.Package));
String name = set.getName() + " heap";
append(name, SimpleTextAttributes.REGULAR_ATTRIBUTES, name);
}
else if (node.getAdapter() instanceof NativeCallStackSet) {
ClassifierSet set = (ClassifierSet)node.getAdapter();
setIconColorized(StudioIcons.Profiler.Overlays.METHOD_STACK);
String name = set.getName();
append(name, SimpleTextAttributes.REGULAR_ATTRIBUTES, name);
}
else if (node.getAdapter() instanceof NativeAllocationMethodSet) {
ClassifierSet set = (ClassifierSet)node.getAdapter();
setIconColorized(StudioIcons.Profiler.Overlays.ARRAY_STACK);
String name = set.getName();
append(name, SimpleTextAttributes.REGULAR_ATTRIBUTES, name);
}
if (node.getAdapter() instanceof ClassifierSet classifierSet && myCaptureObject != null) {
myProblemCounts.clear();
// Build a list of problems to check for
List<ProblemDescriptor> problemsToCheck = new ArrayList<>();
CaptureObjectInstanceFilter leakFilter = myCaptureObject.getActivityFragmentLeakFilter();
if (leakFilter != null) {
problemsToCheck.add(new ProblemDescriptor(leakFilter, "leak", "leaks"));
}
CaptureObjectInstanceFilter bitmapFilter = myCaptureObject.getBitmapDuplicationFilter();
if (bitmapFilter != null) {
problemsToCheck.add(new ProblemDescriptor(bitmapFilter, "duplicate bitmap", "duplicate bitmaps"));
}
// Check for each problem and update counts
for (ProblemDescriptor problem : problemsToCheck) {
long count = classifierSet.getInstanceFilterMatchCount(problem.filter);
if (count > 0) {
myProblemCounts.put(problem, count);
}
}
myTotalProblemCount = myProblemCounts.values().stream().mapToLong(Long::longValue).sum();
setToolTipText(generateProblemTooltip());
}
setTextAlign(SwingConstants.LEFT);
}
private String generateProblemTooltip() {
if (myProblemCounts.isEmpty()) {
return null;
}
List<String> problemDescriptions = myProblemCounts.entrySet().stream()
.map(entry -> {
long count = entry.getValue();
ProblemDescriptor problem = entry.getKey();
String name = (count == 1) ? problem.singularName : problem.pluralName;
return String.format(Locale.US, "%,d %s", count, name);
})
.collect(Collectors.toList());
String summary;
int size = problemDescriptions.size();
if (size == 1) {
summary = problemDescriptions.get(0);
}
else if (size == 2) {
summary = problemDescriptions.get(0) + " and " + problemDescriptions.get(1);
}
else {
String lastProblem = "and " + problemDescriptions.get(size - 1);
summary = String.join(", ", problemDescriptions.subList(0, size - 1)) + ", " + lastProblem;
}
return (myTotalProblemCount > 1 ? "There are " : "There is ") + summary;
}
};
}