Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for builder singular for Guavas ImmutableTable #937

Merged
merged 2 commits into from
Nov 17, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Reinier Zwitserloot <[email protected]>
Robbert Jan Grootjans <[email protected]>
Roel Spilker <[email protected]>
Sander Koning <[email protected]>
Szymon Pacanowski <[email protected]>
Taiki Sugawara <[email protected]>

By adding your name to this list, you grant full and irrevocable copyright and patent indemnity to Project Lombok and all use of Project Lombok, and you certify that you have the right to do so for all commits you add to Project Lombok.
17 changes: 9 additions & 8 deletions src/core/lombok/core/GuavaTypeMap.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2015 The Project Lombok Authors.
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -28,7 +28,7 @@
public final class GuavaTypeMap {
private static final Map<String, String> TYPE_TO_GUAVA_TYPE; static {
Map<String, String> m = new HashMap<String, String>();

m.put("java.util.NavigableSet", "ImmutableSortedSet");
m.put("java.util.NavigableMap", "ImmutableSortedMap");
m.put("java.util.SortedSet", "ImmutableSortedSet");
Expand All @@ -37,22 +37,23 @@ public final class GuavaTypeMap {
m.put("java.util.Map", "ImmutableMap");
m.put("java.util.Collection", "ImmutableList");
m.put("java.util.List", "ImmutableList");

m.put("com.google.common.collect.ImmutableSet", "ImmutableSet");
m.put("com.google.common.collect.ImmutableSortedSet", "ImmutableSortedSet");
m.put("com.google.common.collect.ImmutableMap", "ImmutableMap");
m.put("com.google.common.collect.ImmutableBiMap", "ImmutableBiMap");
m.put("com.google.common.collect.ImmutableSortedMap", "ImmutableSortedMap");
m.put("com.google.common.collect.ImmutableList", "ImmutableList");
m.put("com.google.common.collect.ImmutableCollection", "ImmutableList");

m.put("com.google.common.collect.ImmutableTable", "ImmutableTable");

TYPE_TO_GUAVA_TYPE = Collections.unmodifiableMap(m);
}

public static String getGuavaTypeName(String fqn) {
String target = TYPE_TO_GUAVA_TYPE.get(fqn);
return target != null ? target : "ImmutableList";
}

private GuavaTypeMap() {}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2015 The Project Lombok Authors.
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -28,15 +28,15 @@

@ProviderFor(EclipseSingularizer.class)
public class EclipseGuavaSetListSingularizer extends EclipseGuavaSingularizer {
// TODO com.google.common.collect.ImmutableTable
// TODO com.google.common.collect.ImmutableRangeSet
// TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset
@Override public LombokImmutableList<String> getSupportedTypes() {
return LombokImmutableList.of(
"com.google.common.collect.ImmutableCollection",
"com.google.common.collect.ImmutableList",
"com.google.common.collect.ImmutableSet",
"com.google.common.collect.ImmutableSortedSet");
"com.google.common.collect.ImmutableCollection",
"com.google.common.collect.ImmutableList",
"com.google.common.collect.ImmutableSet",
"com.google.common.collect.ImmutableSortedSet",
"com.google.common.collect.ImmutableTable");
}

@Override protected boolean isMap() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2015 The Project Lombok Authors.
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand Down Expand Up @@ -63,19 +63,22 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
protected static final char[][] JAVA_UTIL_MAP = {
{'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'}
};

protected static final char[][] GUAVA_COLLECT_TABLE = {
{'c', 'o', 'm'}, {'g', 'o', 'o', 'g', 'l', 'e'}, {'c', 'o', 'm', 'm', 'o', 'n'}, {'c', 'o', 'l', 'l', 'e', 'c', 't'}, {'T', 'a', 'b', 'l', 'e'}
};

protected String getSimpleTargetTypeName(SingularData data) {
return GuavaTypeMap.getGuavaTypeName(data.getTargetFqn());
}

protected char[] getBuilderMethodName(SingularData data) {
String simpleTypeName = getSimpleTargetTypeName(data);
if ("ImmutableSortedSet".equals(simpleTypeName) || "ImmutableSortedMap".equals(simpleTypeName)) return "naturalOrder".toCharArray();
return "builder".toCharArray();
}

protected abstract boolean isMap();

protected char[][] makeGuavaTypeName(String simpleName, boolean addBuilder) {
char[][] tokenizedName = new char[addBuilder ? 6 : 5][];
tokenizedName[0] = new char[] {'c', 'o', 'm'};
Expand All @@ -86,12 +89,13 @@ protected char[][] makeGuavaTypeName(String simpleName, boolean addBuilder) {
if (addBuilder) tokenizedName[5] = new char[] { 'B', 'u', 'i', 'l', 'd', 'e', 'r'};
return tokenizedName;
}

@Override public List<EclipseNode> generateFields(SingularData data, EclipseNode builderType) {
char[][] tokenizedName = makeGuavaTypeName(getSimpleTargetTypeName(data), true);
String simpleTypeName = getSimpleTargetTypeName(data);
char[][] tokenizedName = makeGuavaTypeName(simpleTypeName, true);
TypeReference type = new QualifiedTypeReference(tokenizedName, NULL_POSS);
type = addTypeArgs(isMap() ? 2 : 1, false, builderType, type, data.getTypeArgs());
type = addTypeArgs(getTypeArgumentsCount(isMap(), simpleTypeName), false, builderType, type, data.getTypeArgs());

FieldDeclaration buildField = new FieldDeclaration(data.getPluralName(), 0, -1);
buildField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
buildField.modifiers = ClassFileConstants.AccPrivate;
Expand All @@ -100,29 +104,29 @@ protected char[][] makeGuavaTypeName(String simpleName, boolean addBuilder) {
data.setGeneratedByRecursive(buildField);
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}

@Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
generateSingularMethod(returnType, returnStatement, data, builderType, fluent);

returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
}

void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
boolean mapMode = isMap();
char[] keyName = !mapMode ? data.getSingularName() : (new String(data.getSingularName()) + "$key").toCharArray();
char[] valueName = !mapMode ? null : (new String(data.getSingularName()) + "$value").toCharArray();

MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;

List<Statement> statements = new ArrayList<Statement>();
statements.add(createConstructBuilderVarIfNeeded(data, builderType));

FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
thisDotField.receiver = new ThisReference(0, 0);
MessageSend thisDotFieldDotAdd = new MessageSend();
Expand All @@ -134,80 +138,99 @@ void generateSingularMethod(TypeReference returnType, Statement returnStatement,
thisDotFieldDotAdd.arguments = new Expression[] {new SingleNameReference(keyName, 0L)};
}
thisDotFieldDotAdd.receiver = thisDotField;
thisDotFieldDotAdd.selector = (mapMode ? "put" : "add").toCharArray();
thisDotFieldDotAdd.selector = (shouldUsePut(data, mapMode) ? "put" : "add").toCharArray();
statements.add(thisDotFieldDotAdd);
if (returnStatement != null) statements.add(returnStatement);
md.statements = statements.toArray(new Statement[statements.size()]);

if (mapMode) {
TypeReference keyType = cloneParamType(0, data.getTypeArgs(), builderType);
Argument keyParam = new Argument(keyName, 0, keyType, 0);
TypeReference valueType = cloneParamType(1, data.getTypeArgs(), builderType);
Argument valueParam = new Argument(valueName, 0, valueType, 0);
md.arguments = new Argument[] {keyParam, valueParam};
} else {
TypeReference paramType = cloneParamType(0, data.getTypeArgs(), builderType);
Argument param = new Argument(keyName, 0, paramType, 0);
final Argument param;

if (isSpecialTypeOfListSet(data)) {
char[][] cellTypeName = makeGuavaTypeName("Table", true);
cellTypeName[5] = new char[] { 'C', 'e', 'l', 'l' };
TypeReference type = new QualifiedTypeReference(cellTypeName, NULL_POSS);
type = addTypeArgs(3, false, builderType, type, data.getTypeArgs());

param = new Argument(keyName, 0, type, 0);
} else {
TypeReference paramType = cloneParamType(0, data.getTypeArgs(), builderType);
param = new Argument(keyName, 0, paramType, 0);
}

md.arguments = new Argument[] {param};
}
md.returnType = returnType;
md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(mapMode ? "put" : "add", new String(data.getSingularName())).toCharArray();
md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(shouldUsePut(data, mapMode) ? "put" : "add", new String(data.getSingularName())).toCharArray();

data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}

void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
boolean mapMode = isMap();

MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
md.modifiers = ClassFileConstants.AccPublic;

List<Statement> statements = new ArrayList<Statement>();
statements.add(createConstructBuilderVarIfNeeded(data, builderType));

FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
thisDotField.receiver = new ThisReference(0, 0);
MessageSend thisDotFieldDotAddAll = new MessageSend();
thisDotFieldDotAddAll.arguments = new Expression[] {new SingleNameReference(data.getPluralName(), 0L)};
thisDotFieldDotAddAll.receiver = thisDotField;
thisDotFieldDotAddAll.selector = (mapMode ? "putAll" : "addAll").toCharArray();
thisDotFieldDotAddAll.selector = (shouldUsePut(data, mapMode) ? "putAll" : "addAll").toCharArray();
statements.add(thisDotFieldDotAddAll);
if (returnStatement != null) statements.add(returnStatement);

md.statements = statements.toArray(new Statement[statements.size()]);

TypeReference paramType;
if (mapMode) {
paramType = new QualifiedTypeReference(JAVA_UTIL_MAP, NULL_POSS);
paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs());
} else {
paramType = new QualifiedTypeReference(TypeConstants.JAVA_LANG_ITERABLE, NULL_POSS);
paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs());
if (isSpecialTypeOfListSet(data)) {
paramType = new QualifiedTypeReference(GUAVA_COLLECT_TABLE, NULL_POSS);
} else {
paramType = new QualifiedTypeReference(TypeConstants.JAVA_LANG_ITERABLE, NULL_POSS);
}

paramType = addTypeArgs(getListSetTypeArgumentsCount(getSimpleTargetTypeName(data)), true, builderType, paramType, data.getTypeArgs());
}
Argument param = new Argument(data.getPluralName(), 0, paramType, 0);
md.arguments = new Argument[] {param};
md.returnType = returnType;
md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(mapMode ? "putAll" : "addAll", new String(data.getPluralName())).toCharArray();
md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(shouldUsePut(data, mapMode) ? "putAll" : "addAll", new String(data.getPluralName())).toCharArray();

data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}

@Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
boolean mapMode = isMap();
TypeReference varType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
varType = addTypeArgs(mapMode ? 2 : 1, false, builderType, varType, data.getTypeArgs());

String simpleTypeName = getSimpleTargetTypeName(data);
int agrumentsCount = getTypeArgumentsCount(mapMode, simpleTypeName);
varType = addTypeArgs(agrumentsCount, false, builderType, varType, data.getTypeArgs());

MessageSend emptyInvoke; {
//ImmutableX.of()
emptyInvoke = new MessageSend();
emptyInvoke.selector = new char[] {'o', 'f'};
emptyInvoke.receiver = new QualifiedNameReference(makeGuavaTypeName(getSimpleTargetTypeName(data), false), NULL_POSS, 0, 0);
emptyInvoke.typeArguments = createTypeArgs(mapMode ? 2 : 1, false, builderType, data.getTypeArgs());
emptyInvoke.receiver = new QualifiedNameReference(makeGuavaTypeName(simpleTypeName, false), NULL_POSS, 0, 0);
emptyInvoke.typeArguments = createTypeArgs(agrumentsCount, false, builderType, data.getTypeArgs());
}

MessageSend invokeBuild; {
//this.pluralName.build();
invokeBuild = new MessageSend();
Expand All @@ -216,32 +239,48 @@ void generatePluralMethod(TypeReference returnType, Statement returnStatement, S
thisDotField.receiver = new ThisReference(0, 0);
invokeBuild.receiver = thisDotField;
}

Expression isNull; {
//this.pluralName == null
FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
thisDotField.receiver = new ThisReference(0, 0);
isNull = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
}

Expression init = new ConditionalExpression(isNull, emptyInvoke, invokeBuild);
LocalDeclaration varDefStat = new LocalDeclaration(data.getPluralName(), 0, 0);
varDefStat.type = varType;
varDefStat.initialization = init;
statements.add(varDefStat);
}

protected Statement createConstructBuilderVarIfNeeded(SingularData data, EclipseNode builderType) {
FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
thisDotField.receiver = new ThisReference(0, 0);
FieldReference thisDotField2 = new FieldReference(data.getPluralName(), 0L);
thisDotField2.receiver = new ThisReference(0, 0);
Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);

MessageSend createBuilderInvoke = new MessageSend();
char[][] tokenizedName = makeGuavaTypeName(getSimpleTargetTypeName(data), false);
createBuilderInvoke.receiver = new QualifiedNameReference(tokenizedName, NULL_POSS, 0, 0);
createBuilderInvoke.selector = getBuilderMethodName(data);
return new IfStatement(cond, new Assignment(thisDotField2, createBuilderInvoke, 0), 0, 0);
}

private int getTypeArgumentsCount(boolean isMap, String simpleTypeName) {
return isMap ? 2 : getListSetTypeArgumentsCount(simpleTypeName);
}

private int getListSetTypeArgumentsCount(String simpleTypeName) {
return "ImmutableTable".equals(simpleTypeName) ? 3 : 1;
}

private boolean shouldUsePut(SingularData data, boolean mapMode) {
return mapMode || isSpecialTypeOfListSet(data);
}

private boolean isSpecialTypeOfListSet(SingularData data) {
return "ImmutableTable".equals(getSimpleTargetTypeName(data));
}
}
Loading