diff --git a/src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java b/src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java index f89d45ed4b..e3de461406 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java @@ -18,7 +18,6 @@ import lombok.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; -import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavadocVisitor; import org.openrewrite.java.migrate.joda.templates.AllTemplates; import org.openrewrite.java.migrate.joda.templates.MethodTemplate; @@ -30,18 +29,14 @@ import java.util.LinkedList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; class JodaTimeVisitor extends ScopeAwareVisitor { - private final boolean safeMigration; - private final JodaTimeRecipe.Accumulator acc; - - public JodaTimeVisitor(JodaTimeRecipe.Accumulator acc, boolean safeMigration, LinkedList scopes) { + public JodaTimeVisitor(JodaTimeRecipe.Accumulator acc, boolean b, LinkedList scopes) { super(scopes); - this.acc = acc; - this.safeMigration = safeMigration; } @Override @@ -65,10 +60,25 @@ public Javadoc visitReference(Javadoc.Reference reference, ExecutionContext ctx) maybeRemoveImport(JODA_DATE_TIME_ZONE); maybeRemoveImport(JODA_TIME_FORMAT); maybeRemoveImport(JODA_DURATION); + maybeRemoveImport(JODA_PERIOD); maybeRemoveImport(JODA_ABSTRACT_INSTANT); maybeRemoveImport(JODA_INSTANT); maybeRemoveImport(JODA_INTERVAL); - maybeRemoveImport("java.util.Locale"); + maybeRemoveImport(JODA_TIME_FORMATTER); + maybeRemoveImport(JAVA_UTIL_LOCALE); + maybeRemoveImport(JODA_LOCAL_DATE_TIME); + maybeRemoveImport(JODA_LOCAL_DATE); + maybeRemoveImport(JODA_LOCAL_TIME); + maybeRemoveImport(JODA_SECONDS); + maybeRemoveImport(JODA_MINUTES); + maybeRemoveImport(JODA_HOURS); + maybeRemoveImport(JODA_DAYS); + maybeRemoveImport(JODA_WEEKS); + maybeRemoveImport(JODA_MONTHS); + maybeRemoveImport(JODA_YEARS); + maybeRemoveImport(JODA_DATE_TIME_UTILS); + maybeRemoveImport(JODA_DATE_MIDNIGHT); + maybeRemoveImport(JODA_GEORGIAN_CHRONOLOGY); maybeAddImport(JAVA_DATE_TIME); maybeAddImport(JAVA_ZONE_OFFSET); @@ -77,13 +87,22 @@ public Javadoc visitReference(Javadoc.Reference reference, ExecutionContext ctx) maybeAddImport(JAVA_TIME_FORMATTER); maybeAddImport(JAVA_TIME_FORMAT_STYLE); maybeAddImport(JAVA_DURATION); + maybeAddImport(JAVA_PERIOD); maybeAddImport(JAVA_LOCAL_DATE); + maybeAddImport(JAVA_LOCAL_DATE_TIME); maybeAddImport(JAVA_LOCAL_TIME); maybeAddImport(JAVA_TEMPORAL_ISO_FIELDS); maybeAddImport(JAVA_CHRONO_FIELD); + maybeAddImport(JAVA_CHRONO_UNIT); maybeAddImport(JAVA_UTIL_DATE); + maybeAddImport(JAVA_ISA_CHRONOLOGY); maybeAddImport(THREE_TEN_EXTRA_INTERVAL); + maybeAddImport(THREE_TEN_EXTRA_DAYS); + maybeAddImport(THREE_TEN_EXTRA_WEEKS); + maybeAddImport(THREE_TEN_EXTRA_MONTHS); + maybeAddImport(THREE_TEN_EXTRA_YEARS); } + return j; } @@ -93,43 +112,64 @@ public Javadoc visitReference(Javadoc.Reference reference, ExecutionContext ctx) if (m.getReturnTypeExpression() == null || !m.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { return m; } - if (safeMigration && !acc.getSafeMethodMap().getOrDefault(m.getMethodType(), false)) { - return m; - } JavaType.Class returnType = TimeClassMap.getJavaTimeType(((JavaType.Class) m.getType()).getFullyQualifiedName()); J.Identifier returnExpr = TypeTree.build(returnType.getClassName()).withType(returnType).withPrefix(Space.format(" ")); - return m.withReturnTypeExpression(returnExpr) - .withMethodType(m.getMethodType().withReturnType(returnType)); + + JavaType.Method methodType = m.getMethodType() + .withReturnType(returnType) + .withParameterTypes(m.getMethodType().getParameterTypes().stream().map(p -> visitTypeParameter(p, ctx)).collect(Collectors.toList())); + + return m + .withReturnTypeExpression(returnExpr) + .withMethodType(methodType); + } + JavaType visitTypeParameter(JavaType parameter, @NonNull ExecutionContext ctx){ + if (parameter instanceof JavaType.Class) {return visitClassTypeParameter((JavaType.Class)parameter, ctx);} + if (parameter instanceof JavaType.Array) {return ((JavaType.Array)parameter).withElemType(visitClassTypeParameter((JavaType.Class)((JavaType.Array) parameter).getElemType(), ctx));} + return parameter; + } + JavaType visitClassTypeParameter(JavaType.Class classParameter, @NonNull ExecutionContext ctx) { + if (!classParameter.isAssignableFrom(JODA_CLASS_PATTERN)) { + return classParameter; + } + return TimeClassMap.getJavaTimeType(classParameter.getFullyQualifiedName()); } @Override public @NonNull J visitVariableDeclarations(@NonNull J.VariableDeclarations multiVariable, @NonNull ExecutionContext ctx) { - if (multiVariable.getTypeExpression() == null || !multiVariable.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { - return super.visitVariableDeclarations(multiVariable, ctx); + J.VariableDeclarations mv = (J.VariableDeclarations) super.visitVariableDeclarations(multiVariable, ctx); + + if (!mv.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { + return mv; } - if (multiVariable.getVariables().stream().anyMatch(acc.getUnsafeVars()::contains)) { - return multiVariable; + + String fullyQualifiedName = ((JavaType.Class) mv.getType()).getFullyQualifiedName(); + JavaType.Class javaTimeType = TimeClassMap.getJavaTimeType(fullyQualifiedName); + String javaTimeShortName = TimeClassMap.getJavaTimeShortName(fullyQualifiedName); + + J.Identifier typeExpression = (J.Identifier) mv.getTypeExpression(); + + if(javaTimeShortName != null){ + typeExpression = typeExpression.withSimpleName(javaTimeShortName); } - J.VariableDeclarations m = (J.VariableDeclarations) super.visitVariableDeclarations(multiVariable, ctx); - return VarTemplates.getTemplate(multiVariable).map(t -> t.apply( - updateCursor(m), - m.getCoordinates().replace(), - VarTemplates.getTemplateArgs(m))).orElse(multiVariable); + + return autoFormat(mv.withTypeExpression(typeExpression.withType(javaTimeType)), ctx); } @Override public @NonNull J visitVariable(@NonNull J.VariableDeclarations.NamedVariable variable, @NonNull ExecutionContext ctx) { - if (!variable.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { - return super.visitVariable(variable, ctx); + J.VariableDeclarations.NamedVariable v = (J.VariableDeclarations.NamedVariable) super.visitVariable(variable, ctx); + if (v.getType() instanceof JavaType.Array && ((JavaType.Array)v.getType()).getElemType().isAssignableFrom(JODA_CLASS_PATTERN)) { + JavaType.Array type = (JavaType.Array) v.getType(); + JavaType.Class elemType = (JavaType.Class) type.getElemType(); + return v.withType(type.withElemType(TimeClassMap.getJavaTimeType(elemType.getFullyQualifiedName()))); } - if (acc.getUnsafeVars().contains(variable) || !(variable.getType() instanceof JavaType.Class)) { - return variable; + if (v.getType() instanceof JavaType.Class && v.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { + return v.withType(TimeClassMap.getJavaTimeType(((JavaType.Class) variable.getType()).getFullyQualifiedName())); } - JavaType.Class jodaType = (JavaType.Class) variable.getType(); - return variable - .withType(TimeClassMap.getJavaTimeType(jodaType.getFullyQualifiedName())) - .withInitializer((Expression) visit(variable.getInitializer(), ctx)); + + return v; } @Override @@ -143,7 +183,7 @@ public Javadoc visitReference(Javadoc.Reference reference, ExecutionContext ctx) } J.Identifier varName = (J.Identifier) a.getVariable(); Optional mayBeVar = findVarInScope(varName.getSimpleName()); - if (!mayBeVar.isPresent() || acc.getUnsafeVars().contains(mayBeVar.get())) { + if (!mayBeVar.isPresent()) { return assignment; } return VarTemplates.getTemplate(assignment).map(t -> t.apply( @@ -183,34 +223,50 @@ public Javadoc visitReference(Javadoc.Reference reference, ExecutionContext ctx) @Override public @NonNull J visitFieldAccess(@NonNull J.FieldAccess fieldAccess, @NonNull ExecutionContext ctx) { J.FieldAccess f = (J.FieldAccess) super.visitFieldAccess(fieldAccess, ctx); - if (TypeUtils.isOfClassType(f.getType(), JODA_DATE_TIME_ZONE) && "UTC".equals(f.getSimpleName())) { - return JavaTemplate.builder("ZoneOffset.UTC") - .imports(JAVA_ZONE_OFFSET) - .build() - .apply(updateCursor(f), f.getCoordinates().replace()); + if (!isJodaVarRef(fieldAccess)) { + return f; } - return f; + + JavaType.Class fieldType = (JavaType.Class) f.getType(); + JavaType.Class javaTimeType = TimeClassMap.getJavaTimeType(fieldType.getFullyQualifiedName()); + + return f.withType(javaTimeType); } @Override public @NonNull J visitIdentifier(@NonNull J.Identifier ident, @NonNull ExecutionContext ctx) { - if (!isJodaVarRef(ident)) { - return super.visitIdentifier(ident, ctx); - } - if (this.safeMigration) { - Optional mayBeVar = findVarInScope(ident.getSimpleName()); - if (!mayBeVar.isPresent() || acc.getUnsafeVars().contains(mayBeVar.get())) { - return ident; - } - } + J.Identifier i = (J.Identifier) super.visitIdentifier(ident, ctx); + //i.getType().isAssignableFrom(JODA_CLASS_PATTERN) + if (!isJodaVarRef(i)) { + return i; + } + JavaType type = i.getType(); + if(type instanceof JavaType.Array) {return visitArrayIdentifier(i, ctx);} + if(type instanceof JavaType.Class) {return visitClassIdentifier(i, ctx);} + + return i; + } + private @NonNull J visitArrayIdentifier(J.Identifier arrayIdentifier, @NonNull ExecutionContext ctx) { + JavaType.Array at = (JavaType.Array)arrayIdentifier.getType(); + JavaType.Array javaTimeType = at.withElemType(TimeClassMap.getJavaTimeType(((JavaType.Class)at.getElemType()).getFullyQualifiedName())); + + return arrayIdentifier.withType(javaTimeType) + .withFieldType(arrayIdentifier.getFieldType().withType(javaTimeType)); + } + private @NonNull J visitClassIdentifier(J.Identifier classIdentifier, @NonNull ExecutionContext ctx) { + JavaType.Class javaTimeType = TimeClassMap.getJavaTimeType(((JavaType.Class)classIdentifier.getType()).getFullyQualifiedName()); + + return classIdentifier.withType(javaTimeType) + .withFieldType(classIdentifier.getFieldType().withType(javaTimeType)); + } - JavaType.FullyQualified jodaType = ((JavaType.Class) ident.getType()); - JavaType.FullyQualified fqType = TimeClassMap.getJavaTimeType(jodaType.getFullyQualifiedName()); - if (fqType == null) { - return ident; + @Override + public J visitArrayAccess(J.ArrayAccess arrayAccess, @NonNull ExecutionContext ctx){ + J.ArrayAccess a = (J.ArrayAccess) super.visitArrayAccess(arrayAccess, ctx); + if (!a.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { + return a; } - return ident.withType(fqType) - .withFieldType(ident.getFieldType().withType(fqType)); + return a.withType(TimeClassMap.getJavaTimeType(((JavaType.Class)a.getType()).getFullyQualifiedName())); } private J migrateMethodCall(MethodCall original, MethodCall updated) { @@ -219,38 +275,50 @@ private J migrateMethodCall(MethodCall original, MethodCall updated) { } MethodTemplate template = AllTemplates.getTemplate(original); if (template == null) { + System.out.println("Joda usage is found but mapping is missing: " + original); return original; // unhandled case } + if (template.getTemplate().getCode().equals(JODA_MULTIPLE_MAPPING_POSSIBLE)) { + System.out.println(JODA_MULTIPLE_MAPPING_POSSIBLE + ": " + original); + return original; // usage with no automated mapping + } + if (template.getTemplate().getCode().equals(JODA_NO_AUTOMATIC_MAPPING_POSSIBLE)) { + System.out.println(JODA_NO_AUTOMATIC_MAPPING_POSSIBLE + ": " + original); + return original; // usage with no automated mapping + } Optional maybeUpdated = applyTemplate(original, updated, template); if (!maybeUpdated.isPresent()) { + System.out.println("Can not apply template: " + template + " to " + original); return original; // unhandled case } Expression updatedExpr = (Expression) maybeUpdated.get(); - if (!safeMigration || !isArgument(original)) { + if (!isArgument(original)) { return updatedExpr; } // this expression is an argument to a method call MethodCall parentMethod = getCursor().getParentTreeCursor().getValue(); - if (parentMethod.getMethodType().getDeclaringType().isAssignableFrom(JODA_CLASS_PATTERN)) { + JavaType.Method parentMethodType = parentMethod.getMethodType(); + if (parentMethodType.getDeclaringType().isAssignableFrom(JODA_CLASS_PATTERN)) { return updatedExpr; } int argPos = parentMethod.getArguments().indexOf(original); - JavaType paramType = parentMethod.getMethodType().getParameterTypes().get(argPos); - if (TypeUtils.isAssignableTo(paramType, updatedExpr.getType())) { - return updatedExpr; - } - String paramName = parentMethod.getMethodType().getParameterNames().get(argPos); - NamedVariable var = acc.getVarTable().getVarByName(parentMethod.getMethodType(), paramName); - if (var != null && !acc.getUnsafeVars().contains(var)) { - return updatedExpr; + List parameterTypes = parentMethodType.getParameterTypes(); + int parameterTypesSize = parameterTypes.size(); + + //try to process method with variable arguments + if(argPos > parameterTypesSize) + { + //todo find better way to detect (...) in method arguments + if (parameterTypes.get(parameterTypesSize - 1).toString().endsWith("[]")){ + return updatedExpr; + } + return original; } - return original; + + return updatedExpr; } private J.MethodInvocation migrateNonJodaMethod(J.MethodInvocation original, J.MethodInvocation updated) { - if (safeMigration && !acc.getSafeMethodMap().getOrDefault(updated.getMethodType(), false)) { - return original; - } JavaType.Class returnType = (JavaType.Class) updated.getMethodType().getReturnType(); JavaType.Class updatedReturnType = TimeClassMap.getJavaTimeType(returnType.getFullyQualifiedName()); if (updatedReturnType == null) { @@ -282,7 +350,18 @@ private Optional applyTemplate(MethodCall original, MethodCall updated, Metho } private boolean isJodaVarRef(@Nullable Expression expr) { - if (expr == null || expr.getType() == null || !expr.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { + if (expr == null) { + return false; + } + JavaType type = expr.getType(); + if (type == null) { + return false; + } + if(type instanceof JavaType.Array){ + type = ((JavaType.Array) type).getElemType(); + } + + if (!type.isAssignableFrom(JODA_CLASS_PATTERN )) { return false; } if (expr instanceof J.FieldAccess) { diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/TimeClassNames.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/TimeClassNames.java index 4cb88c0554..688e0ba6af 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/TimeClassNames.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/TimeClassNames.java @@ -15,29 +15,43 @@ */ package org.openrewrite.java.migrate.joda.templates; +import org.openrewrite.java.JavaTemplate; + import java.util.regex.Pattern; public class TimeClassNames { public static final Pattern JODA_CLASS_PATTERN = Pattern.compile("org\\.joda\\.time\\..*"); + public static final String JODA_MULTIPLE_MAPPING_POSSIBLE = "Multiple mapping is possible.Update manually"; + public static final String JODA_NO_AUTOMATIC_MAPPING_POSSIBLE = "Not possible to migrate with recipe.Update manually"; + public static final JavaTemplate JODA_MULTIPLE_MAPPING_POSSIBLE_TEMPLATE = JavaTemplate.builder(JODA_MULTIPLE_MAPPING_POSSIBLE).build(); + public static final JavaTemplate JODA_NO_AUTOMATIC_MAPPING_POSSIBLE_TEMPLATE = JavaTemplate.builder(JODA_NO_AUTOMATIC_MAPPING_POSSIBLE).build(); // java util public static final String JAVA_UTIL_DATE = "java.util.Date"; + public static final String JAVA_UTIL_LOCALE = "java.util.Locale"; // Joda-Time classes public static final String JODA_TIME_PKG = "org.joda.time"; public static final String JODA_ABSTRACT_DATE_TIME = JODA_TIME_PKG + ".base.AbstractDateTime"; public static final String JODA_ABSTRACT_DURATION = JODA_TIME_PKG + ".base.AbstractDuration"; public static final String JODA_ABSTRACT_INTERVAL = JODA_TIME_PKG + ".base.AbstractInterval"; + public static final String JODA_ABSTRACT_PARTIAL = JODA_TIME_PKG + ".base.AbstractPartial"; public static final String JODA_BASE_DATE_TIME = JODA_TIME_PKG + ".base.BaseDateTime"; public static final String JODA_DATE_TIME = JODA_TIME_PKG + ".DateTime"; + public static final String JODA_DATE_MIDNIGHT = JODA_TIME_PKG + ".DateMidnight"; public static final String JODA_DATE_TIME_ZONE = JODA_TIME_PKG + ".DateTimeZone"; public static final String JODA_TIME_FORMAT = JODA_TIME_PKG + ".format.DateTimeFormat"; public static final String JODA_TIME_FORMATTER = JODA_TIME_PKG + ".format.DateTimeFormatter"; public static final String JODA_LOCAL_DATE = JODA_TIME_PKG + ".LocalDate"; + public static final String JODA_LOCAL_DATE_PROPERTY = JODA_TIME_PKG + ".LocalDate.Property"; public static final String JODA_LOCAL_TIME = JODA_TIME_PKG + ".LocalTime"; + public static final String JODA_LOCAL_DATE_TIME = JODA_TIME_PKG + ".LocalDateTime"; public static final String JODA_DATE_TIME_FIELD_TYPE = JODA_TIME_PKG + ".DateTimeFieldType"; public static final String JODA_DURATION_FIELD_TYPE = JODA_TIME_PKG + ".DurationFieldType"; public static final String JODA_DURATION = JODA_TIME_PKG + ".Duration"; + public static final String JODA_ABSTRACT_PERIOD = JODA_TIME_PKG + ".base.AbstractPeriod"; + public static final String JODA_PERIOD = JODA_TIME_PKG + ".Period"; + public static final String JODA_PERIOD_TYPE = JODA_TIME_PKG + "PeriodType"; public static final String JODA_READABLE_DURATION = JODA_TIME_PKG + ".ReadableDuration"; public static final String JODA_BASE_DURATION = JODA_TIME_PKG + ".base.BaseDuration"; public static final String JODA_ABSTRACT_INSTANT = JODA_TIME_PKG + ".base.AbstractInstant"; @@ -45,11 +59,22 @@ public class TimeClassNames { public static final String JODA_INSTANT = JODA_TIME_PKG + ".Instant"; public static final String JODA_INTERVAL = JODA_TIME_PKG + ".Interval"; public static final String JODA_BASE_INTERVAL = JODA_TIME_PKG + ".base.BaseInterval"; + public static final String JODA_SECONDS = JODA_TIME_PKG + ".Seconds"; + public static final String JODA_MINUTES = JODA_TIME_PKG + ".Minutes"; + public static final String JODA_HOURS = JODA_TIME_PKG + ".Hours"; + public static final String JODA_DAYS = JODA_TIME_PKG + ".Days"; + public static final String JODA_WEEKS = JODA_TIME_PKG + ".Weeks"; + public static final String JODA_MONTHS = JODA_TIME_PKG + ".Months"; + public static final String JODA_YEARS = JODA_TIME_PKG + ".Years"; + public static final String JODA_DATE_TIME_UTILS = JODA_TIME_PKG + ".DateTimeUtils"; + public static final String JODA_BASIC_CHRONOLOGY = JODA_TIME_PKG + ".chrono.BasicChronology"; + public static final String JODA_GEORGIAN_CHRONOLOGY = JODA_TIME_PKG + ".chrono.GregorianChronology"; // Java Time classes public static final String JAVA_TIME_PKG = "java.time"; public static final String JAVA_DATE_TIME = JAVA_TIME_PKG + ".ZonedDateTime"; public static final String JAVA_DURATION = JAVA_TIME_PKG + ".Duration"; + public static final String JAVA_PERIOD = JAVA_TIME_PKG + ".Period"; public static final String JAVA_ZONE_OFFSET = JAVA_TIME_PKG + ".ZoneOffset"; public static final String JAVA_ZONE_ID = JAVA_TIME_PKG + ".ZoneId"; public static final String JAVA_INSTANT = JAVA_TIME_PKG + ".Instant"; @@ -58,10 +83,17 @@ public class TimeClassNames { public static final String JAVA_TEMPORAL_ADJUSTER = JAVA_TIME_PKG + ".temporal.TemporalAdjuster"; public static final String JAVA_LOCAL_DATE = JAVA_TIME_PKG + ".LocalDate"; public static final String JAVA_LOCAL_TIME = JAVA_TIME_PKG + ".LocalTime"; + public static final String JAVA_LOCAL_DATE_TIME = JAVA_TIME_PKG + ".LocalDateTime"; public static final String JAVA_TEMPORAL_ISO_FIELDS = JAVA_TIME_PKG + ".temporal.IsoFields"; public static final String JAVA_CHRONO_FIELD = JAVA_TIME_PKG + ".temporal.ChronoField"; + public static final String JAVA_CHRONO_UNIT = JAVA_TIME_PKG + ".temporal.ChronoUnit"; + public static final String JAVA_ISA_CHRONOLOGY = JAVA_TIME_PKG + ".chrono.IsoChronology"; // ThreeTen-Extra classes public static final String THREE_TEN_EXTRA_PKG = "org.threeten.extra"; public static final String THREE_TEN_EXTRA_INTERVAL = THREE_TEN_EXTRA_PKG + ".Interval"; + public static final String THREE_TEN_EXTRA_DAYS = THREE_TEN_EXTRA_PKG + ".Days"; + public static final String THREE_TEN_EXTRA_WEEKS = THREE_TEN_EXTRA_PKG + ".Weeks"; + public static final String THREE_TEN_EXTRA_MONTHS = THREE_TEN_EXTRA_PKG + ".Months"; + public static final String THREE_TEN_EXTRA_YEARS = THREE_TEN_EXTRA_PKG + ".Years"; } diff --git a/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeRecipeTest.java b/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeRecipeTest.java index b24e8630ce..f350d92383 100644 --- a/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeRecipeTest.java +++ b/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeRecipeTest.java @@ -65,7 +65,7 @@ public void foo() { } @Test - void dontChangeClassVariable() { + void changeClassVariable() { // not supported yet //language=java rewriteRun( @@ -85,18 +85,16 @@ public static class B { } """, """ - import org.joda.time.DateTime; - import java.time.ZonedDateTime; class A { public void foo() { ZonedDateTime dt = ZonedDateTime.now(); System.out.println(dt); - System.out.println(new B().dateTime.toDateTime()); + System.out.println(new B().dateTime); } public static class B { - DateTime dateTime = new DateTime(); + ZonedDateTime dateTime = ZonedDateTime.now(); } } """ @@ -105,7 +103,7 @@ public static class B { } @Test - void safeMethodParamMigrationAcrossClassBoundary() { + void methodParamMigrationAcrossClassBoundary() { //language=java rewriteRun( java( @@ -115,7 +113,7 @@ void safeMethodParamMigrationAcrossClassBoundary() { class A { public void foo() { new B().print(new DateTime()); - System.out.println(new B().dateTime); // dateTime is class variable, not handled yet + System.out.println(new B().dateTime); } } @@ -127,19 +125,17 @@ public void print(DateTime dateTime) { } """, """ - import org.joda.time.DateTime; - import java.time.ZonedDateTime; class A { public void foo() { new B().print(ZonedDateTime.now()); - System.out.println(new B().dateTime); // dateTime is class variable, not handled yet + System.out.println(new B().dateTime); } } class B { - DateTime dateTime = new DateTime(); + ZonedDateTime dateTime = ZonedDateTime.now(); public void print(ZonedDateTime dateTime) { System.out.println(dateTime); } @@ -283,6 +279,47 @@ public void bar(ZonedDateTime dt) { ); } + @Test + void migrateMethodWithVariableParams() { + //language=java + rewriteRun( + java( + """ + import org.joda.time.DateTime; + import org.joda.time.Seconds; + + class A { + public void foo() { + DateTime bar = new Bar().bar(0, Seconds.seconds(1), new DateTime(), new DateTime()); + } + + private static class Bar { + public DateTime bar(int index, Seconds seconds, DateTime... dt) { + return dt[index]; + } + } + } + """, + """ + import java.time.Duration; + import java.time.ZonedDateTime; + + class A { + public void foo() { + ZonedDateTime bar = new Bar().bar(0, Duration.ofSeconds(1), ZonedDateTime.now(), ZonedDateTime.now()); + } + + private static class Bar { + public ZonedDateTime bar(int index, Duration seconds, ZonedDateTime... dt) { + return dt[index]; + } + } + } + """ + ) + ); + } + @Test void migrationWithRecord() { //language=java @@ -370,7 +407,7 @@ public void bar(ZonedDateTime dt) { } @Test - void migrateMethodWithSafeReturnExpressionAndUnsafeParam() { + void migrateMethodWithReturnExpressionAndParam() { //language=java rewriteRun( java( @@ -395,15 +432,14 @@ public void bar() { } """, """ - import org.joda.time.DateTime; import org.threeten.extra.Interval; import java.time.ZoneId; import java.time.ZonedDateTime; class A { - public ZonedDateTime foo(DateTime dt) { - DateTime d = dt.toDateMidnight(); + public ZonedDateTime foo(ZonedDateTime dt) { + ZonedDateTime d = dt.toLocalDate().atStartOfDay(ZoneId.systemDefault()); ZonedDateTime d2 = ZonedDateTime.now(); Interval interval = Interval.of(d2.toInstant(), d2.plusDays(1).toInstant()); return interval.getEnd().atZone(ZoneId.systemDefault()); @@ -411,7 +447,7 @@ public ZonedDateTime foo(DateTime dt) { private static class Bar { public void bar() { - ZonedDateTime d = foo(new DateTime()); + ZonedDateTime d = foo(ZonedDateTime.now()); System.out.println(d.toInstant().toEpochMilli()); } } @@ -422,7 +458,7 @@ public void bar() { } @Test - void doNotMigrateUnsafeMethodParam() { + void migrateMethodParam() { //language=java rewriteRun( java( @@ -440,33 +476,67 @@ public void bar(DateTime dt) { } } } + """, + """ + import java.time.ZoneId; + import java.time.ZonedDateTime; + + class A { + public void foo() { + new Bar().bar(ZonedDateTime.now()); + } + + private static class Bar { + public void bar(ZonedDateTime dt) { + dt.toLocalDate().atStartOfDay(ZoneId.systemDefault()); // template doesn't exist for toDateMidnight + } + } + } """ ) ); } @Test - void dontMigrateMethodInvocationIfSelectExprIsNotMigrated() { + void migrateMethodInvocation() { //language=java rewriteRun( java( """ - import org.joda.time.Interval; + import org.joda.time.Interval; - class A { - private Query query = new Query(); - public void foo() { - query.interval().getEndMillis(); - } - static class Query { - private Interval interval; + class A { + private Query query = new Query(); + public void foo() { + query.interval().getEndMillis(); + } + static class Query { + private Interval interval; - public Interval interval() { - return interval; - } - } - } - """ + public Interval interval() { + return interval; + } + } + } + """, + """ + import org.threeten.extra.Interval; + + class A { + private Query query = new Query(); + public void foo() { + query.interval().getEnd().toEpochMilli(); + } + + static class Query { + private Interval interval; + + public Interval interval() { + return interval; + } + } + } + """ ) ); } @@ -477,18 +547,18 @@ void migrateWithMethodReferenceInComment() { rewriteRun( java( """ - import org.joda.time.DateTime; - - class A { - public void foo() { - /** - * some method reference in comment - * {@link java.util.List#add(DateTime)} - */ - System.out.println(new DateTime()); - } - } - """, + import org.joda.time.DateTime; + + class A { + public void foo() { + /** + * some method reference in comment + * {@link java.util.List#add(DateTime)} + */ + System.out.println(new DateTime()); + } + } + """, """ import java.time.ZonedDateTime; @@ -502,7 +572,7 @@ public void foo() { } } """ - ) + ) ); } }