Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Joda time to Java time: Add few more templates for migration #594

Merged
merged 2 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we should probably set a higher memory limit than the default 512MB there. I am a bit surprised that this project needs more than that, though. The unit tests shouldn't be particularly demanding on memory, but maybe something has increased our consumption from the last time I looked

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe applyTemplate operation is very heavy on memory. And this JodaTime has too many apply template calls. Everytime it OOMed on applyTemplate call.

}
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,98 @@
*/
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.*;

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