package com.github.ompc.greys.core.command; import com.github.ompc.greys.core.command.annotation.Cmd; import com.github.ompc.greys.core.command.annotation.IndexArg; import com.github.ompc.greys.core.command.annotation.NamedArg; import com.github.ompc.greys.core.manager.ReflectManager; import com.github.ompc.greys.core.server.Session; import com.github.ompc.greys.core.textui.TLadder; import com.github.ompc.greys.core.textui.TTable; import com.github.ompc.greys.core.textui.ext.TGaMethodInfo; import com.github.ompc.greys.core.util.GaMethod; import com.github.ompc.greys.core.util.affect.RowAffect; import com.github.ompc.greys.core.util.matcher.ClassMatcher; import com.github.ompc.greys.core.util.matcher.GaMethodMatcher; import com.github.ompc.greys.core.util.matcher.PatternMatcher; import com.github.ompc.greys.core.util.matcher.TrueMatcher; import org.apache.commons.lang3.StringUtils; import java.lang.instrument.Instrumentation; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * 展示方法信息 * * @author oldmanpushcart@gmail.com */ @Cmd(name = "sm", sort = 1, summary = "Search the method of classes loaded by JVM", eg = { "sm -Ed org\\.apache\\.commons\\.lang\\.StringUtils .*", "sm org.apache.commons.????.StringUtils *", "sm -d org.apache.commons.lang.StringUtils", "sm *String????s *" }) public class SearchMethodCommand implements Command { @IndexArg(index = 0, name = "class-pattern", summary = "Path and classname of Pattern Matching") private String classPattern; @IndexArg(index = 1, isRequired = false, name = "method-pattern", summary = "Method of Pattern Matching") private String methodPattern; @NamedArg(name = "d", summary = "Display the details of method") private boolean isDetail = false; @NamedArg(name = "E", summary = "Enable regular expression to match (wildcard matching by default)") private boolean isRegEx = false; private final ReflectManager reflectManager = ReflectManager.Factory.getInstance(); @Override public Action getAction() { return new RowAction() { @Override public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { final RowAffect affect = new RowAffect(); final ClassMatcher classMatcher = new ClassMatcher(new PatternMatcher(isRegEx, classPattern)); final TTable tTable = new TTable(new TTable.ColumnDefine[]{ new TTable.ColumnDefine(30, TTable.Align.LEFT), new TTable.ColumnDefine(100, TTable.Align.LEFT), }) .addRow("DECLARED-CLASS", "VISIBLE-METHOD") .padding(1); for (Class<?> clazz : reflectManager.searchClassWithSubClass(classMatcher)) { renderingMethodSummary(tTable, clazz, affect); } printer.print(tTable.rendering()).finish(); return affect; } }; } /* * 构造方法名匹配 * 这里修复一个网友的咨询,如果methodPattern不填,是否可以默认为匹配为所有方法 * 这个是我的一个疏忽,在老的版本中不填methodPattern确实greys会自动默认进行全方法匹配 * 在某一个版本的优化中我随意去掉了这个功能,导致用户行为习惯受阻,非常抱歉 */ private GaMethodMatcher toGaMethodMatcher() { return new GaMethodMatcher( StringUtils.isBlank(methodPattern) ? new TrueMatcher<String>() : new PatternMatcher(isRegEx, methodPattern) ); } /* * 渲染类方法摘要信息 */ private void renderingMethodSummary(final TTable view, final Class<?> clazz, final RowAffect affect) { final TLadder classLadderView = new TLadder(); final GaMethodMatcher gaMethodMatcher = toGaMethodMatcher(); final Collection<GaMethod> gaMethods = reflectManager.searchClassGaMethods(clazz, gaMethodMatcher); final Set<Class<?>> uniqueSet = new HashSet<Class<?>>(); classLadderView.addItem(clazz.getName()); uniqueSet.add(clazz); for (GaMethod gaMethod : gaMethods) { final Class<?> declaringClass = gaMethod.getDeclaringClass(); if (!uniqueSet.contains(declaringClass)) { classLadderView.addItem(declaringClass.getName()); uniqueSet.add(declaringClass); } if (isDetail) { view.addRow(classLadderView.rendering(), new TGaMethodInfo(gaMethod).rendering()); } else { view.addRow(classLadderView.rendering(), gaMethod.getName()); } affect.rCnt(1); } } }