package de.plushnikov.intellij.plugin.processor.modifier; import com.google.common.collect.Iterables; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiEnumConstant; import com.intellij.psi.PsiField; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiModifierList; import com.intellij.psi.util.PsiTreeUtil; import de.plushnikov.intellij.plugin.psi.LombokLightFieldBuilder; import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil; import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil; import lombok.AccessLevel; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.Set; /** * Processor for <strong>experimental</strong> {@literal @FieldDefaults} feature of Lombok. * * @author Alexej Kubarev * @see <a href="https://projectlombok.org/features/experimental/FieldDefaults.html">Lombok Feature: Field Defaults</a> */ public class FieldDefaultsModifierProcessor implements ModifierProcessor { @Override public boolean isSupported(@NotNull PsiModifierList modifierList) { // FieldDefaults only change modifiers of class fields // but nor for enum constants or lombok generated fields final PsiElement psiElement = modifierList.getParent(); if (!(psiElement instanceof PsiField) || psiElement instanceof PsiEnumConstant || psiElement instanceof LombokLightFieldBuilder) { return false; } PsiClass searchableClass = PsiTreeUtil.getParentOfType(modifierList, PsiClass.class, true); return null != searchableClass && PsiAnnotationSearchUtil.isAnnotatedWith(searchableClass, lombok.experimental.FieldDefaults.class); } @Override public void transformModifiers(@NotNull PsiModifierList modifierList, @NotNull final Set<String> modifiers) { PsiClass searchableClass = PsiTreeUtil.getParentOfType(modifierList, PsiClass.class, true); if (searchableClass == null) { return; // Should not get here, but safer to check } PsiAnnotation fieldDefaultsAnnotation = PsiAnnotationSearchUtil.findAnnotation(searchableClass, lombok.experimental.FieldDefaults.class); if (fieldDefaultsAnnotation == null) { return; // Should not get here, but safer to check } PsiField parentElement = (PsiField) modifierList.getParent(); // FINAL // Is @FieldDefaults(makeFinal = true)? if ((PsiAnnotationUtil.getBooleanAnnotationValue(fieldDefaultsAnnotation, "makeFinal", false)) && (!PsiAnnotationSearchUtil.isAnnotatedWith(parentElement, lombok.experimental.NonFinal.class))) { modifiers.add(PsiModifier.FINAL); } // VISIBILITY Collection<String> defaultLevels = PsiAnnotationUtil.getAnnotationValues(fieldDefaultsAnnotation, "level", String.class); final AccessLevel defaultAccessLevel = AccessLevel.valueOf(Iterables.getFirst(defaultLevels, AccessLevel.NONE.name())); if (// If explicit visibility modifier is set - no point to continue. !hasPackagePrivateModifier(modifierList) || // If @PackagePrivate is requested, leave the field as is PsiAnnotationSearchUtil.isAnnotatedWith(parentElement, lombok.experimental.PackagePrivate.class)) { return; } switch (defaultAccessLevel) { case PRIVATE: modifiers.add(PsiModifier.PRIVATE); modifiers.remove(PsiModifier.PACKAGE_LOCAL); break; case PROTECTED: modifiers.add(PsiModifier.PROTECTED); modifiers.remove(PsiModifier.PACKAGE_LOCAL); break; case PUBLIC: modifiers.add(PsiModifier.PUBLIC); modifiers.remove(PsiModifier.PACKAGE_LOCAL); break; default: break; } } private boolean hasPackagePrivateModifier(@NotNull PsiModifierList modifierList) { return !(modifierList.hasExplicitModifier(PsiModifier.PUBLIC) || modifierList.hasExplicitModifier(PsiModifier.PRIVATE) || modifierList.hasExplicitModifier(PsiModifier.PROTECTED)); } }