/* * 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 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package lombok.eclipse.handlers.singulars; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.BinaryExpression; import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.BreakStatement; import org.eclipse.jdt.internal.compiler.ast.CaseStatement; import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.ForStatement; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.IntLiteral; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.PostfixExpression; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import lombok.ConfigurationKeys; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { protected static final char[][] JAVA_UTIL_ARRAYLIST = { {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'A', 'r', 'r', 'a', 'y', 'L', 'i', 's', 't'} }; protected static final char[][] JAVA_UTIL_LIST = { {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'L', 'i', 's', 't'} }; protected static final char[][] JAVA_UTIL_MAP = { {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'} }; protected static final char[][] JAVA_UTIL_MAP_ENTRY = { {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'}, {'E', 'n', 't', 'r', 'y'} }; protected static final char[][] JAVA_UTIL_COLLECTIONS = { {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'C', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', 's'} }; protected final EclipseSingularizer guavaListSetSingularizer = new EclipseGuavaSetListSingularizer(); protected final EclipseSingularizer guavaMapSingularizer = new EclipseGuavaMapSingularizer(); protected boolean useGuavaInstead(EclipseNode node) { return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA)); } protected List<Statement> createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType) { List<Statement> switchContents = new ArrayList<Statement>(); char[] keyName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName(); if (emptyCollectionMethod != null) { // case 0: (empty); break; switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'0'}, null), 0, 0)); /* pluralName = java.util.Collections.emptyCollectionMethod(); */ { MessageSend invoke = new MessageSend(); invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0); invoke.selector = emptyCollectionMethod.toCharArray(); switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0)); } switchContents.add(new BreakStatement(null, 0, 0)); } if (singletonCollectionMethod != null) { // case 1: (singleton); break; switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'1'}, null), 0, 0)); /* !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0)); mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0)); */ { FieldReference thisDotKey = new FieldReference(keyName, 0L); thisDotKey.receiver = new ThisReference(0, 0); MessageSend thisDotKeyGet0 = new MessageSend(); thisDotKeyGet0.receiver = thisDotKey; thisDotKeyGet0.selector = new char[] {'g', 'e', 't'}; thisDotKeyGet0.arguments = new Expression[] {makeIntLiteral(new char[] {'0'}, null)}; Expression[] args; if (mapMode) { char[] valueName = (new String(data.getPluralName()) + "$value").toCharArray(); FieldReference thisDotValue = new FieldReference(valueName, 0L); thisDotValue.receiver = new ThisReference(0, 0); MessageSend thisDotValueGet0 = new MessageSend(); thisDotValueGet0.receiver = thisDotValue; thisDotValueGet0.selector = new char[] {'g', 'e', 't'}; thisDotValueGet0.arguments = new Expression[] {makeIntLiteral(new char[] {'0'}, null)}; args = new Expression[] {thisDotKeyGet0, thisDotValueGet0}; } else { args = new Expression[] {thisDotKeyGet0}; } MessageSend invoke = new MessageSend(); invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0); invoke.selector = singletonCollectionMethod.toCharArray(); invoke.arguments = args; switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0)); } switchContents.add(new BreakStatement(null, 0, 0)); } { // default: switchContents.add(new CaseStatement(null, 0, 0)); switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType)); } SwitchStatement switchStat = new SwitchStatement(); switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]); switchStat.expression = getSize(builderType, keyName, true); TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS); localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs()); LocalDeclaration varDefStat = new LocalDeclaration(data.getPluralName(), 0, 0); varDefStat.type = localShadowerType; return Arrays.asList(varDefStat, switchStat); } protected List<Statement> createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType) { char[] varName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName(); Statement createStat; { // pluralName = new java.util.TargetType(initialCap); Expression[] constructorArgs = null; if (addInitialCapacityArg) { // this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE; // lessThanCutOff = this.varName.size() < MAX_POWER_OF_2 Expression lessThanCutoff = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral("0x40000000".toCharArray(), null), OperatorIds.LESS); FieldReference integerMaxValue = new FieldReference("MAX_VALUE".toCharArray(), 0L); integerMaxValue.receiver = new QualifiedNameReference(TypeConstants.JAVA_LANG_INTEGER, NULL_POSS, 0, 0); Expression sizeFormulaLeft = new BinaryExpression(makeIntLiteral(new char[] {'1'}, null), getSize(builderType, varName, nullGuard), OperatorIds.PLUS); Expression sizeFormulaRightLeft = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral(new char[] {'3'}, null), OperatorIds.MINUS); Expression sizeFormulaRight = new BinaryExpression(sizeFormulaRightLeft, makeIntLiteral(new char[] {'3'}, null), OperatorIds.DIVIDE); Expression sizeFormula = new BinaryExpression(sizeFormulaLeft, sizeFormulaRight, OperatorIds.PLUS); Expression cond = new ConditionalExpression(lessThanCutoff, sizeFormula, integerMaxValue); constructorArgs = new Expression[] {cond}; } TypeReference targetTypeRef = new QualifiedTypeReference(new char[][] {TypeConstants.JAVA, TypeConstants.UTIL, targetType.toCharArray()}, NULL_POSS); targetTypeRef = addTypeArgs(mapMode ? 2 : 1, false, builderType, targetTypeRef, data.getTypeArgs()); AllocationExpression constructorCall = new AllocationExpression(); constructorCall.type = targetTypeRef; constructorCall.arguments = constructorArgs; if (defineVar) { TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS); localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs()); LocalDeclaration localShadowerDecl = new LocalDeclaration(data.getPluralName(), 0, 0); localShadowerDecl.type = localShadowerType; localShadowerDecl.initialization = constructorCall; createStat = localShadowerDecl; } else { createStat = new Assignment(new SingleNameReference(data.getPluralName(), 0L), constructorCall, 0); } } Statement fillStat; { if (mapMode) { // for (int $i = 0; $i < this.pluralname$key.size(); i++) pluralname.put(this.pluralname$key.get($i), this.pluralname$value.get($i)); char[] iVar = new char[] {'$', 'i'}; MessageSend pluralnameDotPut = new MessageSend(); pluralnameDotPut.selector = new char[] {'p', 'u', 't'}; pluralnameDotPut.receiver = new SingleNameReference(data.getPluralName(), 0L); FieldReference thisDotKey = new FieldReference(varName, 0L); thisDotKey.receiver = new ThisReference(0, 0); FieldReference thisDotValue = new FieldReference((new String(data.getPluralName()) + "$value").toCharArray(), 0L); thisDotValue.receiver = new ThisReference(0, 0); MessageSend keyArg = new MessageSend(); keyArg.receiver = thisDotKey; keyArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)}; keyArg.selector = new char[] {'g', 'e', 't'}; MessageSend valueArg = new MessageSend(); valueArg.receiver = thisDotValue; valueArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)}; valueArg.selector = new char[] {'g', 'e', 't'}; pluralnameDotPut.arguments = new Expression[] {keyArg, valueArg}; LocalDeclaration forInit = new LocalDeclaration(iVar, 0, 0); forInit.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); forInit.initialization = makeIntLiteral(new char[] {'0'}, null); Expression checkExpr = new BinaryExpression(new SingleNameReference(iVar, 0L), getSize(builderType, varName, nullGuard), OperatorIds.LESS); Expression incrementExpr = new PostfixExpression(new SingleNameReference(iVar, 0L), IntLiteral.One, OperatorIds.PLUS, 0); fillStat = new ForStatement(new Statement[] {forInit}, checkExpr, new Statement[] {incrementExpr}, pluralnameDotPut, true, 0, 0); } else { // pluralname.addAll(this.pluralname); MessageSend pluralnameDotAddAll = new MessageSend(); pluralnameDotAddAll.selector = new char[] {'a', 'd', 'd', 'A', 'l', 'l'}; pluralnameDotAddAll.receiver = new SingleNameReference(data.getPluralName(), 0L); FieldReference thisDotPluralname = new FieldReference(varName, 0L); thisDotPluralname.receiver = new ThisReference(0, 0); pluralnameDotAddAll.arguments = new Expression[] {thisDotPluralname}; fillStat = pluralnameDotAddAll; } if (nullGuard) { FieldReference thisDotField = new FieldReference(varName, 0L); thisDotField.receiver = new ThisReference(0, 0); Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL); fillStat = new IfStatement(cond, fillStat, 0, 0); } } Statement unmodifiableStat; { // pluralname = Collections.unmodifiableInterfaceType(pluralname); Expression arg = new SingleNameReference(data.getPluralName(), 0L); MessageSend invoke = new MessageSend(); invoke.arguments = new Expression[] {arg}; invoke.selector = ("unmodifiable" + data.getTargetSimpleType()).toCharArray(); invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0); unmodifiableStat = new Assignment(new SingleNameReference(data.getPluralName(), 0L), invoke, 0); } return Arrays.asList(createStat, fillStat, unmodifiableStat); } protected Statement createConstructBuilderVarIfNeeded(SingularData data, EclipseNode builderType, boolean mapMode) { char[] v1Name, v2Name; if (mapMode) { String n = new String(data.getPluralName()); v1Name = (n + "$key").toCharArray(); v2Name = (n + "$value").toCharArray(); } else { v1Name = data.getPluralName(); v2Name = null; } FieldReference thisDotField = new FieldReference(v1Name, 0L); thisDotField.receiver = new ThisReference(0, 0); Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); thisDotField = new FieldReference(v1Name, 0L); thisDotField.receiver = new ThisReference(0, 0); TypeReference v1Type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS); v1Type = addTypeArgs(1, false, builderType, v1Type, data.getTypeArgs()); AllocationExpression constructArrayList = new AllocationExpression(); constructArrayList.type = v1Type; Assignment initV1 = new Assignment(thisDotField, constructArrayList, 0); Statement thenPart; if (mapMode) { thisDotField = new FieldReference(v2Name, 0L); thisDotField.receiver = new ThisReference(0, 0); TypeReference v2Type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS); List<TypeReference> tArgs = data.getTypeArgs(); if (tArgs != null && tArgs.size() > 1) tArgs = Collections.singletonList(tArgs.get(1)); else tArgs = Collections.emptyList(); v2Type = addTypeArgs(1, false, builderType, v2Type, tArgs); constructArrayList = new AllocationExpression(); constructArrayList.type = v2Type; Assignment initV2 = new Assignment(thisDotField, constructArrayList, 0); Block b = new Block(0); b.statements = new Statement[] {initV1, initV2}; thenPart = b; } else { thenPart = initV1; } return new IfStatement(cond, thenPart, 0, 0); } }