checkstyle + gradle + git pre-commit 实现代码提交前对代码规范的检查
我们的目的是想在代码commit之前去做这个检查,把不符合规范的代码标记出来,直到修改完成之后才允许提交。脚本涉及到几个重要的文件:1.pre-commit, 这个是git的一个hook文件,可以帮助我们在commit前去做一些事情,比如,调用我们第二步定义的checkstyle。2.checkstyle.gradle,这里面主要包含gradle的一个task,通过依赖checkstyle插件,然后定义我们自己的checkstyle task 去获取commit前的一些待提交文件,再运用我们定义的规则去做检查。3.checkstyle.xml文件,这个就是定义规则的文件,参考了google编码规范和华为编码规范。4.suppressions.xml,这个是过滤选项,也即是可以排除某些指定文件不受代码规范的检查,比如协议文件。
由于现在AndroidStudio已经支持了checkstyle插件,所以我们只要在.gradle文件里面引用就可以了。(我们这里采用的是checkstyle 6.5)
1.通常,我们会包装自己的checkstyle脚本,将其命名为checkstyle.gradle。代码如下:
apply plugin: 'checkstyle' dependencies {//也可依赖于生成的jar包,以后再说 //compile fileTree(dir: 'libs', include: ['*.jar']) //checkstyle files('../custom-checkstyle/custom-checkstyle.jar') //checkstyle task 依赖 custom-checkstyle module, custom-checkstyle moudle有自定义的Check checkstyle project(':custom-checkstyle')// for using compiled version //checkstyle 'com.puppycrawl.tools:checkstyle:6.5' }def reportsDir = "${project.buildDir}/reports" checkstyle {//工具版本 toolVersion '6.5' //配置文件路径 configFile file("${project.rootDir}/checkstyle.xml")//filter路径 configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/suppressions.xml").absolutePath }task checkstyle(type: Checkstyle, group: 'verification') {try {//try一下,即使发生异常也不影响正常编译 def isCheck = true //是否打开代码规范检查的开关 def isCommit = project.hasProperty('checkCommit') //是否是提交前检查 if (isCheck) {if (isCommit) {//检测代码路径 //source project.rootDir //--- 检查项目中所有的文件, 比较慢, 下面分模块检查, 主要是src下面的java文件 source 'xxx/src' source 'lib-xxx/src' source 'src' //submodules的检查 //排除项 exclude '**/gen/**' exclude '**/test/**' exclude '**/res/**' exclude '**/androidTest/**' exclude '**/R.java' exclude '**/BuildConfig.java' //判断是否是git pre-commit hook触发的checkstyle //如果是,只检测要提交的java文件,否则检测路径下的所有java文件 if (project.hasProperty('checkCommit') && project.property("checkCommit")) {def ft = filterCommitter(getChangeFiles())def includeList = new ArrayList<String>()for (int i = 0; i < ft.size(); i++) {String spliter = ft.getAt(i)String[] spliterlist = spliter.split("/")String fileName = spliterlist[spliterlist.length - 1]includeList.add("**/" + fileName)}if (includeList.size() == 0) {exclude '**/*.java' } else {println("includeList==" + includeList)include includeList}} else {include '**/*.java' }classpath = files()reports { // 支持html和xml两种报告形式,可以任选其一(html更具有可读性) xml.enabled = false html.enabled = true xml {destination file("$reportsDir/checkstyle/checkstyle.xml")}html {destination file("$reportsDir/checkstyle/checkstyle.html")}}} else { //如果不是提交触发的,也就是对项目进行构建,那么需要对pre-commit文件进行copy def forceCopy = false //如有需要,可以强制去更新客户端的pre-commit文件 try {copyPreCommitFile(forceCopy)//copySubmodulesPreCommitFile(forceCopy) } catch (Exception e) {println(e)}}}}catch (Exception e){println("checkstyle catch an exception.")e.printStackTrace()} }//src是一个文件路径,target是一个目录路径 def copyFile(boolean forceUpdate, String src, String target){def fileName = "pre-commit" def targetFile = file(target + "/" + fileName)if(targetFile.exists() && targetFile.isFile() && !forceUpdate){ //目标文件存在且没有强制更新,不需要copy操作 println(targetFile.absolutePath + " exist.")}else {//targetFile.delete() def srcFile = file(src)if (srcFile.isFile()) {copy {from srcFileinto target}}}//targetFile = file(target + "/" + fileName) if(targetFile.isFile()) {if (!targetFile.canExecute()) {targetFile.setExecutable(true)}if (!targetFile.canWrite()) {targetFile.setWritable(true)}} }//把根目录下的pre-commit文件复制到.git-->hooks目录 def copyPreCommitFile(boolean forceUpdate){def src = "${project.rootDir}/pre-commit" def target = "${project.rootDir}/.git/hooks" copyFile(forceUpdate, src, target)println("copyPreCommitFile") }//把submodules目录下的pre-commit文件复制到.git-->modules-->submodules-->XXXmoudles-->hooks 目录 def copySubmodulesPreCommitFile(boolean forceUpdate){def src = "${project.rootDir}/submodules/pre-commit" def submodulesDir = "${project.rootDir}/.git/modules/submodules" File file = new File(submodulesDir)File[] fileList = file.listFiles()if(fileList != null && fileList.length > 0) {def size = fileList.length for (int i = 0; i < size; i++) {if (fileList[i].isDirectory()) {//target = "${project.rootDir}/.git/modules/submodules/XXX/hooks" def target = submodulesDir + "/" + fileList[i].getName() + "/hooks" copyFile(forceUpdate, src, target)}}}println("copySubmodulesPreCommitFile") }//过滤java文件 def filterCommitter(String gitstatusinfo) {ArrayList<String> filterList = new ArrayList<String>()String[] lines = gitstatusinfo.split("\\n")for (String line : lines) {if (!line.startsWith("D ") && line.contains(".java") ) {String[] spliters = line.trim().split(" ")for (String str : spliters) {if (str.contains(".java")) {filterList.add(str)}}}}return filterList }//获取git commit待提交的文件列表 def getChangeFiles() {try {String changeInfo = 'git status -s'.execute(null, project.rootDir).text.trim()return changeInfo == null ? "" : changeInfo} catch (Exception e) {return "" } }
2.定制自己的checkstyle.xml文件,就是代码检查需要应用的哪些规则,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" "http://www.puppycrawl.com/dtds/configuration_1_2.dtd"> <module name="Checker"> <property name="charset" value="UTF-8"/> <property name="severity" value="error"/> <module name="SuppressionCommentFilter"/> <module name="SuppressionFilter"> <property name="file" value="${checkstyleSuppressionsPath}"/> </module> <!--代码规范--> <module name="TreeWalker"> <!--1.代码块检查--> <!--检查空白块 option, 需要去掉空白块, 选项 text,stmt default stmt --> <!--<module name="EmptyBlock">--> <!--<property name="option" value="text"/>--> <!--<property name="tokens"--> <!--value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>--> <!--</module>--> <!--Empty catch block. 检查空的 catch块, 需要把异常堆栈打印出来, 方便定位原因 --> <module name="EmptyCatchBlock" /> <!--option: 定义左大括号'{'显示位置,eol在同一行显示,nl在下一行显示 maxLineLength: 大括号'{'所在行行最多容纳的字符数 tokens: 该属性适用的类型,例:CLASS_DEF,INTERFACE_DEF,METHOD_DEF,CTOR_DEF --> <module name="LeftCurly"> <property name="option" value="eol"/> </module> <!-- NeedBraces 检查是否应该使用括号的地方没有加括号, 主要在if, else时有这样的情况 tokens: 定义检查的类型 --> <module name="NeedBraces"/> <!-- Checks the placement of right curly braces ('}') for else, try, and catch tokens. The policy to verify is specified using property option. option: 右大括号是否单独一行显示 tokens: 定义检查的类型 --> <module name="RightCurly"> <property name="id" value="RightCurlySame"/> <property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_DO"/> </module> <module name="RightCurly"> <property name="id" value="RightCurlyAlone"/> <property name="option" value="alone"/> <property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/> </module> <!--2.类检查--> <!--Checks that a class which has only private constructors is declared as final.只有私有构造器的类必须声明为final--> <module name="FinalClass" /> <!--Fields and methods should be before inner classes. 内部类检查,确保内部类在外部类的底部--> <!--<module name="InnerTypeLast" />--> <!--检查每个java文件是否只含有一个顶层类--> <module name="OneTopLevelClass" /> <!--自定义Check规则--> <module name="com.xxx.customcheckstyle.ProhibitExtendCheck"> <property name="subClassWhiteList" value="XXWebViewClient" /> <property name="superClassSet" value="WebViewClient" /> </module> <!-- Anonymous inner class length is {0,number,integer} lines (max allowed is {1,number,integer}).匿名类的最大行数,缺省为20 --> <!--<module name="AnonInnerLength">--> <!--<property name="max" value="200" />--> <!--</module>--> <!--3.代码检查--> <!--Default should be last label in the switch. switch 中default的位置检查,在所有case下面--> <module name="DefaultComesLast" /> <!--空行检查 一行 ; --> <module name="EmptyStatement" /> <!--String literal expressions should be on the left side of an equals comparison. 使用 equals 时 避免出现null 如 str.equls("xx") 正确使用 "xx".equls(str) --> <module name="EqualsAvoidNull" /> <!--检查 重写equals方法时是否有重写hashCode方法--> <module name="EqualsHashCode" /> <!--检查 switch 语句 中 case 代码块是否包含 break, return, throw or continue--> <module name="FallThrough" /> <!--检查局部变量,参数 是否跟全局变量同名 ignoreConstructorParameter 忽略构造方法的参数 default false ignoreSetter 忽略setter default false setterCanReturnItsClass 忽略返回 this 类型的setter default false --> <!--<module name="HiddenField">--> <!--<property name="ignoreConstructorParameter" value="true" />--> <!--<property name="ignoreSetter" value="true" />--> <!--<property name="setterCanReturnItsClass" value="true" />--> <!--</module>--> <!--检查switch 是否包含 default--> <module name="MissingSwitchDefault" /> <!--检查同个文件中是否出现多个同样的字面量--> <!--<module name="MultipleStringLiterals" />--> <!-- 每一行只能定义一个变量 如 Button b1,b2,b3; --> <module name="MultipleVariableDeclarations" /> <!-- if-else嵌套语句个数 最多5层 --> <module name="NestedIfDepth"> <property name="max" value="5"/> </module> <!-- try-catch 嵌套语句个数 最多2层 --> <module name="NestedTryDepth"> <property name="max" value="2"/> </module> <!--一行只允许一条语句,即末尾一个 ; --> <module name="OneStatementPerLine" /> <!--重载方法是否在同个地方--> <!--<module name="OverloadMethodsDeclarationOrder" />--> <!--Assignment of parameter 'phone' is not allowed. 参数中不允许有赋值语句--> <!--<module name="ParameterAssignment" />--> <!-- Checks for overly complicated boolean expressions. Currently finds code like if (b == true), b || true, !false, etc. 检查boolean值是否冗余的地方.Expression can be simplified. Rationale: Complex boolean logic makes code hard to understand and maintain. --> <module name="SimplifyBooleanExpression"/> <!--字符串比较检查,字符串比较用equals--> <module name="StringLiteralEquality" /> <!--检查 重写的 clone 方法是否调用了 super.clone()--> <module name="SuperClone" /> <!--检查 重写的 finalize 方法是否调用了 super.finalize()--> <module name="SuperFinalize" /> <!--Unnecessary parentheses around expression. 检查多余的圆括号--> <module name="UnnecessaryParentheses" /> <!-- 每行字符数 --> <module name="LineLength"> <property name="max" value="200" /> </module> <!-- Checks for long methods and constructors. max default 150行. max=500 设置长度500 --> <!--<module name="MethodLength">--> <!--<property name="max" value="500"/>--> <!--</module>--> <!-- ModifierOrder 检查修饰符的顺序,默认是 public,protected,private,abstract,static,final,transient,volatile,synchronized,native --> <module name="ModifierOrder" /> <!-- 检查是否有多余的修饰符,例如:接口中的方法不必使用public、abstract修饰 --> <module name="RedundantModifier" /> <!-- Checks the number of parameters of a method or constructor. max default 7个. --> <module name="ParameterNumber"> <property name="max" value="9" /> </module> <!-- Checks that long constants are defined with an upper ell. That is ' L' and not 'l'. This is in accordance to the Java Language Specification, Section 3.10.1. 检查是否在long类型是否定义了大写的L.字母小写l和数字1(一)很相似。Should use uppercase 'L'. looks a lot like 1. --> <module name="UpperEll"/> <!-- A check for TODO: comments. Actually it is a generic regular expression matcher on Java comments. To check for other patterns in Java comments, set property format. 检查是否存在TODO(待处理) TODO是javaIDE自动生成的。一般代码写完后要去掉。Comment matches to-do format 'TODO:'. --> <module name="TodoComment"/> <!-- Checks the style of array type definitions. Some like Java-style: public static void main(String[] args) and some like C-style: public static void main(String args[]) 检查再定义数组时,采用java风格还是c风格,例如:int[] num是java风格,int num[]是c风格。默认是java风格--> <module name="ArrayTypeStyle"/> <!--4.导包检查--> <!-- 必须导入类的完整路径,即不能使用*导入所需的类 --> <module name="AvoidStarImport" /> <!-- 检查是否从非法的包中导入了类 illegalPkgs: 定义非法的包名称--> <module name="IllegalImport"/> <!-- defaults to sun.* packages --> <!-- 检查是否导入了不必显示导入的类--> <module name="RedundantImport" /> <!-- 检查是否导入的包没有使用--> <module name="UnusedImports" /> <!-- 检查方法的javadoc的注释 scope: 可以检查的方法的范围,例如:public只能检查public修饰的方法,private可以检查所有的方法 allowMissingParamTags: 是否忽略对参数注释的检查 allowMissingThrowsTags: 是否忽略对throws注释的检查 allowMissingReturnTag: 是否忽略对return注释的检查 --> <!--<module name="JavadocMethod">--> <!--<property name="scope" value="public"/>--> <!--<property name="allowMissingParamTags" value="false"/>--> <!--<property name="allowMissingThrowsTags" value="false"/>--> <!--<property name="allowMissingReturnTag" value="false"/>--> <!--<property name="tokens" value="METHOD_DEF"/>--> <!--<property name="allowUndeclaredRTE" value="true"/>--> <!--<property name="allowThrowsTagsForSubclasses" value="true"/>--> <!--<!–允许get set 方法没有注释–>--> <!--<property name="allowMissingPropertyJavadoc" value="true"/>--> <!--</module>--> <!-- 检查类和接口的javadoc 默认不检查author 和version tags authorFormat: 检查author标签的格式 versionFormat: 检查version标签的格式 scope: 可以检查的类的范围,例如:public只能检查public修饰的类,private可以检查所有的类 excludeScope: 不能检查的类的范围,例如:public,public的类将不被检查,但访问权限小于public的类仍然会检查,其他的权限以此类推 tokens: 该属性适用的类型,例如:CLASS_DEF,INTERFACE_DEF --> <!--<module name="JavadocType">--> <!--<property name="scope" value="public"/>--> <!--<property name="tokens" value="CLASS_DEF,INTERFACE_DEF"/>--> <!--</module>--> <!--First sentence should be present. 检查是否包含不允许使用的词汇--> <!--<module name="SummaryJavadocCheck" />--> <!--5.命名规范检查--> <!-- local, final variables, including catch parameters --> <module name="LocalFinalVariableName" /> <!-- local, non-final variables, including catch parameters--> <module name="LocalVariableName" /> <!-- 静态变量命名 不能有小写字母,长度(0,39) --> <module name="StaticVariableName"> <property name="format" value="(^s[A-Z][a-zA-Z0-9]{0,39}$)" /> </module> <!-- 包命名 小写字母开头 --> <module name="PackageName"> <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" /> </module> <!-- 类的命名,匹配规则默认:(^[A-Z][a-zA-Z0-9]*$),必须以大写字母开始 --> <module name="TypeName"> <property name="format" value="(^[A-Z][a-zA-Z0-9]{0,39}$)"/> <property name="tokens" value="CLASS_DEF"/> </module> <!-- 接口命名,匹配规则:(^I[A-Z][a-zA-Z0-9]*$),必须以大写I开始 --> <module name="TypeName"> <property name="format" value="^I[A-Z][a-zA-Z0-9]*$"/> <property name="tokens" value="INTERFACE_DEF"/> </module> <!-- 方法命名 小写字母开头,长度(0,39) --> <module name="MethodName"> <property name="format" value="(^[a-z][a-zA-Z0-9]{0,39}$)" /> </module> <!-- 成员变量命名 小写字母开头,长度(0,39) --> <module name="MemberName"> <property name="format" value="(^m[A-Z][a-zA-Z0-9]{0,39}$)" /> </module> <!-- 常量命名 不能有小写字母,长度(0,39) --> <module name="ConstantName"> <property name="format" value="(^[A-Z0-9_]{0,39}$)" /> </module> </module> </module>
3.支持添加过滤条件,也就是可以过滤某些文件不受这个规则的检查,定义suppression.xml文件
<?xml version="1.0"?> <!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN" "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd"> <!--checkstyle排除项--> <!--需要过滤协议文件, 注解文件等等--> <!--也可以对某个java文件的某段代码过滤某个规则的检查 <suppress checks="JavadocStyleCheck" files="GeneratedObject.java" lines="50-9999"/> --> <suppressions> <suppress checks="[a-zA-Z0-9]*" files="R.java" /> <suppress checks="[a-zA-Z0-9]*" files="BuildConfig.java" /> <suppress checks="[a-zA-Z0-9]*" files="Test" /> <suppress checks="[a-zA-Z0-9]*" files="Dagger*" /> <suppress checks="[a-zA-Z0-9]*" files=".*_.*Factory.java" /> <suppress checks="[a-zA-Z0-9]*" files=".*ViewInjector.java" /> <suppress checks="[a-zA-Z0-9]*" files=".*_MembersInjector.java" /> <suppress checks="[a-zA-Z0-9]*" files=".*_ViewBinding.java" /> <suppress checks="[a-zA-Z0-9]*Check" files=".*ResProtocal.java" /> <suppress checks="[a-zA-Z0-9]*Check" files=".*ReqProtocal.java" /> <suppress checks="[a-zA-Z0-9]*Check" files="[\\/]protocol[\\/]" /> <!--过滤整个包名下面的协议文件--> <suppress checks="[a-zA-Z0-9]*Check" files="[\\/]proto[\\/]" /> <!--过滤整个包名下面的协议文件--> </suppressions>
4.编写git提交前的脚本文件,此文件需要位于.git--->hooks目录下面,所以我们在上面的task中会做一个自动的拷贝,pre-commit
#!/bin/sh # # An example hook script to verify what is about to be committed. # Called by "git commit" with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if # it wants to stop the commit. # # To enable this hook, rename this file to "pre-commit".if git rev-parse --verify HEAD >/dev/null 2>&1 thenagainst=HEAD else# Initial commit: diff against an empty tree objectagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fiecho "start checkstyle task"SCRIPT_DIR=$(dirname "$0") SCRIPT_ABS_PATH=`cd "$SCRIPT_DIR"; pwd` $SCRIPT_ABS_PATH/../../gradlew -PcheckCommit="true" checkstyle if [ $? -eq 0 ]; thenecho "checkstyle OK"exit 0 elseecho "checkstyle fail, for details see /build/reports/checkstyle/checkstyle.html"exit 1 fi
5.在最外层的build.gradle文件中引用我们自定义的checkstyle.gradle脚本,
apply from: 'checkstyle.gradle'
6.把这些文件的路径再调整一下,然后就大功告成了,提交代码时执行 git commit就会有代码规范的提示了。检查结果是,符合规范的代码可正常提交,不符合规范的代码会统一放到 /build/reports/checkstyle/checkstyle.html文件中,可用浏览器打开,直至按照规范修改完成之后才能提交。
7.如果checkstyle.xml里面的规则满足不了你的需要,那么需要自定义Check规则,那么需要学习Check的语法树,比如,一个class的定义如下,
* <pre> * +--CLASS_DEF * | * +--MODIFIERS * | * +--LITERAL_PUBLIC (public) * +--LITERAL_CLASS (class) * +--IDENT (MyClass) * +--EXTENDS_CLAUSE * +--IMPLEMENTS_CLAUSE * | * +--IDENT (Serializable) * +--OBJBLOCK * | * +--LCURLY ({) * +--RCURLY (}) * </pre>
更多请参考checkstyle插件的源码 TokenTypes类。
8.在项目中新建一个module,命名为custom-checkstyle,修改其build.gradle文件为
apply plugin: 'java' //jar { // destinationDir rootProject.file('custom-checkstyle') //} //sourceCompatibility = JavaVersion.VERSION_1_7 //targetCompatibility = JavaVersion.VERSION_1_7 dependencies {//compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.puppycrawl.tools:checkstyle:6.5' }
9.编写自定义Check规则的代码,需要继承自Check(6.5版本是这个,更高的版本应该是Abstract***Check),比如,我写了一个禁止继承自某个类的Check
package com.bigo.customcheckstyle; import com.google.common.base.Joiner; import com.puppycrawl.tools.checkstyle.api.Check; import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.TokenTypes; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * * 禁止继承某个类(除白名单子类以外) */ public class ProhibitExtendCheck extends Check {/** * log中对应的key */ public static final String MSG_KEY = "Class ''{0}'' should not extend class ''{1}'' directly, looking for help by lianzhan or caikaiwu."; /** * Character separate package names in qualified name of java class. */ public static final String PACKAGE_SEPARATOR = "."; /** * 子类白名单, 对继承没有限制 */ private Set<String> mSubClassWhiteList = new HashSet<>(); /** * 需要检查继承关系的父类的集合 */ private Set<String> mSuperClassSet = new HashSet<>(); @Override public int[] getDefaultTokens() {return getAcceptableTokens(); }@Override public int[] getAcceptableTokens() {return new int[] {TokenTypes.CLASS_DEF}; }@Override public int[] getRequiredTokens() {return getAcceptableTokens(); }/** * checkstyle.xml中对应的属性调用的setter方法 * @param names */ public void setSubClassWhiteList(final String[] names) {if (names != null && names.length > 0) {for (String name : names) {mSubClassWhiteList.add(name); }}}/** * checkstyle.xml中对应的属性调用的setter方法 * @param names */ public void setSuperClassSet(final String[] names) {if (names != null && names.length > 0) {for (String name : names) {mSuperClassSet.add(name); }}}/** * 遍历语法树中的每个结点, 结点信息参考{@link TokenTypes.CLASS_DEF} * @param ast */ @Override public void visitToken(DetailAST ast) {DetailAST currentNode = ast; while (currentNode != null) {if (currentNode.getType() == TokenTypes.CLASS_DEF) {String subClassName = currentNode.findFirstToken(TokenTypes.IDENT).getText(); //获取子类的名字 if (!mSubClassWhiteList.contains(subClassName)) {//不在白名单中 String superClassName = null; DetailAST extendNode = currentNode.findFirstToken(TokenTypes.EXTENDS_CLAUSE); if (extendNode != null) {superClassName = extendNode.findFirstToken(TokenTypes.IDENT).getText(); //获取父类的名字 }if (mSuperClassSet.contains(superClassName)) {log(currentNode.getLineNo(), MSG_KEY, subClassName, superClassName); }}}currentNode = currentNode.getNextSibling(); }}/** * Get super class name of given class. * @param classAst class * @return super class name or null if super class is not specified */ private String getSuperClassName(DetailAST classAst) {String superClassName = null; final DetailAST classExtend = classAst.findFirstToken(TokenTypes.EXTENDS_CLAUSE); if (classExtend != null) {superClassName = extractQualifiedName(classExtend); }return superClassName; }/** * Get name of class(with qualified package if specified) in extend clause. * @param classExtend extend clause to extract class name * @return super class name */ private static String extractQualifiedName(DetailAST classExtend) {final String className; if (classExtend.findFirstToken(TokenTypes.IDENT) == null) {// Name specified with packages, have to traverse DOT final DetailAST firstChild = classExtend.findFirstToken(TokenTypes.DOT); final List<String> qualifiedNameParts = new LinkedList<>(); qualifiedNameParts.add(0, firstChild.findFirstToken(TokenTypes.IDENT).getText()); DetailAST traverse = firstChild.findFirstToken(TokenTypes.DOT); while (traverse != null) {qualifiedNameParts.add(0, traverse.findFirstToken(TokenTypes.IDENT).getText()); traverse = traverse.findFirstToken(TokenTypes.DOT); }className = Joiner.on(PACKAGE_SEPARATOR).join(qualifiedNameParts); }else {className = classExtend.findFirstToken(TokenTypes.IDENT).getText(); }return className; } }
10.到此全部OK了。
checkstyle + gradle + git pre-commit 实现代码提交前对代码规范的检查相关推荐
- 代码风格统一: 使用husky, prettier, eslint在代码提交时自动格式化,并检查代码。...
引言 这个需求主要是组内成员用的编辑器不统一,代码风格也各异,所以在修改代码时,如果格式化了代码,在code review阶段很难分辨修改了的代码.当然,代码风格统一还有其他好处,这里就不不多废话了. ...
- 使用 husky 进行提交前的代码规范校验和 commit 信息检查
husky 是一个 Git Hook 工具,借助 husky 我们可以在 git 提交的不同生命周期进行一些自动化操作.本文主要介绍提交前 eslint 校验和提交时 commit 信息的规范校验. ...
- Git命令按人统计提交次数和代码量
统计个人的代码量 git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git lo ...
- git 只commit不push 会有影响吗_规范化团队 git 提交信息
规范化团队 git 提交信息 同一个工程项目,为了方便管理,git 的 commit 信息最好按照一定的格式规范,以便在需要的时候方便使用.什么是方便的时候,比如出现了一个线上 bug,所以需要回滚操 ...
- git 在ssh情况下提交代码
git --version --git版本 用户目录(~/) vim ~/.gitconfig --编辑用户目录(~/)下的 .gitconfig文件 --输入i 进入编辑模式 ...
- Git:改变世界的一次代码提交
摘要:如果选Linux社区历史上最伟大的一次 Git 代码提交,那一定是 Git 工具项目本身的第一次代码提交. 吾诗已成.无论大神的震怒,还是山崩地裂,都不能把它化为无形! -- 奥维德<变形 ...
- Linus改变世界的一次代码提交:git的诞生
吾诗已成.无论大神的震怒,还是山崩地裂,都不能把它化为无形!-- 奥维德<变形记> Table of Contents 背景 设计 实现 启示 参考 背景 Linux 作为最大也是最成功的 ...
- 可能是历史上最伟大的一次 Git 代码提交
Git 是一个分布式版本控制系统,缔造者是大名鼎鼎的林纳斯·托瓦茲 (Linus Torvalds),Git 最初的目的是为了能更好的管理 Linux 内核源码. PS:为了能够帮助更多的 Java ...
- python代码自动格式化_代码的自动格式化
代码格式化是一个在软件开发过程中值得一提的话题. 所谓代码格式化就是说,程序员在书写代码的过程中或者完成代码开发后对代码书写格式排版的调整,使得已经完成的代码变的更美观整洁也更具有可读性,也能增加完成 ...
最新文章
- grape动态PHP结构(三)——API接口
- PYTHON-模块timedatetime+ 目录规范
- C语言:指针的几种形式
- 1499抢飞天茅台?可惜了,才26万的并发app就崩了!
- 栈、堆、静态存储区和程序的内存布局
- SQL语言之同义词(Oracle)
- 搜索引擎蜘蛛给网站带来的危害,有效指引爬虫对应的措施(最准确搜索引擎蜘蛛名称)...
- python+webdriver(三)
- python单选题库答案_大学慕课2020用Python玩转数据题库及答案
- readyboost提升明显吗_主动降噪影响音质吗?为什么降噪耳机打开降噪后音质会有明显提升...
- 管家婆软件二次开发(在管家婆财贸双全中实现建行支付)
- android gps转换度分秒,GPS坐标单位(度分秒)的换算方法
- jpa mysql lob_Jpa加载Lob字段报Unable to access lob stream异常
- html2canvas 下载图片 报网络错误
- 深入浅出讲解 Python 元类(Metaclass)的使用
- Excel使用公式截取字符串
- Android获取百度音乐下载音乐和歌词下载链接
- 发票扫一扫识别,一键导出表格
- NopCommerce Alipay 支付插件
- 计算机上怎么计算开三次方,怎么用计算器开出三次方,具体怎么操作
热门文章
- 微信小程序js数组中插入“新数据对象”,数据对象中插入“新属性”
- 制作一个自己的对战平台
- Hibernate投入JBoss怀抱
- 【远程连接控制】WinRM和SSH
- Chapter 27 HTTP and WWW 第二十七章HTTP和WWW协议作业
- bcache / 如何使用bcache构建LVM,软RAID / 如何优化bcache
- codeforces 855-B. Marvolo Gaunt's Ring(背包问题)
- 在线教育,百鬼夜行?
- 2021-2027全球与中国可持续环保家具市场现状及未来发展趋势
- html怎么给段落加边框,Word2010怎样为段落加上边框