content: package com.taobao.arthas.core.command.view;
  
  import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderStat;
  import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderUrlStat;
  import com.taobao.arthas.core.command.model.ClassDetailVO;
  import com.taobao.arthas.core.command.model.ClassLoaderModel;
  import com.taobao.arthas.core.command.model.ClassLoaderVO;
  import com.taobao.arthas.core.command.model.ClassSetVO;
  import com.taobao.arthas.core.shell.command.CommandProcess;
  import com.taobao.arthas.core.util.ClassUtils;
  import com.taobao.text.Decoration;
  import com.taobao.text.ui.*;
  import com.taobao.text.util.RenderUtil;
  
  import java.util.Collection;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  
  /**
   * @author gongdewei 2020/4/21
   */
  public class ClassLoaderView extends ResultView<ClassLoaderModel> {
  
      @Override
      public void draw(CommandProcess process, ClassLoaderModel result) {
          if (result.getMatchedClassLoaders() != null) {
              process.write("Matched classloaders: \n");
              drawClassLoaders(process, result.getMatchedClassLoaders(), false);
              process.write("\n");
              return;
          }
          if (result.getClassSet() != null) {
              drawAllClasses(process, result.getClassSet());
          }
          if (result.getResources() != null) {
              drawResources(process, result.getResources());
          }
          if (result.getLoadClass() != null) {
              drawLoadClass(process, result.getLoadClass());
          }
          if (result.getUrls() != null) {
              drawClassLoaderUrls(process, result.getUrls());
          }
          if (result.getClassLoaders() != null){
              drawClassLoaders(process, result.getClassLoaders(), result.getTree());
          }
          if (result.getClassLoaderStats() != null){
              drawClassLoaderStats(process, result.getClassLoaderStats());
          }
          if (result.getUrlStats() != null) {
              drawUrlStats(process, result.getUrlStats());
          }
      }
  
      private void drawUrlStats(CommandProcess process, Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats) {
          for (Entry<ClassLoaderVO, ClassLoaderUrlStat> entry : urlStats.entrySet()) {
              ClassLoaderVO classLoaderVO = entry.getKey();
              ClassLoaderUrlStat urlStat = entry.getValue();
  
              // 忽略 sun.reflect.DelegatingClassLoader 等动态ClassLoader
              if (urlStat.getUsedUrls().isEmpty() && urlStat.getUnUsedUrls().isEmpty()) {
                  continue;
              }
  
              TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
              table.row(new LabelElement(classLoaderVO.getName() + ", hash:" + classLoaderVO.getHash())
                      .style(Decoration.bold.bold()));
              Collection<String> usedUrls = urlStat.getUsedUrls();
              table.row(new LabelElement("Used URLs:").style(Decoration.bold.bold()));
              for (String url : usedUrls) {
                  table.row(url);
              }
              Collection<String> UnnsedUrls = urlStat.getUnUsedUrls();
              table.row(new LabelElement("Unused URLs:").style(Decoration.bold.bold()));
              for (String url : UnnsedUrls) {
                  table.row(url);
              }
              process.write(RenderUtil.render(table, process.width()))
                      .write("\n");
          }
      }
      
      private void drawClassLoaderStats(CommandProcess process, Map<String, ClassLoaderStat> classLoaderStats) {
          Element element = renderStat(classLoaderStats);
          process.write(RenderUtil.render(element, process.width()))
                  .write(com.taobao.arthas.core.util.Constants.EMPTY_STRING);
  
      }
  
      private static TableElement renderStat(Map<String, ClassLoaderStat> classLoaderStats) {
          TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
          table.add(new RowElement().style(Decoration.bold.bold()).add("name", "numberOfInstances", "loadedCountTotal"));
          for (Map.Entry<String, ClassLoaderStat> entry : classLoaderStats.entrySet()) {
              table.row(entry.getKey(), "" + entry.getValue().getNumberOfInstance(), "" + entry.getValue().getLoadedCount());
          }
          return table;
      }
  
      public static void drawClassLoaders(CommandProcess process, Collection<ClassLoaderVO> classLoaders, boolean isTree) {
          Element element = isTree ? renderTree(classLoaders) : renderTable(classLoaders);
          process.write(RenderUtil.render(element, process.width()))
                  .write(com.taobao.arthas.core.util.Constants.EMPTY_STRING);
      }
  
      private void drawClassLoaderUrls(CommandProcess process, List<String> urls) {
          process.write(RenderUtil.render(renderClassLoaderUrls(urls), process.width()));
          process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING);
      }
  
      private void drawLoadClass(CommandProcess process, ClassDetailVO loadClass) {
          process.write(RenderUtil.render(ClassUtils.renderClassInfo(loadClass), process.width()) + "\n");
      }
  
      private void drawAllClasses(CommandProcess process, ClassSetVO classSetVO) {
          process.write(RenderUtil.render(renderClasses(classSetVO), process.width()));
          process.write("\n");
      }
  
      private void drawResources(CommandProcess process, List<String> resources) {
          TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
          for (String resource : resources) {
              table.row(resource);
          }
          process.write(RenderUtil.render(table, process.width()) + "\n");
          process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING);
      }
  
      private Element renderClasses(ClassSetVO classSetVO) {
          TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
          if (classSetVO.getSegment() == 0) {
              table.row(new LabelElement("hash:" + classSetVO.getClassloader().getHash() + ", " + classSetVO.getClassloader().getName())
                      .style(Decoration.bold.bold()));
          }
          for (String className : classSetVO.getClasses()) {
              table.row(new LabelElement(className));
          }
          return table;
      }
  
      private static Element renderClassLoaderUrls(List<String> urls) {
          StringBuilder sb = new StringBuilder();
          for (String url : urls) {
              sb.append(url).append("\n");
          }
          return new LabelElement(sb.toString());
      }
  
      // 统计所有的ClassLoader的信息
      private static TableElement renderTable(Collection<ClassLoaderVO> classLoaderInfos) {
          TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
          table.add(new RowElement().style(Decoration.bold.bold()).add("name", "loadedCount", "hash", "parent"));
          for (ClassLoaderVO classLoaderVO : classLoaderInfos) {
              table.row(classLoaderVO.getName(), "" + classLoaderVO.getLoadedCount(), classLoaderVO.getHash(), classLoaderVO.getParent());
          }
          return table;
      }
  
      // 以树状列出ClassLoader的继承结构
      private static Element renderTree(Collection<ClassLoaderVO> classLoaderInfos) {
          TreeElement root = new TreeElement();
          for (ClassLoaderVO classLoader : classLoaderInfos) {
              TreeElement child = new TreeElement(classLoader.getName());
              root.addChild(child);
              renderSubtree(child, classLoader);
          }
          return root;
      }
  
      private static void renderSubtree(TreeElement parent, ClassLoaderVO parentClassLoader) {
          if (parentClassLoader.getChildren() == null){
              return;
          }
          for (ClassLoaderVO childClassLoader : parentClassLoader.getChildren()) {
              TreeElement child = new TreeElement(childClassLoader.getName());
              parent.addChild(child);
              renderSubtree(child, childClassLoader);
          }
      }
  }
  