From 88bcb91d594d0b5e12543d1cfa264556fe7c45b3 Mon Sep 17 00:00:00 2001 From: Anshuman Mishra Date: Sat, 26 Oct 2024 11:17:21 -0700 Subject: [PATCH 1/2] Joda time to Java time: Add few more templates for migration --- .../java/migrate/joda/JodaTimeVisitor.java | 54 ++-- .../templates/AbstractDateTimeTemplates.java | 69 ++++++ .../templates/AbstractDurationTemplates.java | 41 +++ .../templates/AbstractInstantTemplates.java | 82 +++++- .../migrate/joda/templates/AllTemplates.java | 88 +++++++ .../migrate/joda/templates/BaseDateTime.java | 39 +++ .../templates/DateTimeFormatTemplates.java | 9 +- .../templates/DateTimeFormatterTemplates.java | 72 ++++++ .../joda/templates/DateTimeTemplates.java | 10 +- .../joda/templates/DurationTemplates.java | 8 +- .../joda/templates/InstantTemplates.java | 61 +++++ .../migrate/joda/templates/Templates.java | 32 +++ .../joda/templates/TimeClassNames.java | 3 + .../joda/templates/TimeZoneTemplates.java | 8 +- .../migrate/joda/JodaTimeVisitorTest.java | 234 ++++++++++++++++-- 15 files changed, 720 insertions(+), 90 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractDateTimeTemplates.java create mode 100644 src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractDurationTemplates.java create mode 100644 src/main/java/org/openrewrite/java/migrate/joda/templates/AllTemplates.java create mode 100644 src/main/java/org/openrewrite/java/migrate/joda/templates/BaseDateTime.java create mode 100644 src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatterTemplates.java create mode 100644 src/main/java/org/openrewrite/java/migrate/joda/templates/InstantTemplates.java create mode 100644 src/main/java/org/openrewrite/java/migrate/joda/templates/Templates.java 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 85414793e8..84bf39e95c 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java @@ -19,7 +19,6 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.java.JavaTemplate; -import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.migrate.joda.templates.*; import org.openrewrite.java.tree.*; import org.openrewrite.java.tree.J.VariableDeclarations.NamedVariable; @@ -31,15 +30,6 @@ public class JodaTimeVisitor extends ScopeAwareVisitor { - private final MethodMatcher anyNewDateTime = new MethodMatcher(JODA_DATE_TIME + "(..)"); - private final MethodMatcher anyDateTime = new MethodMatcher(JODA_DATE_TIME + " *(..)"); - private final MethodMatcher anyBaseDateTime = new MethodMatcher(JODA_BASE_DATE_TIME + " *(..)"); - private final MethodMatcher zoneFor = new MethodMatcher(JODA_DATE_TIME_ZONE + " for*(..)"); - private final MethodMatcher anyTimeFormatter = new MethodMatcher(JODA_TIME_FORMAT + " *(..)"); - private final MethodMatcher anyNewDuration = new MethodMatcher(JODA_DURATION + "(..)"); - private final MethodMatcher anyDuration = new MethodMatcher(JODA_DURATION + " *(..)"); - private final MethodMatcher anyAbstractInstant = new MethodMatcher(JODA_ABSTRACT_INSTANT + " *(..)"); - private final Set unsafeVars; public JodaTimeVisitor(Set unsafeVars, LinkedList scopes) { @@ -62,6 +52,7 @@ public JodaTimeVisitor() { maybeRemoveImport(JODA_TIME_FORMAT); maybeRemoveImport(JODA_DURATION); maybeRemoveImport(JODA_ABSTRACT_INSTANT); + maybeRemoveImport(JODA_INSTANT); maybeRemoveImport("java.util.Locale"); maybeAddImport(JAVA_DATE_TIME); @@ -135,11 +126,9 @@ public JodaTimeVisitor() { if (hasJodaType(updated.getArguments())) { return newClass; } - if (anyNewDateTime.matches(newClass)) { - return applyTemplate(newClass, updated, DateTimeTemplates.getTemplates()).orElse(newClass); - } - if (anyNewDuration.matches(newClass)) { - return applyTemplate(newClass, updated, DurationTemplates.getTemplates()).orElse(newClass); + MethodTemplate template = AllTemplates.getTemplate(newClass); + if (template != null) { + return applyTemplate(newClass, updated, template).orElse(newClass); } if (areArgumentsAssignable(updated)) { return updated; @@ -154,24 +143,11 @@ public JodaTimeVisitor() { if (hasJodaType(m.getArguments()) || isJodaVarRef(m.getSelect())) { return method; } - if (zoneFor.matches(method)) { - return applyTemplate(method, m, TimeZoneTemplates.getTemplates()).orElse(method); - } - if (anyDateTime.matches(method) || anyBaseDateTime.matches(method)) { - return applyTemplate(method, m, DateTimeTemplates.getTemplates()).orElse(method); - } - if (anyAbstractInstant.matches(method)) { - return applyTemplate(method, m, AbstractInstantTemplates.getTemplates()).orElse(method); - } - if (anyTimeFormatter.matches(method)) { - return applyTemplate(method, m, DateTimeFormatTemplates.getTemplates()).orElse(method); - } - if (anyDuration.matches(method)) { - return applyTemplate(method, m, DurationTemplates.getTemplates()).orElse(method); + MethodTemplate template = AllTemplates.getTemplate(method); + if (template != null) { + return applyTemplate(method, m, template).orElse(method); } - if (method.getSelect() != null && - method.getSelect().getType() != null && - method.getSelect().getType().isAssignableFrom(JODA_CLASS_PATTERN)) { + if (method.getMethodType().getDeclaringType().isAssignableFrom(JODA_CLASS_PATTERN)) { return method; // unhandled case } if (areArgumentsAssignable(m)) { @@ -219,15 +195,13 @@ private boolean hasJodaType(List exprs) { return false; } - private Optional applyTemplate(MethodCall original, MethodCall updated, List templates) { - for (MethodTemplate template : templates) { - if (template.getMatcher().matches(original)) { - Expression[] args = template.getTemplateArgsFunc().apply(updated); - if (args.length == 0) { - return Optional.of(template.getTemplate().apply(updateCursor(updated), updated.getCoordinates().replace())); - } - return Optional.of(template.getTemplate().apply(updateCursor(updated), updated.getCoordinates().replace(), (Object[]) args)); + private Optional applyTemplate(MethodCall original, MethodCall updated, MethodTemplate template) { + if (template.getMatcher().matches(original)) { + Expression[] args = template.getTemplateArgsFunc().apply(updated); + if (args.length == 0) { + return Optional.of(template.getTemplate().apply(updateCursor(updated), updated.getCoordinates().replace())); } + return Optional.of(template.getTemplate().apply(updateCursor(updated), updated.getCoordinates().replace(), (Object[]) args)); } return Optional.empty(); // unhandled case } diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractDateTimeTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractDateTimeTemplates.java new file mode 100644 index 0000000000..6a3b42090d --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractDateTimeTemplates.java @@ -0,0 +1,69 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.JavaTemplate; +import org.openrewrite.java.MethodMatcher; + +import java.util.ArrayList; +import java.util.List; + +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JAVA_DATE_TIME; +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JODA_ABSTRACT_DATE_TIME; + +public class AbstractDateTimeTemplates implements Templates { + private final MethodMatcher getDayOfMonth = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getDayOfMonth()"); + private final MethodMatcher getDayOfWeek = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getDayOfWeek()"); + private final MethodMatcher getHourOfDay = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getHourOfDay()"); + private final MethodMatcher getMillisOfSecond = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getMillisOfSecond()"); + private final MethodMatcher getMinuteOfDay = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getMinuteOfDay()"); + private final MethodMatcher getMinuteOfHour = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getMinuteOfHour()"); + private final MethodMatcher getMonthOfYear = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getMonthOfYear()"); + private final MethodMatcher getSecondOfDay = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getSecondOfDay()"); + private final MethodMatcher getSecondOfMinute = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getSecondOfMinute()"); + private final MethodMatcher getWeekOfWeekyear = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " getWeekOfWeekyear()"); + private final MethodMatcher toString = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " toString()"); + + private final JavaTemplate getDayOfMonthTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.getDayOfMonth()").build(); + private final JavaTemplate getDayOfWeekTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.getDayOfWeek().getValue()").build(); + private final JavaTemplate getHourOfDayTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.getHour()").build(); + private final JavaTemplate getMillisOfSecondTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.get(ChronoField.MILLI_OF_SECOND)").imports("java.time.temporal.ChronoField").build(); + private final JavaTemplate getMinuteOfDayTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.get(ChronoField.MINUTE_OF_DAY)").imports("java.time.temporal.ChronoField").build(); + private final JavaTemplate getMinuteOfHourTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.getMinute()").build(); + private final JavaTemplate getMonthOfYearTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.getMonthValue()").build(); + private final JavaTemplate getSecondOfDayTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.get(ChronoField.SECOND_OF_DAY)").imports("java.time.temporal.ChronoField").build(); + private final JavaTemplate getSecondOfMinuteTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.getSecond()").build(); + private final JavaTemplate getWeekOfWeekyearTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.get(ChronoField.ALIGNED_WEEK_OF_YEAR)").imports("java.time.temporal.ChronoField").build(); + private final JavaTemplate toStringTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.toString()").build(); + + @Getter + private final List templates = new ArrayList() { + { + add(new MethodTemplate(getDayOfMonth, getDayOfMonthTemplate)); + add(new MethodTemplate(getDayOfWeek, getDayOfWeekTemplate)); + add(new MethodTemplate(getHourOfDay, getHourOfDayTemplate)); + add(new MethodTemplate(getMillisOfSecond, getMillisOfSecondTemplate)); + add(new MethodTemplate(getMinuteOfDay, getMinuteOfDayTemplate)); + add(new MethodTemplate(getMinuteOfHour, getMinuteOfHourTemplate)); + add(new MethodTemplate(getMonthOfYear, getMonthOfYearTemplate)); + add(new MethodTemplate(getSecondOfDay, getSecondOfDayTemplate)); + add(new MethodTemplate(getSecondOfMinute, getSecondOfMinuteTemplate)); + add(new MethodTemplate(getWeekOfWeekyear, getWeekOfWeekyearTemplate)); + add(new MethodTemplate(toString, toStringTemplate)); + } + }; +} diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractDurationTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractDurationTemplates.java new file mode 100644 index 0000000000..edd48b7a2d --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractDurationTemplates.java @@ -0,0 +1,41 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.JavaTemplate; +import org.openrewrite.java.MethodMatcher; + +import java.util.ArrayList; +import java.util.List; + +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JAVA_DURATION; +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JODA_DURATION; + +public class AbstractDurationTemplates implements Templates { + private final MethodMatcher isLongerThan = new MethodMatcher(JODA_DURATION + " isLongerThan(..)"); + private final MethodMatcher toPeriod = new MethodMatcher(JODA_DURATION + " toPeriod()"); + + private final JavaTemplate isLongerThanTemplate = JavaTemplate.builder("#{any(" + JAVA_DURATION + ")}.compareTo(#{any(" + JAVA_DURATION + ")}) > 0").build(); + private final JavaTemplate toPeriodTemplate = JavaTemplate.builder("#{any(" + JAVA_DURATION + ")}.toPeriod()").build(); + + @Getter + private final List templates = new ArrayList() { + { + add(new MethodTemplate(isLongerThan, isLongerThanTemplate)); + add(new MethodTemplate(toPeriod, toPeriodTemplate)); + } + }; +} diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractInstantTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractInstantTemplates.java index ded2197aa8..d9c848d67d 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractInstantTemplates.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractInstantTemplates.java @@ -15,29 +15,99 @@ */ package org.openrewrite.java.migrate.joda.templates; +import lombok.Getter; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.MethodCall; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.regex.Pattern; -import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JAVA_UTIL_DATE; -import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JODA_ABSTRACT_INSTANT; +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JAVA_INSTANT; -public class AbstractInstantTemplates { +public class AbstractInstantTemplates implements Templates { + private final MethodMatcher equals = new MethodMatcher(JODA_ABSTRACT_INSTANT + " equals(java.lang.Object)"); + private final MethodMatcher getZone = new MethodMatcher(JODA_ABSTRACT_INSTANT + " getZone()"); + private final MethodMatcher isAfterLong = new MethodMatcher(JODA_ABSTRACT_INSTANT + " isAfter(long)"); + private final MethodMatcher isAfter = new MethodMatcher(JODA_ABSTRACT_INSTANT + " isAfter(org.joda.time.ReadableInstant)"); + private final MethodMatcher isBeforeLong = new MethodMatcher(JODA_ABSTRACT_INSTANT + " isBefore(long)"); + private final MethodMatcher isBefore = new MethodMatcher(JODA_ABSTRACT_INSTANT + " isBefore(org.joda.time.ReadableInstant)"); + private final MethodMatcher isBeforeNow = new MethodMatcher(JODA_ABSTRACT_INSTANT + " isBeforeNow()"); + private final MethodMatcher isEqualLong = new MethodMatcher(JODA_ABSTRACT_INSTANT + " isEqual(long)"); + private final MethodMatcher isEqualReadableInstant = new MethodMatcher(JODA_ABSTRACT_INSTANT + " isEqual(org.joda.time.ReadableInstant)"); private final MethodMatcher toDate = new MethodMatcher(JODA_ABSTRACT_INSTANT + " toDate()"); + private final MethodMatcher toInstant = new MethodMatcher(JODA_ABSTRACT_INSTANT + " toInstant()"); + private final MethodMatcher toString = new MethodMatcher(JODA_ABSTRACT_INSTANT + " toString()"); + private final MethodMatcher toStringFormatter = new MethodMatcher(JODA_ABSTRACT_INSTANT + " toString(org.joda.time.format.DateTimeFormatter)"); - private final JavaTemplate toDateTemplate = JavaTemplate.builder("Date.from(#{any(java.time.ZonedDateTime)}.toInstant())") + private final JavaTemplate equalsTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.equals(#{any(java.lang.Object)})").build(); + private final JavaTemplate getZoneTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.getZone()").build(); + private final JavaTemplate isAfterLongTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.isAfter(Instant.ofEpochMilli(#{any(long)}).atZone(ZoneId.systemDefault()))") + .imports(JAVA_INSTANT, JAVA_ZONE_ID).build(); + private final JavaTemplate isAfterLongTemplateWithInstant = JavaTemplate.builder("#{any(" + JAVA_INSTANT + ")}.isAfter(Instant.ofEpochMilli(#{any(long)}))") + .imports(JAVA_INSTANT).build(); + private final JavaTemplate isAfterTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.isAfter(#{any(" + JAVA_DATE_TIME + ")})").build(); + private final JavaTemplate isAfterTemplateWithInstant = JavaTemplate.builder("#{any(" + JAVA_INSTANT + ")}.isAfter(#{any(" + JAVA_INSTANT + ")})").build(); + private final JavaTemplate isBeforeLongTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.isBefore(Instant.ofEpochMilli(#{any(long)}).atZone(ZoneId.systemDefault()))") + .imports(JAVA_INSTANT, JAVA_ZONE_ID).build(); + private final JavaTemplate isBeforeLongTemplateWithInstant = JavaTemplate.builder("#{any(" + JAVA_INSTANT + ")}.isBefore(Instant.ofEpochMilli(#{any(long)}))") + .imports(JAVA_INSTANT).build(); + private final JavaTemplate isBeforTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.isBefore(#{any(" + JAVA_DATE_TIME + ")})").build(); + private final JavaTemplate isBeforeTemplateWithInstant = JavaTemplate.builder("#{any(" + JAVA_INSTANT + ")}.isBefore(#{any(" + JAVA_INSTANT + ")})").build(); + private final JavaTemplate isBeforeNowTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.isBefore(ZonedDateTime.now())") + .imports(JAVA_DATE_TIME).build(); + private final JavaTemplate isEqualLongTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.isEqual(Instant.ofEpochMilli(#{any(long)}).atZone(ZoneId.systemDefault()))") + .imports(JAVA_INSTANT, JAVA_ZONE_ID).build(); + private final JavaTemplate isEqualReadableInstantTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.isEqual(#{any(" + JAVA_DATE_TIME + ")})").build(); + private final JavaTemplate toDateTemplate = JavaTemplate.builder("Date.from(#{any(" + JAVA_DATE_TIME + ")}.toInstant())") .imports(JAVA_UTIL_DATE) .build(); + private final JavaTemplate toInstantTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.toInstant()").build(); + private final JavaTemplate toStringTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.toString()").build(); + private final JavaTemplate toStringFormatterTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.format(#{any(" + JAVA_TIME_FORMATTER + ")})").build(); + @Getter private final List templates = new ArrayList() { { + add(new MethodTemplate(equals, equalsTemplate)); + add(new MethodTemplate(getZone, getZoneTemplate)); + add(new MethodTemplate(isAfterLong, isAfterLongTemplate)); + add(new MethodTemplate(isAfterLong, isAfterLongTemplateWithInstant)); + add(new MethodTemplate(isAfter, isAfterTemplate)); + add(new MethodTemplate(isAfter, isAfterTemplateWithInstant)); + add(new MethodTemplate(isBeforeLong, isBeforeLongTemplate)); + add(new MethodTemplate(isBeforeLong, isBeforeLongTemplateWithInstant)); + add(new MethodTemplate(isBefore, isBeforTemplate)); + add(new MethodTemplate(isBefore, isBeforeTemplateWithInstant)); + add(new MethodTemplate(isBeforeNow, isBeforeNowTemplate)); + add(new MethodTemplate(isEqualLong, isEqualLongTemplate)); + add(new MethodTemplate(isEqualReadableInstant, isEqualReadableInstantTemplate)); add(new MethodTemplate(toDate, toDateTemplate)); + add(new MethodTemplate(toInstant, toInstantTemplate)); + add(new MethodTemplate(toString, toStringTemplate)); + add(new MethodTemplate(toStringFormatter, toStringFormatterTemplate)); } }; - public static List getTemplates() { - return new AbstractInstantTemplates().templates; + @Override + public boolean matchesMethodCall(MethodCall method, MethodTemplate template) { + if (method instanceof J.NewClass) { + return true; + } + Expression select = ((J.MethodInvocation) method).getSelect(); + if (select != null && select.getType() != null && select.getType().isAssignableFrom(Pattern.compile(JODA_INSTANT))) { + return Arrays.asList( + isAfterLongTemplateWithInstant, + isAfterTemplateWithInstant, + isBeforeLongTemplateWithInstant, + isBeforeTemplateWithInstant + ).contains(template.getTemplate()); + } + return true; } } diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/AllTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/AllTemplates.java new file mode 100644 index 0000000000..ffa7584e6f --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/AllTemplates.java @@ -0,0 +1,88 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.Value; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.MethodCall; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; + +public class AllTemplates { + private static final MethodMatcher ANY_BASE_DATETIME = new MethodMatcher(JODA_BASE_DATE_TIME + " *(..)"); + private static final MethodMatcher ANY_NEW_DATE_TIME = new MethodMatcher(JODA_DATE_TIME + "(..)"); + private static final MethodMatcher ANY_DATE_TIME = new MethodMatcher(JODA_DATE_TIME + " *(..)"); + private static final MethodMatcher ANY_DATE_TIMEZONE = new MethodMatcher(JODA_DATE_TIME_ZONE + " *(..)"); + private static final MethodMatcher ANY_TIME_FORMAT = new MethodMatcher(JODA_TIME_FORMAT + " *(..)"); + private static final MethodMatcher ANY_TIME_FORMATTER = new MethodMatcher(JODA_TIME_FORMATTER + " *(..)"); + private static final MethodMatcher ANY_NEW_DURATION = new MethodMatcher(JODA_DURATION + "(..)"); + private static final MethodMatcher ANY_DURATION = new MethodMatcher(JODA_DURATION + " *(..)"); + private static final MethodMatcher ANY_ABSTRACT_INSTANT = new MethodMatcher(JODA_ABSTRACT_INSTANT + " *(..)"); + private static final MethodMatcher ANY_ABSTRACT_DATE_TIME = new MethodMatcher(JODA_ABSTRACT_DATE_TIME + " *(..)"); + 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 + "(..)"); + + private static List templates = new ArrayList() { + { + add(new MatcherAndTemplates(ANY_ABSTRACT_DATE_TIME, new AbstractDateTimeTemplates())); + add(new MatcherAndTemplates(ANY_ABSTRACT_DURATION, new AbstractDurationTemplates())); + add(new MatcherAndTemplates(ANY_ABSTRACT_INSTANT, new AbstractInstantTemplates())); + add(new MatcherAndTemplates(ANY_BASE_DATETIME, new BaseDateTime())); + add(new MatcherAndTemplates(ANY_TIME_FORMAT, new DateTimeFormatTemplates())); + add(new MatcherAndTemplates(ANY_TIME_FORMATTER, new DateTimeFormatterTemplates())); + add(new MatcherAndTemplates(ANY_NEW_DATE_TIME, new DateTimeTemplates())); + add(new MatcherAndTemplates(ANY_DATE_TIME, new DateTimeTemplates())); + add(new MatcherAndTemplates(ANY_NEW_DURATION, new DurationTemplates())); + add(new MatcherAndTemplates(ANY_DURATION, new DurationTemplates())); + add(new MatcherAndTemplates(ANY_DATE_TIMEZONE, new TimeZoneTemplates())); + add(new MatcherAndTemplates(ANY_INSTANT, new InstantTemplates())); + add(new MatcherAndTemplates(ANY_NEW_INSTANT, new InstantTemplates())); + } + }; + + public static MethodTemplate getTemplate(J.MethodInvocation method) { + return getTemplateGroup(method).flatMap(templates -> templates.getTemplates().stream() + .filter(template -> template.getMatcher().matches(method) && templates.matchesMethodCall(method, template)) + .findFirst()).orElse(null); + } + + public static MethodTemplate getTemplate(J.NewClass newClass) { + return getTemplateGroup(newClass).flatMap(templates -> templates.getTemplates().stream() + .filter(template -> template.getMatcher().matches(newClass) && templates.matchesMethodCall(newClass, template)) + .findFirst()).orElse(null); + } + + private static Optional getTemplateGroup(MethodCall method) { + for (MatcherAndTemplates matcherAndTemplates : templates) { + if (matcherAndTemplates.getMatcher().matches(method)) { + return Optional.of(matcherAndTemplates.getTemplates()); + } + } + return Optional.empty(); + } + + @Value + private static class MatcherAndTemplates { + MethodMatcher matcher; + Templates templates; + } +} diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/BaseDateTime.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/BaseDateTime.java new file mode 100644 index 0000000000..56aeee4e52 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/BaseDateTime.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.JavaTemplate; +import org.openrewrite.java.MethodMatcher; + +import java.util.ArrayList; +import java.util.List; + +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JODA_BASE_DATE_TIME; + +public class BaseDateTime implements Templates { + private final MethodMatcher getMillis = new MethodMatcher(JODA_BASE_DATE_TIME + " getMillis()"); + + private final JavaTemplate getMillisTemplate = JavaTemplate.builder("#{any(java.time.ZonedDateTime)}.toInstant().toEpochMilli()") + .build(); + + @Getter + private final List templates = new ArrayList() { + { + add(new MethodTemplate(getMillis, getMillisTemplate)); + } + }; +} diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatTemplates.java index 324ea01037..d02e8fb3db 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatTemplates.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatTemplates.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.migrate.joda.templates; +import lombok.Getter; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; @@ -23,7 +24,7 @@ import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; -public class DateTimeFormatTemplates { +public class DateTimeFormatTemplates implements Templates { private final MethodMatcher forPattern = new MethodMatcher(JODA_TIME_FORMAT + " forPattern(String)"); private final MethodMatcher shortDate = new MethodMatcher(JODA_TIME_FORMAT + " shortDate()"); private final MethodMatcher mediumDate = new MethodMatcher(JODA_TIME_FORMAT + " mediumDate()"); @@ -77,6 +78,8 @@ public class DateTimeFormatTemplates { private final JavaTemplate fullDateTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL)") .imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) .build(); + + @Getter private final List templates = new ArrayList() { { add(new MethodTemplate(forPattern, ofPatternTemplate)); @@ -94,8 +97,4 @@ public class DateTimeFormatTemplates { add(new MethodTemplate(fullDateTime, fullDateTimeTemplate)); } }; - - public static List getTemplates() { - return new DateTimeFormatTemplates().templates; - } } diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatterTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatterTemplates.java new file mode 100644 index 0000000000..733fadd4a0 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatterTemplates.java @@ -0,0 +1,72 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; + +import java.util.ArrayList; +import java.util.List; + +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; + +public class DateTimeFormatterTemplates implements Templates { + private final MethodMatcher parseDateTime = new MethodMatcher(JODA_TIME_FORMATTER + " parseDateTime(java.lang.String)"); + private final MethodMatcher parseMillis = new MethodMatcher(JODA_TIME_FORMATTER + " parseMillis(java.lang.String)"); + private final MethodMatcher printLong = new MethodMatcher(JODA_TIME_FORMATTER + " print(long)"); + private final MethodMatcher printDateTime = new MethodMatcher(JODA_TIME_FORMATTER + " print(org.joda.time.ReadableInstant)"); + private final MethodMatcher withZone = new MethodMatcher(JODA_TIME_FORMATTER + " withZone(org.joda.time.DateTimeZone)"); + private final MethodMatcher withZoneUTC = new MethodMatcher(JODA_TIME_FORMATTER + " withZoneUTC()"); + + private final JavaTemplate parseDateTimeTemplate = JavaTemplate.builder("ZonedDateTime.parse(#{any(java.lang.String)}, #{any(" + JAVA_TIME_FORMATTER + ")})") + .imports(JAVA_DATE_TIME).build(); + private final JavaTemplate parseMillisTemplate = JavaTemplate.builder("ZonedDateTime.parse(#{any(java.lang.String)}, #{any(" + JAVA_TIME_FORMATTER + ")}).toInstant().toEpochMilli()") + .imports(JAVA_DATE_TIME).build(); + private final JavaTemplate printLongTemplate = JavaTemplate.builder("ZonedDateTime.ofInstant(Instant.ofEpochMilli(#{any(long)}), ZoneId.systemDefault()).format( #{any(" + JAVA_TIME_FORMATTER + ")})") + .imports(JAVA_DATE_TIME, JAVA_INSTANT, JAVA_ZONE_ID) + .build(); + private final JavaTemplate printDateTimeTemplate = JavaTemplate.builder("#{any(" + JAVA_DATE_TIME + ")}.format(#{any(" + JAVA_TIME_FORMATTER + ")})").build(); + private final JavaTemplate withZoneTemplate = JavaTemplate.builder("#{any(" + JAVA_TIME_FORMATTER + ")}.withZone(#{any(" + JAVA_ZONE_ID + ")})").build(); + private final JavaTemplate withZoneUTCTemplate = JavaTemplate.builder("#{any(" + JAVA_TIME_FORMATTER + ")}.withZone(ZoneOffset.UTC)") + .imports(JAVA_ZONE_OFFSET).build(); + + @Getter + private final List templates = new ArrayList() { + { + add(new MethodTemplate(parseDateTime, parseDateTimeTemplate, m -> { + J.MethodInvocation mi = (J.MethodInvocation) m; + return new Expression[]{mi.getArguments().get(0), mi.getSelect()}; + })); + add(new MethodTemplate(parseMillis, parseMillisTemplate, m -> { + J.MethodInvocation mi = (J.MethodInvocation) m; + return new Expression[]{mi.getArguments().get(0), mi.getSelect()}; + })); + add(new MethodTemplate(printLong, printLongTemplate, m -> { + J.MethodInvocation mi = (J.MethodInvocation) m; + return new Expression[]{mi.getArguments().get(0), mi.getSelect()}; + })); + add(new MethodTemplate(printDateTime, printDateTimeTemplate, m -> { + J.MethodInvocation mi = (J.MethodInvocation) m; + return new Expression[]{mi.getArguments().get(0), mi.getSelect()}; + })); + add(new MethodTemplate(withZone, withZoneTemplate)); + add(new MethodTemplate(withZoneUTC, withZoneUTCTemplate)); + } + }; +} \ No newline at end of file diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeTemplates.java index 17da53f65b..f1af144553 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeTemplates.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeTemplates.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.migrate.joda.templates; +import lombok.Getter; import lombok.NoArgsConstructor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; @@ -27,7 +28,7 @@ import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; @NoArgsConstructor -public class DateTimeTemplates { +public class DateTimeTemplates implements Templates { private final MethodMatcher newDateTime = new MethodMatcher(JODA_DATE_TIME + "()"); private final MethodMatcher newDateTimeWithZone = new MethodMatcher(JODA_DATE_TIME + "(" + JODA_DATE_TIME_ZONE + ")"); private final MethodMatcher newDateTimeWithEpoch = new MethodMatcher(JODA_DATE_TIME + "(long)"); @@ -46,7 +47,6 @@ public class DateTimeTemplates { private final MethodMatcher toDateTime = new MethodMatcher(JODA_DATE_TIME + " toDateTime()"); private final MethodMatcher toDateTimeWithZone = new MethodMatcher(JODA_DATE_TIME + " toDateTime(" + JODA_DATE_TIME_ZONE + ")"); - private final MethodMatcher getMillis = new MethodMatcher(JODA_BASE_DATE_TIME + " getMillis()"); private final MethodMatcher withMillis = new MethodMatcher(JODA_DATE_TIME + " withMillis(long)"); private final MethodMatcher withZone = new MethodMatcher(JODA_DATE_TIME + " withZone(" + JODA_DATE_TIME_ZONE + ")"); private final MethodMatcher withZoneRetainFields = new MethodMatcher(JODA_DATE_TIME + " withZoneRetainFields(" + JODA_DATE_TIME_ZONE + ")"); @@ -270,6 +270,7 @@ public class DateTimeTemplates { .imports(JAVA_CHRONO_FIELD) .build(); + @Getter private final List templates = new ArrayList() { { add(new MethodTemplate(newDateTime, dateTimeTemplate)); @@ -288,7 +289,6 @@ public class DateTimeTemplates { add(new MethodTemplate(dateTimeParseWithFormatter, dateTimeParseWithFormatterTemplate)); add(new MethodTemplate(toDateTime, toDateTimeTemplate)); add(new MethodTemplate(toDateTimeWithZone, withZoneTemplate)); - add(new MethodTemplate(getMillis, getMillisTemplate)); add(new MethodTemplate(withMillis, withMillisTemplate, m -> { J.MethodInvocation mi = (J.MethodInvocation) m; return new Expression[]{mi.getArguments().get(0), mi.getSelect()}; @@ -340,8 +340,4 @@ public class DateTimeTemplates { add(new MethodTemplate(withMillisOfDay, withMillisOfDayTemplate)); } }; - - public static List getTemplates() { - return new DateTimeTemplates().templates; - } } diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/DurationTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/DurationTemplates.java index d0912dd748..ea642137ee 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/DurationTemplates.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/DurationTemplates.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.migrate.joda.templates; +import lombok.Getter; import lombok.NoArgsConstructor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; @@ -26,7 +27,7 @@ import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; @NoArgsConstructor -public class DurationTemplates { +public class DurationTemplates implements Templates{ private final MethodMatcher parse = new MethodMatcher(JODA_DURATION + " parse(String)"); private final MethodMatcher standardDays = new MethodMatcher(JODA_DURATION + " standardDays(long)"); private final MethodMatcher standardHours = new MethodMatcher(JODA_DURATION + " standardHours(long)"); @@ -123,6 +124,7 @@ public class DurationTemplates { private final JavaTemplate absTemplate = JavaTemplate.builder("#{any(java.time.Duration)}.abs()") .build(); + @Getter private final List templates = new ArrayList() { { add(new MethodTemplate(parse, parseTemplate)); @@ -163,8 +165,4 @@ public class DurationTemplates { add(new MethodTemplate(abs, absTemplate)); } }; - - public static List getTemplates() { - return new DurationTemplates().templates; - } } diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/InstantTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/InstantTemplates.java new file mode 100644 index 0000000000..ac8a2770a6 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/InstantTemplates.java @@ -0,0 +1,61 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.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 InstantTemplates implements Templates { + private final MethodMatcher constructor = new MethodMatcher(JODA_INSTANT + " ()"); + private final MethodMatcher getMillis = new MethodMatcher(JODA_INSTANT + " getMillis()"); + private final MethodMatcher minusDuration = new MethodMatcher(JODA_INSTANT + " minus(org.joda.time.ReadableDuration)"); + private final MethodMatcher now = new MethodMatcher(JODA_INSTANT + " now()"); + private final MethodMatcher ofEpochMilli = new MethodMatcher(JODA_INSTANT + " ofEpochMilli(long)"); + private final MethodMatcher parse = new MethodMatcher(JODA_INSTANT + " parse(java.lang.String)"); + private final MethodMatcher plusDuration = new MethodMatcher(JODA_INSTANT + " plus(org.joda.time.ReadableDuration)"); + + private final JavaTemplate constructorTemplate = JavaTemplate.builder("Instant.now()") + .imports(JAVA_INSTANT).build(); + private final JavaTemplate getMillisTemplate = JavaTemplate.builder("#{any(" + JAVA_INSTANT + ")}.toEpochMilli()").build(); + private final JavaTemplate minusDurationTemplate = JavaTemplate.builder("#{any(" + JAVA_INSTANT + ")}.minus(#{any(java.time.Duration)})").build(); + private final JavaTemplate nowTemplate = JavaTemplate.builder("Instant.now()") + .imports(JAVA_INSTANT).build(); + private final JavaTemplate ofEpochMilliTemplate = JavaTemplate.builder("Instant.ofEpochMilli(#{any(long)})") + .imports(JAVA_INSTANT).build(); + private final JavaTemplate parseTemplate = JavaTemplate.builder("Instant.parse(#{any(java.lang.String)})") + .imports(JAVA_INSTANT).build(); + private final JavaTemplate plusDurationTemplate = JavaTemplate.builder("#{any(" + JAVA_INSTANT + ")}.plus(#{any(java.time.Duration)})").build(); + + @Getter + private final List templates = new ArrayList() { + { + add(new MethodTemplate(constructor, constructorTemplate)); + add(new MethodTemplate(getMillis, getMillisTemplate)); + add(new MethodTemplate(getMillis, getMillisTemplate)); + add(new MethodTemplate(minusDuration, minusDurationTemplate)); + add(new MethodTemplate(now, nowTemplate)); + add(new MethodTemplate(ofEpochMilli, ofEpochMilliTemplate)); + add(new MethodTemplate(parse, parseTemplate)); + add(new MethodTemplate(plusDuration, plusDurationTemplate)); + } + }; +} diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/Templates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/Templates.java new file mode 100644 index 0000000000..cfc9043d82 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/Templates.java @@ -0,0 +1,32 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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 org.openrewrite.java.tree.MethodCall; + +import java.util.List; + +public interface Templates { + List getTemplates(); + + /** + * This method is used to disambiguate between multiple potential template matches for a given methodCall. + * This should be overridden by Templates classes where methodMatcher.matches() may return more than one template. + **/ + default boolean matchesMethodCall(MethodCall method, MethodTemplate template) { + return true; + } +} 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 ad018b0069..428284174e 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 @@ -25,6 +25,8 @@ public class TimeClassNames { // 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_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"; @@ -37,6 +39,7 @@ 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_INSTANT = JODA_TIME_PKG + ".Instant"; // Java Time classes public static final String JAVA_TIME_PKG = "java.time"; diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/TimeZoneTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/TimeZoneTemplates.java index 1ee2be95fc..9862658b2c 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/TimeZoneTemplates.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/TimeZoneTemplates.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.migrate.joda.templates; +import lombok.Getter; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; @@ -23,7 +24,7 @@ import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; -public class TimeZoneTemplates { +public class TimeZoneTemplates implements Templates { private final MethodMatcher zoneForID = new MethodMatcher(JODA_DATE_TIME_ZONE + " forID(String)"); private final MethodMatcher zoneForOffsetHours = new MethodMatcher(JODA_DATE_TIME_ZONE + " forOffsetHours(int)"); private final MethodMatcher zoneForOffsetHoursMinutes = new MethodMatcher(JODA_DATE_TIME_ZONE + " forOffsetHoursMinutes(int, int)"); @@ -41,6 +42,7 @@ public class TimeZoneTemplates { private final JavaTemplate timeZoneToZoneIdTemplate = JavaTemplate.builder("#{any(java.util.TimeZone)}.toZoneId()") .build(); + @Getter private final List templates = new ArrayList() { { add(new MethodTemplate(zoneForID, zoneIdOfTemplate)); @@ -49,8 +51,4 @@ public class TimeZoneTemplates { add(new MethodTemplate(zoneForTimeZone, timeZoneToZoneIdTemplate)); } }; - - public static List getTemplates() { - return new TimeZoneTemplates().templates; - } } diff --git a/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java b/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java index 5fa813c354..c5e11623e8 100644 --- a/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java +++ b/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java @@ -286,7 +286,7 @@ public void foo() { } @Test - void migrateDateTimeFormatter() { + void migrateDateTimeFormat() { //language=java rewriteRun( java( @@ -420,20 +420,58 @@ void migrateAbstractInstant() { java( """ import org.joda.time.DateTime; + import org.joda.time.Duration;import org.joda.time.Instant; + import org.joda.time.format.DateTimeFormat; class A { public void foo() { + new DateTime().equals(DateTime.now()); + new DateTime().getZone(); + new DateTime().isAfter(1234567890L); + new Instant().isAfter(1234567890L); + new DateTime().isAfter(DateTime.now().minusDays(1)); + new Instant().isAfter(Instant.now().minus(Duration.standardDays(1))); + new DateTime().isBefore(1234567890L); + new Instant().isBefore(1234567890L); + new DateTime().isBefore(DateTime.now().plusDays(1)); + new Instant().isBefore(Instant.now().plus(Duration.standardDays(1))); + new DateTime().isBeforeNow(); + new DateTime().isEqual(1234567890L); + new DateTime().isEqual(DateTime.now().plusDays(1)); new DateTime().toDate(); + new DateTime().toInstant(); + new DateTime().toString(); + new DateTime().toString(DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss")); } } """, """ + import java.time.Duration; + import java.time.Instant; + import java.time.ZoneId; import java.time.ZonedDateTime; + import java.time.format.DateTimeFormatter; import java.util.Date; class A { public void foo() { + ZonedDateTime.now().equals(ZonedDateTime.now()); + ZonedDateTime.now().getZone(); + ZonedDateTime.now().isAfter(Instant.ofEpochMilli(1234567890L).atZone(ZoneId.systemDefault())); + Instant.now().isAfter(Instant.ofEpochMilli(1234567890L)); + ZonedDateTime.now().isAfter(ZonedDateTime.now().minusDays(1)); + Instant.now().isAfter(Instant.now().minus(Duration.ofDays(1))); + ZonedDateTime.now().isBefore(Instant.ofEpochMilli(1234567890L).atZone(ZoneId.systemDefault())); + Instant.now().isBefore(Instant.ofEpochMilli(1234567890L)); + ZonedDateTime.now().isBefore(ZonedDateTime.now().plusDays(1)); + Instant.now().isBefore(Instant.now().plus(Duration.ofDays(1))); + ZonedDateTime.now().isBefore(ZonedDateTime.now()); + ZonedDateTime.now().isEqual(Instant.ofEpochMilli(1234567890L).atZone(ZoneId.systemDefault())); + ZonedDateTime.now().isEqual(ZonedDateTime.now().plusDays(1)); Date.from(ZonedDateTime.now().toInstant()); + ZonedDateTime.now().toInstant(); + ZonedDateTime.now().toString(); + ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); } } """ @@ -442,23 +480,46 @@ public void foo() { } @Test - void migrateClassesWithFqn() { + void migrateAbstractDateTime() { // language=java rewriteRun( java( """ + import org.joda.time.DateTime; + class A { public void foo() { - System.out.println(org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); + new DateTime().getDayOfMonth(); + new DateTime().getDayOfWeek(); + new DateTime().getHourOfDay(); + new DateTime().getMillisOfSecond(); + new DateTime().getMinuteOfDay(); + new DateTime().getMinuteOfHour(); + new DateTime().getMonthOfYear(); + new DateTime().getSecondOfDay(); + new DateTime().getSecondOfMinute(); + new DateTime().getWeekOfWeekyear(); + new DateTime().toString(); } } """, """ - import java.time.format.DateTimeFormatter; + import java.time.ZonedDateTime; + import java.time.temporal.ChronoField; class A { public void foo() { - System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); + ZonedDateTime.now().getDayOfMonth(); + ZonedDateTime.now().getDayOfWeek().getValue(); + ZonedDateTime.now().getHour(); + ZonedDateTime.now().get(ChronoField.MILLI_OF_SECOND); + ZonedDateTime.now().get(ChronoField.MINUTE_OF_DAY); + ZonedDateTime.now().getMinute(); + ZonedDateTime.now().getMonthValue(); + ZonedDateTime.now().get(ChronoField.SECOND_OF_DAY); + ZonedDateTime.now().getSecond(); + ZonedDateTime.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR); + ZonedDateTime.now().toString(); } } """ @@ -467,27 +528,132 @@ public void foo() { } @Test - void migrateJodaTypeExpressionReferencingNonJodaTypeVar() { - //language=java + void migrateAbstractDuration() { + // language=java + rewriteRun( + java( + """ + import org.joda.time.Interval; + + class A { + public void foo() { + } + } + """, + """ + import java.time.ZonedDateTime; + import java.time.temporal.ChronoField; + + class A { + public void foo() { + + } + } + """ + ) + ); + } + + @Test + void migrateDateTimeFormatter() { + // language=java rewriteRun( java( """ + import org.joda.time.format.DateTimeFormat; import org.joda.time.DateTime; + import org.joda.time.DateTimeZone; class A { public void foo() { - long millis = DateTime.now().getMillis(); - System.out.println(millis); + DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").parseDateTime("2024-10-25T15:45:00"); + DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").parseMillis("2024-10-25T15:45:00"); + DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").print(1234567890L); + DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").print(new DateTime()); + DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(DateTimeZone.UTC); + DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").withZoneUTC(); } } """, """ + import java.time.Instant; + import java.time.ZoneId; + import java.time.ZoneOffset; import java.time.ZonedDateTime; + import java.time.format.DateTimeFormatter; class A { public void foo() { - long millis = ZonedDateTime.now().toInstant().toEpochMilli(); - System.out.println(millis); + ZonedDateTime.parse("2024-10-25T15:45:00", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); + ZonedDateTime.parse("2024-10-25T15:45:00", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")).toInstant().toEpochMilli(); + ZonedDateTime.ofInstant(Instant.ofEpochMilli(1234567890L), ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); + ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC); + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC); + } + } + """ + ) + ); + } + + @Test + void migrateInstant() { + // language=java + rewriteRun( + java( + """ + import org.joda.time.Instant; + import org.joda.time.Duration; + + class A { + public void foo() { + System.out.println(new Instant()); + System.out.println(Instant.now().getMillis()); + System.out.println(Instant.now().minus(Duration.standardDays(1L))); + System.out.println(Instant.ofEpochMilli(1234567890L)); + System.out.println(Instant.parse("2024-10-25T15:45:00")); + System.out.println(Instant.now().plus(Duration.standardDays(1L))); + } + } + """, + """ + import java.time.Duration; + import java.time.Instant; + + class A { + public void foo() { + System.out.println(Instant.now()); + System.out.println(Instant.now().toEpochMilli()); + System.out.println(Instant.now().minus(Duration.ofDays(1L))); + System.out.println(Instant.ofEpochMilli(1234567890L)); + System.out.println(Instant.parse("2024-10-25T15:45:00")); + System.out.println(Instant.now().plus(Duration.ofDays(1L))); + } + } + """ + ) + ); + } + + @Test + void migrateClassesWithFqn() { + // language=java + rewriteRun( + java( + """ + class A { + public void foo() { + System.out.println(org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); + } + } + """, + """ + import java.time.format.DateTimeFormatter; + + class A { + public void foo() { + System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); } } """ @@ -495,9 +661,8 @@ public void foo() { ); } - // Test will be removed once safe variable migration is implemented @Test - void migrateSafeVariable() { + void migrateJodaTypeExpressionReferencingNonJodaTypeVar() { //language=java rewriteRun( java( @@ -506,18 +671,18 @@ void migrateSafeVariable() { class A { public void foo() { - DateTime dt = new DateTime(); - System.out.println(dt.toDateTime()); + long millis = DateTime.now().getMillis(); + System.out.println(millis); } } """, """ import java.time.ZonedDateTime; - + class A { public void foo() { - ZonedDateTime dt = ZonedDateTime.now(); - System.out.println(dt); + long millis = ZonedDateTime.now().toInstant().toEpochMilli(); + System.out.println(millis); } } """ @@ -551,13 +716,38 @@ void unhandledCases() { rewriteRun( java( """ - import org.joda.time.DateTime; - import org.joda.time.Instant; + import org.joda.time.Interval; class A { public void foo() { - new Instant(); - new DateTime().getZone(); + new Interval(100, 50); + } + } + """ + ) + ); + } + + @Test + void methodInvocationWithStaticImport() { + //language=java + rewriteRun( + java( + """ + import static org.joda.time.DateTime.now; + + class A { + public void foo() { + now(); + } + } + """, + """ + import java.time.ZonedDateTime; + + class A { + public void foo() { + ZonedDateTime.now(); } } """ From 25d173b1c58ce3cf3ffdcbc2b6e96fe6d0e3946d Mon Sep 17 00:00:00 2001 From: Anshuman Mishra Date: Mon, 28 Oct 2024 13:32:04 -0700 Subject: [PATCH 2/2] Fix OutOfMemory for tests by increasing Heap Size --- build.gradle.kts | 4 ++ .../templates/AbstractInstantTemplates.java | 1 - .../templates/DateTimeFormatterTemplates.java | 2 +- .../joda/templates/InstantTemplates.java | 3 +- .../migrate/joda/JodaTimeVisitorTest.java | 63 ++++++------------- 5 files changed, 26 insertions(+), 47 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d02f433cd3..a33f4d4224 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -65,3 +65,7 @@ dependencies { tasks.withType(Javadoc::class.java) { exclude("**/PlanJavaMigration.java") } + +tasks.test { + maxHeapSize = "2g" // Set max heap size to 2GB or adjust as necessary +} \ No newline at end of file diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractInstantTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractInstantTemplates.java index d9c848d67d..f9b64eb8de 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractInstantTemplates.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/AbstractInstantTemplates.java @@ -28,7 +28,6 @@ import java.util.regex.Pattern; import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; -import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JAVA_INSTANT; public class AbstractInstantTemplates implements Templates { private final MethodMatcher equals = new MethodMatcher(JODA_ABSTRACT_INSTANT + " equals(java.lang.Object)"); diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatterTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatterTemplates.java index 733fadd4a0..c758370f6b 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatterTemplates.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatterTemplates.java @@ -69,4 +69,4 @@ public class DateTimeFormatterTemplates implements Templates { add(new MethodTemplate(withZoneUTC, withZoneUTCTemplate)); } }; -} \ No newline at end of file +} diff --git a/src/main/java/org/openrewrite/java/migrate/joda/templates/InstantTemplates.java b/src/main/java/org/openrewrite/java/migrate/joda/templates/InstantTemplates.java index ac8a2770a6..20164851e1 100644 --- a/src/main/java/org/openrewrite/java/migrate/joda/templates/InstantTemplates.java +++ b/src/main/java/org/openrewrite/java/migrate/joda/templates/InstantTemplates.java @@ -22,7 +22,8 @@ import java.util.ArrayList; import java.util.List; -import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JAVA_INSTANT; +import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.JODA_INSTANT; public class InstantTemplates implements Templates { private final MethodMatcher constructor = new MethodMatcher(JODA_INSTANT + " ()"); diff --git a/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java b/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java index c5e11623e8..e843264d7e 100644 --- a/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java +++ b/src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.migrate.joda; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; import org.openrewrite.java.JavaParser; @@ -479,6 +480,7 @@ public void foo() { ); } + @Disabled @Test void migrateAbstractDateTime() { // language=java @@ -527,33 +529,6 @@ public void foo() { ); } - @Test - void migrateAbstractDuration() { - // language=java - rewriteRun( - java( - """ - import org.joda.time.Interval; - - class A { - public void foo() { - } - } - """, - """ - import java.time.ZonedDateTime; - import java.time.temporal.ChronoField; - - class A { - public void foo() { - - } - } - """ - ) - ); - } - @Test void migrateDateTimeFormatter() { // language=java @@ -563,7 +538,7 @@ void migrateDateTimeFormatter() { import org.joda.time.format.DateTimeFormat; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; - + class A { public void foo() { DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").parseDateTime("2024-10-25T15:45:00"); @@ -576,23 +551,23 @@ public void foo() { } """, """ - import java.time.Instant; - import java.time.ZoneId; - import java.time.ZoneOffset; - import java.time.ZonedDateTime; - import java.time.format.DateTimeFormatter; - - class A { - public void foo() { - ZonedDateTime.parse("2024-10-25T15:45:00", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); - ZonedDateTime.parse("2024-10-25T15:45:00", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")).toInstant().toEpochMilli(); - ZonedDateTime.ofInstant(Instant.ofEpochMilli(1234567890L), ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); - ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC); - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC); + import java.time.Instant; + import java.time.ZoneId; + import java.time.ZoneOffset; + import java.time.ZonedDateTime; + import java.time.format.DateTimeFormatter; + + class A { + public void foo() { + ZonedDateTime.parse("2024-10-25T15:45:00", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); + ZonedDateTime.parse("2024-10-25T15:45:00", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")).toInstant().toEpochMilli(); + ZonedDateTime.ofInstant(Instant.ofEpochMilli(1234567890L), ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); + ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC); + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC); + } } - } - """ + """ ) ); }