/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.source.formatter.checkstyle.checks;
import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.source.formatter.checkstyle.util.DetailASTUtil;
import com.liferay.source.formatter.util.ThreadSafeClassLibrary;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.FileContents;
import com.puppycrawl.tools.checkstyle.api.FileText;
import com.puppycrawl.tools.checkstyle.api.FullIdent;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.DefaultDocletTagFactory;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.Type;
import com.thoughtworks.qdox.parser.ParseException;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Hugo Huijser
*/
public class PersistenceCallCheck extends AbstractCheck {
public static final String MSG_ILLEGAL_PERSISTENCE_CALL =
"persistence.call.illegal";
@Override
public int[] getDefaultTokens() {
return new int[] {TokenTypes.CLASS_DEF};
}
@Override
public void visitToken(DetailAST detailAST) {
DetailAST parent = detailAST.getParent();
if (parent != null) {
return;
}
FileContents fileContents = getFileContents();
String fileName = StringUtil.replace(
fileContents.getFileName(), '\\', '/');
if (!fileName.contains("/modules/")) {
return;
}
FileText fileText = fileContents.getText();
String content = (String)fileText.getFullText();
JavaDocBuilder javaDocBuilder = new JavaDocBuilder(
new DefaultDocletTagFactory(), new ThreadSafeClassLibrary());
try {
javaDocBuilder.addSource(new UnsyncStringReader(content));
}
catch (ParseException pe) {
return;
}
JavaClass javaClass = _getJavaClass(javaDocBuilder, fileName);
javaDocBuilder = _addExtendedClassSource(
javaDocBuilder, javaClass, fileName);
List<String> importNames = _getImportNames(detailAST);
Map<String, String> variablesMap = _getVariablesMap(javaDocBuilder);
List<DetailAST> methodCallASTList = DetailASTUtil.getAllChildTokens(
detailAST, true, TokenTypes.METHOD_CALL);
for (DetailAST methodCallAST : methodCallASTList) {
_checkMethodCall(
methodCallAST, importNames, variablesMap,
javaClass.getPackageName());
}
}
private JavaDocBuilder _addExtendedClassSource(
JavaDocBuilder javaDocBuilder, JavaClass javaClass, String fileName) {
Type superClassType = javaClass.getSuperClass();
String fullyQualifiedName = superClassType.getFullyQualifiedName();
if (!fullyQualifiedName.startsWith("com.liferay")) {
return javaDocBuilder;
}
int pos = fileName.lastIndexOf("/com/liferay/");
String extendedClassFileName =
fileName.substring(0, pos + 1) +
StringUtil.replace(fullyQualifiedName, '.', '/') + ".java";
try {
javaDocBuilder.addSource(new File(extendedClassFileName));
}
catch (Exception e) {
}
return javaDocBuilder;
}
private void _checkClass(
String className, List<String> importNames, String packageName,
int lineNo) {
for (String importName : importNames) {
if (!importName.endsWith("." + className)) {
continue;
}
int pos = importName.indexOf(".service.persistence.");
if (pos == -1) {
return;
}
if (!packageName.startsWith(importName.substring(0, pos))) {
log(lineNo, MSG_ILLEGAL_PERSISTENCE_CALL, importName);
}
}
}
private void _checkMethodCall(
DetailAST methodCallAST, List<String> importNames,
Map<String, String> variablesMap, String packageName) {
DetailAST childAST = methodCallAST.getFirstChild();
if (childAST.getType() != TokenTypes.DOT) {
return;
}
childAST = childAST.getFirstChild();
if (childAST.getType() != TokenTypes.IDENT) {
return;
}
DetailAST siblingAST = childAST.getNextSibling();
if (siblingAST.getType() == TokenTypes.IDENT) {
String methodName = siblingAST.getText();
if (methodName.equals("clearCache") ||
methodName.startsWith("create")) {
return;
}
}
String fieldName = childAST.getText();
if (fieldName.matches("[A-Z].*")) {
_checkClass(
fieldName, importNames, packageName, methodCallAST.getLineNo());
}
else {
_checkVariable(
fieldName, variablesMap, packageName,
methodCallAST.getLineNo());
}
}
private void _checkVariable(
String variableName, Map<String, String> variablesMap,
String packageName, int lineNo) {
String fullyQualifiedTypeName = variablesMap.get(variableName);
if (fullyQualifiedTypeName == null) {
return;
}
int pos = fullyQualifiedTypeName.indexOf(".service.persistence.");
if (pos == -1) {
return;
}
if (!packageName.startsWith(fullyQualifiedTypeName.substring(0, pos))) {
log(lineNo, MSG_ILLEGAL_PERSISTENCE_CALL, fullyQualifiedTypeName);
}
}
private List<String> _getImportNames(DetailAST detailAST) {
List<String> importASTList = new ArrayList<>();
DetailAST sibling = detailAST.getPreviousSibling();
while (true) {
if (sibling.getType() == TokenTypes.IMPORT) {
FullIdent importIdent = FullIdent.createFullIdentBelow(sibling);
importASTList.add(importIdent.getText());
}
else {
break;
}
sibling = sibling.getPreviousSibling();
}
return importASTList;
}
private JavaClass _getJavaClass(
JavaDocBuilder javaDocBuilder, String fileName) {
int pos = fileName.lastIndexOf("/");
String className = fileName.substring(pos + 1, fileName.length() - 5);
for (JavaClass javaClass : javaDocBuilder.getClasses()) {
if (className.equals(javaClass.getName())) {
return javaClass;
}
}
return null;
}
private Map<String, String> _getVariablesMap(
JavaDocBuilder javaDocBuilder) {
Map<String, String> variablesMap = new HashMap<>();
for (JavaClass javaClass : javaDocBuilder.getClasses()) {
for (JavaField javaField : javaClass.getFields()) {
String fieldName = javaField.getName();
Type fieldType = javaField.getType();
String fullyQualifiedTypeName =
fieldType.getFullyQualifiedName();
variablesMap.put(fieldName, fullyQualifiedTypeName);
}
}
return variablesMap;
}
}