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.PsiFile;
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.psi.LombokLightMethodBuilder;
import de.plushnikov.intellij.plugin.util.PsiClassUtil;
import de.plushnikov.intellij.plugin.util.PsiMethodUtil;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
/**
* @author Florian Böhm
*/
public class UtilityClassProcessor extends AbstractClassProcessor {
public UtilityClassProcessor() {
super(PsiMethod.class, UtilityClass.class);
}
@Override
protected boolean validate(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
return validateOnRightType(psiClass, builder) && validateNoConstructorsDefined(psiClass, builder);
}
private boolean validateNoConstructorsDefined(PsiClass psiClass, ProblemBuilder builder) {
Collection<PsiMethod> psiMethods = PsiClassUtil.collectClassConstructorIntern(psiClass);
if (!psiMethods.isEmpty()) {
builder.addError("@UtilityClasses cannot have declared constructors.");
return false;
}
return true;
}
public static boolean validateOnRightType(PsiClass psiClass, ProblemBuilder builder) {
if (checkWrongType(psiClass)) {
builder.addError("@UtilityClass is only supported on a class (can't be an interface, enum, or annotation).");
return false;
}
PsiElement context = psiClass.getContext();
if (context == null) {
return false;
}
if (!(context instanceof PsiFile)) {
PsiElement contextUp = context;
while (true) {
if (contextUp instanceof PsiClass) {
PsiClass psiClassUp = (PsiClass) contextUp;
if (psiClassUp.getContext() instanceof PsiFile) {
return true;
}
Boolean isStatic = isStatic(psiClassUp.getModifierList());
if (isStatic || checkWrongType(psiClassUp)) {
contextUp = contextUp.getContext();
} else {
builder.addError("@UtilityClass automatically makes the class static, however, this class cannot be made static.");
return false;
}
} else {
builder.addError("@UtilityClass cannot be placed on a method local or anonymous inner class, or any class nested in such a class.");
return false;
}
}
}
return true;
}
private static boolean isStatic(PsiModifierList modifierList) {
return modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC);
}
private static boolean checkWrongType(PsiClass psiClass) {
return psiClass != null && (psiClass.isInterface() || psiClass.isEnum() || psiClass.isAnnotationType());
}
@Override
protected void generatePsiElements(@NotNull final PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List<? super PsiElement> target) {
LombokLightMethodBuilder constructor = new LombokLightMethodBuilder(psiClass.getManager(), psiClass.getName())
.withConstructor(true)
.withContainingClass(psiClass)
.withNavigationElement(psiAnnotation)
.withModifier(PsiModifier.PRIVATE);
String methodBody = String.format("throw new %s(%s);", "java.lang.UnsupportedOperationException", "\"This is a utility class and cannot be instantiated\"");
constructor.withBody(PsiMethodUtil.createCodeBlockFromText(methodBody, psiClass));
target.add(constructor);
}
}