com.xenoamess.p3c.pmd.lang.java.rule.oop.PojoMustOverrideToStringRule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of p3c-pmd Show documentation
Show all versions of p3c-pmd Show documentation
Alibaba Java Coding Guidelines PMD implementations(XenoAmess
TPM)
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xenoamess.p3c.pmd.lang.java.rule.oop;
import com.xenoamess.p3c.pmd.I18nResources;
import com.xenoamess.p3c.pmd.lang.java.rule.AbstractPojoRule;
import com.xenoamess.p3c.pmd.lang.java.util.PojoUtils;
import com.xenoamess.p3c.pmd.lang.java.util.ViolationUtils;
import net.sourceforge.pmd.lang.java.ast.ASTBlock;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.lang.java.ast.ASTExtendsList;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import org.jaxen.JaxenException;
/**
* [Mandatory] The toString method must be implemented in a POJO class. The super.toString method should be called
* in front of the whole implementation if the current class extends another POJO class.
*
* @author zenghou.fw
* @date 2016/11/25
*/
public class PojoMustOverrideToStringRule extends AbstractPojoRule {
private static final String XPATH = "ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration"
+ "[@Public='true' and @Name='toString' and @Arity='0']";
private static final String TO_STRING_XPATH = "//PrimaryExpression[PrimaryPrefix[Name"
+ "[(ends-with(@Image, '.toString'))]]["
+ "(../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal" +
"[@StringLiteral"
+ "='true'])" + " and ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 )]]"
+ "[not(ancestor::Expression/ConditionalAndExpression//EqualityExpression[@Image='!=']//NullLiteral)]"
+ "[not(ancestor::Expression/ConditionalOrExpression//EqualityExpression[@Image='==']//NullLiteral)]";
private static final String LOMBOK_NAME_XPATH = "/Name["
+ "(@Image='Data' and //ImportDeclaration[@ImportedName='lombok.Data' or @ImportedName='lombok'])"
+ " or (@Image='ToString' and //ImportDeclaration[@ImportedName='lombok.ToString' or " +
"@ImportedName='lombok'])"
+ " or (@Image='lombok.Data') or (@Image='lombok.ToString')]";
private static final String LOMBOK_XPATH = "../Annotation/MarkerAnnotation" + LOMBOK_NAME_XPATH
+ "|../Annotation/NormalAnnotation" + LOMBOK_NAME_XPATH;
private static final String MESSAGE_KEY_PREFIX = "java.oop.PojoMustOverrideToStringRule.violation.msg";
@Override
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
if (node.isInterface()) {
return super.visit(node, data);
}
if (!isPojo(node)) {
return super.visit(node, data);
}
if (node.isAbstract() || withLombokAnnotation(node)) {
return super.visit(node, data);
}
if (!node.hasDescendantMatchingXPath(XPATH)) {
ViolationUtils.addViolationWithPrecisePosition(this, node, data,
I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".notostring", node.getImage()));
} else {
checkForExtend(node, data);
}
return super.visit(node, data);
}
private void checkForExtend(ASTClassOrInterfaceDeclaration node, Object data) {
/*
* The super.toString method should be called in front of the whole implementation
* if the current class extends another POJO class
* -> this part not checked
*/
ASTExtendsList extendsList = node.getFirstChildOfType(ASTExtendsList.class);
if (extendsList == null) {
return;
}
String baseName = extendsList.getFirstChildOfType(ASTClassOrInterfaceType.class).getImage();
if (!PojoUtils.isPojo(baseName)) {
return;
}
try {
// toString() definition
ASTMethodDeclaration toStringMethod = (ASTMethodDeclaration) node.findChildNodesWithXPath(XPATH).get(0);
ASTBlock block = toStringMethod.getBody();
if (block.hasDescendantMatchingXPath(TO_STRING_XPATH)) {
addViolationWithMessage(data, block, MESSAGE_KEY_PREFIX + ".usesuper");
}
} catch (JaxenException e) {
throw new RuntimeException("XPath expression " + XPATH + " failed: " + e.getLocalizedMessage(), e);
}
}
/**
* Class with lombok @Data will be skipped
* @param node node
*/
private boolean withLombokAnnotation(ASTClassOrInterfaceDeclaration node) {
return node.hasDescendantMatchingXPath(LOMBOK_XPATH);
}
}