ADT: Extract String IDs from Layout XML strings.
This commit is contained in:
@@ -666,6 +666,12 @@
|
|||||||
id="com.android.ide.eclipse.adt.refactoring.extract.string"
|
id="com.android.ide.eclipse.adt.refactoring.extract.string"
|
||||||
name="Extract Android String">
|
name="Extract Android String">
|
||||||
</command>
|
</command>
|
||||||
|
<keyBinding
|
||||||
|
commandId="com.android.ide.eclipse.adt.refactoring.extract.string"
|
||||||
|
contextId="org.eclipse.ui.globalScope"
|
||||||
|
keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration"
|
||||||
|
keySequence="M3+M2+A S">
|
||||||
|
</keyBinding>
|
||||||
</extension>
|
</extension>
|
||||||
<extension
|
<extension
|
||||||
point="org.eclipse.ltk.core.refactoring.refactoringContributions">
|
point="org.eclipse.ltk.core.refactoring.refactoringContributions">
|
||||||
|
|||||||
@@ -310,12 +310,12 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
|||||||
Object[] choices = null;
|
Object[] choices = null;
|
||||||
if (attrInfo.isInValue) {
|
if (attrInfo.isInValue) {
|
||||||
// Editing an attribute's value... Get the attribute name and then the
|
// Editing an attribute's value... Get the attribute name and then the
|
||||||
// possible choice for the tuple(parent,attribute)
|
// possible choices for the tuple(parent,attribute)
|
||||||
String value = attrInfo.value;
|
String value = attrInfo.value;
|
||||||
if (value.startsWith("'") || value.startsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$
|
if (value.startsWith("'") || value.startsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
value = value.substring(1);
|
value = value.substring(1);
|
||||||
// The prefix that was found at the beginning only scan for characters
|
// The prefix that was found at the beginning only scan for characters
|
||||||
// valid of tag name. We now know the real prefix for this attribute's
|
// valid for tag name. We now know the real prefix for this attribute's
|
||||||
// value, which is needed to generate the completion choices below.
|
// value, which is needed to generate the completion choices below.
|
||||||
attrInfo.correctedPrefix = value;
|
attrInfo.correctedPrefix = value;
|
||||||
} else {
|
} else {
|
||||||
@@ -772,7 +772,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
|||||||
IDescriptorProvider descriptorProvider = data.getDescriptorProvider(mDescriptorId);
|
IDescriptorProvider descriptorProvider = data.getDescriptorProvider(mDescriptorId);
|
||||||
|
|
||||||
if (descriptorProvider != null) {
|
if (descriptorProvider != null) {
|
||||||
mRootDescriptor = new ElementDescriptor("",
|
mRootDescriptor = new ElementDescriptor("", //$NON-NLS-1$
|
||||||
descriptorProvider.getRootElementDescriptors());
|
descriptorProvider.getRootElementDescriptors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -565,6 +565,11 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
|
|||||||
* Callers <em>must</em> call model.releaseFromRead() when done, typically
|
* Callers <em>must</em> call model.releaseFromRead() when done, typically
|
||||||
* in a try..finally clause.
|
* in a try..finally clause.
|
||||||
*
|
*
|
||||||
|
* Portability note: this uses getModelManager which is part of wst.sse.core; however
|
||||||
|
* the interface returned is part of wst.sse.core.internal.provisional so we can
|
||||||
|
* expect it to change in a distant future if they start cleaning their codebase,
|
||||||
|
* however unlikely that is.
|
||||||
|
*
|
||||||
* @return The model for the XML document or null if cannot be obtained from the editor
|
* @return The model for the XML document or null if cannot be obtained from the editor
|
||||||
*/
|
*/
|
||||||
public final IStructuredModel getModelForRead() {
|
public final IStructuredModel getModelForRead() {
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import org.eclipse.swt.widgets.Composite;
|
|||||||
*/
|
*/
|
||||||
public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor {
|
public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor {
|
||||||
|
|
||||||
|
/** The {@link ResourceType} that this reference attribute can accept. It can be null,
|
||||||
|
* in which case any reference type can be used. */
|
||||||
private ResourceType mResourceType;
|
private ResourceType mResourceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,6 +68,12 @@ public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns the {@link ResourceType} that this reference attribute can accept.
|
||||||
|
* It can be null, in which case any reference type can be used. */
|
||||||
|
public ResourceType getResourceType() {
|
||||||
|
return mResourceType;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A new {@link UiResourceAttributeNode} linked to this reference descriptor.
|
* @return A new {@link UiResourceAttributeNode} linked to this reference descriptor.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
|
|||||||
/** Keep track of the current workbench window. */
|
/** Keep track of the current workbench window. */
|
||||||
private IWorkbenchWindow mWindow;
|
private IWorkbenchWindow mWindow;
|
||||||
private ITextSelection mSelection;
|
private ITextSelection mSelection;
|
||||||
|
private IEditorPart mEditor;
|
||||||
private IFile mFile;
|
private IFile mFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,7 +108,8 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
|
|||||||
if (selection instanceof ITextSelection) {
|
if (selection instanceof ITextSelection) {
|
||||||
mSelection = (ITextSelection) selection;
|
mSelection = (ITextSelection) selection;
|
||||||
if (mSelection.getLength() > 0) {
|
if (mSelection.getLength() > 0) {
|
||||||
mFile = getSelectedFile();
|
mEditor = getActiveEditor();
|
||||||
|
mFile = getSelectedFile(mEditor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +121,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
|
|||||||
*/
|
*/
|
||||||
public void run(IAction action) {
|
public void run(IAction action) {
|
||||||
if (mSelection != null && mFile != null) {
|
if (mSelection != null && mFile != null) {
|
||||||
ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mSelection);
|
ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mEditor, mSelection);
|
||||||
RefactoringWizard wizard = new ExtractStringWizard(ref, mFile.getProject());
|
RefactoringWizard wizard = new ExtractStringWizard(ref, mFile.getProject());
|
||||||
RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
|
RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
|
||||||
try {
|
try {
|
||||||
@@ -130,6 +132,21 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the active editor (hopefully matching our selection) or null.
|
||||||
|
*/
|
||||||
|
private IEditorPart getActiveEditor() {
|
||||||
|
IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
|
||||||
|
if (wwin != null) {
|
||||||
|
IWorkbenchPage page = wwin.getActivePage();
|
||||||
|
if (page != null) {
|
||||||
|
return page.getActiveEditor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the active {@link IFile} (hopefully matching our selection) or null.
|
* Returns the active {@link IFile} (hopefully matching our selection) or null.
|
||||||
* The file is only returned if it's a file from a project with an Android nature.
|
* The file is only returned if it's a file from a project with an Android nature.
|
||||||
@@ -138,12 +155,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
|
|||||||
* for the refactoring. This check is performed when the refactoring is invoked since
|
* for the refactoring. This check is performed when the refactoring is invoked since
|
||||||
* it can then produce meaningful error messages as needed.
|
* it can then produce meaningful error messages as needed.
|
||||||
*/
|
*/
|
||||||
private IFile getSelectedFile() {
|
private IFile getSelectedFile(IEditorPart editor) {
|
||||||
IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
|
|
||||||
if (wwin != null) {
|
|
||||||
IWorkbenchPage page = wwin.getActivePage();
|
|
||||||
if (page != null) {
|
|
||||||
IEditorPart editor = page.getActiveEditor();
|
|
||||||
if (editor != null) {
|
if (editor != null) {
|
||||||
IEditorInput input = editor.getEditorInput();
|
IEditorInput input = editor.getEditorInput();
|
||||||
|
|
||||||
@@ -162,8 +174,6 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,17 @@
|
|||||||
package com.android.ide.eclipse.adt.internal.refactorings.extractstring;
|
package com.android.ide.eclipse.adt.internal.refactorings.extractstring;
|
||||||
|
|
||||||
import com.android.ide.eclipse.adt.AndroidConstants;
|
import com.android.ide.eclipse.adt.AndroidConstants;
|
||||||
|
import com.android.ide.eclipse.adt.internal.editors.AndroidEditor;
|
||||||
|
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
|
||||||
|
import com.android.ide.eclipse.adt.internal.editors.descriptors.ReferenceAttributeDescriptor;
|
||||||
|
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
|
||||||
|
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
|
||||||
import com.android.ide.eclipse.adt.internal.project.AndroidManifestParser;
|
import com.android.ide.eclipse.adt.internal.project.AndroidManifestParser;
|
||||||
|
import com.android.ide.eclipse.adt.internal.resources.ResourceType;
|
||||||
|
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
|
||||||
|
import com.android.sdklib.SdkConstants;
|
||||||
|
|
||||||
|
import org.eclipse.core.resources.IContainer;
|
||||||
import org.eclipse.core.resources.IFile;
|
import org.eclipse.core.resources.IFile;
|
||||||
import org.eclipse.core.resources.IProject;
|
import org.eclipse.core.resources.IProject;
|
||||||
import org.eclipse.core.resources.IResource;
|
import org.eclipse.core.resources.IResource;
|
||||||
@@ -63,6 +72,16 @@ import org.eclipse.text.edits.MultiTextEdit;
|
|||||||
import org.eclipse.text.edits.ReplaceEdit;
|
import org.eclipse.text.edits.ReplaceEdit;
|
||||||
import org.eclipse.text.edits.TextEdit;
|
import org.eclipse.text.edits.TextEdit;
|
||||||
import org.eclipse.text.edits.TextEditGroup;
|
import org.eclipse.text.edits.TextEditGroup;
|
||||||
|
import org.eclipse.ui.IEditorPart;
|
||||||
|
import org.eclipse.wst.sse.core.StructuredModelManager;
|
||||||
|
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
|
||||||
|
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
|
||||||
|
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
|
||||||
|
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
|
||||||
|
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
|
||||||
|
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
|
||||||
|
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -70,6 +89,7 @@ import java.io.InputStream;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -85,11 +105,6 @@ import java.util.Map;
|
|||||||
* <li> The action finds the {@link ICompilationUnit} being edited as well as the current
|
* <li> The action finds the {@link ICompilationUnit} being edited as well as the current
|
||||||
* {@link ITextSelection}. The action creates a new instance of this refactoring as
|
* {@link ITextSelection}. The action creates a new instance of this refactoring as
|
||||||
* well as an {@link ExtractStringWizard} and runs the operation.
|
* well as an {@link ExtractStringWizard} and runs the operation.
|
||||||
* <li> TODO: to support refactoring from an XML file, the action should give the {@link IFile}
|
|
||||||
* and then here we would have to determine whether it's a suitable Android XML file or a
|
|
||||||
* suitable Java file.
|
|
||||||
* TODO: enumerate the exact valid contexts in Android XML files, e.g. attributes in layout
|
|
||||||
* files or text elements (e.g. <string>foo</string>) for values, etc.
|
|
||||||
* <li> Step 1 of the refactoring is to check the preliminary conditions. Right now we check
|
* <li> Step 1 of the refactoring is to check the preliminary conditions. Right now we check
|
||||||
* that the java source is not read-only and is in sync. We also try to find a string under
|
* that the java source is not read-only and is in sync. We also try to find a string under
|
||||||
* the selection. If this fails, the refactoring is aborted.
|
* the selection. If this fails, the refactoring is aborted.
|
||||||
@@ -113,8 +128,6 @@ import java.util.Map;
|
|||||||
* <li> Create an AST rewriter to edit the source Java file and replace all occurences by the
|
* <li> Create an AST rewriter to edit the source Java file and replace all occurences by the
|
||||||
* new computed R.string.foo. Also need to rewrite imports to import R as needed.
|
* new computed R.string.foo. Also need to rewrite imports to import R as needed.
|
||||||
* If there's already a conflicting R included, we need to insert the FQCN instead.
|
* If there's already a conflicting R included, we need to insert the FQCN instead.
|
||||||
* <li> TODO: If the source is an XML file, determine if we need to change an attribute or a
|
|
||||||
* a text element.
|
|
||||||
* <li> TODO: Have a pref in the wizard: [x] Change other XML Files
|
* <li> TODO: Have a pref in the wizard: [x] Change other XML Files
|
||||||
* <li> TODO: Have a pref in the wizard: [x] Change other Java Files
|
* <li> TODO: Have a pref in the wizard: [x] Change other Java Files
|
||||||
* </ul>
|
* </ul>
|
||||||
@@ -142,9 +155,14 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
|
|
||||||
/** The {@link Mode} of operation of the refactoring. */
|
/** The {@link Mode} of operation of the refactoring. */
|
||||||
private final Mode mMode;
|
private final Mode mMode;
|
||||||
|
/** Non-null when editing an Android Resource XML file: identifies the attribute name
|
||||||
|
* of the value being edited. When null, the source is an Android Java file. */
|
||||||
|
private String mXmlAttributeName;
|
||||||
/** The file model being manipulated.
|
/** The file model being manipulated.
|
||||||
* Value is null when not on {@link Mode#EDIT_SOURCE} mode. */
|
* Value is null when not on {@link Mode#EDIT_SOURCE} mode. */
|
||||||
private final IFile mFile;
|
private final IFile mFile;
|
||||||
|
/** The editor. Non-null when invoked from {@link ExtractStringAction}. Null otherwise. */
|
||||||
|
private final IEditorPart mEditor;
|
||||||
/** The project that contains {@link #mFile} and that contains the target XML file to modify. */
|
/** The project that contains {@link #mFile} and that contains the target XML file to modify. */
|
||||||
private final IProject mProject;
|
private final IProject mProject;
|
||||||
/** The start of the selection in {@link #mFile}.
|
/** The start of the selection in {@link #mFile}.
|
||||||
@@ -180,9 +198,9 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
private static final String KEY_SEL_START = "sel-start"; //$NON-NLS-1$
|
private static final String KEY_SEL_START = "sel-start"; //$NON-NLS-1$
|
||||||
private static final String KEY_SEL_END = "sel-end"; //$NON-NLS-1$
|
private static final String KEY_SEL_END = "sel-end"; //$NON-NLS-1$
|
||||||
private static final String KEY_TOK_ESC = "tok-esc"; //$NON-NLS-1$
|
private static final String KEY_TOK_ESC = "tok-esc"; //$NON-NLS-1$
|
||||||
|
private static final String KEY_XML_ATTR_NAME = "xml-attr-name"; //$NON-NLS-1$
|
||||||
|
|
||||||
public ExtractStringRefactoring(Map<String, String> arguments)
|
public ExtractStringRefactoring(Map<String, String> arguments) throws NullPointerException {
|
||||||
throws NullPointerException {
|
|
||||||
mMode = Mode.valueOf(arguments.get(KEY_MODE));
|
mMode = Mode.valueOf(arguments.get(KEY_MODE));
|
||||||
|
|
||||||
IPath path = Path.fromPortableString(arguments.get(KEY_PROJECT));
|
IPath path = Path.fromPortableString(arguments.get(KEY_PROJECT));
|
||||||
@@ -195,11 +213,15 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
mSelectionStart = Integer.parseInt(arguments.get(KEY_SEL_START));
|
mSelectionStart = Integer.parseInt(arguments.get(KEY_SEL_START));
|
||||||
mSelectionEnd = Integer.parseInt(arguments.get(KEY_SEL_END));
|
mSelectionEnd = Integer.parseInt(arguments.get(KEY_SEL_END));
|
||||||
mTokenString = arguments.get(KEY_TOK_ESC);
|
mTokenString = arguments.get(KEY_TOK_ESC);
|
||||||
|
mXmlAttributeName = arguments.get(KEY_XML_ATTR_NAME);
|
||||||
} else {
|
} else {
|
||||||
mFile = null;
|
mFile = null;
|
||||||
mSelectionStart = mSelectionEnd = -1;
|
mSelectionStart = mSelectionEnd = -1;
|
||||||
mTokenString = null;
|
mTokenString = null;
|
||||||
|
mXmlAttributeName = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mEditor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> createArgumentMap() {
|
private Map<String, String> createArgumentMap() {
|
||||||
@@ -211,6 +233,7 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
args.put(KEY_SEL_START, Integer.toString(mSelectionStart));
|
args.put(KEY_SEL_START, Integer.toString(mSelectionStart));
|
||||||
args.put(KEY_SEL_END, Integer.toString(mSelectionEnd));
|
args.put(KEY_SEL_END, Integer.toString(mSelectionEnd));
|
||||||
args.put(KEY_TOK_ESC, mTokenString);
|
args.put(KEY_TOK_ESC, mTokenString);
|
||||||
|
args.put(KEY_XML_ATTR_NAME, mXmlAttributeName);
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
@@ -222,11 +245,13 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
* or an existing one.
|
* or an existing one.
|
||||||
*
|
*
|
||||||
* @param file The source file to process. Cannot be null. File must exist in workspace.
|
* @param file The source file to process. Cannot be null. File must exist in workspace.
|
||||||
|
* @param editor
|
||||||
* @param selection The selection in the source file. Cannot be null or empty.
|
* @param selection The selection in the source file. Cannot be null or empty.
|
||||||
*/
|
*/
|
||||||
public ExtractStringRefactoring(IFile file, ITextSelection selection) {
|
public ExtractStringRefactoring(IFile file, IEditorPart editor, ITextSelection selection) {
|
||||||
mMode = Mode.EDIT_SOURCE;
|
mMode = Mode.EDIT_SOURCE;
|
||||||
mFile = file;
|
mFile = file;
|
||||||
|
mEditor = editor;
|
||||||
mProject = file.getProject();
|
mProject = file.getProject();
|
||||||
mSelectionStart = selection.getOffset();
|
mSelectionStart = selection.getOffset();
|
||||||
mSelectionEnd = mSelectionStart + Math.max(0, selection.getLength() - 1);
|
mSelectionEnd = mSelectionStart + Math.max(0, selection.getLength() - 1);
|
||||||
@@ -243,6 +268,7 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
public ExtractStringRefactoring(IProject project, boolean enforceNew) {
|
public ExtractStringRefactoring(IProject project, boolean enforceNew) {
|
||||||
mMode = enforceNew ? Mode.SELECT_NEW_ID : Mode.SELECT_ID;
|
mMode = enforceNew ? Mode.SELECT_NEW_ID : Mode.SELECT_ID;
|
||||||
mFile = null;
|
mFile = null;
|
||||||
|
mEditor = null;
|
||||||
mProject = project;
|
mProject = project;
|
||||||
mSelectionStart = mSelectionEnd = -1;
|
mSelectionStart = mSelectionEnd = -1;
|
||||||
}
|
}
|
||||||
@@ -304,10 +330,10 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
RefactoringStatus status = new RefactoringStatus();
|
RefactoringStatus status = new RefactoringStatus();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
monitor.beginTask("Checking preconditions...", 5);
|
monitor.beginTask("Checking preconditions...", 6);
|
||||||
|
|
||||||
if (mMode != Mode.EDIT_SOURCE) {
|
if (mMode != Mode.EDIT_SOURCE) {
|
||||||
monitor.worked(5);
|
monitor.worked(6);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,11 +360,36 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
// That was not a Java file. Ignore.
|
// That was not a Java file. Ignore.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mUnit == null) {
|
if (mUnit != null) {
|
||||||
// Check this an XML file and get the selection and its context.
|
monitor.worked(1);
|
||||||
// TODO
|
return status;
|
||||||
status.addFatalError("Selection must be inside a Java source file.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check this a Layout XML file and get the selection and its context.
|
||||||
|
if (mFile != null && AndroidConstants.EXT_XML.equals(mFile.getFileExtension())) {
|
||||||
|
|
||||||
|
// Currently we only support Android resource XML files, so they must have a path
|
||||||
|
// similar to
|
||||||
|
// project/res/<type>[-<configuration>]/*.xml
|
||||||
|
// There is no support for sub folders, so the segment count must be 4.
|
||||||
|
// We don't need to check the type folder name because a/ we only accept
|
||||||
|
// an AndroidEditor source and b/ aapt generates a compilation error for
|
||||||
|
// unknown folders.
|
||||||
|
IPath path = mFile.getFullPath();
|
||||||
|
// check if we are inside the project/res/* folder.
|
||||||
|
if (path.segmentCount() == 4) {
|
||||||
|
if (path.segment(1).equalsIgnoreCase(SdkConstants.FD_RESOURCES)) {
|
||||||
|
if (!findSelectionInXmlFile(mFile, status, monitor)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status.isOK()) {
|
||||||
|
status.addFatalError("Selection must be inside a Java source or an Android Layout XML file.");
|
||||||
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
monitor.done();
|
monitor.done();
|
||||||
}
|
}
|
||||||
@@ -353,6 +404,7 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
* to the status.
|
* to the status.
|
||||||
*
|
*
|
||||||
* On success, advance the monitor by 3.
|
* On success, advance the monitor by 3.
|
||||||
|
* Returns status.isOK().
|
||||||
*/
|
*/
|
||||||
private boolean findSelectionInJavaUnit(ICompilationUnit unit,
|
private boolean findSelectionInJavaUnit(ICompilationUnit unit,
|
||||||
RefactoringStatus status, IProgressMonitor monitor) {
|
RefactoringStatus status, IProgressMonitor monitor) {
|
||||||
@@ -373,7 +425,7 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
token = scanner.getNextToken()) {
|
token = scanner.getNextToken()) {
|
||||||
if (scanner.getCurrentTokenStartPosition() <= mSelectionStart &&
|
if (scanner.getCurrentTokenStartPosition() <= mSelectionStart &&
|
||||||
scanner.getCurrentTokenEndPosition() >= mSelectionEnd) {
|
scanner.getCurrentTokenEndPosition() >= mSelectionEnd) {
|
||||||
// found the token, but only keep of the right type
|
// found the token, but only keep if the right type
|
||||||
if (token == ITerminalSymbols.TokenNameStringLiteral) {
|
if (token == ITerminalSymbols.TokenNameStringLiteral) {
|
||||||
mTokenString = new String(scanner.getCurrentTokenSource());
|
mTokenString = new String(scanner.getCurrentTokenSource());
|
||||||
}
|
}
|
||||||
@@ -412,6 +464,205 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
monitor.worked(1);
|
monitor.worked(1);
|
||||||
return status.isOK();
|
return status.isOK();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Try to find the selected XML element. This implementation replies on the refactoring
|
||||||
|
* originating from an Android Layout Editor. We rely on some internal properties of the
|
||||||
|
* Structured XML editor to retrieve file content to avoid parsing it again. We also rely
|
||||||
|
* on our specific Android XML model to get element & attribute descriptor properties.
|
||||||
|
*
|
||||||
|
* If selection matches a string literal, capture it, otherwise add a fatal error
|
||||||
|
* to the status.
|
||||||
|
*
|
||||||
|
* On success, advance the monitor by 1.
|
||||||
|
* Returns status.isOK().
|
||||||
|
*/
|
||||||
|
private boolean findSelectionInXmlFile(IFile file,
|
||||||
|
RefactoringStatus status,
|
||||||
|
IProgressMonitor monitor) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!(mEditor instanceof AndroidEditor)) {
|
||||||
|
status.addFatalError("Only the Android XML Editor is currently supported.");
|
||||||
|
return status.isOK();
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidEditor editor = (AndroidEditor) mEditor;
|
||||||
|
IStructuredModel smodel = null;
|
||||||
|
Node node = null;
|
||||||
|
String attrName = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// See the portability note in AndroidEditor#getModelForRead() javadoc.
|
||||||
|
smodel = editor.getModelForRead();
|
||||||
|
if (smodel != null) {
|
||||||
|
// The structured model gives the us the actual XML Node element where the
|
||||||
|
// offset is. By using this Node, we can find the exact UiElementNode of our
|
||||||
|
// model and thus we'll be able to get the properties of the attribute -- to
|
||||||
|
// check if it accepts a string reference. This does not however tell us if
|
||||||
|
// the selection is actually in an attribute value, nor which attribute is
|
||||||
|
// being edited.
|
||||||
|
for(int offset = mSelectionStart; offset >= 0 && node == null; --offset) {
|
||||||
|
node = (Node) smodel.getIndexedRegion(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node == null) {
|
||||||
|
status.addFatalError("The selection does not match any element in the XML document.");
|
||||||
|
return status.isOK();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.getNodeType() != Node.ELEMENT_NODE) {
|
||||||
|
status.addFatalError("The selection is not inside an actual XML element.");
|
||||||
|
return status.isOK();
|
||||||
|
}
|
||||||
|
|
||||||
|
IStructuredDocument sdoc = smodel.getStructuredDocument();
|
||||||
|
if (sdoc != null) {
|
||||||
|
// Portability note: all the structured document implementation is
|
||||||
|
// under wst.sse.core.internal.provisional so we can expect it to change in
|
||||||
|
// a distant future if they start cleaning their codebase, however unlikely
|
||||||
|
// that is.
|
||||||
|
|
||||||
|
int selStart = mSelectionStart;
|
||||||
|
IStructuredDocumentRegion region =
|
||||||
|
sdoc.getRegionAtCharacterOffset(selStart);
|
||||||
|
if (region != null &&
|
||||||
|
DOMRegionContext.XML_TAG_NAME.equals(region.getType())) {
|
||||||
|
// The region gives us the textual representation of the XML element
|
||||||
|
// where the selection starts, split using sub-regions. We now just
|
||||||
|
// need to iterate through the sub-regions to find which one
|
||||||
|
// contains the actual selection. We're interested in an attribute
|
||||||
|
// value however when we find one we want to memorize the attribute
|
||||||
|
// name that was defined just before.
|
||||||
|
|
||||||
|
int startInRegion = selStart - region.getStartOffset();
|
||||||
|
|
||||||
|
int nb = region.getNumberOfRegions();
|
||||||
|
ITextRegionList list = region.getRegions();
|
||||||
|
|
||||||
|
for (int i = 0; i < nb; i++) {
|
||||||
|
ITextRegion subRegion = list.get(i);
|
||||||
|
String type = subRegion.getType();
|
||||||
|
|
||||||
|
if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(type)) {
|
||||||
|
attrName = region.getText(subRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subRegion.getStart() <= startInRegion &&
|
||||||
|
startInRegion <= subRegion.getEnd() &&
|
||||||
|
DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(type)) {
|
||||||
|
// We found the value. Only accept it if not empty
|
||||||
|
// and if we found an attribute name before.
|
||||||
|
String text = region.getText(subRegion);
|
||||||
|
|
||||||
|
// The attribute value will contain the XML quotes. Remove them.
|
||||||
|
int len = text.length();
|
||||||
|
if (len >= 2 &&
|
||||||
|
text.charAt(0) == '"' &&
|
||||||
|
text.charAt(len - 1) == '"') {
|
||||||
|
text = text.substring(1, len - 1);
|
||||||
|
} else if (len >= 2 &&
|
||||||
|
text.charAt(0) == '\'' &&
|
||||||
|
text.charAt(len - 1) == '\'') {
|
||||||
|
text = text.substring(1, len - 1);
|
||||||
|
}
|
||||||
|
if (text.length() > 0 && attrName != null) {
|
||||||
|
mTokenString = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTokenString == null) {
|
||||||
|
status.addFatalError(
|
||||||
|
"The selection is not inside an actual XML attribute value.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTokenString != null && node != null && attrName != null) {
|
||||||
|
|
||||||
|
UiElementNode rootUiNode = editor.getUiRootNode();
|
||||||
|
UiElementNode currentUiNode =
|
||||||
|
rootUiNode == null ? null : rootUiNode.findXmlNode(node);
|
||||||
|
ReferenceAttributeDescriptor attrDesc = null;
|
||||||
|
|
||||||
|
if (currentUiNode != null) {
|
||||||
|
// remove any namespace prefix from the attribute name
|
||||||
|
String name = attrName;
|
||||||
|
int pos = name.indexOf(':');
|
||||||
|
if (pos > 0 && pos < name.length() - 1) {
|
||||||
|
name = name.substring(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UiAttributeNode attrNode : currentUiNode.getUiAttributes()) {
|
||||||
|
if (attrNode.getDescriptor().getXmlLocalName().equals(name)) {
|
||||||
|
AttributeDescriptor desc = attrNode.getDescriptor();
|
||||||
|
if (desc instanceof ReferenceAttributeDescriptor) {
|
||||||
|
attrDesc = (ReferenceAttributeDescriptor) desc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The attribute descriptor is a resource reference. It must either accept
|
||||||
|
// of any resource type or specifically accept string types.
|
||||||
|
if (attrDesc != null &&
|
||||||
|
(attrDesc.getResourceType() == null ||
|
||||||
|
attrDesc.getResourceType() == ResourceType.STRING)) {
|
||||||
|
// We have one more check to do: is the current string value already
|
||||||
|
// an Android XML string reference? If so, we can't edit it.
|
||||||
|
if (mTokenString.startsWith("@")) { //$NON-NLS-1$
|
||||||
|
int pos1 = 0;
|
||||||
|
if (mTokenString.length() > 1 && mTokenString.charAt(1) == '+') {
|
||||||
|
pos1++;
|
||||||
|
}
|
||||||
|
int pos2 = mTokenString.indexOf('/');
|
||||||
|
if (pos2 > pos1) {
|
||||||
|
String kind = mTokenString.substring(pos1 + 1, pos2);
|
||||||
|
mTokenString = null;
|
||||||
|
status.addFatalError(String.format(
|
||||||
|
"The attribute %1$s already contains a %2$s reference.",
|
||||||
|
attrName,
|
||||||
|
kind));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTokenString != null) {
|
||||||
|
// We're done with all our checks. mTokenString contains the
|
||||||
|
// current attribute value. We don't memorize the region nor the
|
||||||
|
// attribute, however we memorize the textual attribute name so
|
||||||
|
// that we can offer replacement for all its occurrences.
|
||||||
|
mXmlAttributeName = attrName;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
mTokenString = null;
|
||||||
|
status.addFatalError(String.format(
|
||||||
|
"The attribute %1$s does not accept a string reference.",
|
||||||
|
attrName));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// We shouldn't get here: we're missing one of the token string, the node
|
||||||
|
// or the attribute name. All of them have been checked earlier so don't
|
||||||
|
// set any specific error.
|
||||||
|
mTokenString = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (smodel != null) {
|
||||||
|
smodel.releaseFromRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
monitor.worked(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status.isOK();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests from org.eclipse.jdt.internal.corext.refactoringChecks#validateEdit()
|
* Tests from org.eclipse.jdt.internal.corext.refactoringChecks#validateEdit()
|
||||||
@@ -514,9 +765,18 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mMode == Mode.EDIT_SOURCE) {
|
if (mMode == Mode.EDIT_SOURCE) {
|
||||||
|
List<Change> changes = null;
|
||||||
|
if (mXmlAttributeName != null) {
|
||||||
|
// Prepare the change to the Android resource XML file
|
||||||
|
changes = computeXmlSourceChanges(mFile,
|
||||||
|
mXmlStringId, mTokenString, mXmlAttributeName,
|
||||||
|
status, monitor);
|
||||||
|
|
||||||
|
} else {
|
||||||
// Prepare the change to the Java compilation unit
|
// Prepare the change to the Java compilation unit
|
||||||
List<Change> changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString,
|
changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString,
|
||||||
status, SubMonitor.convert(monitor, 1));
|
status, SubMonitor.convert(monitor, 1));
|
||||||
|
}
|
||||||
if (changes != null) {
|
if (changes != null) {
|
||||||
mChanges.addAll(changes);
|
mChanges.addAll(changes);
|
||||||
}
|
}
|
||||||
@@ -634,7 +894,7 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
* We need to deal with the case where the element is written as <resources/>, in
|
* We need to deal with the case where the element is written as <resources/>, in
|
||||||
* which case the caller will want to replace /> by ">...</...>". To do that we return
|
* which case the caller will want to replace /> by ">...</...>". To do that we return
|
||||||
* two values: the first offset of the closing tag (e.g. / or >) and the length, which
|
* two values: the first offset of the closing tag (e.g. / or >) and the length, which
|
||||||
* can only be 1 or 2. If it's 2, the caller have to deal with /> instead of just >.
|
* can only be 1 or 2. If it's 2, the caller has to deal with /> instead of just >.
|
||||||
*
|
*
|
||||||
* @param contents An existing buffer to parse.
|
* @param contents An existing buffer to parse.
|
||||||
* @param tag The tag to look for.
|
* @param tag The tag to look for.
|
||||||
@@ -730,6 +990,205 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the changes to be made to the source Android XML file(s) and
|
||||||
|
* returns a list of {@link Change}.
|
||||||
|
*/
|
||||||
|
private List<Change> computeXmlSourceChanges(IFile sourceFile,
|
||||||
|
String xmlStringId,
|
||||||
|
String tokenString,
|
||||||
|
String xmlAttrName,
|
||||||
|
RefactoringStatus status,
|
||||||
|
IProgressMonitor monitor) {
|
||||||
|
|
||||||
|
if (!sourceFile.exists()) {
|
||||||
|
status.addFatalError(String.format("XML file '%1$s' does not exist.",
|
||||||
|
sourceFile.getFullPath().toOSString()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the initial condition check we validated that this file is part of
|
||||||
|
// an Android resource folder, with a folder path that looks like
|
||||||
|
// /project/res/<type>-<configuration>/<filename.xml>
|
||||||
|
// Here we are going to offer XML source change for the same filename accross all
|
||||||
|
// configurations of the same res type. E.g. if we're processing a res/layout/main.xml
|
||||||
|
// file then we want to offer changes for res/layout-fr/main.xml. We compute such a
|
||||||
|
// list here.
|
||||||
|
HashSet<IFile> files = new HashSet<IFile>();
|
||||||
|
files.add(sourceFile);
|
||||||
|
|
||||||
|
if (AndroidConstants.EXT_XML.equals(sourceFile.getFileExtension())) {
|
||||||
|
IPath path = sourceFile.getFullPath();
|
||||||
|
if (path.segmentCount() == 4 && path.segment(1).equals(SdkConstants.FD_RESOURCES)) {
|
||||||
|
IProject project = sourceFile.getProject();
|
||||||
|
String filename = path.segment(3);
|
||||||
|
String initialTypeName = path.segment(2);
|
||||||
|
ResourceFolderType type = ResourceFolderType.getFolderType(initialTypeName);
|
||||||
|
|
||||||
|
IContainer res = sourceFile.getParent().getParent();
|
||||||
|
if (type != null && res != null && res.getType() == IResource.FOLDER) {
|
||||||
|
try {
|
||||||
|
for (IResource r : res.members()) {
|
||||||
|
if (r != null && r.getType() == IResource.FOLDER) {
|
||||||
|
String name = r.getName();
|
||||||
|
// Skip the initial folder name, it's already in the list.
|
||||||
|
if (!name.equals(initialTypeName)) {
|
||||||
|
// Only accept the same folder type (e.g. layout-*)
|
||||||
|
ResourceFolderType t =
|
||||||
|
ResourceFolderType.getFolderType(initialTypeName);
|
||||||
|
if (t == type) {
|
||||||
|
// recompute the path
|
||||||
|
IPath p = res.getFullPath().append(name).append(filename);
|
||||||
|
IResource f = project.findMember(p);
|
||||||
|
if (f != null && f instanceof IFile) {
|
||||||
|
files.add((IFile) f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (CoreException e) {
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubMonitor subMonitor = SubMonitor.convert(monitor, Math.min(1, files.size()));
|
||||||
|
|
||||||
|
ArrayList<Change> changes = new ArrayList<Change>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Portability note: getModelManager is part of wst.sse.core however the
|
||||||
|
// interface returned is part of wst.sse.core.internal.provisional so we can
|
||||||
|
// expect it to change in a distant future if they start cleaning their codebase,
|
||||||
|
// however unlikely that is.
|
||||||
|
IModelManager modelManager = StructuredModelManager.getModelManager();
|
||||||
|
|
||||||
|
for (IFile file : files) {
|
||||||
|
|
||||||
|
IStructuredDocument sdoc = modelManager.createStructuredDocumentFor(file);
|
||||||
|
|
||||||
|
if (sdoc == null) {
|
||||||
|
status.addFatalError("XML structured document not found"); //$NON-NLS-1$
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFileChange xmlChange = new TextFileChange(getName(), file);
|
||||||
|
xmlChange.setTextType("xml"); //$NON-NLS-1$
|
||||||
|
|
||||||
|
MultiTextEdit multiEdit = new MultiTextEdit();
|
||||||
|
ArrayList<TextEditGroup> editGroups = new ArrayList<TextEditGroup>();
|
||||||
|
|
||||||
|
String quotedReplacement = quotedAttrValue("@string/" + xmlStringId);
|
||||||
|
|
||||||
|
// Prepare the change set
|
||||||
|
try {
|
||||||
|
for (IStructuredDocumentRegion region : sdoc.getStructuredDocumentRegions()) {
|
||||||
|
// Only look at XML "top regions"
|
||||||
|
if (!DOMRegionContext.XML_TAG_NAME.equals(region.getType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nb = region.getNumberOfRegions();
|
||||||
|
ITextRegionList list = region.getRegions();
|
||||||
|
String lastAttrName = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < nb; i++) {
|
||||||
|
ITextRegion subRegion = list.get(i);
|
||||||
|
String type = subRegion.getType();
|
||||||
|
|
||||||
|
if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(type)) {
|
||||||
|
// Memorize the last attribute name seen
|
||||||
|
lastAttrName = region.getText(subRegion);
|
||||||
|
|
||||||
|
} else if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(type)) {
|
||||||
|
// Check this is the attribute and the original string
|
||||||
|
String text = region.getText(subRegion);
|
||||||
|
|
||||||
|
int len = text.length();
|
||||||
|
if (len >= 2 &&
|
||||||
|
text.charAt(0) == '"' &&
|
||||||
|
text.charAt(len - 1) == '"') {
|
||||||
|
text = text.substring(1, len - 1);
|
||||||
|
} else if (len >= 2 &&
|
||||||
|
text.charAt(0) == '\'' &&
|
||||||
|
text.charAt(len - 1) == '\'') {
|
||||||
|
text = text.substring(1, len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xmlAttrName.equals(lastAttrName) && tokenString.equals(text)) {
|
||||||
|
|
||||||
|
// Found an occurrence. Create a change for it.
|
||||||
|
TextEdit edit = new ReplaceEdit(
|
||||||
|
region.getStartOffset() + subRegion.getStart(),
|
||||||
|
subRegion.getTextLength(),
|
||||||
|
quotedReplacement);
|
||||||
|
TextEditGroup editGroup = new TextEditGroup(
|
||||||
|
"Replace attribute string by ID",
|
||||||
|
edit);
|
||||||
|
|
||||||
|
multiEdit.addChild(edit);
|
||||||
|
editGroups.add(editGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Since we use some internal APIs, use a broad catch-all to report any
|
||||||
|
// unexpected issue rather than crash the whole refactoring.
|
||||||
|
status.addFatalError(
|
||||||
|
String.format("XML refactoring error: %1$s", t.getMessage()));
|
||||||
|
} finally {
|
||||||
|
if (multiEdit.hasChildren()) {
|
||||||
|
xmlChange.setEdit(multiEdit);
|
||||||
|
for (TextEditGroup group : editGroups) {
|
||||||
|
xmlChange.addTextEditChangeGroup(
|
||||||
|
new TextEditChangeGroup(xmlChange, group));
|
||||||
|
}
|
||||||
|
changes.add(xmlChange);
|
||||||
|
}
|
||||||
|
subMonitor.worked(1);
|
||||||
|
}
|
||||||
|
} // for files
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
status.addFatalError(String.format("XML model IO error: %1$s.", e.getMessage()));
|
||||||
|
} catch (CoreException e) {
|
||||||
|
status.addFatalError(String.format("XML model core error: %1$s.", e.getMessage()));
|
||||||
|
} finally {
|
||||||
|
if (changes.size() > 0) {
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a quoted attribute value suitable to be placed after an attributeName=
|
||||||
|
* statement in an XML stream.
|
||||||
|
*
|
||||||
|
* According to http://www.w3.org/TR/2008/REC-xml-20081126/#NT-AttValue
|
||||||
|
* the attribute value can be either quoted using ' or " and the corresponding
|
||||||
|
* entities ' or " must be used inside.
|
||||||
|
*/
|
||||||
|
private String quotedAttrValue(String attrValue) {
|
||||||
|
if (attrValue.indexOf('"') == -1) {
|
||||||
|
// no double-quotes inside, use double-quotes around.
|
||||||
|
return '"' + attrValue + '"';
|
||||||
|
}
|
||||||
|
if (attrValue.indexOf('\'') == -1) {
|
||||||
|
// no single-quotes inside, use single-quotes around.
|
||||||
|
return '\'' + attrValue + '\'';
|
||||||
|
}
|
||||||
|
// If we get here, there's a mix. Opt for double-quote around and replace
|
||||||
|
// inner double-quotes.
|
||||||
|
attrValue = attrValue.replace("\"", """); //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
return '"' + attrValue + '"';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the changes to be made to Java file(s) and returns a list of {@link Change}.
|
* Computes the changes to be made to Java file(s) and returns a list of {@link Change}.
|
||||||
*/
|
*/
|
||||||
@@ -766,7 +1225,8 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
if (error != null) {
|
if (error != null) {
|
||||||
status.addFatalError(
|
status.addFatalError(
|
||||||
String.format("Failed to parse file %1$s: %2$s.",
|
String.format("Failed to parse file %1$s: %2$s.",
|
||||||
manifestFile.getFullPath(), error));
|
manifestFile == null ? "" : manifestFile.getFullPath(), //$NON-NLS-1$
|
||||||
|
error));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,12 +1316,12 @@ public class ExtractStringRefactoring extends Refactoring {
|
|||||||
// TextFileChange and accumulate in changes. Right now only one source is
|
// TextFileChange and accumulate in changes. Right now only one source is
|
||||||
// modified.
|
// modified.
|
||||||
|
|
||||||
|
subMonitor.worked(1);
|
||||||
|
|
||||||
if (changes.size() > 0) {
|
if (changes.size() > 0) {
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
subMonitor.worked(1);
|
|
||||||
|
|
||||||
} catch (CoreException e) {
|
} catch (CoreException e) {
|
||||||
// ImportRewrite.rewriteImports failed.
|
// ImportRewrite.rewriteImports failed.
|
||||||
status.addFatalError(e.getMessage());
|
status.addFatalError(e.getMessage());
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toString();
|
return result == null ? null : result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int compareTo(FolderConfiguration folderConfig) {
|
public int compareTo(FolderConfiguration folderConfig) {
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
HOST=`uname`
|
||||||
|
if [ "${HOST:0:6}" == "CYGWIN" ]; then
|
||||||
|
if [ "x$1" == "x" ] || [ `basename "$1"` != "layoutlib.jar" ]; then
|
||||||
|
echo "Usage: $0 sdk/platforms/xxx/data/layoutlib.jar"
|
||||||
|
echo "Argument 1 should be the path to the layoutlib.jar that should be updated by create_bridge_symlinks.sh."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo "### $0 executing"
|
echo "### $0 executing"
|
||||||
|
|
||||||
function die() {
|
function die() {
|
||||||
|
|||||||
Reference in New Issue
Block a user