Package de.plushnikov.intellij.plugin.processor.field

Source Code of de.plushnikov.intellij.plugin.processor.field.WitherFieldProcessor

package de.plushnikov.intellij.plugin.processor.field;

import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.util.text.StringUtil;
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.PsiParameter;
import com.intellij.psi.PsiType;
import com.intellij.util.StringBuilderSpinAllocator;
import de.plushnikov.intellij.plugin.problem.ProblemBuilder;
import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder;
import de.plushnikov.intellij.plugin.psi.LombokLightParameter;
import de.plushnikov.intellij.plugin.quickfix.PsiQuickFixFactory;
import de.plushnikov.intellij.plugin.thirdparty.LombokUtils;
import de.plushnikov.intellij.plugin.util.LombokProcessorUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import de.plushnikov.intellij.plugin.util.PsiClassUtil;
import de.plushnikov.intellij.plugin.util.PsiMethodUtil;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Wither;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.List;

import static de.plushnikov.intellij.plugin.util.PsiElementUtil.typesAreEquivalent;
import static java.lang.String.format;

public class WitherFieldProcessor extends AbstractFieldProcessor {

  private static final String WITHER_PREFIX = "with";

  public WitherFieldProcessor() {
    super(Wither.class, PsiMethod.class);
  }

  @Override
  protected boolean validate(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiField psiField, @NotNull ProblemBuilder builder) {

    boolean valid = validateVisibility(psiAnnotation);
    valid &= validNonStatic(psiField, psiAnnotation, builder);
    valid &= validHasConstructor(psiField, builder);
    valid &= validIsWitherUnique(psiField, psiAnnotation, builder);

    return valid;
  }

  protected boolean validateVisibility(@NotNull PsiAnnotation psiAnnotation) {
    final String methodVisibility = LombokProcessorUtil.getMethodModifier(psiAnnotation);
    return null != methodVisibility;
  }

  @Override
  protected void generatePsiElements(@NotNull PsiField psiField, @NotNull PsiAnnotation psiAnnotation, @NotNull List<? super PsiElement> target) {
    String methodModifier = LombokProcessorUtil.getMethodModifier(psiAnnotation);
    if (methodModifier != null) {
      AccessorsInfo accessorsInfo = AccessorsInfo.build(psiField);
      PsiMethod method = createWitherMethod(psiField, methodModifier, accessorsInfo);
      if (method != null) {
        target.add(method);
      }
    }
  }

  private String witherName(String fieldName, boolean isBoolean) {
    if (isBoolean && fieldName.startsWith("is") && fieldName.length() > 2 && Character.isUpperCase(fieldName.charAt(2))) {
      return WITHER_PREFIX + fieldName.substring(2);
    } else {
      return defaultWitherName(fieldName);
    }
  }

  private String defaultWitherName(String fieldName) {
    return WITHER_PREFIX + StringUtil.capitalize(fieldName);
  }

  private boolean validNonStatic(PsiField field, PsiAnnotation annotation, @NotNull final ProblemBuilder builder) {
    if (field.hasModifierProperty(PsiModifier.STATIC)) {
      builder.addError(format("'@%s' on static field is not allowed", annotation.getQualifiedName()),
          PsiQuickFixFactory.createModifierListFix(field, PsiModifier.STATIC, false, false));
      return false;
    }
    return true;
  }

  private boolean hasRightConstructor(PsiField field) {
    return null != getRightConstructor(field);
  }

  private PsiMethod getRightConstructor(PsiField field) {
    PsiClass psiClass = field.getContainingClass();
    if (psiClass != null) {
      for (PsiMethod psiMethod : PsiClassUtil.collectClassConstructorIntern(psiClass)) {
        if (hasParam(field, psiMethod)) {
          return psiMethod;
        }
      }
    }
    return null;
  }

  private boolean hasParam(PsiField field, PsiMethod psiMethod) {
    for (PsiParameter param : psiMethod.getParameterList().getParameters()) {
      if (typesAreEquivalent(param.getType(), field.getType())) {
        return true;
      }
    }
    return false;
  }

  private boolean validHasConstructor(@NotNull final PsiField field, @NotNull final ProblemBuilder builder) {
    final PsiClass fieldContainingClass = field.getContainingClass();
    if (null == fieldContainingClass) {
      return false;
    }
    if (PsiAnnotationUtil.isAnnotatedWith(fieldContainingClass, AllArgsConstructor.class)) {
      return true;
    }
    if (hasRightConstructor(field)) {
      return true;
    }
    final boolean hasRequiredArgsConstAnnotation = PsiAnnotationUtil.isAnnotatedWith(fieldContainingClass, RequiredArgsConstructor.class);
    final boolean isFinal = field.hasModifierProperty(PsiModifier.FINAL);
    final boolean hasNonNullAnnotation = PsiAnnotationUtil.isAnnotatedWith(field, NonNull.class);

    if (hasRequiredArgsConstAnnotation && (isFinal || hasNonNullAnnotation)) {
      return true;
    } else {
      builder.addWarning("Compilation will fail : no constructor with a parameter of type '%s' was found",
          field.getType().getCanonicalText());
      return false;
    }
  }

  private boolean validIsWitherUnique(@NotNull PsiField psiField, @NotNull PsiAnnotation annotation, @NotNull final ProblemBuilder builder) {
    final PsiClass fieldContainingClass = psiField.getContainingClass();
    final String psiFieldName = psiField.getName();
    if (psiFieldName != null && fieldContainingClass != null) {
      final String witherName = witherName(psiFieldName, isBooleanField(psiField));
      if (PsiMethodUtil.hasSimilarMethod(PsiClassUtil.collectClassMethodsIntern(fieldContainingClass), witherName, 1)
          || PsiMethodUtil.hasSimilarMethod(PsiClassUtil.collectClassMethodsIntern(fieldContainingClass), defaultWitherName(psiFieldName), 1)) {
        builder.addWarning("No '@%s' generated : a method named '%s' taking one parameter already exists", annotation.getQualifiedName(), witherName);
        return false;
      }
    }
    return true;
  }

  @Nullable
  public PsiMethod createWitherMethod(@NotNull PsiField psiField, @NotNull String methodModifier, @NotNull AccessorsInfo accessorsInfo) {
    LombokLightMethodBuilder result = null;
    final PsiClass psiFieldContainingClass = psiField.getContainingClass();
    if (psiFieldContainingClass != null) {
      final PsiType returnType = PsiClassUtil.getTypeWithGenerics(psiFieldContainingClass);
      final String psiFieldName = psiField.getName();
      final PsiType psiFieldType = psiField.getType();

      result = new LombokLightMethodBuilder(psiField.getManager(), witherName(accessorsInfo.removePrefix(psiFieldName), isBooleanField(psiField)))
          .withMethodReturnType(returnType)
          .withContainingClass(psiFieldContainingClass)
          .withNavigationElement(psiField)
          .withModifier(methodModifier);

      final LombokLightParameter methodParameter = new LombokLightParameter(psiFieldName, psiFieldType, result, JavaLanguage.INSTANCE);
      copyAnnotations(psiField, methodParameter.getModifierList(), LombokUtils.NON_NULL_PATTERN, LombokUtils.NULLABLE_PATTERN, LombokUtils.DEPRECATED_PATTERN);
      result.withParameter(methodParameter);

      final String paramString = getConstructorCall(psiField, psiFieldContainingClass);
      final String blockText = String.format("return this.%s == %s ? this : new %s(%s);", psiFieldName, psiFieldName, returnType.getCanonicalText(), paramString);
      result.withBody(PsiMethodUtil.createCodeBlockFromText(blockText, psiFieldContainingClass));
    }
    return result;
  }

  private boolean isBooleanField(PsiField psiField) {
    return PsiType.BOOLEAN.equals(psiField.getType());
  }

  private String getConstructorCall(@NotNull PsiField psiField, @NotNull PsiClass psiClass) {
    final StringBuilder paramString = StringBuilderSpinAllocator.alloc();
    try {
      final Collection<PsiField> psiFields = PsiClassUtil.collectClassFieldsIntern(psiClass);
      for (PsiField classField : psiFields) {
        final String classFieldName = classField.getName();
        if (classFieldName.startsWith(LombokUtils.LOMBOK_INTERN_FIELD_MARKER)) {
          continue;
        }
        if (classField.hasModifierProperty(PsiModifier.STATIC)) {
          continue;
        }
        if (classField.hasModifierProperty(PsiModifier.FINAL) && null != classField.getInitializer()) {
          continue;
        }

        if (classField.equals(psiField)) {
          paramString.append(classFieldName);
        } else {
          paramString.append("this.").append(classFieldName);
        }
        paramString.append(',');
      }
      if (paramString.length() > 1) {
        paramString.deleteCharAt(paramString.length() - 1);
      }
      return paramString.toString();
    } finally {
      StringBuilderSpinAllocator.dispose(paramString);
    }
  }
}
TOP

Related Classes of de.plushnikov.intellij.plugin.processor.field.WitherFieldProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.