blob: 79153f3a273ce99305bf81c228234cd9c008e486 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.fix;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.ltk.core.refactoring.CategorizedTextEditGroup;
import org.eclipse.ltk.core.refactoring.GroupCategory;
import org.eclipse.ltk.core.refactoring.GroupCategorySet;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.formatter.IndentManipulation;
import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
import org.eclipse.jdt.internal.corext.refactoring.nls.NLSUtil;
import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
import org.eclipse.jdt.ui.text.java.IProblemLocation;
import org.eclipse.jdt.internal.ui.text.correction.ProblemLocation;
/**
* Fix which solves various issues with strings.
* Supported:
* Add missing $NON-NLS$ tag
* Remove unnecessary $NON-NLS$ tag
*
*/
public class StringFix implements IProposableFix, ICleanUpFix {
private final TextEditGroup[] fEditGroups;
private final String fName;
private final ICompilationUnit fCompilationUnit;
public static StringFix createFix(CompilationUnit compilationUnit, IProblemLocation problem, boolean removeNLSTag, boolean addNLSTag) throws CoreException {
TextEdit addEdit= null;
ICompilationUnit cu= (ICompilationUnit)compilationUnit.getJavaElement();
if (addNLSTag) {
addEdit= NLSUtil.createNLSEdit(cu, problem.getOffset());
}
ReplaceEdit removeEdit= null;
if (removeNLSTag) {
IBuffer buffer= cu.getBuffer();
if (buffer != null) {
removeEdit= getReplace(problem.getOffset(), problem.getLength(), buffer, true);
}
}
if (addEdit != null && removeEdit != null) {
String label= FixMessages.StringFix_AddRemoveNonNls_description;
return new StringFix(label, compilationUnit, new TextEditGroup[] {new TextEditGroup(label, addEdit), new TextEditGroup(label, removeEdit)});
} else if (addEdit != null) {
String label= FixMessages.StringFix_AddNonNls_description;
return new StringFix(label, compilationUnit, new TextEditGroup[] {new TextEditGroup(label, addEdit)});
} else if (removeEdit != null) {
String label= FixMessages.StringFix_RemoveNonNls_description;
return new StringFix(label, compilationUnit, new TextEditGroup[] {new TextEditGroup(label, removeEdit)});
} else {
return null;
}
}
public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, boolean addNLSTag, boolean removeNLSTag) throws CoreException, JavaModelException {
if (!addNLSTag && !removeNLSTag)
return null;
IProblem[] problems= compilationUnit.getProblems();
IProblemLocation[] locations= new IProblemLocation[problems.length];
for (int i= 0; i < problems.length; i++) {
locations[i]= new ProblemLocation(problems[i]);
}
return createCleanUp(compilationUnit, addNLSTag, removeNLSTag, locations);
}
public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, IProblemLocation[] problems, boolean addNLSTag, boolean removeNLSTag) throws CoreException, JavaModelException {
if (!addNLSTag && !removeNLSTag)
return null;
return createCleanUp(compilationUnit, addNLSTag, removeNLSTag, problems);
}
private static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, boolean addNLSTag, boolean removeNLSTag, IProblemLocation[] problems) throws CoreException, JavaModelException {
ICompilationUnit cu= (ICompilationUnit)compilationUnit.getJavaElement();
if (!cu.isStructureKnown())
return null; //[clean up] 'Remove unnecessary $NLS-TAGS$' removes necessary ones in case of syntax errors: https://bugs.eclipse.org/bugs/show_bug.cgi?id=285814 :
List<CategorizedTextEditGroup> result= new ArrayList<>();
List<IProblemLocation> missingNLSProblems= new ArrayList<>();
for (int i= 0; i < problems.length; i++) {
IProblemLocation problem= problems[i];
if (addNLSTag && problem.getProblemId() == IProblem.NonExternalizedStringLiteral) {
missingNLSProblems.add(problem);
}
if (removeNLSTag && problem.getProblemId() == IProblem.UnnecessaryNLSTag) {
IBuffer buffer= cu.getBuffer();
if (buffer != null) {
TextEdit edit= StringFix.getReplace(problem.getOffset(), problem.getLength(), buffer, false);
if (edit != null) {
String label= FixMessages.StringFix_RemoveNonNls_description;
result.add(new CategorizedTextEditGroup(label, edit, new GroupCategorySet(new GroupCategory(label, label, label))));
}
}
}
}
if (!missingNLSProblems.isEmpty()) {
int[] positions= new int[missingNLSProblems.size()];
int i=0;
for (Iterator<IProblemLocation> iter= missingNLSProblems.iterator(); iter.hasNext();) {
IProblemLocation problem= iter.next();
positions[i]= problem.getOffset();
i++;
}
TextEdit[] edits= NLSUtil.createNLSEdits(cu, positions);
if (edits != null) {
for (int j= 0; j < edits.length; j++) {
String label= FixMessages.StringFix_AddNonNls_description;
result.add(new CategorizedTextEditGroup(label, edits[j], new GroupCategorySet(new GroupCategory(label, label, label))));
}
}
}
if (result.isEmpty())
return null;
return new StringFix("", compilationUnit, result.toArray(new TextEditGroup[result.size()])); //$NON-NLS-1$
}
private static ReplaceEdit getReplace(int offset, int length, IBuffer buffer, boolean removeLeadingIndents) {
String replaceString= new String();
boolean hasMoreInComment= false;
// look after the tag
int next= offset + length;
while (next < buffer.getLength()) {
char ch= buffer.getChar(next);
if (IndentManipulation.isIndentChar(ch)) {
next++; // remove all whitespace
} else if (IndentManipulation.isLineDelimiterChar(ch)) {
length= next - offset;
break;
} else if (ch == '/') {
next++;
if (next == buffer.getLength() || buffer.getChar(next) != '/') {
replaceString= "//"; //$NON-NLS-1$
} else {
length= next - offset - 1;
}
hasMoreInComment= true;
break;
} else {
replaceString= "//"; //$NON-NLS-1$
hasMoreInComment= true;
break;
}
}
if (!hasMoreInComment && removeLeadingIndents) {
while (offset > 0 && IndentManipulation.isIndentChar(buffer.getChar(offset - 1))) {
offset--;
length++;
}
}
if (length > 0) {
ReplaceEdit replaceEdit= new ReplaceEdit(offset, length, replaceString);
return replaceEdit;
} else {
return null;
}
}
private StringFix(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(getDisplayString(), 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 String getAdditionalProposalInfo() {
return null;
}
@Override
public String getDisplayString() {
return fName;
}
@Override
public IStatus getStatus() {
return null;
}
}