Skip to content

Commit

Permalink
Fix for #1079: support StaticMethodCallExpression in convert to property
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Apr 9, 2020
1 parent 7d14e8a commit fc282f8
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -226,37 +226,51 @@ final class QuickAssistTests extends QuickFixTestSuite {

@Test
void testConvertToProperty1() {
assertProposalNotOffered(
'"".length()',
4, 0, new ConvertAccessorToPropertyProposal())
}

@Test
void testConvertToProperty2() {
assertProposalNotOffered(
'[].set(1, null)',
4, 0, new ConvertAccessorToPropertyProposal())
}

@Test
void testConvertToProperty3() {
assertConversion(
'"".isEmpty()',
'"".empty',
4, 0, new ConvertAccessorToPropertyProposal())
}

@Test
void testConvertToProperty2() {
void testConvertToProperty4() {
assertConversion(
'"".getBytes()',
'"".bytes',
4, 0, new ConvertAccessorToPropertyProposal())
}

@Test
void testConvertToProperty3() {
void testConvertToProperty5() {
assertProposalNotOffered(
'"".getBytes("UTF-8")',
4, 0, new ConvertAccessorToPropertyProposal())
}

@Test
void testConvertToProperty4() {
void testConvertToProperty6() {
assertConversion(
'new Date().setTime(1L);',
'new Date().time = 1L;',
'set', new ConvertAccessorToPropertyProposal())
}

@Test
void testConvertToProperty4a() {
void testConvertToProperty6a() {
setJavaPreference(FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR, JavaCore.DO_NOT_INSERT)
try {
assertConversion(
Expand All @@ -269,17 +283,57 @@ final class QuickAssistTests extends QuickFixTestSuite {
}

@Test
void testConvertToProperty5() {
assertProposalNotOffered(
'[].set(1, null)',
4, 0, new ConvertAccessorToPropertyProposal())
void testConvertToProperty7() {
assertConversion('''\
|class Foo {
| def bar
| void test() {
| getBar()
| }
|}
|'''.stripMargin(), '''\
|class Foo {
| def bar
| void test() {
| bar
| }
|}
|'''.stripMargin(),
'getBar', new ConvertAccessorToPropertyProposal())
}

@Test
void testConvertToProperty6() {
assertProposalNotOffered(
'"".length()',
4, 0, new ConvertAccessorToPropertyProposal())
void testConvertToProperty8() {
assertConversion('''\
|class Foo {
| static getBar() {}
| static test() {
| getBar()
| }
|}
|'''.stripMargin(), '''\
|class Foo {
| static getBar() {}
| static test() {
| bar
| }
|}
|'''.stripMargin(),
'getBar', new ConvertAccessorToPropertyProposal())
}

@Test
void testConvertToProperty9() {
addGroovySource '''\
|class Bar {
| static getBaz() {}
|}
|'''.stripMargin(), 'foo', 'Bar'

assertConversion(
'import static foo.Bar.getBaz\ngetBaz().hashCode()',
'import static foo.Bar.getBaz\nbaz.hashCode()',
'getBaz', new ConvertAccessorToPropertyProposal())
}

@Test
Expand Down Expand Up @@ -513,7 +567,7 @@ final class QuickAssistTests extends QuickFixTestSuite {
assertConversion(
'v && g && a',
'g && v && a',
'&&', new SwapLeftAndRightOperandsProposal())
' &&', new SwapLeftAndRightOperandsProposal())
}

@Test
Expand Down Expand Up @@ -1964,7 +2018,7 @@ final class QuickAssistTests extends QuickFixTestSuite {
//

private void assertConversion(String original, String expected, String target, GroovyQuickAssistProposal proposal) {
int offset = (target == null ? 0 : original.indexOf(target)),
int offset = (target == null ? 0 : original.lastIndexOf(target)),
length = (target == null ? 0 : target.length())
assertConversion(original, expected, offset, length, proposal)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
* Copyright 2009-2018 the original author or authors.
* Copyright 2009-2020 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
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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,
Expand All @@ -27,7 +27,9 @@
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.MethodCall;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
import org.codehaus.groovy.eclipse.GroovyPlugin;
import org.codehaus.groovy.eclipse.codebrowsing.fragments.ASTFragmentKind;
import org.codehaus.groovy.eclipse.codebrowsing.fragments.IASTFragment;
Expand All @@ -52,7 +54,7 @@ public class ConvertToPropertyAction extends Action {

private final GroovyEditor editor;

public ConvertToPropertyAction(GroovyEditor editor) {
public ConvertToPropertyAction(final GroovyEditor editor) {
this.editor = editor;
setText("Replace Accessor call with Property read/write");
setActionDefinitionId("org.codehaus.groovy.eclipse.ui.convertToProperty");
Expand All @@ -78,49 +80,56 @@ public void run() {
}
}

public static TextEdit createEdit(GroovyCompilationUnit gcu, int pos, int len) {
public static TextEdit createEdit(final GroovyCompilationUnit gcu, final int pos, final int len) {
ModuleNodeInfo info = gcu.getModuleInfo(true);
if (!info.isEmpty()) {
MethodCall call = null;

ASTNode node = new ASTNodeFinder(new Region(pos, len)).doVisit(info.module);
if (node instanceof ConstantExpression) {
IASTFragment fragment = new FindSurroundingNode(new Region(node)).doVisitSurroundingNode(info.module);
if (fragment.kind() == ASTFragmentKind.METHOD_CALL) {
MethodCallExpression call = (MethodCallExpression) fragment.getAssociatedNode();
if (call != null && !call.isUsingGenerics() && call.getArguments() instanceof ArgumentListExpression) {
ArgumentListExpression args = (ArgumentListExpression) call.getArguments();

Matcher match; // check for accessor or mutator
if (args.getExpressions().isEmpty() && (match = compile("(?:get|is)(\\p{javaJavaIdentifierPart}+)").matcher(call.getMethodAsString())).matches()) {
int offset = node.getStart(),
length = (args.getEnd() + 1) - offset;
String propertyName = match.group(1);

// replace "getPropertyName()" with "propertyName"
return new ReplaceEdit(offset, length, decapitalize(propertyName));

} else if (args.getExpressions().size() == 1 && (match = compile("set(\\p{javaJavaIdentifierPart}+)").matcher(call.getMethodAsString())).matches()) {
int offset = node.getStart(),
length = args.getStart() - offset;
String propertyName = match.group(1);

// replace "setPropertyName(value_expression)" or "setPropertyName value_expression"
// with "propertyName = value_expression" (check prefs for spaces around assignment)
MultiTextEdit edits = new MultiTextEdit();
Map<String, String> options = gcu.getJavaProject().getOptions(true);
StringBuilder replacement = new StringBuilder(decapitalize(propertyName));
if (JavaCore.INSERT.equals(options.get(FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR)))
replacement.append(' ');
replacement.append('=');
if (JavaCore.INSERT.equals(options.get(FORMATTER_INSERT_SPACE_AFTER_ASSIGNMENT_OPERATOR)))
replacement.append(' ');

edits.addChild(new ReplaceEdit(offset, length, replacement.toString()));
if (gcu.getContents()[args.getEnd()] == ')') edits.addChild(new DeleteEdit(args.getEnd(), 1));

return edits;
}
MethodCallExpression expr = (MethodCallExpression) fragment.getAssociatedNode();
if (expr != null && !expr.isUsingGenerics()) {
call = expr;
}
}
} else if (node instanceof StaticMethodCallExpression) {
call = (StaticMethodCallExpression) node;
}

if (call != null && call.getArguments() instanceof ArgumentListExpression) {
ArgumentListExpression args = (ArgumentListExpression) call.getArguments();

Matcher match; // check for accessor or mutator
if (args.getExpressions().isEmpty() && (match = compile("(?:get|is)(\\p{javaJavaIdentifierPart}+)").matcher(call.getMethodAsString())).matches()) {
int offset = node.getStart(), length = (args.getEnd() + 1) - offset;
String propertyName = match.group(1);

// replace "getPropertyName()" with "propertyName"
return new ReplaceEdit(offset, length, decapitalize(propertyName));

} else if (args.getExpressions().size() == 1 && (match = compile("set(\\p{javaJavaIdentifierPart}+)").matcher(call.getMethodAsString())).matches()) {
int offset = node.getStart(), length = args.getStart() - offset;
String propertyName = match.group(1);

// replace "setPropertyName(value_expression)" or "setPropertyName value_expression"
// with "propertyName = value_expression" (check prefs for spaces around assignment)
MultiTextEdit edits = new MultiTextEdit();
Map<String, String> options = gcu.getJavaProject().getOptions(true);
StringBuilder replacement = new StringBuilder(decapitalize(propertyName));
if (JavaCore.INSERT.equals(options.get(FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR))) {
replacement.append(' ');
}
replacement.append('=');
if (JavaCore.INSERT.equals(options.get(FORMATTER_INSERT_SPACE_AFTER_ASSIGNMENT_OPERATOR))) {
replacement.append(' ');
}
edits.addChild(new ReplaceEdit(offset, length, replacement.toString()));
if (gcu.getContents()[args.getEnd()] == ')') edits.addChild(new DeleteEdit(args.getEnd(), 1));

return edits;
}
}
}
return null;
Expand Down

0 comments on commit fc282f8

Please sign in to comment.