Package de.plushnikov.intellij.plugin.processor.clazz

Source Code of de.plushnikov.intellij.plugin.processor.clazz.ToStringProcessor

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

import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiArrayType;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiType;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.StringBuilderSpinAllocator;
import de.plushnikov.intellij.plugin.extension.UserMapKeys;
import de.plushnikov.intellij.plugin.problem.ProblemBuilder;
import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder;
import de.plushnikov.intellij.plugin.quickfix.PsiQuickFixFactory;
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import de.plushnikov.intellij.plugin.util.PsiClassUtil;
import de.plushnikov.intellij.plugin.util.PsiFieldUtil;
import de.plushnikov.intellij.plugin.util.PsiMethodUtil;
import lombok.ToString;
import org.jetbrains.annotations.NotNull;

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

/**
* Inspect and validate @ToString lombok annotation on a class
* Creates toString() method for fields of this class
*
* @author Plushnikov Michail
*/
public class ToStringProcessor extends AbstractClassProcessor {

  public static final String METHOD_NAME = "toString";

  public ToStringProcessor() {
    super(ToString.class, PsiMethod.class);
  }

  @Override
  protected boolean validate(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
    final boolean result = validateAnnotationOnRigthType(psiClass, builder);
    if (result) {
      validateExistingMethods(psiClass, builder);
    }

    final Collection<String> excludeProperty = PsiAnnotationUtil.getAnnotationValues(psiAnnotation, "exclude", String.class);
    final Collection<String> ofProperty = PsiAnnotationUtil.getAnnotationValues(psiAnnotation, "of", String.class);

    if (!excludeProperty.isEmpty() && !ofProperty.isEmpty()) {
      builder.addWarning("exclude and of are mutually exclusive; the 'exclude' parameter will be ignored",
          PsiQuickFixFactory.createChangeAnnotationParameterFix(psiAnnotation, "exclude", null));
    } else {
      validateExcludeParam(psiClass, builder, psiAnnotation, excludeProperty);
    }
    validateOfParam(psiClass, builder, psiAnnotation, ofProperty);

    return result;
  }

  protected boolean validateAnnotationOnRigthType(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
    boolean result = true;
    if (psiClass.isAnnotationType() || psiClass.isInterface()) {
      builder.addError("@ToString is only supported on a class or enum type");
      result = false;
    }
    return result;
  }

  protected boolean validateExistingMethods(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
    boolean result = true;

    final Collection<PsiMethod> classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass);
    if (PsiMethodUtil.hasMethodByName(classMethods, METHOD_NAME)) {
      builder.addWarning("Not generated '%s'(): A method with same name already exists", METHOD_NAME);
      result = false;
    }

    return result;
  }

  protected void generatePsiElements(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List<? super PsiElement> target) {
    target.addAll(createToStringMethod(psiClass, psiAnnotation));
  }

  @NotNull
  public Collection<PsiMethod> createToStringMethod(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) {
    final Collection<PsiMethod> classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass);
    if (PsiMethodUtil.hasMethodByName(classMethods, METHOD_NAME)) {
      return Collections.emptyList();
    }

    final PsiManager psiManager = psiClass.getManager();
    LombokLightMethodBuilder method = new LombokLightMethodBuilder(psiManager, METHOD_NAME)
        .withMethodReturnType(PsiType.getJavaLangString(psiManager, GlobalSearchScope.allScope(psiClass.getProject())))
        .withContainingClass(psiClass)
        .withNavigationElement(psiAnnotation)
        .withModifier(PsiModifier.PUBLIC);

    final String paramString = createParamString(psiClass, psiAnnotation);
    final String blockText = String.format("return \"%s(%s)\";", psiClass.getQualifiedName(), paramString);
    method.withBody(PsiMethodUtil.createCodeBlockFromText(blockText, psiClass));

    Collection<PsiField> toStringFields = PsiFieldUtil.filterFieldsByModifiers(psiClass.getFields(), PsiModifier.STATIC);
    UserMapKeys.addReadUsageFor(toStringFields);

    return Collections.<PsiMethod>singletonList(method);
  }

  private String createParamString(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) {
    final boolean includeFieldNames = PsiAnnotationUtil.getAnnotationValue(psiAnnotation, "includeFieldNames", Boolean.class, Boolean.TRUE);
    final boolean callSuper = PsiAnnotationUtil.getAnnotationValue(psiAnnotation, "callSuper", Boolean.class, Boolean.FALSE);
    final boolean doNotUseGetters = PsiAnnotationUtil.getAnnotationValue(psiAnnotation, "doNotUseGetters", Boolean.class, Boolean.FALSE);

    final StringBuilder paramString = StringBuilderSpinAllocator.alloc();
    try {
      if (callSuper) {
        paramString.append("super=\" + super.toString() + \", ");
      }

      final Collection<PsiField> psiFields = filterFields(psiClass, psiAnnotation, false);
      for (PsiField classField : psiFields) {
        final String fieldName = classField.getName();

        if (includeFieldNames) {
          paramString.append(fieldName).append('=');
        }
        paramString.append("\"+");

        final PsiType classFieldType = classField.getType();
        if (classFieldType instanceof PsiArrayType) {
          final PsiType componentType = ((PsiArrayType) classFieldType).getComponentType();
          if (componentType instanceof PsiPrimitiveType) {
            paramString.append("java.util.Arrays.toString(");
          } else {
            paramString.append("java.util.Arrays.deepToString(");
          }
        }

        final String fieldAccessor = buildAttributeNameString(doNotUseGetters, classField, psiClass);
        paramString.append("this.").append(fieldAccessor);

        if (classFieldType instanceof PsiArrayType) {
          paramString.append(")");
        }

        paramString.append("+\", ");
      }
      if (paramString.length() > 2) {
        paramString.delete(paramString.length() - 2, paramString.length());
      }
      return paramString.toString();
    } finally {
      StringBuilderSpinAllocator.dispose(paramString);
    }
  }

}
TOP

Related Classes of de.plushnikov.intellij.plugin.processor.clazz.ToStringProcessor

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.