ColoredTreeCellRenderer getNameColumnRenderer()

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;
      }
    };
  }