/**
* 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.checks;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.MapUtil;
import com.liferay.portal.kernel.util.SetUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.source.formatter.parser.JavaClass;
import com.liferay.source.formatter.parser.JavaConstructor;
import com.liferay.source.formatter.parser.JavaMethod;
import com.liferay.source.formatter.parser.JavaTerm;
import com.liferay.source.formatter.parser.JavaVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Hugo Huijser
*/
public class JavaVariableTypeCheck extends BaseJavaTermCheck {
@Override
public void init() {
_annotationsExclusions = _getAnnotationsExclusions();
_defaultPrimitiveValues = _getDefaultPrimitiveValues();
_immutableFieldTypes = _getImmutableFieldTypes();
}
@Override
public boolean isPortalCheck() {
return true;
}
@Override
protected String doProcess(
String fileName, String absolutePath, JavaTerm javaTerm,
String fileContent) {
if (isExcludedPath(_CHECK_JAVA_FIELD_TYPES_EXCLUDES, absolutePath)) {
return javaTerm.getContent();
}
JavaClass javaClass = (JavaClass)javaTerm;
String classContent = javaClass.getContent();
for (JavaTerm childJavaTerm : javaClass.getChildJavaTerms()) {
if (childJavaTerm instanceof JavaVariable) {
classContent = _checkFieldType(
absolutePath, javaClass, classContent,
(JavaVariable)childJavaTerm);
}
}
return classContent;
}
@Override
protected String[] getCheckableJavaTermNames() {
return new String[] {JAVA_CLASS};
}
private String _checkFieldType(
String absolutePath, JavaClass javaClass, String classContent,
JavaVariable javaVariable) {
String accessModifier = javaVariable.getAccessModifier();
if (accessModifier.equals(JavaTerm.ACCESS_MODIFIER_PUBLIC)) {
return classContent;
}
String fieldType = _getFieldType(javaVariable);
boolean isFinal = _containsNonAccessModifier(javaVariable, "final");
if (!isFinal) {
classContent = _formatDefaultValue(
classContent, javaVariable, fieldType);
}
if (!accessModifier.equals(JavaTerm.ACCESS_MODIFIER_PRIVATE)) {
return classContent;
}
if (isFinal) {
if (!javaVariable.isStatic() &&
(_immutableFieldTypes.contains(fieldType) ||
(fieldType.equals("Log") &&
!isExcludedPath(_STATIC_LOG_EXCLUDES, absolutePath)))) {
classContent = _formatStaticableFieldType(
classContent, javaVariable.getContent());
}
}
else if (!_containsNonAccessModifier(javaVariable, "volatile")) {
classContent = _formatFinalableFieldType(
classContent, javaClass, javaVariable, fieldType);
}
return classContent;
}
private boolean _containsNonAccessModifier(
JavaVariable javaVariable, String modifier) {
Pattern pattern = Pattern.compile(
javaVariable.getAccessModifier() +
" (((final|static|synchronized|transient|volatile)(\n| ))*)");
Matcher matcher = pattern.matcher(javaVariable.getContent());
if (matcher.find()) {
String nonAccessModifiers = matcher.group(1);
if (nonAccessModifiers.contains(modifier)) {
return true;
}
}
return false;
}
private String _formatDefaultValue(
String classContent, JavaVariable javaVariable, String fieldType) {
String defaultValue = null;
if (StringUtil.isLowerCase(fieldType)) {
defaultValue = _defaultPrimitiveValues.get(fieldType);
}
else {
defaultValue = StringPool.NULL;
}
Pattern isDefaultValuePattern = Pattern.compile(
" =\\s+" + defaultValue + ";(\\s+)$");
Matcher matcher = isDefaultValuePattern.matcher(
javaVariable.getContent());
if (matcher.find()) {
return StringUtil.replace(
classContent, javaVariable.getContent(),
matcher.replaceFirst(";$1"));
}
return classContent;
}
private String _formatFinalableFieldType(
String classContent, JavaClass javaClass, JavaVariable javaVariable,
String fieldType) {
for (String annotation : _annotationsExclusions) {
if (javaVariable.hasAnnotation(annotation)) {
return classContent;
}
}
JavaClass parentJavaClass = javaClass;
while (true) {
if (parentJavaClass.getParentJavaClass() == null) {
break;
}
parentJavaClass = parentJavaClass.getParentJavaClass();
}
List<JavaTerm> allChildJavaTerms = _getAllChildJavaTerms(
parentJavaClass);
StringBundler sb = new StringBundler(6);
sb.append("(((\\+\\+( ?))|(--( ?)))");
sb.append(javaVariable.getName());
sb.append(")|((\\b|\\.)");
sb.append(javaVariable.getName());
sb.append("((( )((=)|(\\+=)|(-=)|(\\*=)|(/=)|(%=)))");
sb.append("|(\\+\\+)|(--)|(( )((\\|=)|(&=)|(^=)))))");
Pattern pattern = Pattern.compile(sb.toString());
if (!_isFinalableField(
javaClass, javaVariable, pattern, allChildJavaTerms)) {
return classContent;
}
String javaVariableContent = javaVariable.getContent();
String newJavaVariableContent = StringUtil.replaceFirst(
javaVariableContent, fieldType, "final " + fieldType);
return StringUtil.replace(
classContent, javaVariableContent, newJavaVariableContent);
}
private String _formatStaticableFieldType(
String classContent, String javaVariableContent) {
if (!javaVariableContent.contains(StringPool.EQUAL)) {
return classContent;
}
String newJavaVariableContent = StringUtil.replaceFirst(
javaVariableContent, "private final", "private static final");
return StringUtil.replace(
classContent, javaVariableContent, newJavaVariableContent);
}
private List<JavaTerm> _getAllChildJavaTerms(JavaClass javaClass) {
List<JavaTerm> childJavaTerms = new ArrayList<>();
for (JavaTerm childJavaTerm : javaClass.getChildJavaTerms()) {
childJavaTerms.add(childJavaTerm);
if (childJavaTerm instanceof JavaClass) {
JavaClass childJavaClass = (JavaClass)childJavaTerm;
childJavaTerms.addAll(_getAllChildJavaTerms(childJavaClass));
}
}
return childJavaTerms;
}
private List<String> _getAnnotationsExclusions() {
return ListUtil.fromArray(
new String[] {
"ArquillianResource", "Autowired", "BeanReference", "Captor",
"Inject", "Mock", "Parameter", "Reference", "ServiceReference",
"SuppressWarnings", "Value"
});
}
private Map<String, String> _getDefaultPrimitiveValues() {
return MapUtil.fromArray(
new String[] {
"boolean", "false", "char", "'\\\\0'", "byte", "0", "double",
"0\\.0", "float", "0\\.0", "int", "0", "long", "0", "short", "0"
});
}
private String _getFieldType(JavaVariable javaVariable) {
StringBundler sb = new StringBundler(4);
sb.append(javaVariable.getAccessModifier());
sb.append(" (((final|static|synchronized|transient|volatile)(\n| ))*)");
sb.append("([\\s\\S]*?)");
sb.append(javaVariable.getName());
Pattern pattern = Pattern.compile(sb.toString());
Matcher matcher = pattern.matcher(javaVariable.getContent());
if (matcher.find()) {
return StringUtil.trim(matcher.group(5));
}
return null;
}
private Set<String> _getImmutableFieldTypes() {
Set<String> immutableFieldTypes = SetUtil.fromArray(
new String[] {
"boolean", "byte", "char", "double", "float", "int", "long",
"short", "Boolean", "Byte", "Character", "Class", "Double",
"Float", "Int", "Long", "Number", "Short", "String"
});
immutableFieldTypes.addAll(getPropertyList("immutable.field.types"));
return immutableFieldTypes;
}
private boolean _isFinalableField(
JavaClass javaClass, JavaVariable javaVariable, Pattern pattern,
List<JavaTerm> allChildJavaTerms) {
for (JavaTerm childJavaTerm : allChildJavaTerms) {
String content = childJavaTerm.getContent();
Matcher matcher = pattern.matcher(content);
if (!matcher.find() || !content.contains(javaVariable.getName())) {
continue;
}
if (childJavaTerm instanceof JavaConstructor) {
JavaClass constructorClass = childJavaTerm.getParentJavaClass();
String constructorClassName = constructorClass.getName();
if (!constructorClassName.equals(javaClass.getName())) {
return false;
}
}
else if (childJavaTerm instanceof JavaMethod) {
return false;
}
else if ((childJavaTerm instanceof JavaVariable) &&
content.contains("{\n\n")) {
return false;
}
}
return true;
}
private static final String _CHECK_JAVA_FIELD_TYPES_EXCLUDES =
"check.java.field.types.excludes";
private static final String _STATIC_LOG_EXCLUDES = "static.log.excludes";
private List<String> _annotationsExclusions;
private Map<String, String> _defaultPrimitiveValues;
private Set<String> _immutableFieldTypes;
}