### Eclipse Workspace Patch 1.0 #P org.codehaus.groovy.eclipse.quickfix Index: src/org/codehaus/groovy/eclipse/quickassist/GroovyQuickAssist.java =================================================================== --- src/org/codehaus/groovy/eclipse/quickassist/GroovyQuickAssist.java (revision 23145) +++ src/org/codehaus/groovy/eclipse/quickassist/GroovyQuickAssist.java (working copy) @@ -41,7 +41,9 @@ new ConvertToSingleLineStringCompletionProposal(context).hasProposals() || new RemoveUnnecessarySemicolonsCompletionProposal(context).hasProposals() || new SwapOperandsCompletionProposal(context).hasProposals() || - new SplitAssigmentCompletionProposal(context).hasProposals(); + new SplitAssigmentCompletionProposal(context).hasProposals() || + new ConvertToForLoopProposal(context).hasProposals(); + } return false; } @@ -89,6 +91,10 @@ proposalList.add(splitAssignment); } + ConvertToForLoopProposal convertToFor = new ConvertToForLoopProposal(context); + if (convertToFor.hasProposals()) { + proposalList.add(convertToFor); + } return proposalList.toArray(new IJavaCompletionProposal[0]); } Index: src/org/codehaus/groovy/eclipse/quickassist/ConvertToForLoopProposal.java =================================================================== --- src/org/codehaus/groovy/eclipse/quickassist/ConvertToForLoopProposal.java (revision 0) +++ src/org/codehaus/groovy/eclipse/quickassist/ConvertToForLoopProposal.java (revision 0) @@ -0,0 +1,258 @@ +/* + * Copyright 2011 SpringSource, a division of VMware, Inc + * + * andrew - Initial API and implementation + * + * 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 + * + * 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.codehaus.groovy.eclipse.quickassist; + + +import java.lang.String; + +import java.util.List; + + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ModuleNode; + +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.eclipse.codebrowsing.requestor.ASTNodeFinder; +import org.codehaus.groovy.eclipse.codebrowsing.requestor.Region; +import org.codehaus.groovy.eclipse.core.GroovyCore; +import org.codehaus.groovy.eclipse.refactoring.formatter.GroovyIndentationService; +import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.internal.ui.JavaPluginImages; +import org.eclipse.jdt.ui.text.java.IInvocationContext; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.contentassist.ContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.swt.graphics.Point; +import org.eclipse.text.edits.DeleteEdit; +import org.eclipse.text.edits.InsertEdit; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; + +/** + * Converts an each expression to a for loop + * + * @author Andrew Eisenberg + * @created Mar 13, 2012 + */ +public class ConvertToForLoopProposal extends + AbstractGroovyCompletionProposal { + + private class MethodCallFinder extends ASTNodeFinder { + public MethodCallFinder(Region r) { + super(r); + } + + @Override + public void visitMethodCallExpression(MethodCallExpression call) { + if (call.getMethodAsString().equals("each")) { + check(call); + } + super.visitMethodCallExpression(call); + } + } + + private final GroovyCompilationUnit unit; + private final int length; + private final int offset; + int idx = 0; + + private MethodCallExpression expr; + public ConvertToForLoopProposal(IInvocationContext context) { + super(context); + ICompilationUnit compUnit = context.getCompilationUnit(); + if (compUnit instanceof GroovyCompilationUnit) { + this.unit = (GroovyCompilationUnit) compUnit; + } else { + this.unit = null; + } + length = context.getSelectionLength(); + offset = context.getSelectionOffset(); + + } + + public int getRelevance() { + return 0; + } + + public void apply(IDocument document) { + TextEdit thisEdit = findReplacement(document); + try { + if (thisEdit != null) { + thisEdit.apply(document); + } + } catch (Exception e) { + GroovyCore.logException("Problem applying quickfix.", e); + } + } + + public Point getSelection(IDocument document) { + // this is not right. We should be updating the position based on the text changes + return new Point(offset, length+offset); + } + + public String getAdditionalProposalInfo() { + return getDisplayString(); + } + + public String getDisplayString() { + return "Convert to for loop"; + } + + public IContextInformation getContextInformation() { + return new ContextInformation(getImage(), getDisplayString(), getDisplayString()); + } + + @Override + protected String getImageBundleLocation() { + return JavaPluginImages.IMG_CORRECTION_CHANGE; + } + + @Override + public boolean hasProposals() { + if (unit == null) { + return false; + } + Region region = new Region(offset, length); + ModuleNode moduleNode = unit.getModuleNode(); + + MethodCallFinder finder = new MethodCallFinder(region); + ASTNode node = finder.doVisit(moduleNode); + + if (node instanceof MethodCallExpression) { + expr = (MethodCallExpression) node; + if ((expr.getArguments() instanceof ArgumentListExpression) + && (((ArgumentListExpression) expr.getArguments()).getExpressions().size() == 1) + && (expr.getObjectExpression() != null)) { + return true; + } + } + return false; + } + + // TODO more work to do here. + private TextEdit findReplacement(IDocument doc) { + TextEdit edits = new MultiTextEdit(); + Expression list[] = new Expression[1]; + List listStmt; + String it = "it"; + StringBuilder args = new StringBuilder(); + StringBuilder statement = new StringBuilder(); + String prtLine = "println"; + int c = 0; + // getting any expression before the method call + Expression objectExpr = expr.getObjectExpression(); + String strObjectExpr = objectExpr.getText().toString(); + // getting the argument + String argExpr = (String) expr.getArguments().getText(); + // getting the ClosureExpression to get the ReturnStatement + list[0] = (Expression) ((ArgumentListExpression) expr.getArguments()) + .getExpression(0); + ClosureExpression clsExpr = (ClosureExpression) list[0]; + Statement stExpr = clsExpr.getCode();// getting the statement expression + listStmt = ((BlockStatement) stExpr).getStatements();// getting the ReturnStatement + String statExpre = listStmt.get(0).getText();// ready to be manipulated + + // getting the variables + if (argExpr.length() < 13) {// default variable it + args.append(it); + } + /* to be implemented in the future.... */ + // else if(argExpr.length() >= 51) {//two variables + // + // for(c = 0; c < argExpr.length() - 1; c++){ + // + // if(argExpr.charAt(c) == ','){//we need just one.. + // idx = c; + // + // args = argExpr.substring((idx - 1), (idx));//getting argument + // variable + // + // break; + // } + // } + // } + else if (argExpr.length() >= 12 && argExpr.length() < 51) {// one variable + for (c = 3; c < argExpr.length() - 1; c++) { + if (argExpr.charAt(c) == '-') { + idx = c; + // getting the argument variable + args.append(argExpr.substring((idx - 2), (idx - 1))); + break; + } + } + } + // getting the Return statement + if (statExpre.length() > 19) { + // looking for println statements + if (prtLine.equalsIgnoreCase(statExpre.substring(12, 19))) { + statement.append(prtLine + " "); + for (c = 19; c < statExpre.length() - 1; c++) { + if (statExpre.charAt(c) == '('// getting everything inside parenthesis + || statExpre.charAt(c) == ')') { + ;// NOP + } else { + statement.append(statExpre.substring(c, c + 1)); + } + } + } + } else {// Return Statement does not include a println statement + for (c = 6; c < statExpre.length() - 1; c++) { + // getting everything inside parenthesis + if (statExpre.charAt(c) == '(' || statExpre.charAt(c) == ')') { + ;// NOP + } else { + statement.append(statExpre.substring(c, c + 1)); + } + } + } + + // setting up the indentation + int insertAt = objectExpr.getStart(); + int endAt = stExpr.getEnd(); + try { + + int lineNr = doc.getLineOfOffset(insertAt); + String spaceArg = GroovyIndentationService + .getLineLeadingWhiteSpace(doc, lineNr); + + String elements = "for (" + args + " in " + strObjectExpr + ")" + + "{" + "\n" + spaceArg + statement + "\n" + spaceArg + "}"; + + edits.addChild(new DeleteEdit(insertAt, endAt - insertAt)); + + edits.addChild(new ReplaceEdit(insertAt, 0, elements)); + if (edits != null) { + return edits; + } + } catch (Exception e) { + + GroovyCore.logException("Oops.", e); + return null; + } + return edits; + } +}