package de.plushnikov.intellij.plugin.processor.field;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import de.plushnikov.intellij.plugin.lombokconfig.ConfigDiscovery;
import de.plushnikov.intellij.plugin.lombokconfig.ConfigKey;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
/**
* @author Plushnikov Michail
*/
public class AccessorsInfo {
public static final AccessorsInfo EMPTY = new AccessorsInfo(false, false, false);
private final boolean fluent;
private final boolean chain;
private final String[] prefixes;
private final boolean doNotUseIsPrefix;
private AccessorsInfo(boolean fluentValue, boolean chainValue, boolean doNotUseIsPrefix, String... prefixes) {
this.fluent = fluentValue;
this.chain = chainValue;
this.doNotUseIsPrefix = doNotUseIsPrefix;
this.prefixes = null == prefixes ? new String[0] : prefixes;
}
@NotNull
public static AccessorsInfo build(boolean fluentValue, boolean chainValue, boolean doNotUseIsPrefix, String... prefixes) {
return new AccessorsInfo(fluentValue, chainValue, doNotUseIsPrefix, prefixes);
}
@NotNull
public static AccessorsInfo build(@NotNull PsiField psiField) {
final PsiAnnotation accessorsFieldAnnotation = PsiAnnotationSearchUtil.findAnnotation(psiField, Accessors.class);
final PsiClass containingClass = psiField.getContainingClass();
if (null != accessorsFieldAnnotation) {
return buildFromAnnotation(accessorsFieldAnnotation, containingClass);
} else {
return build(containingClass);
}
}
@NotNull
public static AccessorsInfo build(@Nullable PsiClass psiClass) {
PsiClass containingClass = psiClass;
while (null != containingClass) {
final PsiAnnotation accessorsClassAnnotation = PsiAnnotationSearchUtil.findAnnotation(containingClass, Accessors.class);
if (null != accessorsClassAnnotation) {
return buildFromAnnotation(accessorsClassAnnotation, containingClass);
}
containingClass = containingClass.getContainingClass();
}
return buildAccessorsInfo(psiClass, null, null, Collections.<String>emptySet());
}
@NotNull
private static AccessorsInfo buildFromAnnotation(@NotNull PsiAnnotation accessorsAnnotation, @Nullable PsiClass psiClass) {
Boolean chainDeclaredValue = PsiAnnotationUtil.getDeclaredBooleanAnnotationValue(accessorsAnnotation, "chain");
Boolean fluentDeclaredValue = PsiAnnotationUtil.getDeclaredBooleanAnnotationValue(accessorsAnnotation, "fluent");
Collection<String> prefixes = PsiAnnotationUtil.getAnnotationValues(accessorsAnnotation, "prefix", String.class);
return buildAccessorsInfo(psiClass, chainDeclaredValue, fluentDeclaredValue, prefixes);
}
@NotNull
private static AccessorsInfo buildAccessorsInfo(@Nullable PsiClass psiClass, @Nullable Boolean chainDeclaredValue,
@Nullable Boolean fluentDeclaredValue, @NotNull Collection<String> prefixDeclared) {
final boolean isFluent;
final boolean isChained;
final boolean doNotUseIsPrefix;
final String[] prefixes;
if (null != psiClass) {
if (null == fluentDeclaredValue) {
isFluent = ConfigDiscovery.getInstance().getBooleanLombokConfigProperty(ConfigKey.ACCESSORS_FLUENT, psiClass);
} else {
isFluent = fluentDeclaredValue;
}
if (null == chainDeclaredValue) {
isChained = ConfigDiscovery.getInstance().getBooleanLombokConfigProperty(ConfigKey.ACCESSORS_CHAIN, psiClass);
} else {
isChained = chainDeclaredValue;
}
if (prefixDeclared.isEmpty()) {
prefixes = ConfigDiscovery.getInstance().getMultipleValueLombokConfigProperty(ConfigKey.ACCESSORS_PREFIX, psiClass);
} else {
prefixes = prefixDeclared.toArray(new String[prefixDeclared.size()]);
}
doNotUseIsPrefix = ConfigDiscovery.getInstance().getBooleanLombokConfigProperty(ConfigKey.GETTER_NO_IS_PREFIX, psiClass);
} else {
isFluent = null == fluentDeclaredValue ? false : fluentDeclaredValue;
isChained = null == chainDeclaredValue ? false : chainDeclaredValue;
prefixes = prefixDeclared.toArray(new String[prefixDeclared.size()]);
doNotUseIsPrefix = false;
}
boolean isChainDeclaredOrImplicit = isChained || (isFluent && null == chainDeclaredValue);
return new AccessorsInfo(isFluent, isChainDeclaredOrImplicit, doNotUseIsPrefix, prefixes);
}
public boolean isFluent() {
return fluent;
}
public boolean isChain() {
return chain;
}
public boolean isDoNotUseIsPrefix() {
return doNotUseIsPrefix;
}
public String[] getPrefixes() {
return prefixes;
}
public boolean prefixDefinedAndStartsWith(String fieldName) {
if (prefixes.length == 0) {
return true;
}
for (String prefix : prefixes) {
if (canPrefixApply(fieldName, prefix)) {
return true;
}
}
return false;
}
public String removePrefix(String fieldName) {
for (String prefix : prefixes) {
if (canPrefixApply(fieldName, prefix)) {
return prefix.isEmpty() ? fieldName : decapitalizeLikeLombok(fieldName.substring(prefix.length()));
}
}
return fieldName;
}
private boolean canPrefixApply(String fieldName, String prefix) {
final int prefixLength = prefix.length();
return prefixLength == 0 || fieldName.startsWith(prefix) && fieldName.length() > prefixLength &&
(!Character.isLetter(prefix.charAt(prefix.length() - 1)) || Character.isUpperCase(fieldName.charAt(prefixLength)));
}
private String decapitalizeLikeLombok(String name) {
if (name == null || name.isEmpty()) {
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
}