diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java | 606 |
1 files changed, 0 insertions, 606 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java deleted file mode 100644 index 5ac5f5c4e..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringInputPage.java +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.refactorings.extractstring; - - -import com.android.SdkConstants; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector; -import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.SelectorMode; -import com.android.resources.ResourceFolderType; - -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Group; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @see ExtractStringRefactoring - */ -class ExtractStringInputPage extends UserInputWizardPage { - - /** Last res file path used, shared across the session instances but specific to the - * current project. The default for unknown projects is {@link #DEFAULT_RES_FILE_PATH}. */ - private static HashMap<String, String> sLastResFilePath = new HashMap<String, String>(); - - /** The project where the user selection happened. */ - private final IProject mProject; - - /** Text field where the user enters the new ID to be generated or replaced with. */ - private Combo mStringIdCombo; - /** Text field where the user enters the new string value. */ - private Text mStringValueField; - /** The configuration selector, to select the resource path of the XML file. */ - private ConfigurationSelector mConfigSelector; - /** The combo to display the existing XML files or enter a new one. */ - private Combo mResFileCombo; - /** Checkbox asking whether to replace in all Java files. */ - private Button mReplaceAllJava; - /** Checkbox asking whether to replace in all XML files with same name but other res config */ - private Button mReplaceAllXml; - - /** Regex pattern to read a valid res XML file path. It checks that the are 2 folders and - * a leaf file name ending with .xml */ - private static final Pattern RES_XML_FILE_REGEX = Pattern.compile( - "/res/[a-z][a-zA-Z0-9_-]+/[^.]+\\.xml"); //$NON-NLS-1$ - /** Absolute destination folder root, e.g. "/res/" */ - private static final String RES_FOLDER_ABS = - AdtConstants.WS_RESOURCES + AdtConstants.WS_SEP; - /** Relative destination folder root, e.g. "res/" */ - private static final String RES_FOLDER_REL = - SdkConstants.FD_RESOURCES + AdtConstants.WS_SEP; - - private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml"; //$NON-NLS-1$ - - private XmlStringFileHelper mXmlHelper = new XmlStringFileHelper(); - - private final OnConfigSelectorUpdated mOnConfigSelectorUpdated = new OnConfigSelectorUpdated(); - - private ModifyListener mValidateOnModify = new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - validatePage(); - } - }; - - private SelectionListener mValidateOnSelection = new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - validatePage(); - } - }; - - public ExtractStringInputPage(IProject project) { - super("ExtractStringInputPage"); //$NON-NLS-1$ - mProject = project; - } - - /** - * Create the UI for the refactoring wizard. - * <p/> - * Note that at that point the initial conditions have been checked in - * {@link ExtractStringRefactoring}. - * <p/> - * - * Note: the special tag below defines this as the entry point for the WindowsDesigner Editor. - * @wbp.parser.entryPoint - */ - @Override - public void createControl(Composite parent) { - Composite content = new Composite(parent, SWT.NONE); - GridLayout layout = new GridLayout(); - content.setLayout(layout); - - createStringGroup(content); - createResFileGroup(content); - createOptionGroup(content); - - initUi(); - setControl(content); - } - - /** - * Creates the top group with the field to replace which string and by what - * and by which options. - * - * @param content A composite with a 1-column grid layout - */ - public void createStringGroup(Composite content) { - - final ExtractStringRefactoring ref = getOurRefactoring(); - - Group group = new Group(content, SWT.NONE); - group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - group.setText("New String"); - if (ref.getMode() == ExtractStringRefactoring.Mode.EDIT_SOURCE) { - group.setText("String Replacement"); - } - - GridLayout layout = new GridLayout(); - layout.numColumns = 2; - group.setLayout(layout); - - // line: Textfield for string value (based on selection, if any) - - Label label = new Label(group, SWT.NONE); - label.setText("&String"); - - String selectedString = ref.getTokenString(); - - mStringValueField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER); - mStringValueField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - mStringValueField.setText(selectedString != null ? selectedString : ""); //$NON-NLS-1$ - - ref.setNewStringValue(mStringValueField.getText()); - - mStringValueField.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - validatePage(); - } - }); - - // line : Textfield for new ID - - label = new Label(group, SWT.NONE); - label.setText("ID &R.string."); - if (ref.getMode() == ExtractStringRefactoring.Mode.EDIT_SOURCE) { - label.setText("&Replace by R.string."); - } else if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) { - label.setText("New &R.string."); - } - - mStringIdCombo = new Combo(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER | SWT.DROP_DOWN); - mStringIdCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - mStringIdCombo.setText(guessId(selectedString)); - mStringIdCombo.forceFocus(); - - ref.setNewStringId(mStringIdCombo.getText().trim()); - - mStringIdCombo.addModifyListener(mValidateOnModify); - mStringIdCombo.addSelectionListener(mValidateOnSelection); - } - - /** - * Creates the lower group with the fields to choose the resource confirmation and - * the target XML file. - * - * @param content A composite with a 1-column grid layout - */ - private void createResFileGroup(Composite content) { - - Group group = new Group(content, SWT.NONE); - GridData gd = new GridData(GridData.FILL_HORIZONTAL); - gd.grabExcessVerticalSpace = true; - group.setLayoutData(gd); - group.setText("XML resource to edit"); - - GridLayout layout = new GridLayout(); - layout.numColumns = 2; - group.setLayout(layout); - - // line: selection of the res config - - Label label; - label = new Label(group, SWT.NONE); - label.setText("&Configuration:"); - - mConfigSelector = new ConfigurationSelector(group, SelectorMode.DEFAULT); - gd = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL); - gd.horizontalSpan = 2; - gd.widthHint = ConfigurationSelector.WIDTH_HINT; - gd.heightHint = ConfigurationSelector.HEIGHT_HINT; - mConfigSelector.setLayoutData(gd); - mConfigSelector.setOnChangeListener(mOnConfigSelectorUpdated); - - // line: selection of the output file - - label = new Label(group, SWT.NONE); - label.setText("Resource &file:"); - - mResFileCombo = new Combo(group, SWT.DROP_DOWN); - mResFileCombo.select(0); - mResFileCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - mResFileCombo.addModifyListener(mOnConfigSelectorUpdated); - } - - /** - * Creates the bottom option groups with a few checkboxes. - * - * @param content A composite with a 1-column grid layout - */ - private void createOptionGroup(Composite content) { - Group options = new Group(content, SWT.NONE); - options.setText("Options"); - GridData gd_Options = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1); - gd_Options.widthHint = 77; - options.setLayoutData(gd_Options); - options.setLayout(new GridLayout(1, false)); - - mReplaceAllJava = new Button(options, SWT.CHECK); - mReplaceAllJava.setToolTipText("When checked, the exact same string literal will be replaced in all Java files."); - mReplaceAllJava.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mReplaceAllJava.setText("Replace in all &Java files"); - mReplaceAllJava.addSelectionListener(mValidateOnSelection); - - mReplaceAllXml = new Button(options, SWT.CHECK); - mReplaceAllXml.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mReplaceAllXml.setToolTipText("When checked, string literals will be replaced in other XML resource files having the same name but located in different resource configuration folders."); - mReplaceAllXml.setText("Replace in all &XML files for different configuration"); - mReplaceAllXml.addSelectionListener(mValidateOnSelection); - } - - // -- Start of internal part ---------- - // Hide everything down-below from WindowsDesigner Editor - //$hide>>$ - - /** - * Init UI just after it has been created the first time. - */ - private void initUi() { - // set output file name to the last one used - String projPath = mProject.getFullPath().toPortableString(); - String filePath = sLastResFilePath.get(projPath); - - mResFileCombo.setText(filePath != null ? filePath : DEFAULT_RES_FILE_PATH); - mOnConfigSelectorUpdated.run(); - validatePage(); - } - - /** - * Utility method to guess a suitable new XML ID based on the selected string. - */ - public static String guessId(String text) { - if (text == null) { - return ""; //$NON-NLS-1$ - } - - // make lower case - text = text.toLowerCase(Locale.US); - - // everything not alphanumeric becomes an underscore - text = text.replaceAll("[^a-zA-Z0-9]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$ - - // the id must be a proper Java identifier, so it can't start with a number - if (text.length() > 0 && !Character.isJavaIdentifierStart(text.charAt(0))) { - text = "_" + text; //$NON-NLS-1$ - } - return text; - } - - /** - * Returns the {@link ExtractStringRefactoring} instance used by this wizard page. - */ - private ExtractStringRefactoring getOurRefactoring() { - return (ExtractStringRefactoring) getRefactoring(); - } - - /** - * Validates fields of the wizard input page. Displays errors as appropriate and - * enable the "Next" button (or not) by calling {@link #setPageComplete(boolean)}. - * - * If validation succeeds, this updates the text id & value in the refactoring object. - * - * @return True if the page has been positively validated. It may still have warnings. - */ - private boolean validatePage() { - boolean success = true; - - ExtractStringRefactoring ref = getOurRefactoring(); - - ref.setReplaceAllJava(mReplaceAllJava.getSelection()); - ref.setReplaceAllXml(mReplaceAllXml.isEnabled() && mReplaceAllXml.getSelection()); - - // Analyze fatal errors. - - String text = mStringIdCombo.getText().trim(); - if (text == null || text.length() < 1) { - setErrorMessage("Please provide a resource ID."); - success = false; - } else { - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - boolean ok = i == 0 ? - Character.isJavaIdentifierStart(c) : - Character.isJavaIdentifierPart(c); - if (!ok) { - setErrorMessage(String.format( - "The resource ID must be a valid Java identifier. The character %1$c at position %2$d is not acceptable.", - c, i+1)); - success = false; - break; - } - } - - // update the field in the refactoring object in case of success - if (success) { - ref.setNewStringId(text); - } - } - - String resFile = mResFileCombo.getText(); - if (success) { - if (resFile == null || resFile.length() == 0) { - setErrorMessage("A resource file name is required."); - success = false; - } else if (!RES_XML_FILE_REGEX.matcher(resFile).matches()) { - setErrorMessage("The XML file name is not valid."); - success = false; - } - } - - // Analyze info & warnings. - - if (success) { - setErrorMessage(null); - - ref.setTargetFile(resFile); - sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile); - - String idValue = mXmlHelper.valueOfStringId(mProject, resFile, text); - if (idValue != null) { - String msg = String.format("%1$s already contains a string ID '%2$s' with value '%3$s'.", - resFile, - text, - idValue); - if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) { - setErrorMessage(msg); - success = false; - } else { - setMessage(msg, WizardPage.WARNING); - } - } else if (mProject.findMember(resFile) == null) { - setMessage( - String.format("File %2$s does not exist and will be created.", - text, resFile), - WizardPage.INFORMATION); - } else { - setMessage(null); - } - } - - if (success) { - // Also update the text value in case of success. - ref.setNewStringValue(mStringValueField.getText()); - } - - setPageComplete(success); - return success; - } - - private void updateStringValueCombo() { - String resFile = mResFileCombo.getText(); - Map<String, String> ids = mXmlHelper.getResIdsForFile(mProject, resFile); - - // get the current text from the combo, to make sure we don't change it - String currText = mStringIdCombo.getText(); - - // erase the choices and fill with the given ids - mStringIdCombo.removeAll(); - mStringIdCombo.setItems(ids.keySet().toArray(new String[ids.size()])); - - // set the current text to preserve it in case it changed - if (!currText.equals(mStringIdCombo.getText())) { - mStringIdCombo.setText(currText); - } - } - - private class OnConfigSelectorUpdated implements Runnable, ModifyListener { - - /** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */ - private final Pattern mPathRegex = Pattern.compile( - "(/res/[a-z][a-zA-Z0-9_-]+/)(.+)"); //$NON-NLS-1$ - - /** Temporary config object used to retrieve the Config Selector value. */ - private FolderConfiguration mTempConfig = new FolderConfiguration(); - - private HashMap<String, TreeSet<String>> mFolderCache = - new HashMap<String, TreeSet<String>>(); - private String mLastFolderUsedInCombo = null; - private boolean mInternalConfigChange; - private boolean mInternalFileComboChange; - - /** - * Callback invoked when the {@link ConfigurationSelector} has been changed. - * <p/> - * The callback does the following: - * <ul> - * <li> Examine the current file name to retrieve the XML filename, if any. - * <li> Recompute the path based on the configuration selector (e.g. /res/values-fr/). - * <li> Examine the path to retrieve all the files in it. Keep those in a local cache. - * <li> If the XML filename from step 1 is not in the file list, it's a custom file name. - * Insert it and sort it. - * <li> Re-populate the file combo with all the choices. - * <li> Select the original XML file. - */ - @Override - public void run() { - if (mInternalConfigChange) { - return; - } - - // get current leafname, if any - String leafName = ""; //$NON-NLS-1$ - String currPath = mResFileCombo.getText(); - Matcher m = mPathRegex.matcher(currPath); - if (m.matches()) { - // Note: groups 1 and 2 cannot be null. - leafName = m.group(2); - currPath = m.group(1); - } else { - // There was a path but it was invalid. Ignore it. - currPath = ""; //$NON-NLS-1$ - } - - // recreate the res path from the current configuration - mConfigSelector.getConfiguration(mTempConfig); - StringBuffer sb = new StringBuffer(RES_FOLDER_ABS); - sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES)); - sb.append(AdtConstants.WS_SEP); - - String newPath = sb.toString(); - - if (newPath.equals(currPath) && newPath.equals(mLastFolderUsedInCombo)) { - // Path has not changed. No need to reload. - return; - } - - // Get all the files at the new path - - TreeSet<String> filePaths = mFolderCache.get(newPath); - - if (filePaths == null) { - filePaths = new TreeSet<String>(); - - IFolder folder = mProject.getFolder(newPath); - if (folder != null && folder.exists()) { - try { - for (IResource res : folder.members()) { - String name = res.getName(); - if (res.getType() == IResource.FILE && name.endsWith(".xml")) { - filePaths.add(newPath + name); - } - } - } catch (CoreException e) { - // Ignore. - } - } - - mFolderCache.put(newPath, filePaths); - } - - currPath = newPath + leafName; - if (leafName.length() > 0 && !filePaths.contains(currPath)) { - filePaths.add(currPath); - } - - // Fill the combo - try { - mInternalFileComboChange = true; - - mResFileCombo.removeAll(); - - for (String filePath : filePaths) { - mResFileCombo.add(filePath); - } - - int index = -1; - if (leafName.length() > 0) { - index = mResFileCombo.indexOf(currPath); - if (index >= 0) { - mResFileCombo.select(index); - } - } - - if (index == -1) { - mResFileCombo.setText(currPath); - } - - mLastFolderUsedInCombo = newPath; - - } finally { - mInternalFileComboChange = false; - } - - // finally validate the whole page - updateStringValueCombo(); - validatePage(); - } - - /** - * Callback invoked when {@link ExtractStringInputPage#mResFileCombo} has been - * modified. - */ - @Override - public void modifyText(ModifyEvent e) { - if (mInternalFileComboChange) { - return; - } - - String wsFolderPath = mResFileCombo.getText(); - - // This is a custom path, we need to sanitize it. - // First it should start with "/res/". Then we need to make sure there are no - // relative paths, things like "../" or "./" or even "//". - wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/"); //$NON-NLS-1$ //$NON-NLS-2$ - wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", ""); //$NON-NLS-1$ //$NON-NLS-2$ - wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", ""); //$NON-NLS-1$ //$NON-NLS-2$ - - // We get "res/foo" from selections relative to the project when we want a "/res/foo" path. - if (wsFolderPath.startsWith(RES_FOLDER_REL)) { - wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length()); - - mInternalFileComboChange = true; - mResFileCombo.setText(wsFolderPath); - mInternalFileComboChange = false; - } - - if (wsFolderPath.startsWith(RES_FOLDER_ABS)) { - wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length()); - - int pos = wsFolderPath.indexOf(AdtConstants.WS_SEP_CHAR); - if (pos >= 0) { - wsFolderPath = wsFolderPath.substring(0, pos); - } - - String[] folderSegments = wsFolderPath.split(SdkConstants.RES_QUALIFIER_SEP); - - if (folderSegments.length > 0) { - String folderName = folderSegments[0]; - - if (folderName != null && !folderName.equals(wsFolderPath)) { - // update config selector - mInternalConfigChange = true; - mConfigSelector.setConfiguration(folderSegments); - mInternalConfigChange = false; - } - } - } - - updateStringValueCombo(); - validatePage(); - } - } - - // End of hiding from SWT Designer - //$hide<<$ - -} |