-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[1/x] Implement joda to java time migration recipe (#567)
* [1/x] Implement joda to java time migration recipe * Update src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * add licence & test fix * license * Update src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatTemplates.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatTemplates.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/java/migrate/joda/templates/DurationTemplates.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeTemplates.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/java/migrate/joda/templates/MethodTemplate.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/java/migrate/joda/templates/TimeClassNames.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/java/migrate/joda/templates/TimeZoneTemplates.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/java/migrate/joda/templates/TimeZoneTemplates.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/java/migrate/joda/JodaTimeVisitorTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: Sam Snyder <sam@moderne.io> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
ae8faf5
commit 631f5dc
Showing
9 changed files
with
1,652 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
180 changes: 180 additions & 0 deletions
180
src/main/java/org/openrewrite/java/migrate/joda/JodaTimeVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
/* | ||
* 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; | ||
|
||
import lombok.NonNull; | ||
import org.openrewrite.ExecutionContext; | ||
import org.openrewrite.java.JavaVisitor; | ||
import org.openrewrite.java.migrate.joda.templates.*; | ||
import org.openrewrite.java.migrate.joda.templates.MethodTemplate; | ||
import org.openrewrite.java.migrate.joda.templates.TimeZoneTemplates; | ||
import org.openrewrite.java.tree.Expression; | ||
import org.openrewrite.java.tree.J; | ||
import org.openrewrite.java.tree.JavaType; | ||
import org.openrewrite.java.tree.MethodCall; | ||
import org.openrewrite.java.tree.TypeUtils; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.regex.Pattern; | ||
|
||
import static org.openrewrite.java.migrate.joda.templates.TimeClassNames.*; | ||
|
||
|
||
public class JodaTimeVisitor extends JavaVisitor<ExecutionContext> { | ||
|
||
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 + " *(..)"); | ||
|
||
@Override | ||
public J visitCompilationUnit(@NonNull J.CompilationUnit cu, @NonNull ExecutionContext ctx) { | ||
maybeRemoveImport(JODA_DATE_TIME); | ||
maybeRemoveImport(JODA_DATE_TIME_ZONE); | ||
maybeRemoveImport(JODA_TIME_FORMAT); | ||
maybeRemoveImport(JODA_DURATION); | ||
maybeRemoveImport("java.util.Locale"); | ||
|
||
maybeAddImport(JAVA_DATE_TIME); | ||
maybeAddImport(JAVA_ZONE_OFFSET); | ||
maybeAddImport(JAVA_ZONE_ID); | ||
maybeAddImport(JAVA_INSTANT); | ||
maybeAddImport(JAVA_TIME_FORMATTER); | ||
maybeAddImport(JAVA_TIME_FORMAT_STYLE); | ||
maybeAddImport(JAVA_DURATION); | ||
maybeAddImport(JAVA_LOCAL_DATE); | ||
maybeAddImport(JAVA_LOCAL_TIME); | ||
maybeAddImport(JAVA_TEMPORAL_ISO_FIELDS); | ||
maybeAddImport(JAVA_CHRONO_FIELD); | ||
return super.visitCompilationUnit(cu, ctx); | ||
} | ||
|
||
@Override | ||
public J visitVariable(@NonNull J.VariableDeclarations.NamedVariable variable, @NonNull ExecutionContext ctx) { | ||
// TODO implement logic for safe variable migration | ||
if (variable.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { | ||
return variable; | ||
} | ||
return super.visitVariable(variable, ctx); | ||
} | ||
|
||
@Override | ||
public J visitNewClass(@NonNull J.NewClass newClass, @NonNull ExecutionContext ctx) { | ||
MethodCall updated = (MethodCall) super.visitNewClass(newClass, ctx); | ||
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); | ||
} | ||
if (areArgumentsAssignable(updated)) { | ||
return updated; | ||
} | ||
return newClass; | ||
} | ||
|
||
|
||
@Override | ||
public J visitMethodInvocation(@NonNull J.MethodInvocation method, @NonNull ExecutionContext ctx) { | ||
J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); | ||
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 (anyTimeFormatter.matches(method)) { | ||
return applyTemplate(method, m, DateTimeFormatTemplates.getTemplates()).orElse(method); | ||
} | ||
if (anyDuration.matches(method)) { | ||
return applyTemplate(method, m, DurationTemplates.getTemplates()).orElse(method); | ||
} | ||
if (areArgumentsAssignable(m)) { | ||
return m; | ||
} | ||
return method; | ||
} | ||
|
||
@Override | ||
public J visitFieldAccess(@NonNull J.FieldAccess fieldAccess, @NonNull ExecutionContext ctx) { | ||
J.FieldAccess f = (J.FieldAccess) super.visitFieldAccess(fieldAccess, ctx); | ||
if (TypeUtils.isOfClassType(f.getType(), JODA_DATE_TIME_ZONE) && f.getSimpleName().equals("UTC")) { | ||
return JavaTemplate.builder("ZoneOffset.UTC") | ||
.imports(JAVA_ZONE_OFFSET) | ||
.build() | ||
.apply(updateCursor(f), f.getCoordinates().replace()); | ||
} | ||
return f; | ||
} | ||
|
||
private boolean hasJodaType(List<Expression> exprs) { | ||
for (Expression expr : exprs) { | ||
JavaType exprType = expr.getType(); | ||
if (exprType != null && exprType.isAssignableFrom(Pattern.compile("org.joda.time.*"))) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private Optional<MethodCall> 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(), args)); | ||
} | ||
} | ||
return Optional.empty(); // unhandled case | ||
} | ||
|
||
private boolean areArgumentsAssignable(MethodCall m) { | ||
if (m.getArguments().size() != m.getMethodType().getParameterTypes().size()) { | ||
return false; | ||
} | ||
for (int i = 0; i < m.getArguments().size(); i++) { | ||
if (!TypeUtils.isAssignableTo(m.getMethodType().getParameterTypes().get(i), m.getArguments().get(i).getType())) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
private boolean isJodaVarRef(Expression expr) { | ||
if (expr.getType() == null || !expr.getType().isAssignableFrom(JODA_CLASS_PATTERN)) { | ||
return false; | ||
} | ||
if (expr instanceof J.FieldAccess) { | ||
return ((J.FieldAccess) expr).getName().getFieldType() != null; | ||
} | ||
if (expr instanceof J.Identifier) { | ||
return ((J.Identifier) expr).getFieldType() != null; | ||
} | ||
return false; | ||
} | ||
} |
133 changes: 133 additions & 0 deletions
133
src/main/java/org/openrewrite/java/migrate/joda/templates/DateTimeFormatTemplates.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* | ||
* 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. | ||
*/ | ||
/* | ||
* 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. | ||
*/ | ||
/* | ||
* 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 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 DateTimeFormatTemplates { | ||
private final MethodMatcher forPattern = new MethodMatcher(JODA_TIME_FORMAT + " forPattern(String)"); | ||
private final MethodMatcher forStyle = new MethodMatcher(JODA_TIME_FORMAT + " forStyle(String)"); | ||
private final MethodMatcher patternForStyle = new MethodMatcher(JODA_TIME_FORMAT + " patternForStyle(String, java.util.Locale)"); | ||
private final MethodMatcher shortDate = new MethodMatcher(JODA_TIME_FORMAT + " shortDate()"); | ||
private final MethodMatcher mediumDate = new MethodMatcher(JODA_TIME_FORMAT + " mediumDate()"); | ||
private final MethodMatcher longDate = new MethodMatcher(JODA_TIME_FORMAT + " longDate()"); | ||
private final MethodMatcher fullDate = new MethodMatcher(JODA_TIME_FORMAT + " fullDate()"); | ||
private final MethodMatcher shortTime = new MethodMatcher(JODA_TIME_FORMAT + " shortTime()"); | ||
private final MethodMatcher mediumTime = new MethodMatcher(JODA_TIME_FORMAT + " mediumTime()"); | ||
private final MethodMatcher longTime = new MethodMatcher(JODA_TIME_FORMAT + " longTime()"); | ||
private final MethodMatcher fullTime = new MethodMatcher(JODA_TIME_FORMAT + " fullTime()"); | ||
private final MethodMatcher shortDateTime = new MethodMatcher(JODA_TIME_FORMAT + " shortDateTime()"); | ||
private final MethodMatcher mediumDateTime = new MethodMatcher(JODA_TIME_FORMAT + " mediumDateTime()"); | ||
private final MethodMatcher longDateTime = new MethodMatcher(JODA_TIME_FORMAT + " longDateTime()"); | ||
private final MethodMatcher fullDateTime = new MethodMatcher(JODA_TIME_FORMAT + " fullDateTime()"); | ||
|
||
private final JavaTemplate ofPatternTemplate = JavaTemplate.builder("DateTimeFormatter.ofPattern(#{any(String)})") | ||
.imports("java.time.format.DateTimeFormatter") | ||
.build(); | ||
private final JavaTemplate shortDateTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate mediumDateTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate longDateTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate fullDateTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate shortTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate mediumTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate longTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate fullTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedTime(FormatStyle.FULL)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate shortDateTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate mediumDateTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate longDateTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.LONG)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final JavaTemplate fullDateTimeTemplate = JavaTemplate.builder("DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL)") | ||
.imports(JAVA_TIME_FORMATTER, JAVA_TIME_FORMAT_STYLE) | ||
.build(); | ||
private final List<MethodTemplate> templates = new ArrayList<MethodTemplate>() { | ||
{ | ||
add(new MethodTemplate(forPattern, ofPatternTemplate)); | ||
add(new MethodTemplate(shortDate, shortDateTemplate)); | ||
add(new MethodTemplate(mediumDate, mediumDateTemplate)); | ||
add(new MethodTemplate(longDate, longDateTemplate)); | ||
add(new MethodTemplate(fullDate, fullDateTemplate)); | ||
add(new MethodTemplate(shortTime, shortTimeTemplate)); | ||
add(new MethodTemplate(mediumTime, mediumTimeTemplate)); | ||
add(new MethodTemplate(longTime, longTimeTemplate)); | ||
add(new MethodTemplate(fullTime, fullTimeTemplate)); | ||
add(new MethodTemplate(shortDateTime, shortDateTimeTemplate)); | ||
add(new MethodTemplate(mediumDateTime, mediumDateTimeTemplate)); | ||
add(new MethodTemplate(longDateTime, longDateTimeTemplate)); | ||
add(new MethodTemplate(fullDateTime, fullDateTimeTemplate)); | ||
} | ||
}; | ||
|
||
public static List<MethodTemplate> getTemplates() { | ||
return new DateTimeFormatTemplates().templates; | ||
} | ||
} |
Oops, something went wrong.