package de.plushnikov.intellij.plugin.processor.clazz;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiModifierList;
import de.plushnikov.intellij.plugin.problem.ProblemBuilder;
import de.plushnikov.intellij.plugin.processor.LombokPsiElementUsage;
import de.plushnikov.intellij.plugin.processor.field.AccessorsInfo;
import de.plushnikov.intellij.plugin.processor.field.WitherFieldProcessor;
import de.plushnikov.intellij.plugin.thirdparty.LombokUtils;
import de.plushnikov.intellij.plugin.util.LombokProcessorUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.PsiClassUtil;
import lombok.experimental.Wither;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class WitherProcessor extends AbstractClassProcessor {
private final WitherFieldProcessor fieldProcessor;
public WitherProcessor(WitherFieldProcessor fieldProcessor) {
super(PsiMethod.class, Wither.class);
this.fieldProcessor = fieldProcessor;
}
@Override
protected boolean validate(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
return validateAnnotationOnRightType(psiClass, builder) && validateVisibility(psiAnnotation) && fieldProcessor.validConstructor(psiClass, builder);
}
private boolean validateAnnotationOnRightType(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
boolean result = true;
if (psiClass.isAnnotationType() || psiClass.isInterface() || psiClass.isEnum()) {
builder.addError("@Wither is only supported on a class or a field.");
result = false;
}
return result;
}
private boolean validateVisibility(@NotNull PsiAnnotation psiAnnotation) {
final String methodVisibility = LombokProcessorUtil.getMethodModifier(psiAnnotation);
return null != methodVisibility;
}
protected void generatePsiElements(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List<? super PsiElement> target) {
final String methodVisibility = LombokProcessorUtil.getMethodModifier(psiAnnotation);
if (methodVisibility != null) {
final AccessorsInfo accessorsInfo = AccessorsInfo.build(psiClass);
target.addAll(createFieldWithers(psiClass, psiAnnotation, methodVisibility, accessorsInfo));
}
}
@NotNull
private Collection<PsiMethod> createFieldWithers(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull String methodModifier, @NotNull AccessorsInfo accessors) {
Collection<PsiMethod> result = new ArrayList<PsiMethod>();
final Collection<PsiField> witherFields = getWitherFields(psiClass);
for (PsiField witherField : witherFields) {
PsiMethod method = fieldProcessor.createWitherMethod(witherField, methodModifier, accessors);
if (method != null) {
result.add(method);
}
}
return result;
}
@NotNull
private Collection<PsiField> getWitherFields(@NotNull PsiClass psiClass) {
Collection<PsiField> witherFields = new ArrayList<PsiField>();
for (PsiField psiField : psiClass.getFields()) {
boolean createWither = true;
PsiModifierList modifierList = psiField.getModifierList();
if (null != modifierList) {
// Skip static fields.
createWither = !modifierList.hasModifierProperty(PsiModifier.STATIC);
// Skip final fields
createWither &= !(modifierList.hasModifierProperty(PsiModifier.FINAL) && psiField.hasInitializer());
// Skip fields that start with $
createWither &= !psiField.getName().startsWith(LombokUtils.LOMBOK_INTERN_FIELD_MARKER);
// Skip fields having Wither annotation already
createWither &= !PsiAnnotationSearchUtil.isAnnotatedWith(psiField, Wither.class);
}
if (createWither) {
witherFields.add(psiField);
}
}
return witherFields;
}
@Override
public LombokPsiElementUsage checkFieldUsage(@NotNull PsiField psiField, @NotNull PsiAnnotation psiAnnotation) {
final PsiClass containingClass = psiField.getContainingClass();
if (null != containingClass) {
if (PsiClassUtil.getNames(getWitherFields(containingClass)).contains(psiField.getName())) {
return LombokPsiElementUsage.READ_WRITE;
}
}
return LombokPsiElementUsage.NONE;
}
}