Skip to content

Commit

Permalink
Joda time to Java time: Add few more templates for migration
Browse files Browse the repository at this point in the history
  • Loading branch information
amishra-u committed Oct 28, 2024
1 parent 88cf8f5 commit 88bcb91
Show file tree
Hide file tree
Showing 15 changed files with 720 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,15 +30,6 @@

public class JodaTimeVisitor extends ScopeAwareVisitor {

private final MethodMatcher anyNewDateTime = new MethodMatcher(JODA_DATE_TIME + "<constructor>(..)");
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 + "<constructor>(..)");
private final MethodMatcher anyDuration = new MethodMatcher(JODA_DURATION + " *(..)");
private final MethodMatcher anyAbstractInstant = new MethodMatcher(JODA_ABSTRACT_INSTANT + " *(..)");

private final Set<NamedVariable> unsafeVars;

public JodaTimeVisitor(Set<NamedVariable> unsafeVars, LinkedList<VariablesInScope> scopes) {
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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)) {
Expand Down Expand Up @@ -219,15 +195,13 @@ private boolean hasJodaType(List<Expression> exprs) {
return false;
}

private Optional<J> applyTemplate(MethodCall original, MethodCall updated, List<MethodTemplate> 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<J> 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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.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<MethodTemplate> templates = new ArrayList<MethodTemplate>() {
{
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));
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.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<MethodTemplate> templates = new ArrayList<MethodTemplate>() {
{
add(new MethodTemplate(isLongerThan, isLongerThanTemplate));
add(new MethodTemplate(toPeriod, toPeriodTemplate));
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<MethodTemplate> templates = new ArrayList<MethodTemplate>() {
{
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<MethodTemplate> 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;
}
}
Loading

0 comments on commit 88bcb91

Please sign in to comment.