blob: 7190217387cbea16fd846034026828c0fdf346a5 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}