blob: 74bbf4e639a5933cadc729ce0ea6843e70372a7b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.refactoring.nls;
import java.util.Arrays;
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;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Messages;
public class NLSSourceModifier {
private final String fSubstitutionPattern;
private final boolean fIsEclipseNLS;
private NLSSourceModifier(String substitutionPattern, boolean isEclipseNLS) {
fSubstitutionPattern= substitutionPattern;
fIsEclipseNLS= isEclipseNLS;
}
public static Change create(ICompilationUnit cu, NLSSubstitution[] subs, String substitutionPattern, IPackageFragment accessorPackage, String accessorClassName, boolean isEclipseNLS) throws CoreException {
NLSSourceModifier sourceModification= new NLSSourceModifier(substitutionPattern, isEclipseNLS);
String message= Messages.format(NLSMessages.NLSSourceModifier_change_description, cu.getElementName());
TextChange change= new CompilationUnitChange(message, cu);
MultiTextEdit multiTextEdit= new MultiTextEdit();
change.setEdit(multiTextEdit);
accessorClassName= sourceModification.createImportForAccessor(multiTextEdit, accessorClassName, accessorPackage, cu);
for (int i= 0; i < subs.length; i++) {
NLSSubstitution substitution= subs[i];
int newState= substitution.getState();
if (substitution.hasStateChanged()) {
if (newState == NLSSubstitution.EXTERNALIZED) {
if (substitution.getInitialState() == NLSSubstitution.INTERNALIZED) {
sourceModification.addNLS(substitution, change, accessorClassName);
} else if (substitution.getInitialState() == NLSSubstitution.IGNORED) {
sourceModification.addAccessor(substitution, change, accessorClassName);
}
} else if (newState == NLSSubstitution.INTERNALIZED) {
if (substitution.getInitialState() == NLSSubstitution.IGNORED) {
sourceModification.deleteTag(substitution, change);
if (substitution.isValueRename()) {
sourceModification.replaceValue(substitution, change);
}
} else if (substitution.getInitialState() == NLSSubstitution.EXTERNALIZED) {
sourceModification.deleteAccessor(substitution, change, cu);
if (!isEclipseNLS)
sourceModification.deleteTag(substitution, change);
}
} else if (newState == NLSSubstitution.IGNORED) {
if (substitution.getInitialState() == NLSSubstitution.INTERNALIZED) {
sourceModification.addNLS(substitution, change, accessorClassName);
if (substitution.isValueRename()) {
sourceModification.replaceValue(substitution, change);
}
} else {
if (substitution.getInitialState() == NLSSubstitution.EXTERNALIZED) {
sourceModification.deleteAccessor(substitution, change, cu);
}
}
}
} else {
if (newState == NLSSubstitution.EXTERNALIZED) {
if (substitution.isKeyRename()) {
sourceModification.replaceKey(substitution, change);
}
if (substitution.isAccessorRename()) {
sourceModification.replaceAccessor(substitution, change);
}
} else {
if (substitution.isValueRename()) {
sourceModification.replaceValue(substitution, change);
}
}
}
}
return change;
}
/**
* @param substitution
* @param change
*/
private void replaceAccessor(NLSSubstitution substitution, TextChange change) {
AccessorClassReference accessorClassRef= substitution.getAccessorClassReference();
if (accessorClassRef != null) {
Region region= accessorClassRef.getRegion();
int len= accessorClassRef.getName().length();
String[] args= {accessorClassRef.getName(), substitution.getUpdatedAccessor()};
TextChangeCompatibility.addTextEdit(change, Messages.format(NLSMessages.NLSSourceModifier_replace_accessor, args),
new ReplaceEdit(region.getOffset(), len, substitution.getUpdatedAccessor())); //
}
}
private void replaceKey(NLSSubstitution substitution, TextChange change) {
Region region= substitution.getNLSElement().getPosition();
String[] args= {substitution.getInitialKey(), substitution.getKey()};
ReplaceEdit replaceEdit;
if (fIsEclipseNLS)
replaceEdit= new ReplaceEdit(region.getOffset(), region.getLength(), substitution.getKey());
else
replaceEdit= new ReplaceEdit(region.getOffset(), region.getLength(), '\"' + unwindEscapeChars(substitution.getKey()) + '\"'); //
TextChangeCompatibility.addTextEdit(change, Messages.format(NLSMessages.NLSSourceModifier_replace_key, args), replaceEdit);
}
private void replaceValue(NLSSubstitution substitution, TextChange change) {
Region region= substitution.getNLSElement().getPosition();
String[] args= {substitution.getInitialValue(), substitution.getValueNonEmpty()};
TextChangeCompatibility.addTextEdit(change, Messages.format(NLSMessages.NLSSourceModifier_replace_value, args),
new ReplaceEdit(region.getOffset(), region.getLength(), '\"' + unwindEscapeChars(substitution.getValueNonEmpty()) + '\"')); //
}
private void deleteAccessor(NLSSubstitution substitution, TextChange change, ICompilationUnit cu) throws CoreException {
AccessorClassReference accessorClassRef= substitution.getAccessorClassReference();
if (accessorClassRef != null) {
Region region= accessorClassRef.getRegion();
String[] args= {substitution.getValueNonEmpty(), substitution.getKey()};
String label= Messages.format(NLSMessages.NLSSourceModifier_remove_accessor, args);
String replaceString= '\"' + unwindEscapeChars(substitution.getValueNonEmpty()) + '\"';
TextChangeCompatibility.addTextEdit(change, label, new ReplaceEdit(region.getOffset(), region.getLength(), replaceString));
if (fIsEclipseNLS && substitution.getState() != NLSSubstitution.INTERNALIZED) {
Region position= substitution.getNLSElement().getPosition();
int lineStart= getLineStart(cu.getBuffer(), position.getOffset());
int lineEnd= getLineEnd(cu.getBuffer(), position.getOffset());
String cuLine= cu.getBuffer().getText(lineStart, lineEnd - lineStart);
StringBuffer buf= new StringBuffer(cuLine);
buf.replace(region.getOffset() - lineStart, region.getOffset() + region.getLength() - lineStart, replaceString);
try {
NLSLine[] allLines= NLSScanner.scan(buf.toString());
NLSLine nlsLine= allLines[0];
NLSElement element= findElement(nlsLine, position.getOffset() - lineStart - accessorClassRef.getName().length() - 1);
if (element == null || element.hasTag())
return;
NLSElement[] elements= nlsLine.getElements();
int indexInElementList= Arrays.asList(elements).indexOf(element);
String editText= ' ' + NLSElement.createTagText(indexInElementList + 1); //tags are 1-based
TextChangeCompatibility.addTextEdit(change, label, new InsertEdit(lineEnd, editText));
} catch (InvalidInputException e) {
}
}
}
}
private int getLineEnd(IBuffer buffer, int offset) {
int pos= offset;
int length= buffer.getLength();
while (pos < length && !isDelemiter(buffer.getChar(pos))) {
pos++;
}
return pos;
}
private int getLineStart(IBuffer buffer, int offset) {
int pos= offset;
while (pos >= 0 && !isDelemiter(buffer.getChar(pos))) {
pos--;
}
return pos + 1;
}
private boolean isDelemiter(char ch) {
String[] delem= TextUtilities.DELIMITERS;
for (int i= 0; i < delem.length; i++) {
if (delem[i].length() == 1 && ch == delem[i].charAt(0))
return true;
}
return false;
}
private static boolean isPositionInElement(NLSElement element, int position) {
Region elementPosition= element.getPosition();
return (elementPosition.getOffset() <= position && position <= elementPosition.getOffset() + elementPosition.getLength());
}
private static NLSElement findElement(NLSLine line, int position) {
NLSElement[] elements= line.getElements();
for (int i= 0; i < elements.length; i++) {
NLSElement element= elements[i];
if (isPositionInElement(element, position))
return element;
}
return null;
}
// TODO: not dry
private String unwindEscapeChars(String s) {
StringBuffer sb= new StringBuffer(s.length());
int length= s.length();
for (int i= 0; i < length; i++) {
char c= s.charAt(i);
sb.append(getUnwoundString(c));
}
return sb.toString();
}
private String getUnwoundString(char c) {
switch (c) {
case '\b' :
return "\\b";//$NON-NLS-1$
case '\t' :
return "\\t";//$NON-NLS-1$
case '\n' :
return "\\n";//$NON-NLS-1$
case '\f' :
return "\\f";//$NON-NLS-1$
case '\r' :
return "\\r";//$NON-NLS-1$
case '\\' :
return "\\\\";//$NON-NLS-1$
}
return String.valueOf(c);
}
private void deleteTag(NLSSubstitution substitution, TextChange change) {
Region textRegion= substitution.getNLSElement().getTagPosition();
TextChangeCompatibility.addTextEdit(change, NLSMessages.NLSSourceModifier_remove_tag,
new DeleteEdit(textRegion.getOffset(), textRegion.getLength()));
}
private String createImportForAccessor(MultiTextEdit parent, String accessorClassName, IPackageFragment accessorPackage, ICompilationUnit cu) throws CoreException {
IType type= accessorPackage.getCompilationUnit(accessorClassName + JavaModelUtil.DEFAULT_CU_SUFFIX).getType(accessorClassName);
String fullyQualifiedName= type.getFullyQualifiedName();
ImportRewrite importRewrite= StubUtility.createImportRewrite(cu, true);
String nameToUse= importRewrite.addImport(fullyQualifiedName);
TextEdit edit= importRewrite.rewriteImports(null);
parent.addChild(edit);
return nameToUse;
}
private void addNLS(NLSSubstitution sub, TextChange change, String accessorName) {
if (sub.getState() == NLSSubstitution.INTERNALIZED)
return;
NLSElement element= sub.getNLSElement();
addAccessor(sub, change, accessorName);
if (!fIsEclipseNLS || sub.getState() == NLSSubstitution.IGNORED) {
// Add $NON-NLS-n tag
String arg= sub.getState() == NLSSubstitution.EXTERNALIZED ? sub.getKey() : sub.getValueNonEmpty();
String name= Messages.format(NLSMessages.NLSSourceModifier_add_tag, arg);
TextChangeCompatibility.addTextEdit(change, name, createAddTagChange(element));
}
}
private void addAccessor(NLSSubstitution sub, TextChange change, String accessorName) {
if (sub.getState() == NLSSubstitution.EXTERNALIZED) {
NLSElement element= sub.getNLSElement();
Region position= element.getPosition();
String[] args= {sub.getValueNonEmpty(), sub.getKey()};
String text= Messages.format(NLSMessages.NLSSourceModifier_externalize, args);
String resourceGetter= createResourceGetter(sub.getKey(), accessorName);
TextChangeCompatibility.addTextEdit(change, text, new ReplaceEdit(position.getOffset(), position.getLength(), resourceGetter));
if (fIsEclipseNLS) {
Region tagPosition= element.getTagPosition();
if (tagPosition != null) {
TextChangeCompatibility.addTextEdit(change, text, new DeleteEdit(tagPosition.getOffset(), tagPosition.getLength()));
}
}
}
}
private TextEdit createAddTagChange(NLSElement element) {
int offset= element.getTagPosition().getOffset(); //to be changed
String text= ' ' + element.getTagText();
return new InsertEdit(offset, text);
}
private String createResourceGetter(String key, String accessorName) {
StringBuffer buf= new StringBuffer();
buf.append(accessorName);
buf.append('.');
if (fIsEclipseNLS)
buf.append(key);
else {
//we just replace the first occurrence of KEY in the pattern
int i= fSubstitutionPattern.indexOf(NLSRefactoring.KEY);
if (i != -1) {
buf.append(fSubstitutionPattern.substring(0, i));
buf.append('"').append(key).append('"');
buf.append(fSubstitutionPattern.substring(i + NLSRefactoring.KEY.length()));
}
}
return buf.toString();
}
}