Skip to content

Commit

Permalink
Issue #77
Browse files Browse the repository at this point in the history
added validation of @Singular Field Type
  • Loading branch information
Michail Plushnikov committed Jun 20, 2015
1 parent 8f0f5c1 commit 70a8c71
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 78 deletions.
Expand Up @@ -21,7 +21,7 @@
import de.plushnikov.intellij.plugin.processor.clazz.ToStringProcessor;
import de.plushnikov.intellij.plugin.processor.clazz.constructor.NoArgsConstructorProcessor;
import de.plushnikov.intellij.plugin.processor.field.AccessorsInfo;
import de.plushnikov.intellij.plugin.processor.handler.singular.AbstractSingularHandler;
import de.plushnikov.intellij.plugin.processor.handler.singular.BuilderElementHandler;
import de.plushnikov.intellij.plugin.processor.handler.singular.SingularHandlerFactory;
import de.plushnikov.intellij.plugin.psi.LombokLightClassBuilder;
import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder;
Expand All @@ -30,6 +30,7 @@
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import de.plushnikov.intellij.plugin.util.PsiClassUtil;
import de.plushnikov.intellij.plugin.util.PsiMethodUtil;
import de.plushnikov.intellij.plugin.util.PsiTypeUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
Expand Down Expand Up @@ -85,7 +86,26 @@ public boolean validate(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAn
final PsiType psiBuilderType = getBuilderType(psiClass);
final String builderClassName = getBuilderClassName(psiClass, psiAnnotation, psiBuilderType);
result = validateBuilderClassName(builderClassName, psiAnnotation.getProject(), problemBuilder) &&
validateExistingBuilderClass(builderClassName, psiClass, problemBuilder);
validateExistingBuilderClass(builderClassName, psiClass, problemBuilder) &&
validateSingularVariables(psiClass, problemBuilder);
}
return result;
}

private boolean validateSingularVariables(@NotNull PsiClass psiClass, @NotNull ProblemBuilder problemBuilder) {
boolean result = true;

final Collection<PsiField> builderFields = getBuilderFields(psiClass, Collections.<PsiField>emptySet());
for (PsiVariable builderVariable : builderFields) {
final PsiAnnotation singularAnnotation = PsiAnnotationUtil.findAnnotation(builderVariable, Singular.class);
if (null != singularAnnotation) {
final String qualifiedName = PsiTypeUtil.getQualifiedName(builderVariable.getType());
if (SingularHandlerFactory.isInvalidSingularType(qualifiedName)) {
problemBuilder.addError("Lombok does not know how to create the singular-form builder methods for type '%s'; " +
"they won't be generated.", qualifiedName != null ? qualifiedName : builderVariable.getType().getCanonicalText());
result = false;
}
}
}
return result;
}
Expand Down Expand Up @@ -320,7 +340,7 @@ public Collection<PsiField> generateFields(@NotNull Collection<? extends PsiVari
List<PsiField> fields = new ArrayList<PsiField>();
for (PsiVariable psiVariable : psiVariables) {
final PsiAnnotation singularAnnotation = PsiAnnotationUtil.findAnnotation(psiVariable, Singular.class);
AbstractSingularHandler handler = SingularHandlerFactory.getHandlerFor(psiVariable, singularAnnotation);
BuilderElementHandler handler = SingularHandlerFactory.getHandlerFor(psiVariable, singularAnnotation);
handler.addBuilderField(fields, psiVariable, psiBuilderClass, accessorsInfo);
}
return fields;
Expand Down Expand Up @@ -355,12 +375,9 @@ protected Collection<PsiMethod> createFieldMethods(@NotNull Collection<? extends
for (PsiVariable psiVariable : psiVariables) {
// skip methods already defined in builder class
if (!existedMethodNames.contains(psiVariable.getName())) {
// get Field of the root class not of the Builder class
final PsiVariable originalFieldElement = (PsiVariable) psiVariable.getNavigationElement();

final PsiAnnotation singularAnnotation = PsiAnnotationUtil.findAnnotation(originalFieldElement, Singular.class);
AbstractSingularHandler handler = SingularHandlerFactory.getHandlerFor(originalFieldElement, singularAnnotation);
handler.addBuilderMethod(methods, originalFieldElement, innerClass, fluentBuilder, returnType, singularAnnotation, accessorsInfo);
final PsiAnnotation singularAnnotation = PsiAnnotationUtil.findAnnotation(psiVariable, Singular.class);
BuilderElementHandler handler = SingularHandlerFactory.getHandlerFor(psiVariable, singularAnnotation);
handler.addBuilderMethod(methods, psiVariable, innerClass, fluentBuilder, returnType, singularAnnotation, accessorsInfo);
}
}
return methods;
Expand Down
Expand Up @@ -23,8 +23,9 @@

import java.util.List;

public abstract class AbstractSingularHandler {
public abstract class AbstractSingularHandler implements BuilderElementHandler {

@Override
public void addBuilderField(@NotNull List<PsiField> fields, @NotNull PsiVariable psiVariable, @NotNull PsiClass innerClass, @NotNull AccessorsInfo accessorsInfo) {
final String fieldName = psiVariable.getName();

Expand All @@ -42,6 +43,7 @@ protected PsiType getBuilderFieldType(@NotNull PsiType psiType, @NotNull Project
return PsiTypeUtil.getCollectionClassType((PsiClassType) psiType, project, CommonClassNames.JAVA_UTIL_ARRAY_LIST);
}

@Override
public void addBuilderMethod(@NotNull List<PsiMethod> methods, @NotNull PsiVariable psiVariable, @NotNull PsiClass innerClass, boolean fluentBuilder, PsiType returnType, PsiAnnotation singularAnnotation, @NotNull AccessorsInfo accessorsInfo) {
final String psiFieldName = psiVariable.getName();
final String singularName = createSingularName(singularAnnotation, psiFieldName);
Expand Down Expand Up @@ -71,21 +73,13 @@ public void addBuilderMethod(@NotNull List<PsiMethod> methods, @NotNull PsiVaria
methods.add(allAddMethod);
}

protected void addOneMethodParameter(@NotNull String singularName, @NotNull PsiType psiFieldType, @NotNull LombokLightMethodBuilder methodBuilder) {
protected abstract void addOneMethodParameter(@NotNull String singularName, @NotNull PsiType psiFieldType, @NotNull LombokLightMethodBuilder methodBuilder);

}

protected void addAllMethodParameter(@NotNull String singularName, @NotNull PsiType psiFieldType, @NotNull LombokLightMethodBuilder methodBuilder) {

}
protected abstract void addAllMethodParameter(@NotNull String singularName, @NotNull PsiType psiFieldType, @NotNull LombokLightMethodBuilder methodBuilder);

protected String getOneMethodBody(@NotNull String singularName, @NotNull String psiFieldName, boolean fluentBuilder) {
return "";
}
protected abstract String getOneMethodBody(@NotNull String singularName, @NotNull String psiFieldName, boolean fluentBuilder);

protected String getAllMethodBody(@NotNull String singularName, boolean fluentBuilder) {
return "";
}
protected abstract String getAllMethodBody(@NotNull String singularName, boolean fluentBuilder);

protected String createSingularName(PsiAnnotation singularAnnotation, String psiFieldName) {
String singularName = PsiAnnotationUtil.getStringAnnotationValue(singularAnnotation, "value");
Expand Down
@@ -0,0 +1,18 @@
package de.plushnikov.intellij.plugin.processor.handler.singular;

import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiVariable;
import de.plushnikov.intellij.plugin.processor.field.AccessorsInfo;
import org.jetbrains.annotations.NotNull;

import java.util.List;

public interface BuilderElementHandler {
void addBuilderField(@NotNull List<PsiField> fields, @NotNull PsiVariable psiVariable, @NotNull PsiClass innerClass, @NotNull AccessorsInfo accessorsInfo);

void addBuilderMethod(@NotNull List<PsiMethod> methods, @NotNull PsiVariable psiVariable, @NotNull PsiClass innerClass, boolean fluentBuilder, PsiType returnType, PsiAnnotation singularAnnotation, @NotNull AccessorsInfo accessorsInfo);
}
@@ -0,0 +1,22 @@
package de.plushnikov.intellij.plugin.processor.handler.singular;

import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiVariable;
import de.plushnikov.intellij.plugin.processor.field.AccessorsInfo;
import org.jetbrains.annotations.NotNull;

import java.util.List;

public class EmptyBuilderElementHandler implements BuilderElementHandler {
@Override
public void addBuilderField(@NotNull List<PsiField> fields, @NotNull PsiVariable psiVariable, @NotNull PsiClass innerClass, @NotNull AccessorsInfo accessorsInfo) {
}

@Override
public void addBuilderMethod(@NotNull List<PsiMethod> methods, @NotNull PsiVariable psiVariable, @NotNull PsiClass innerClass, boolean fluentBuilder, PsiType returnType, PsiAnnotation singularAnnotation, @NotNull AccessorsInfo accessorsInfo) {
}
}
@@ -1,6 +1,5 @@
package de.plushnikov.intellij.plugin.processor.handler.singular;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
Expand All @@ -18,24 +17,19 @@
import java.text.MessageFormat;
import java.util.List;

public class NonSingularHandler extends AbstractSingularHandler {
public class NonSingularHandler implements BuilderElementHandler {
public static final String SETTER_PREFIX = "set";

public void addBuilderField(@NotNull List<PsiField> fields, @NotNull PsiVariable psiVariable, @NotNull PsiClass innerClass, @NotNull AccessorsInfo accessorsInfo) {
final String fieldName = accessorsInfo.removePrefix(psiVariable.getName());
final LombokLightFieldBuilder fieldBuilder =
new LombokLightFieldBuilder(psiVariable.getManager(), fieldName, getBuilderFieldType(psiVariable.getType(), psiVariable.getProject()))
new LombokLightFieldBuilder(psiVariable.getManager(), fieldName, psiVariable.getType())
.withModifier(PsiModifier.PRIVATE)
.withNavigationElement(psiVariable)
.withContainingClass(innerClass);
fields.add(fieldBuilder);
}

@NotNull
protected PsiType getBuilderFieldType(@NotNull PsiType psiType, @NotNull Project project) {
return psiType;
}

public void addBuilderMethod(@NotNull List<PsiMethod> methods, @NotNull PsiVariable psiVariable, @NotNull PsiClass innerClass, boolean fluentBuilder, PsiType returnType, PsiAnnotation singularAnnotation, @NotNull AccessorsInfo accessorsInfo) {
final String psiFieldName = accessorsInfo.removePrefix(psiVariable.getName());

Expand All @@ -53,7 +47,7 @@ private String createSetterName(@NotNull String fieldName, boolean isFluent) {
return isFluent ? fieldName : SETTER_PREFIX + StringUtil.capitalize(fieldName);
}

protected String getAllMethodBody(@NotNull String psiFieldName, boolean fluentBuilder) {
private String getAllMethodBody(@NotNull String psiFieldName, boolean fluentBuilder) {
final String codeBlockTemplate = "this.{0} = {0};{1}";
return MessageFormat.format(codeBlockTemplate, psiFieldName, fluentBuilder ? "\nreturn this;" : "");
}
Expand Down
@@ -1,6 +1,5 @@
package de.plushnikov.intellij.plugin.processor.handler.singular;

import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiType;
Expand All @@ -9,8 +8,9 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class SingularHandlerFactory {

Expand All @@ -23,68 +23,61 @@ public class SingularHandlerFactory {
private static final String[] GUAVA_SETS = new String[]{"com.google.common.collect.ImmutableSet", "com.google.common.collect.ImmutableSortedSet"};
private static final String[] GUAVA_MAPS = new String[]{"com.google.common.collect.ImmutableMap", "com.google.common.collect.ImmutableBiMap", "com.google.common.collect.ImmutableSortedMap"};

private static final Map<String, String> COLLECTION_TYPES = new HashMap<String, String>() {{
putAll(toShortNames(JAVA_LANG_ITERABLE, JAVA_UTIL_COLLECTION, JAVA_UTIL_LIST));
putAll(toShortNames(JAVA_SETS));
private static final Set<String> COLLECTION_TYPES = new HashSet<String>() {{
addAll(toSet(JAVA_LANG_ITERABLE, JAVA_UTIL_COLLECTION, JAVA_UTIL_LIST));
addAll(toSet(JAVA_SETS));
}};

private static final Map<String, String> GUAVA_COLLECTION_TYPES = new HashMap<String, String>() {{
putAll(toShortNames(GUAVE_COLLECTIONS));
putAll(toShortNames(GUAVA_SETS));
private static final Set<String> GUAVA_COLLECTION_TYPES = new HashSet<String>() {{
addAll(toSet(GUAVE_COLLECTIONS));
addAll(toSet(GUAVA_SETS));
}};

private static final Map<String, String> MAP_TYPES = new HashMap<String, String>() {{
putAll(toShortNames(JAVA_MAPS));
private static final Set<String> MAP_TYPES = new HashSet<String>() {{
addAll(toSet(JAVA_MAPS));
}};
private static final Map<String, String> GUAVA_MAP_TYPES = new HashMap<String, String>() {{
putAll(toShortNames(GUAVA_MAPS));
private static final Set<String> GUAVA_MAP_TYPES = new HashSet<String>() {{
addAll(toSet(GUAVA_MAPS));
}};
private static final Map<String, String> VALID_SINGULAR_TYPES = new HashMap<String, String>() {{
putAll(COLLECTION_TYPES);
private static final Set<String> VALID_SINGULAR_TYPES = new HashSet<String>() {{
addAll(COLLECTION_TYPES);

putAll(toShortNames(JAVA_MAPS));
putAll(toShortNames(JAVA_SETS));
putAll(toShortNames(GUAVE_COLLECTIONS));
putAll(toShortNames(GUAVA_SETS));
putAll(toShortNames(GUAVA_MAPS));
addAll(toSet(JAVA_MAPS));
addAll(toSet(GUAVE_COLLECTIONS));
addAll(toSet(GUAVA_SETS));
addAll(toSet(GUAVA_MAPS));
}};

private static Map<String, String> toShortNames(String... from) {
final Map<String, String> result = new HashMap<String, String>();
for (String string : from) {
result.put(StringUtil.getShortName(string), string);
result.put(string, string);
}
return result;
private static Set<String> toSet(String... from) {
return new HashSet<String>(Arrays.asList(from));
}

public static boolean isInvalidSingularType(@Nullable String qualifiedName) {
return qualifiedName == null || !VALID_SINGULAR_TYPES.contains(qualifiedName);
}

@NotNull
public static AbstractSingularHandler getHandlerFor(@NotNull PsiVariable psiVariable, @Nullable PsiAnnotation singularAnnotation) {
public static BuilderElementHandler getHandlerFor(@NotNull PsiVariable psiVariable, @Nullable PsiAnnotation singularAnnotation) {
if (null == singularAnnotation) {
return new NonSingularHandler();
}

final PsiType psiType = psiVariable.getType();

final String qualifiedName = PsiTypeUtil.getQualifiedName(psiType);
if (qualifiedName == null || !VALID_SINGULAR_TYPES.containsKey(qualifiedName)) {
//TODO add Error "Lombok does not know how to create the singular-form builder methods for type '" + qualifiedName + "'; they won't be generated."
return new NonSingularHandler();
if (!isInvalidSingularType(qualifiedName)) {
if (COLLECTION_TYPES.contains(qualifiedName)) {
return new SingularCollectionHandler();
}
if (MAP_TYPES.contains(qualifiedName)) {
return new SingularMapHandler();
}
if (GUAVA_COLLECTION_TYPES.contains(qualifiedName)) {
return new SingularGuavaCollectionHandler(qualifiedName, qualifiedName.contains("Sorted"));
}
if (GUAVA_MAP_TYPES.contains(qualifiedName)) {
return new SingularGuavaMapHandler(qualifiedName, qualifiedName.contains("Sorted"));
}
}

if (COLLECTION_TYPES.containsKey(qualifiedName)) {
return new SingularCollectionHandler();
}
if (MAP_TYPES.containsKey(qualifiedName)) {
return new SingularMapHandler();
}
if (GUAVA_COLLECTION_TYPES.containsKey(qualifiedName)) {
return new SingularGuavaCollectionHandler(GUAVA_COLLECTION_TYPES.get(qualifiedName), qualifiedName.contains("Sorted"));
}
if (GUAVA_MAP_TYPES.containsKey(qualifiedName)) {
return new SingularGuavaMapHandler(GUAVA_MAP_TYPES.get(qualifiedName), qualifiedName.contains("Sorted"));
}

return new NonSingularHandler();
return new EmptyBuilderElementHandler();
}
}

0 comments on commit 70a8c71

Please sign in to comment.