| /******************************************************************************* |
| * Copyright (c) 2018 itemis AG (http://www.itemis.eu) and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Karsten Thoms (itemis) - initial API and implementation |
| * Red Hat Inc. - copied and modified to replace extraneous semicolons |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.fix; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Map; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| |
| import org.eclipse.text.edits.ReplaceEdit; |
| import org.eclipse.text.edits.TextEdit; |
| import org.eclipse.text.edits.TextEditGroup; |
| |
| import org.eclipse.jdt.core.IBuffer; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.EmptyStatement; |
| import org.eclipse.jdt.core.dom.EnumDeclaration; |
| import org.eclipse.jdt.core.dom.FieldDeclaration; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.PackageDeclaration; |
| import org.eclipse.jdt.core.dom.TypeDeclaration; |
| import org.eclipse.jdt.core.refactoring.CompilationUnitChange; |
| |
| import org.eclipse.jdt.internal.corext.fix.CleanUpConstants; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility; |
| |
| import org.eclipse.jdt.ui.cleanup.CleanUpRequirements; |
| import org.eclipse.jdt.ui.cleanup.ICleanUpFix; |
| import org.eclipse.jdt.ui.text.java.IProblemLocation; |
| |
| /** |
| * A fix that removes redundant semi-colons from package declarations, field declarations, |
| * method declarations, and removes empty statements from blocks. Does not touch |
| * empty statements belonging to loops (e.g. empty for-loop) nor semicolons used |
| * in a for-loop statement itself (e.g. for(;;)). |
| */ |
| public class RedundantSemicolonsCleanUp extends AbstractMultiFix implements ICleanUpFix { |
| |
| private TextEditGroup[] fEditGroups; |
| private String fName; |
| private ICompilationUnit fCompilationUnit; |
| final static Pattern pattern= Pattern.compile("^((\\s*;)+)"); //$NON-NLS-1$ |
| |
| public RedundantSemicolonsCleanUp() { |
| this(Collections.emptyMap()); |
| } |
| |
| public RedundantSemicolonsCleanUp(Map<String, String> options) { |
| super(options); |
| } |
| |
| @Override |
| public CleanUpRequirements getRequirements() { |
| boolean requireAST= isEnabled(CleanUpConstants.REMOVE_REDUNDANT_SEMICOLONS); |
| Map<String, String> requiredOptions= null; |
| return new CleanUpRequirements(requireAST, false, false, requiredOptions); |
| } |
| |
| @Override |
| public String[] getStepDescriptions() { |
| if (isEnabled(CleanUpConstants.REMOVE_REDUNDANT_SEMICOLONS)) { |
| return new String[] { MultiFixMessages.RedundantSemicolonsCleanup_description }; |
| } |
| return new String[0]; |
| } |
| |
| @SuppressWarnings("nls") |
| @Override |
| public String getPreview() { |
| StringBuffer buf= new StringBuffer(); |
| buf.append("\n"); |
| buf.append("enum color {\n"); |
| buf.append(" red, yellow, green\n"); |
| if (isEnabled(CleanUpConstants.REMOVE_REDUNDANT_SEMICOLONS)) { |
| buf.append("}\n"); |
| } else { |
| buf.append("};\n"); |
| } |
| buf.append("\npublic class IFoo {\n"); |
| if (isEnabled(CleanUpConstants.REMOVE_REDUNDANT_SEMICOLONS)) { |
| buf.append(" int a= 3;\n"); |
| buf.append(" public void foo() {}\n"); |
| buf.append("}\n"); |
| } else { |
| buf.append(" int a= 3;;\n"); |
| buf.append(" public void foo() {;};\n"); |
| buf.append("};\n"); |
| } |
| |
| return buf.toString(); |
| } |
| |
| private void searchNode(ASTNode node, String contents, String label, ArrayList<TextEditGroup> textedits) { |
| int start= node.getStartPosition(); |
| int length= node.getLength(); |
| |
| int trailing = findTrailingSemicolons(contents, start + length); |
| |
| if (trailing > 0) { |
| ReplaceEdit edit = new ReplaceEdit(start + length, trailing, ""); //$NON-NLS-1$ |
| textedits.add(new TextEditGroup(label, edit)); |
| } |
| } |
| |
| @Override |
| protected ICleanUpFix createFix(CompilationUnit unit) throws CoreException { |
| if (!isEnabled(CleanUpConstants.REMOVE_REDUNDANT_SEMICOLONS)) { |
| return null; |
| } |
| ICompilationUnit compilationUnit = (ICompilationUnit)unit.getJavaElement(); |
| IBuffer buffer= compilationUnit.getBuffer(); |
| String contents= buffer.getContents(); |
| |
| String label= MultiFixMessages.RedundantSemicolonsCleanup_description; |
| ArrayList<TextEditGroup> textedits= new ArrayList<>(); |
| |
| unit.accept(new ASTVisitor() { |
| @Override |
| public boolean visit(PackageDeclaration node) { |
| searchNode(node, contents, label, textedits); |
| return false; |
| } |
| |
| @Override |
| public boolean visit(FieldDeclaration node) { |
| searchNode(node, contents, label, textedits); |
| return false; |
| } |
| |
| @Override |
| public boolean visit(MethodDeclaration node) { |
| searchNode(node, contents, label, textedits); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(EnumDeclaration node) { |
| searchNode(node, contents, label, textedits); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(TypeDeclaration node) { |
| searchNode(node, contents, label, textedits); |
| return true; |
| } |
| |
| @Override |
| public boolean visit(EmptyStatement node) { |
| ASTNode parent= node.getParent(); |
| if (parent instanceof Block) { |
| int start= node.getStartPosition(); |
| ReplaceEdit edit= new ReplaceEdit(start, 1, ""); //$NON-NLS-1$ |
| textedits.add(new TextEditGroup(label, edit)); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean visit(Block node) { |
| searchNode(node, contents, label, textedits); |
| return true; |
| } |
| }); |
| |
| if (textedits.size() > 0) { |
| return new RedundantSemicolonsCleanUp(label, unit, textedits.toArray(new TextEditGroup[0])); |
| } |
| return null; |
| } |
| |
| private int findTrailingSemicolons(String contents, int startLocation) { |
| int i= startLocation; |
| Matcher matcher= pattern.matcher(contents.substring(i)); |
| if (matcher.find(0)) { |
| return matcher.end(2); |
| } |
| return -1; |
| } |
| |
| private RedundantSemicolonsCleanUp(String name, CompilationUnit compilationUnit, TextEditGroup[] groups) { |
| fName= name; |
| fCompilationUnit= (ICompilationUnit)compilationUnit.getJavaElement(); |
| fEditGroups= groups; |
| } |
| |
| @Override |
| public CompilationUnitChange createChange(IProgressMonitor progressMonitor) throws CoreException { |
| if (fEditGroups == null || fEditGroups.length == 0) |
| return null; |
| |
| CompilationUnitChange result= new CompilationUnitChange(fName, fCompilationUnit); |
| for (int i= 0; i < fEditGroups.length; i++) { |
| TextEdit[] edits= fEditGroups[i].getTextEdits(); |
| String groupName= fEditGroups[i].getName(); |
| for (int j= 0; j < edits.length; j++) { |
| TextChangeCompatibility.addTextEdit(result, groupName, edits[j]); |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public boolean canFix(ICompilationUnit compilationUnit, IProblemLocation problem) { |
| return false; |
| } |
| |
| @Override |
| protected ICleanUpFix createFix(CompilationUnit unit, IProblemLocation[] problems) throws CoreException { |
| return null; |
| } |
| |
| } |