-
Notifications
You must be signed in to change notification settings - Fork 51
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
Replace week year (YYYY) with year (yyyy) in date formats #129
Conversation
src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java
Show resolved
Hide resolved
src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java
Outdated
Show resolved
Hide resolved
src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java
Outdated
Show resolved
Hide resolved
System.out.println("getting here"); | ||
|
||
return JavaTemplate.builder("new SimpleDateFormat(#{any(java.lang.String)})") | ||
.contextSensitive() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure about the contextSensitive here; what made you add that?
src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java
Outdated
Show resolved
Hide resolved
src/test/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYearTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial feedback already. Needs a bit more work I think, but good start.
src/test/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYearTest.java
Outdated
Show resolved
Hide resolved
src/test/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYearTest.java
Outdated
Show resolved
Hide resolved
…thYear.java Co-authored-by: Tim te Beek <[email protected]>
…thYearTest.java Co-authored-by: Tim te Beek <[email protected]>
…thYearTest.java Co-authored-by: Tim te Beek <[email protected]>
…thYearTest.java Co-authored-by: Tim te Beek <[email protected]>
src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java
Outdated
Show resolved
Hide resolved
I added a couple more tests to test cases like "YY" being used in format patterns. I think this |
Added 167ef5e to demonstrate an issue with the current implementation; you're replacing |
I thought the preconditions would stop it from running on the wrong strings. I ran across code samples where the string pattern would often be in just a standalone string variables. Other using preconditions to make sure the Date libraries are being used, I could also maybe add some extra logic to check for a larger date pattern after finding |
The preconditions determine whether to run on a compilation unit (typically file?) I believe; not on individual Strings. I think we'd want to use the cursor here to find if there's a wrapping method invocation around the String, and if that matches any of the methods we cover here. So start with |
167ef5e
to
42289b1
Compare
I couldn't find any method on |
Noticed there's now two failing tests; are you planning to pick those up and should I wait with a review?
|
Yes I will be picking those up. I was kind of stuck on them yesterday but now I have a new idea that maybe will help. Although I'm still not sure how we will know to edit formats that are assigned to variables. I could visit Identifiers as well but is there a way to access the identifiers value? |
We do have some data flow analysis support that I've not used before myself; you can try to look for examples or ask Jonathan Leitschuh, although it's also fine to for now leave out cases where the format strings are in variables or constants, and only focus on direct use of literals going into the methods identified here. |
Cursor c = getCursor().dropParentWhile(is -> is instanceof J.Parentheses || !(is instanceof Tree)); | ||
if (c.getMessage("KEY") != null) { | ||
String value = li.getValueSource(); | ||
if (value != null && value.contains("YY")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the check specifically here for at least YY
? I'm reading the specification and it seems to allow for single Y
as well.. 🤔
For parsing with the abbreviated year pattern ("y" or "yy"), SimpleDateFormat must interpret the abbreviated year relative to some century. It does this by adjusting dates to be within 80 years before and 20 years after the time the SimpleDateFormat instance is created. For example, using a pattern of "MM/dd/yy" and a SimpleDateFormat instance created on Jan 1, 1997, the string "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64" would be interpreted as May 4, 1964. During parsing, only strings consisting of exactly two digits, as defined by Character.isDigit(char), will be parsed into the default century. Any other numeric string, such as a one digit string, a three or more digit string, or a two digit string that isn't all digits (for example, "-1"), is interpreted literally. So "01/02/3" or "01/02/003" are parsed, using the same pattern, as Jan 2, 3 AD. Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
Otherwise, calendar system specific forms are applied. For both formatting and parsing, if the number of pattern letters is 4 or more, a calendar specific long form is used. Otherwise, a calendar specific short or abbreviated form is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And we might need to guard against the (quite hypothetical) use single quotes
Text can be quoted using single quotes (') to avoid interpretation. "''" represents a single quote. All other characters are not interpreted; they're simply copied into the output string during formatting or matched against the input string during parsing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok the latest commits should address these issues
|
||
public class ReplaceWeekYearWithYear extends Recipe { | ||
public static final MethodMatcher SIMPLE_DATE_FORMAT_CONSTRUCTOR_MATCHER = new MethodMatcher("java.text.SimpleDateFormat <constructor>(..)"); | ||
public static final MethodMatcher OF_PATTERN_MATCHER = new MethodMatcher("java.time.format.DateTimeFormatter ofPattern(..)"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detail: I suggest declaring these constants as private. API surface area is always easy to extend, but more difficult to shrink.
|
||
String newValue = replaceY(value); | ||
|
||
return li.withValueSource(newValue).withValue(newValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks a bit wrong to me. The valueSource
will be the value with quotes (possibly even Java 17 block quotes, for which we should have a test probably), while the value
is without quotes. We should update it correctly here.
} | ||
} | ||
|
||
return output.toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect that when we do the J.Literal#withValue()
call it may end up producing a new J.Literal
object. So I think we should here in the loop update a boolean
variable if there was any change made and only then return this result. Otherwise the input value to this method. Does that make sense to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, ya I can add a check to see if there are any changes actually being made.
Ok I've added another test case and made those last changes. |
@AlekSimpson I did a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
This review was on the initial commit. I have addressed all the feedback from it and made changes accordingly.
What's changed?
This PR introduces a new recipe to replace date formats that use week year (YYYY) with the year format (yyyy).
What's your motivation?
#58
Anything in particular you'd like reviewers to focus on?
I got the first test to pass but the second one I found a little more tricky. I tried using a
visitMethodInvocation
visitor for it. However, the.format()
invocation was being found instead of theofPattern
call. I tried getting the select of the method which did give me the format invocation but getting the visitor to account for the case when onlyofPattern
is called was tough. Do you think this is the right approach for this test? Maybe I should try using a visitor likevisitLiteral
and then checking if the literal is passed into anofPattern
invocation instead?Anyone you would like to review specifically?
@timtebeek
Any additional context
RSPEC-3986
Checklist
./gradlew licenseFormat