Skip to content

Commit

Permalink
Joda-Time to Java time: Add templates for migrating Joda Interval to …
Browse files Browse the repository at this point in the history
…Threeten-Extra Interval
  • Loading branch information
amishra-u committed Dec 3, 2024
1 parent 05b8264 commit eb96f7f
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 5 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ dependencies {

testImplementation("com.google.guava:guava:33.0.0-jre")
testImplementation("joda-time:joda-time:2.12.3")
testImplementation("org.threeten:threeten-extra:1.8.0")

testRuntimeOnly("com.fasterxml.jackson.datatype:jackson-datatype-jsr353")
testRuntimeOnly("com.fasterxml.jackson.core:jackson-core")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public Javadoc visitReference(Javadoc.Reference reference, ExecutionContext ctx)
maybeRemoveImport(JODA_DURATION);
maybeRemoveImport(JODA_ABSTRACT_INSTANT);
maybeRemoveImport(JODA_INSTANT);
maybeRemoveImport(JODA_INTERVAL);
maybeRemoveImport("java.util.Locale");

maybeAddImport(JAVA_DATE_TIME);
Expand All @@ -79,6 +80,7 @@ public Javadoc visitReference(Javadoc.Reference reference, ExecutionContext ctx)
maybeAddImport(JAVA_TEMPORAL_ISO_FIELDS);
maybeAddImport(JAVA_CHRONO_FIELD);
maybeAddImport(JAVA_UTIL_DATE);
maybeAddImport(THREE_TEN_EXTRA_INTERVAL);
return super.visitCompilationUnit(cu, ctx);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.migrate.joda.templates;

import lombok.Getter;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;

import java.util.ArrayList;
import java.util.List;

import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*;

public class AbstractIntervalTemplates implements Templates {
private final MethodMatcher getStart = new MethodMatcher(JODA_ABSTRACT_INTERVAL + " getStart()");
private final MethodMatcher getEnd = new MethodMatcher(JODA_ABSTRACT_INTERVAL + " getEnd()");
private final MethodMatcher toDuration = new MethodMatcher(JODA_ABSTRACT_INTERVAL + " toDuration()");
private final MethodMatcher toDurationMillis = new MethodMatcher(JODA_ABSTRACT_INTERVAL + " toDurationMillis()");
private final MethodMatcher contains = new MethodMatcher(JODA_ABSTRACT_INTERVAL + " contains(long)");

private final JavaTemplate getStartTemplate = JavaTemplate.builder("#{any(" + THREE_TEN_EXTRA_INTERVAL + ")}.getStart().atZone(ZoneId.systemDefault())")
.javaParser(JavaParser.fromJavaVersion().classpath("threeten-extra"))
.imports(JAVA_ZONE_ID)
.build();
private final JavaTemplate getEndTemplate = JavaTemplate.builder("#{any(" + THREE_TEN_EXTRA_INTERVAL + ")}.getEnd().atZone(ZoneId.systemDefault())")
.javaParser(JavaParser.fromJavaVersion().classpath("threeten-extra"))
.imports(JAVA_ZONE_ID)
.build();
private final JavaTemplate toDurationTemplate = JavaTemplate.builder("#{any(" + THREE_TEN_EXTRA_INTERVAL + ")}.toDuration()")
.javaParser(JavaParser.fromJavaVersion().classpath("threeten-extra"))
.build();
private final JavaTemplate toDurationMillisTemplate = JavaTemplate.builder("#{any(" + THREE_TEN_EXTRA_INTERVAL + ")}.toDuration().toMillis()")
.javaParser(JavaParser.fromJavaVersion().classpath("threeten-extra"))
.build();
private final JavaTemplate containsTemplate = JavaTemplate.builder("#{any(" + THREE_TEN_EXTRA_INTERVAL + ")}.contains(Instant.ofEpochMilli(#{any(long)}))")
.javaParser(JavaParser.fromJavaVersion().classpath("threeten-extra"))
.imports(JAVA_INSTANT)
.build();

@Getter
private final List<MethodTemplate> templates = new ArrayList<MethodTemplate>() {
{
add(new MethodTemplate(getStart, getStartTemplate));
add(new MethodTemplate(getEnd, getEndTemplate));
add(new MethodTemplate(toDuration, toDurationTemplate));
add(new MethodTemplate(toDurationMillis, toDurationMillisTemplate));
add(new MethodTemplate(contains, containsTemplate));
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public class AllTemplates {
private static final MethodMatcher ANY_ABSTRACT_DURATION = new MethodMatcher(JODA_ABSTRACT_DURATION + " *(..)");
private static final MethodMatcher ANY_INSTANT = new MethodMatcher(JODA_INSTANT + " *(..)");
private static final MethodMatcher ANY_NEW_INSTANT = new MethodMatcher(JODA_INSTANT + "<constructor>(..)");
private static final MethodMatcher ANY_NEW_INTERVAL = new MethodMatcher(JODA_INTERVAL + "<constructor>(..)");
private static final MethodMatcher ANY_ABSTRACT_INTERVAL = new MethodMatcher(JODA_ABSTRACT_INTERVAL + " *(..)");

private static List<MatcherAndTemplates> templates = new ArrayList<MatcherAndTemplates>() {
{
Expand All @@ -55,6 +57,8 @@ public class AllTemplates {
add(new MatcherAndTemplates(ANY_DATE_TIMEZONE, new TimeZoneTemplates()));
add(new MatcherAndTemplates(ANY_INSTANT, new InstantTemplates()));
add(new MatcherAndTemplates(ANY_NEW_INSTANT, new InstantTemplates()));
add(new MatcherAndTemplates(ANY_NEW_INTERVAL, new IntervalTemplates()));
add(new MatcherAndTemplates(ANY_ABSTRACT_INTERVAL, new AbstractIntervalTemplates()));
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.migrate.joda.templates;

import lombok.Getter;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.tree.Expression;

import java.util.ArrayList;
import java.util.List;

import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*;

public class IntervalTemplates implements Templates {
private final MethodMatcher interval = new MethodMatcher(JODA_INTERVAL + " <constructor>(long, long)");
private final MethodMatcher intervalWithTimeZone = new MethodMatcher(JODA_INTERVAL + " <constructor>(long, long, " + JODA_DATE_TIME_ZONE + ")");
private final MethodMatcher intervalWithDateTime = new MethodMatcher(JODA_INTERVAL + " <constructor>(" + JODA_READABLE_INSTANT + ", " + JODA_READABLE_INSTANT + ")");
private final MethodMatcher intervalWithDateTimeAndDuration = new MethodMatcher(JODA_INTERVAL + " <constructor>(" + JODA_READABLE_INSTANT + ", " + JODA_READABLE_DURATION + ")");

private final JavaTemplate intervalTemplate = JavaTemplate.builder("Interval.of(Instant.ofEpochMilli(#{any(long)}), Instant.ofEpochMilli(#{any(long)}))")
.javaParser(JavaParser.fromJavaVersion().classpath("threeten-extra"))
.imports(JAVA_INSTANT, THREE_TEN_EXTRA_INTERVAL)
.build();
private final JavaTemplate intervalWithDateTimeTemplate = JavaTemplate.builder("Interval.of(#{any(" + JAVA_DATE_TIME + ")}.toInstant(), #{any(" + JAVA_DATE_TIME + ")}.toInstant())")
.javaParser(JavaParser.fromJavaVersion().classpath("threeten-extra"))
.imports(THREE_TEN_EXTRA_INTERVAL)
.build();
private final JavaTemplate intervalWithDateTimeAndDurationTemplate = JavaTemplate.builder("Interval.of(#{any(" + JAVA_DATE_TIME + ")}.toInstant(), #{any(" + JAVA_DURATION + ")})")
.javaParser(JavaParser.fromJavaVersion().classpath("threeten-extra"))
.imports(THREE_TEN_EXTRA_INTERVAL)
.build();

@Getter
private final List<MethodTemplate> templates = new ArrayList<MethodTemplate>() {
{
add(new MethodTemplate(interval, intervalTemplate));
add(new MethodTemplate(intervalWithTimeZone, intervalTemplate,
m -> new Expression[]{m.getArguments().get(0), m.getArguments().get(1)}));
add(new MethodTemplate(intervalWithDateTime, intervalWithDateTimeTemplate));
add(new MethodTemplate(intervalWithDateTimeAndDuration, intervalWithDateTimeAndDurationTemplate));
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class TimeClassNames {
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_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_TIME_ZONE = JODA_TIME_PKG + ".DateTimeZone";
Expand All @@ -39,7 +40,9 @@ public class TimeClassNames {
public static final String JODA_DURATION = JODA_TIME_PKG + ".Duration";
public static final String JODA_READABLE_DURATION = JODA_TIME_PKG + ".ReadableDuration";
public static final String JODA_ABSTRACT_INSTANT = JODA_TIME_PKG + ".base.AbstractInstant";
public static final String JODA_READABLE_INSTANT = JODA_TIME_PKG + ".ReadableInstant";
public static final String JODA_INSTANT = JODA_TIME_PKG + ".Instant";
public static final String JODA_INTERVAL = JODA_TIME_PKG + ".Interval";

// Java Time classes
public static final String JAVA_TIME_PKG = "java.time";
Expand All @@ -55,4 +58,8 @@ public class TimeClassNames {
public static final String JAVA_LOCAL_TIME = JAVA_TIME_PKG + ".LocalTime";
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";

// 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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class VarTemplates {
put(JODA_LOCAL_TIME, JAVA_LOCAL_TIME);
put(JODA_DATE_TIME_ZONE, JAVA_ZONE_ID);
put(JODA_DURATION, JAVA_DURATION);
put(JODA_INTERVAL, THREE_TEN_EXTRA_INTERVAL);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class JodaTimeVisitorTest implements RewriteTest {
public void defaults(RecipeSpec spec) {
spec
.recipe(toRecipe(() -> new JodaTimeVisitor(new JodaTimeRecipe.Accumulator(), true, new LinkedList<>())))
.parser(JavaParser.fromJavaVersion().classpath("joda-time"));
.parser(JavaParser.fromJavaVersion().classpath("joda-time", "threeten-extra"));
}

@DocumentExample
Expand Down Expand Up @@ -695,11 +695,11 @@ void unhandledCases() {
rewriteRun(
java(
"""
import org.joda.time.Interval;
import org.joda.time.PeriodType;
class A {
public void foo() {
new Interval(100, 50);
PeriodType.standard();
}
}
"""
Expand Down Expand Up @@ -782,11 +782,90 @@ void unhandledVarDeclaration() {
rewriteRun(
java(
"""
import org.joda.time.PeriodType;
class A {
public void foo(PeriodType periodType) {
periodType = PeriodType.days();
}
}
"""
)
);
}

@Test
void migrateInterval() {
// language=java
rewriteRun(
java(
"""
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Interval;
import org.joda.time.DateTimeZone;
class A {
public void foo() {
System.out.println(new Interval(50, 100));
System.out.println(new Interval(50, 100, DateTimeZone.UTC));
System.out.println(new Interval(DateTime.now(), DateTime.now().plusDays(1)));
System.out.println(new Interval(DateTime.now(), Duration.standardDays(1)));
}
}
""",
"""
import org.threeten.extra.Interval;
import java.time.Duration;
import java.time.Instant;
import java.time.ZonedDateTime;
class A {
public void foo(Interval interval) {
interval = new Interval(100, 50);
public void foo() {
System.out.println(Interval.of(Instant.ofEpochMilli(50), Instant.ofEpochMilli(100)));
System.out.println(Interval.of(Instant.ofEpochMilli(50), Instant.ofEpochMilli(100)));
System.out.println(Interval.of(ZonedDateTime.now().toInstant(), ZonedDateTime.now().plusDays(1).toInstant()));
System.out.println(Interval.of(ZonedDateTime.now().toInstant(), Duration.ofDays(1)));
}
}
"""
)
);
}

@Test
void migrateAbstractInterval() {
// language=java
rewriteRun(
java(
"""
import org.joda.time.DateTime;
import org.joda.time.Interval;
class A {
public void foo() {
new Interval(50, 100).getStart();
new Interval(50, 100).getEnd();
new Interval(50, 100).toDuration();
new Interval(50, 100).toDurationMillis();
new Interval(50, 100).contains(75);
}
}
""",
"""
import org.threeten.extra.Interval;
import java.time.Instant;
import java.time.ZoneId;
class A {
public void foo() {
Interval.of(Instant.ofEpochMilli(50), Instant.ofEpochMilli(100)).getStart().atZone(ZoneId.systemDefault());
Interval.of(Instant.ofEpochMilli(50), Instant.ofEpochMilli(100)).getEnd().atZone(ZoneId.systemDefault());
Interval.of(Instant.ofEpochMilli(50), Instant.ofEpochMilli(100)).toDuration();
Interval.of(Instant.ofEpochMilli(50), Instant.ofEpochMilli(100)).toDuration().toMillis();
Interval.of(Instant.ofEpochMilli(50), Instant.ofEpochMilli(100)).contains(Instant.ofEpochMilli(75));
}
}
"""
Expand Down

0 comments on commit eb96f7f

Please sign in to comment.