aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java579
1 files changed, 0 insertions, 579 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java
deleted file mode 100644
index 9b1770d82..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleRefactoring.java
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
- * Copyright (C) 2011 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.editors.layout.refactoring;
-
-import static com.android.SdkConstants.ANDROID_NS_NAME;
-import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_HINT;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN;
-import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.ATTR_NAME;
-import static com.android.SdkConstants.ATTR_ON_CLICK;
-import static com.android.SdkConstants.ATTR_PARENT;
-import static com.android.SdkConstants.ATTR_SRC;
-import static com.android.SdkConstants.ATTR_STYLE;
-import static com.android.SdkConstants.ATTR_TEXT;
-import static com.android.SdkConstants.EXT_XML;
-import static com.android.SdkConstants.FD_RESOURCES;
-import static com.android.SdkConstants.FD_RES_VALUES;
-import static com.android.SdkConstants.PREFIX_ANDROID;
-import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
-import static com.android.SdkConstants.REFERENCE_STYLE;
-import static com.android.SdkConstants.TAG_ITEM;
-import static com.android.SdkConstants.TAG_RESOURCES;
-import static com.android.SdkConstants.XMLNS_PREFIX;
-import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.VisibleForTesting;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.resources.ResourceResolver;
-import com.android.ide.common.xml.XmlFormatStyle;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
-import com.android.utils.Pair;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.OperationCanceledException;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.jface.text.ITextSelection;
-import org.eclipse.jface.viewers.ITreeSelection;
-import org.eclipse.ltk.core.refactoring.Change;
-import org.eclipse.ltk.core.refactoring.Refactoring;
-import org.eclipse.ltk.core.refactoring.RefactoringStatus;
-import org.eclipse.ltk.core.refactoring.TextFileChange;
-import org.eclipse.text.edits.InsertEdit;
-import org.eclipse.text.edits.MultiTextEdit;
-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.IndexedRegion;
-import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
-import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
-import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-/**
- * Extracts the selection and writes it out as a separate layout file, then adds an
- * include to that new layout file. Interactively asks the user for a new name for the
- * layout.
- * <p>
- * Remaining work to do / Possible enhancements:
- * <ul>
- * <li>Optionally look in other files in the project and attempt to set style attributes
- * in other cases where the style attributes match?
- * <li>If the elements we are extracting from already contain a style attribute, set that
- * style as the parent style of the current style?
- * <li>Add a parent-style picker to the wizard (initialized with the above if applicable)
- * <li>Pick up indentation settings from the XML module
- * <li>Integrate with themes somehow -- make an option to have the extracted style go into
- * the theme instead
- * </ul>
- */
-@SuppressWarnings("restriction") // XML model
-public class ExtractStyleRefactoring extends VisualRefactoring {
- private static final String KEY_NAME = "name"; //$NON-NLS-1$
- private static final String KEY_REMOVE_EXTRACTED = "removeextracted"; //$NON-NLS-1$
- private static final String KEY_REMOVE_ALL = "removeall"; //$NON-NLS-1$
- private static final String KEY_APPLY_STYLE = "applystyle"; //$NON-NLS-1$
- private static final String KEY_PARENT = "parent"; //$NON-NLS-1$
- private String mStyleName;
- /** The name of the file in res/values/ that the style will be added to. Normally
- * res/values/styles.xml - but unit tests pick other names */
- private String mStyleFileName = "styles.xml";
- /** Set a style reference on the extracted elements? */
- private boolean mApplyStyle;
- /** Remove the attributes that were extracted? */
- private boolean mRemoveExtracted;
- /** List of attributes chosen by the user to be extracted */
- private List<Attr> mChosenAttributes = new ArrayList<Attr>();
- /** Remove all attributes that match the extracted attributes names, regardless of value */
- private boolean mRemoveAll;
- /** The parent style to extend */
- private String mParent;
- /** The full list of available attributes in the refactoring */
- private Map<String, List<Attr>> mAvailableAttributes;
-
- /**
- * This constructor is solely used by {@link Descriptor},
- * to replay a previous refactoring.
- * @param arguments argument map created by #createArgumentMap.
- */
- ExtractStyleRefactoring(Map<String, String> arguments) {
- super(arguments);
- mStyleName = arguments.get(KEY_NAME);
- mRemoveExtracted = Boolean.parseBoolean(arguments.get(KEY_REMOVE_EXTRACTED));
- mRemoveAll = Boolean.parseBoolean(arguments.get(KEY_REMOVE_ALL));
- mApplyStyle = Boolean.parseBoolean(arguments.get(KEY_APPLY_STYLE));
- mParent = arguments.get(KEY_PARENT);
- if (mParent != null && mParent.length() == 0) {
- mParent = null;
- }
- }
-
- public ExtractStyleRefactoring(
- IFile file,
- LayoutEditorDelegate delegate,
- ITextSelection selection,
- ITreeSelection treeSelection) {
- super(file, delegate, selection, treeSelection);
- }
-
- @VisibleForTesting
- ExtractStyleRefactoring(List<Element> selectedElements, LayoutEditorDelegate editor) {
- super(selectedElements, editor);
- }
-
- @Override
- public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
- OperationCanceledException {
- RefactoringStatus status = new RefactoringStatus();
-
- try {
- pm.beginTask("Checking preconditions...", 6);
-
- if (mSelectionStart == -1 || mSelectionEnd == -1) {
- status.addFatalError("No selection to extract");
- return status;
- }
-
- // This also ensures that we have a valid DOM model:
- if (mElements.size() == 0) {
- status.addFatalError("Nothing to extract");
- return status;
- }
-
- pm.worked(1);
- return status;
-
- } finally {
- pm.done();
- }
- }
-
- @Override
- protected VisualRefactoringDescriptor createDescriptor() {
- String comment = getName();
- return new Descriptor(
- mProject.getName(), //project
- comment, //description
- comment, //comment
- createArgumentMap());
- }
-
- @Override
- protected Map<String, String> createArgumentMap() {
- Map<String, String> args = super.createArgumentMap();
- args.put(KEY_NAME, mStyleName);
- args.put(KEY_REMOVE_EXTRACTED, Boolean.toString(mRemoveExtracted));
- args.put(KEY_REMOVE_ALL, Boolean.toString(mRemoveAll));
- args.put(KEY_APPLY_STYLE, Boolean.toString(mApplyStyle));
- args.put(KEY_PARENT, mParent != null ? mParent : "");
-
- return args;
- }
-
- @Override
- public String getName() {
- return "Extract Style";
- }
-
- void setStyleName(String styleName) {
- mStyleName = styleName;
- }
-
- void setStyleFileName(String styleFileName) {
- mStyleFileName = styleFileName;
- }
-
- void setChosenAttributes(List<Attr> attributes) {
- mChosenAttributes = attributes;
- }
-
- void setRemoveExtracted(boolean removeExtracted) {
- mRemoveExtracted = removeExtracted;
- }
-
- void setApplyStyle(boolean applyStyle) {
- mApplyStyle = applyStyle;
- }
-
- void setRemoveAll(boolean removeAll) {
- mRemoveAll = removeAll;
- }
-
- void setParent(String parent) {
- mParent = parent;
- }
-
- // ---- Actual implementation of Extract Style modification computation ----
-
- /**
- * Returns two items: a map from attribute name to a list of attribute nodes of that
- * name, and a subset of these attributes that fall within the text selection
- * (used to drive initial selection in the wizard)
- */
- Pair<Map<String, List<Attr>>, Set<Attr>> getAvailableAttributes() {
- mAvailableAttributes = new TreeMap<String, List<Attr>>();
- Set<Attr> withinSelection = new HashSet<Attr>();
- for (Element element : getElements()) {
- IndexedRegion elementRegion = getRegion(element);
- boolean allIncluded =
- (mOriginalSelectionStart <= elementRegion.getStartOffset() &&
- mOriginalSelectionEnd >= elementRegion.getEndOffset());
-
- NamedNodeMap attributeMap = element.getAttributes();
- for (int i = 0, n = attributeMap.getLength(); i < n; i++) {
- Attr attribute = (Attr) attributeMap.item(i);
-
- String name = attribute.getLocalName();
- if (!isStylableAttribute(name)) {
- // Don't offer to extract attributes that don't make sense in
- // styles (like "id" or "style"), or attributes that the user
- // probably does not want to define in styles (like layout
- // attributes such as layout_width, or the label of a button etc).
- // This makes the options offered listed in the wizard simpler.
- // In special cases where the user *does* want to set one of these
- // attributes, they can always do it manually so optimize for
- // the common case here.
- continue;
- }
-
- // Skip attributes that are in a namespace other than the Android one
- String namespace = attribute.getNamespaceURI();
- if (namespace != null && !ANDROID_URI.equals(namespace)) {
- continue;
- }
-
- if (!allIncluded) {
- IndexedRegion region = getRegion(attribute);
- boolean attributeIncluded = mOriginalSelectionStart < region.getEndOffset() &&
- mOriginalSelectionEnd >= region.getStartOffset();
- if (attributeIncluded) {
- withinSelection.add(attribute);
- }
- } else {
- withinSelection.add(attribute);
- }
-
- List<Attr> list = mAvailableAttributes.get(name);
- if (list == null) {
- list = new ArrayList<Attr>();
- mAvailableAttributes.put(name, list);
- }
- list.add(attribute);
- }
- }
-
- return Pair.of(mAvailableAttributes, withinSelection);
- }
-
- /**
- * Returns whether the given local attribute name is one the style wizard
- * should present as a selectable attribute to be extracted.
- *
- * @param name the attribute name, not including a namespace prefix
- * @return true if the name is one that the user can extract
- */
- public static boolean isStylableAttribute(String name) {
- return !(name == null
- || name.equals(ATTR_ID)
- || name.startsWith(ATTR_STYLE)
- || (name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) &&
- !name.startsWith(ATTR_LAYOUT_MARGIN))
- || name.equals(ATTR_TEXT)
- || name.equals(ATTR_HINT)
- || name.equals(ATTR_SRC)
- || name.equals(ATTR_ON_CLICK));
- }
-
- IFile getStyleFile(IProject project) {
- return project.getFile(new Path(FD_RESOURCES + WS_SEP + FD_RES_VALUES + WS_SEP
- + mStyleFileName));
- }
-
- @Override
- protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) {
- List<Change> changes = new ArrayList<Change>();
- if (mChosenAttributes.size() == 0) {
- return changes;
- }
-
- IFile file = getStyleFile(mDelegate.getEditor().getProject());
- boolean createFile = !file.exists();
- int insertAtIndex;
- String initialIndent = null;
- if (!createFile) {
- Pair<Integer, String> context = computeInsertContext(file);
- insertAtIndex = context.getFirst();
- initialIndent = context.getSecond();
- } else {
- insertAtIndex = 0;
- }
-
- TextFileChange addFile = new TextFileChange("Create new separate style declaration", file);
- addFile.setTextType(EXT_XML);
- changes.add(addFile);
- String styleString = computeStyleDeclaration(createFile, initialIndent);
- addFile.setEdit(new InsertEdit(insertAtIndex, styleString));
-
- // Remove extracted attributes?
- MultiTextEdit rootEdit = new MultiTextEdit();
- if (mRemoveExtracted || mRemoveAll) {
- for (Attr attribute : mChosenAttributes) {
- List<Attr> list = mAvailableAttributes.get(attribute.getLocalName());
- for (Attr attr : list) {
- if (mRemoveAll || attr.getValue().equals(attribute.getValue())) {
- removeAttribute(rootEdit, attr);
- }
- }
- }
- }
-
- // Set the style attribute?
- if (mApplyStyle) {
- for (Element element : getElements()) {
- String value = PREFIX_RESOURCE_REF + REFERENCE_STYLE + mStyleName;
- setAttribute(rootEdit, element, null, null, ATTR_STYLE, value);
- }
- }
-
- if (rootEdit.hasChildren()) {
- IFile sourceFile = mDelegate.getEditor().getInputFile();
- if (sourceFile == null) {
- return changes;
- }
- TextFileChange change = new TextFileChange(sourceFile.getName(), sourceFile);
- change.setTextType(EXT_XML);
- changes.add(change);
-
- if (AdtPrefs.getPrefs().getFormatGuiXml()) {
- MultiTextEdit formatted = reformat(rootEdit, XmlFormatStyle.LAYOUT);
- if (formatted != null) {
- rootEdit = formatted;
- }
- }
-
- change.setEdit(rootEdit);
- }
-
- return changes;
- }
-
- private String computeStyleDeclaration(boolean createFile, String initialIndent) {
- StringBuilder sb = new StringBuilder();
- if (createFile) {
- sb.append(NewXmlFileWizard.XML_HEADER_LINE);
- sb.append('<').append(TAG_RESOURCES).append(' ');
- sb.append(XMLNS_PREFIX).append(ANDROID_NS_NAME).append('=').append('"');
- sb.append(ANDROID_URI);
- sb.append('"').append('>').append('\n');
- }
-
- // Indent. Use the existing indent found for previous <style> elements in
- // the resource file - but if that indent was 0 (e.g. <style> elements are
- // at the left margin) only use it to indent the style elements and use a real
- // nonzero indent for its children.
- String indent = " "; //$NON-NLS-1$
- if (initialIndent == null) {
- initialIndent = indent;
- } else if (initialIndent.length() > 0) {
- indent = initialIndent;
- }
- sb.append(initialIndent);
- String styleTag = "style"; //$NON-NLS-1$ // TODO - use constant in parallel changeset
- sb.append('<').append(styleTag).append(' ').append(ATTR_NAME).append('=').append('"');
- sb.append(mStyleName);
- sb.append('"');
- if (mParent != null) {
- sb.append(' ').append(ATTR_PARENT).append('=').append('"');
- sb.append(mParent);
- sb.append('"');
- }
- sb.append('>').append('\n');
-
- for (Attr attribute : mChosenAttributes) {
- sb.append(initialIndent).append(indent);
- sb.append('<').append(TAG_ITEM).append(' ').append(ATTR_NAME).append('=').append('"');
- // We've already enforced that regardless of prefix, only attributes with
- // an Android namespace can be in the set of chosen attributes. Rewrite the
- // prefix to android here.
- if (attribute.getPrefix() != null) {
- sb.append(ANDROID_NS_NAME_PREFIX);
- }
- sb.append(attribute.getLocalName());
- sb.append('"').append('>');
- sb.append(attribute.getValue());
- sb.append('<').append('/').append(TAG_ITEM).append('>').append('\n');
- }
- sb.append(initialIndent).append('<').append('/').append(styleTag).append('>').append('\n');
-
- if (createFile) {
- sb.append('<').append('/').append(TAG_RESOURCES).append('>').append('\n');
- }
- String styleString = sb.toString();
- return styleString;
- }
-
- /** Computes the location in the file to insert the new style element at, as well as
- * the exact indent string to use to indent the {@code <style>} element.
- * @param file the styles.xml file to insert into
- * @return a pair of an insert offset and an indent string
- */
- private Pair<Integer, String> computeInsertContext(final IFile file) {
- int insertAtIndex = -1;
- // Find the insert of the final </resources> item where we will insert
- // the new style elements.
- String indent = null;
- IModelManager modelManager = StructuredModelManager.getModelManager();
- IStructuredModel model = null;
- try {
- model = modelManager.getModelForRead(file);
- if (model instanceof IDOMModel) {
- IDOMModel domModel = (IDOMModel) model;
- IDOMDocument otherDocument = domModel.getDocument();
- Element root = otherDocument.getDocumentElement();
- Node lastChild = root.getLastChild();
- if (lastChild != null) {
- if (lastChild instanceof IndexedRegion) {
- IndexedRegion region = (IndexedRegion) lastChild;
- insertAtIndex = region.getStartOffset() + region.getLength();
- }
-
- // Compute indent
- while (lastChild != null) {
- if (lastChild.getNodeType() == Node.ELEMENT_NODE) {
- IStructuredDocument document = model.getStructuredDocument();
- indent = AndroidXmlEditor.getIndent(document, lastChild);
- break;
- }
- lastChild = lastChild.getPreviousSibling();
- }
- }
- }
- } catch (IOException e) {
- AdtPlugin.log(e, null);
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- } finally {
- if (model != null) {
- model.releaseFromRead();
- }
- }
-
- if (insertAtIndex == -1) {
- String contents = AdtPlugin.readFile(file);
- insertAtIndex = contents.indexOf("</" + TAG_RESOURCES + ">"); //$NON-NLS-1$
- if (insertAtIndex == -1) {
- insertAtIndex = contents.length();
- }
- }
-
- return Pair.of(insertAtIndex, indent);
- }
-
- @Override
- VisualRefactoringWizard createWizard() {
- return new ExtractStyleWizard(this, mDelegate);
- }
-
- public static class Descriptor extends VisualRefactoringDescriptor {
- public Descriptor(String project, String description, String comment,
- Map<String, String> arguments) {
- super("com.android.ide.eclipse.adt.refactoring.extract.style", //$NON-NLS-1$
- project, description, comment, arguments);
- }
-
- @Override
- protected Refactoring createRefactoring(Map<String, String> args) {
- return new ExtractStyleRefactoring(args);
- }
- }
-
- /**
- * Determines the parent style to be used for this refactoring
- *
- * @return the parent style to be used for this refactoring
- */
- public String getParentStyle() {
- Set<String> styles = new HashSet<String>();
- for (Element element : getElements()) {
- // Includes "" for elements not setting the style
- styles.add(element.getAttribute(ATTR_STYLE));
- }
-
- if (styles.size() > 1) {
- // The elements differ in what style attributes they are set to
- return null;
- }
-
- String style = styles.iterator().next();
- if (style != null && style.length() > 0) {
- return style;
- }
-
- // None of the elements set the style -- see if they have the same widget types
- // and if so offer to extend the theme style for that widget type
-
- Set<String> types = new HashSet<String>();
- for (Element element : getElements()) {
- types.add(element.getTagName());
- }
-
- if (types.size() == 1) {
- String view = DescriptorsUtils.getBasename(types.iterator().next());
-
- ResourceResolver resolver = mDelegate.getGraphicalEditor().getResourceResolver();
- // Look up the theme item name, which for a Button would be "buttonStyle", and so on.
- String n = Character.toLowerCase(view.charAt(0)) + view.substring(1)
- + "Style"; //$NON-NLS-1$
- ResourceValue value = resolver.findItemInTheme(n);
- if (value != null) {
- ResourceValue resolvedValue = resolver.resolveResValue(value);
- String name = resolvedValue.getName();
- if (name != null) {
- if (resolvedValue.isFramework()) {
- return PREFIX_ANDROID + name;
- } else {
- return name;
- }
- }
- }
- }
-
- return null;
- }
-}