diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring')
34 files changed, 0 insertions, 9880 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutAction.java deleted file mode 100644 index 306dd68c8..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutAction.java +++ /dev/null @@ -1,47 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -import org.eclipse.jface.action.IAction; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; - -/** - * Action executed when the "Convert Layout" menu item is invoked. - */ -public class ChangeLayoutAction extends VisualRefactoringAction { - @Override - public void run(IAction action) { - if ((mTextSelection != null || mTreeSelection != null) && mFile != null) { - ChangeLayoutRefactoring ref = new ChangeLayoutRefactoring(mFile, mDelegate, - mTextSelection, mTreeSelection); - RefactoringWizard wizard = new ChangeLayoutWizard(ref, mDelegate); - RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); - try { - op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); - } catch (InterruptedException e) { - // Interrupted. Pass. - } - } - } - - public static IAction create(LayoutEditorDelegate editorDelegate) { - return create("Change Layout...", editorDelegate, ChangeLayoutAction.class); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutContribution.java deleted file mode 100644 index c508b7e92..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutContribution.java +++ /dev/null @@ -1,40 +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 org.eclipse.ltk.core.refactoring.RefactoringContribution; -import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; - -import java.util.Map; - -public class ChangeLayoutContribution extends RefactoringContribution { - - @SuppressWarnings("unchecked") - @Override - public RefactoringDescriptor createDescriptor(String id, String project, String description, - String comment, Map arguments, int flags) throws IllegalArgumentException { - return new ChangeLayoutRefactoring.Descriptor(project, description, comment, arguments); - } - - @SuppressWarnings("unchecked") - @Override - public Map retrieveArgumentMap(RefactoringDescriptor descriptor) { - if (descriptor instanceof ChangeLayoutRefactoring.Descriptor) { - return ((ChangeLayoutRefactoring.Descriptor) descriptor).getArguments(); - } - return super.retrieveArgumentMap(descriptor); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoring.java deleted file mode 100644 index d8c85aab5..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutRefactoring.java +++ /dev/null @@ -1,657 +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_URI; -import static com.android.SdkConstants.ANDROID_WIDGET_PREFIX; -import static com.android.SdkConstants.ATTR_BASELINE_ALIGNED; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE; -import static com.android.SdkConstants.ATTR_LAYOUT_BELOW; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_ORIENTATION; -import static com.android.SdkConstants.EXT_XML; -import static com.android.SdkConstants.FQCN_GESTURE_OVERLAY_VIEW; -import static com.android.SdkConstants.FQCN_GRID_LAYOUT; -import static com.android.SdkConstants.FQCN_LINEAR_LAYOUT; -import static com.android.SdkConstants.FQCN_RELATIVE_LAYOUT; -import static com.android.SdkConstants.FQCN_TABLE_LAYOUT; -import static com.android.SdkConstants.GESTURE_OVERLAY_VIEW; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.TABLE_ROW; -import static com.android.SdkConstants.VALUE_FALSE; -import static com.android.SdkConstants.VALUE_VERTICAL; -import static com.android.SdkConstants.VALUE_WRAP_CONTENT; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.ide.common.xml.XmlFormatStyle; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ViewHierarchy; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.OperationCanceledException; -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.MalformedTreeException; -import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.text.edits.ReplaceEdit; -import org.eclipse.text.edits.TextEdit; -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.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Converts the selected layout into a layout of a different type. - */ -@SuppressWarnings("restriction") // XML model -public class ChangeLayoutRefactoring extends VisualRefactoring { - private static final String KEY_TYPE = "type"; //$NON-NLS-1$ - private static final String KEY_FLATTEN = "flatten"; //$NON-NLS-1$ - - private String mTypeFqcn; - private String mInitializedAttributes; - private boolean mFlatten; - - /** - * This constructor is solely used by {@link Descriptor}, - * to replay a previous refactoring. - * @param arguments argument map created by #createArgumentMap. - */ - ChangeLayoutRefactoring(Map<String, String> arguments) { - super(arguments); - mTypeFqcn = arguments.get(KEY_TYPE); - mFlatten = Boolean.parseBoolean(arguments.get(KEY_FLATTEN)); - } - - @VisibleForTesting - ChangeLayoutRefactoring(List<Element> selectedElements, LayoutEditorDelegate delegate) { - super(selectedElements, delegate); - } - - public ChangeLayoutRefactoring( - IFile file, - LayoutEditorDelegate delegate, - ITextSelection selection, - ITreeSelection treeSelection) { - super(file, delegate, selection, treeSelection); - } - - @Override - public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, - OperationCanceledException { - RefactoringStatus status = new RefactoringStatus(); - - try { - pm.beginTask("Checking preconditions...", 2); - - if (mSelectionStart == -1 || mSelectionEnd == -1) { - status.addFatalError("No selection to convert"); - return status; - } - - if (mElements.size() != 1) { - status.addFatalError("Select precisely one layout to convert"); - 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_TYPE, mTypeFqcn); - args.put(KEY_FLATTEN, Boolean.toString(mFlatten)); - - return args; - } - - @Override - public String getName() { - return "Change Layout"; - } - - void setType(String typeFqcn) { - mTypeFqcn = typeFqcn; - } - - void setInitializedAttributes(String initializedAttributes) { - mInitializedAttributes = initializedAttributes; - } - - void setFlatten(boolean flatten) { - mFlatten = flatten; - } - - @Override - protected List<Element> initElements() { - List<Element> elements = super.initElements(); - - // Don't convert a root GestureOverlayView; convert its child. This looks for - // gesture overlays, and if found, it generates a new child list where the gesture - // overlay children are replaced by their first element children - for (Element element : elements) { - String tagName = element.getTagName(); - if (tagName.equals(GESTURE_OVERLAY_VIEW) - || tagName.equals(FQCN_GESTURE_OVERLAY_VIEW)) { - List<Element> replacement = new ArrayList<Element>(elements.size()); - for (Element e : elements) { - tagName = e.getTagName(); - if (tagName.equals(GESTURE_OVERLAY_VIEW) - || tagName.equals(FQCN_GESTURE_OVERLAY_VIEW)) { - NodeList children = e.getChildNodes(); - Element first = null; - for (int i = 0, n = children.getLength(); i < n; i++) { - Node node = children.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - first = (Element) node; - break; - } - } - if (first != null) { - e = first; - } - } - replacement.add(e); - } - return replacement; - } - } - - return elements; - } - - @Override - protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) { - String name = getViewClass(mTypeFqcn); - - IFile file = mDelegate.getEditor().getInputFile(); - List<Change> changes = new ArrayList<Change>(); - if (file == null) { - return changes; - } - TextFileChange change = new TextFileChange(file.getName(), file); - MultiTextEdit rootEdit = new MultiTextEdit(); - change.setTextType(EXT_XML); - changes.add(change); - - String text = getText(mSelectionStart, mSelectionEnd); - Element layout = getPrimaryElement(); - String oldName = layout.getNodeName(); - int open = text.indexOf(oldName); - int close = text.lastIndexOf(oldName); - - if (open != -1 && close != -1) { - int oldLength = oldName.length(); - rootEdit.addChild(new ReplaceEdit(mSelectionStart + open, oldLength, name)); - if (close != open) { // Gracefully handle <FooLayout/> - rootEdit.addChild(new ReplaceEdit(mSelectionStart + close, oldLength, name)); - } - } - - String oldId = getId(layout); - String newId = ensureIdMatchesType(layout, mTypeFqcn, rootEdit); - // Update any layout references to the old id with the new id - if (oldId != null && newId != null) { - IStructuredModel model = mDelegate.getEditor().getModelForRead(); - try { - IStructuredDocument doc = model.getStructuredDocument(); - if (doc != null) { - List<TextEdit> replaceIds = replaceIds(getAndroidNamespacePrefix(), doc, - mSelectionStart, - mSelectionEnd, oldId, newId); - for (TextEdit edit : replaceIds) { - rootEdit.addChild(edit); - } - } - } finally { - model.releaseFromRead(); - } - } - - String oldType = getOldType(); - String newType = mTypeFqcn; - - if (newType.equals(FQCN_RELATIVE_LAYOUT)) { - if (oldType.equals(FQCN_LINEAR_LAYOUT) && !mFlatten) { - // Hand-coded conversion specifically tailored for linear to relative, provided - // there is no hierarchy flattening - // TODO: use the RelativeLayoutConversionHelper for this; it does a better job - // analyzing gravities etc. - convertLinearToRelative(rootEdit); - removeUndefinedAttrs(rootEdit, layout); - addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, null); - } else { - // Generic conversion to relative - can also flatten the hierarchy - convertAnyToRelative(rootEdit, oldType, newType); - // This already handles removing undefined layout attributes -- right? - //removeUndefinedLayoutAttrs(rootEdit, layout); - } - } else if (newType.equals(FQCN_GRID_LAYOUT)) { - convertAnyToGridLayout(rootEdit); - // Layout attributes on children have already been removed as part of conversion - // during the flattening - removeUndefinedAttrs(rootEdit, layout, false /*removeLayoutAttrs*/); - } else if (oldType.equals(FQCN_RELATIVE_LAYOUT) && newType.equals(FQCN_LINEAR_LAYOUT)) { - convertRelativeToLinear(rootEdit); - removeUndefinedAttrs(rootEdit, layout); - addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, null); - } else if (oldType.equals(FQCN_LINEAR_LAYOUT) && newType.equals(FQCN_TABLE_LAYOUT)) { - convertLinearToTable(rootEdit); - removeUndefinedAttrs(rootEdit, layout); - addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, null); - } else { - convertGeneric(rootEdit, oldType, newType, layout); - } - - if (mInitializedAttributes != null && mInitializedAttributes.length() > 0) { - String namespace = getAndroidNamespacePrefix(); - for (String s : mInitializedAttributes.split(",")) { //$NON-NLS-1$ - String[] nameValue = s.split("="); //$NON-NLS-1$ - String attribute = nameValue[0]; - String value = nameValue[1]; - String prefix = null; - String namespaceUri = null; - if (attribute.startsWith(SdkConstants.ANDROID_NS_NAME_PREFIX)) { - prefix = namespace; - namespaceUri = ANDROID_URI; - attribute = attribute.substring(SdkConstants.ANDROID_NS_NAME_PREFIX.length()); - } - setAttribute(rootEdit, layout, namespaceUri, - prefix, attribute, value); - } - } - - if (AdtPrefs.getPrefs().getFormatGuiXml()) { - MultiTextEdit formatted = reformat(rootEdit, XmlFormatStyle.LAYOUT); - if (formatted != null) { - rootEdit = formatted; - } - } - change.setEdit(rootEdit); - - return changes; - } - - /** Checks whether we need to add any missing attributes on the elements */ - private void addMissingWrapContentAttributes(MultiTextEdit rootEdit, Element layout, - String oldType, String newType, Set<Element> skip) { - if (oldType.equals(FQCN_GRID_LAYOUT) && !newType.equals(FQCN_GRID_LAYOUT)) { - String namespace = getAndroidNamespacePrefix(); - - for (Element child : DomUtilities.getChildren(layout)) { - if (skip != null && skip.contains(child)) { - continue; - } - - if (!child.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH)) { - setAttribute(rootEdit, child, ANDROID_URI, - namespace, ATTR_LAYOUT_WIDTH, VALUE_WRAP_CONTENT); - } - if (!child.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT)) { - setAttribute(rootEdit, child, ANDROID_URI, - namespace, ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT); - } - } - } - } - - /** Hand coded conversion from a LinearLayout to a TableLayout */ - private void convertLinearToTable(MultiTextEdit rootEdit) { - // This is pretty easy; just switch the root tag (already done by the initial generic - // conversion) and then convert all the children into <TableRow> elements. - // Finally, get rid of the orientation attribute, if any. - Element layout = getPrimaryElement(); - removeOrientationAttribute(rootEdit, layout); - - NodeList children = layout.getChildNodes(); - for (int i = 0, n = children.getLength(); i < n; i++) { - Node node = children.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element child = (Element) node; - if (node instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) node; - int start = region.getStartOffset(); - int end = region.getEndOffset(); - String text = getText(start, end); - String oldName = child.getNodeName(); - if (oldName.equals(LINEAR_LAYOUT)) { - removeOrientationAttribute(rootEdit, child); - int open = text.indexOf(oldName); - int close = text.lastIndexOf(oldName); - - if (open != -1 && close != -1) { - int oldLength = oldName.length(); - rootEdit.addChild(new ReplaceEdit(mSelectionStart + open, oldLength, - TABLE_ROW)); - if (close != open) { // Gracefully handle <FooLayout/> - rootEdit.addChild(new ReplaceEdit(mSelectionStart + close, - oldLength, TABLE_ROW)); - } - } - } // else: WRAP in TableLayout! - } - } - } - } - - /** Hand coded conversion from a LinearLayout to a RelativeLayout */ - private void convertLinearToRelative(MultiTextEdit rootEdit) { - // This can be done accurately. - Element layout = getPrimaryElement(); - // Horizontal is the default, so if no value is specified it is horizontal. - boolean isVertical = VALUE_VERTICAL.equals(layout.getAttributeNS(ANDROID_URI, - ATTR_ORIENTATION)); - - String attributePrefix = getAndroidNamespacePrefix(); - - // TODO: Consider gravity of each element - // TODO: Consider weight of each element - // Right now it simply makes a single attachment to keep the order. - - if (isVertical) { - // Align each child to the bottom and left of its parent - NodeList children = layout.getChildNodes(); - String prevId = null; - for (int i = 0, n = children.getLength(); i < n; i++) { - Node node = children.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element child = (Element) node; - String id = ensureHasId(rootEdit, child, null); - if (prevId != null) { - setAttribute(rootEdit, child, ANDROID_URI, attributePrefix, - ATTR_LAYOUT_BELOW, prevId); - } - prevId = id; - } - } - } else { - // Align each child to the left - NodeList children = layout.getChildNodes(); - boolean isBaselineAligned = - !VALUE_FALSE.equals(layout.getAttributeNS(ANDROID_URI, ATTR_BASELINE_ALIGNED)); - - String prevId = null; - for (int i = 0, n = children.getLength(); i < n; i++) { - Node node = children.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element child = (Element) node; - String id = ensureHasId(rootEdit, child, null); - if (prevId != null) { - setAttribute(rootEdit, child, ANDROID_URI, attributePrefix, - ATTR_LAYOUT_TO_RIGHT_OF, prevId); - if (isBaselineAligned) { - setAttribute(rootEdit, child, ANDROID_URI, attributePrefix, - ATTR_LAYOUT_ALIGN_BASELINE, prevId); - } - } - prevId = id; - } - } - } - } - - /** Strips out the android:orientation attribute from the given linear layout element */ - private void removeOrientationAttribute(MultiTextEdit rootEdit, Element layout) { - assert layout.getTagName().equals(LINEAR_LAYOUT); - removeAttribute(rootEdit, layout, ANDROID_URI, ATTR_ORIENTATION); - } - - /** - * Hand coded conversion from a RelativeLayout to a LinearLayout - * - * @param rootEdit the root multi text edit to add edits to - */ - private void convertRelativeToLinear(MultiTextEdit rootEdit) { - // This is going to be lossy... - // TODO: Attempt to "order" the items based on their visual positions - // and insert them in that order in the LinearLayout. - // TODO: Possibly use nesting if necessary, by spatial subdivision, - // to accomplish roughly the same layout as the relative layout specifies. - } - - /** - * Hand coded -generic- conversion from one layout to another. This is not going to be - * an accurate layout transformation; instead it simply migrates the layout attributes - * that are supported, and adds defaults for any new required layout attributes. In - * addition, it attempts to order the children visually based on where they fit in a - * rendering. (Unsupported layout attributes will be removed by the caller at the - * end.) - * <ul> - * <li>Try to handle nesting. Converting a *hierarchy* of layouts into a flatter - * layout for powerful layouts that support it, like RelativeLayout. - * <li>Try to do automatic "inference" about the layout. I can render it and look at - * the ViewInfo positions and sizes. I can render it multiple times, at different - * sizes, to infer "stretchiness" and "weight" properties of the children. - * <li>Try to do indirect transformations. E.g. if I can go from A to B, and B to C, - * then an attempt to go from A to C should perform conversions A to B and then B to - * C. - * </ul> - * - * @param rootEdit the root multi text edit to add edits to - * @param oldType the fully qualified class name of the layout type we are converting - * from - * @param newType the fully qualified class name of the layout type we are converting - * to - * @param layout the layout to be converted - */ - private void convertGeneric(MultiTextEdit rootEdit, String oldType, String newType, - Element layout) { - // TODO: Add hooks for 3rd party conversions getting registered through the - // IViewRule interface. - - // For now we simply go with the default behavior, which is to just strip the - // layout attributes that aren't supported. - removeUndefinedAttrs(rootEdit, layout); - addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, null); - } - - /** - * Removes all the unavailable attributes after a conversion, both on the - * layout element itself as well as the layout attributes of any of the - * children - */ - private void removeUndefinedAttrs(MultiTextEdit rootEdit, Element layout) { - removeUndefinedAttrs(rootEdit, layout, true /*removeLayoutAttrs*/); - } - - private void removeUndefinedAttrs(MultiTextEdit rootEdit, Element layout, - boolean removeLayoutAttrs) { - ViewElementDescriptor descriptor = getElementDescriptor(mTypeFqcn); - if (descriptor == null) { - return; - } - - if (removeLayoutAttrs) { - Set<String> defined = new HashSet<String>(); - AttributeDescriptor[] layoutAttributes = descriptor.getLayoutAttributes(); - for (AttributeDescriptor attribute : layoutAttributes) { - defined.add(attribute.getXmlLocalName()); - } - - NodeList children = layout.getChildNodes(); - for (int i = 0, n = children.getLength(); i < n; i++) { - Node node = children.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element child = (Element) node; - - List<Attr> attributes = findLayoutAttributes(child); - for (Attr attribute : attributes) { - String name = attribute.getLocalName(); - if (!defined.contains(name)) { - // Remove it - try { - removeAttribute(rootEdit, child, attribute.getNamespaceURI(), name); - } catch (MalformedTreeException mte) { - // Sometimes refactoring has modified attribute; not removing - // it is non-fatal so just warn instead of letting refactoring - // operation abort - AdtPlugin.log(IStatus.WARNING, - "Could not remove unsupported attribute %1$s; " + //$NON-NLS-1$ - "already modified during refactoring?", //$NON-NLS-1$ - attribute.getLocalName()); - } - } - } - } - } - } - - // Also remove the unavailable attributes (not layout attributes) on the - // converted element - Set<String> defined = new HashSet<String>(); - AttributeDescriptor[] attributes = descriptor.getAttributes(); - for (AttributeDescriptor attribute : attributes) { - defined.add(attribute.getXmlLocalName()); - } - - // Remove undefined attributes on the layout element itself - NamedNodeMap attributeMap = layout.getAttributes(); - for (int i = 0, n = attributeMap.getLength(); i < n; i++) { - Node attributeNode = attributeMap.item(i); - - String name = attributeNode.getLocalName(); - if (!name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && ANDROID_URI.equals(attributeNode.getNamespaceURI())) { - if (!defined.contains(name)) { - // Remove it - removeAttribute(rootEdit, layout, ANDROID_URI, name); - } - } - } - } - - /** Hand coded conversion from any layout to a RelativeLayout */ - private void convertAnyToRelative(MultiTextEdit rootEdit, String oldType, String newType) { - // To perform a conversion from any other layout type, including nested conversion, - Element layout = getPrimaryElement(); - CanvasViewInfo rootView = mRootView; - if (rootView == null) { - LayoutCanvas canvas = mDelegate.getGraphicalEditor().getCanvasControl(); - ViewHierarchy viewHierarchy = canvas.getViewHierarchy(); - rootView = viewHierarchy.getRoot(); - } - - RelativeLayoutConversionHelper helper = - new RelativeLayoutConversionHelper(this, layout, mFlatten, rootEdit, rootView); - helper.convertToRelative(); - List<Element> deletedElements = helper.getDeletedElements(); - Set<Element> deleted = null; - if (deletedElements != null && deletedElements.size() > 0) { - deleted = new HashSet<Element>(deletedElements); - } - addMissingWrapContentAttributes(rootEdit, layout, oldType, newType, deleted); - } - - /** Hand coded conversion from any layout to a GridLayout */ - private void convertAnyToGridLayout(MultiTextEdit rootEdit) { - // To perform a conversion from any other layout type, including nested conversion, - Element layout = getPrimaryElement(); - CanvasViewInfo rootView = mRootView; - if (rootView == null) { - LayoutCanvas canvas = mDelegate.getGraphicalEditor().getCanvasControl(); - ViewHierarchy viewHierarchy = canvas.getViewHierarchy(); - rootView = viewHierarchy.getRoot(); - } - - GridLayoutConverter converter = new GridLayoutConverter(this, layout, mFlatten, - rootEdit, rootView); - converter.convertToGridLayout(); - } - - 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.convert", //$NON-NLS-1$ - project, description, comment, arguments); - } - - @Override - protected Refactoring createRefactoring(Map<String, String> args) { - return new ChangeLayoutRefactoring(args); - } - } - - String getOldType() { - Element primary = getPrimaryElement(); - if (primary != null) { - String oldType = primary.getTagName(); - if (oldType.indexOf('.') == -1) { - oldType = ANDROID_WIDGET_PREFIX + oldType; - } - return oldType; - } - - return null; - } - - @VisibleForTesting - protected CanvasViewInfo mRootView; - - @VisibleForTesting - public void setRootView(CanvasViewInfo rootView) { - mRootView = rootView; - } - - @Override - VisualRefactoringWizard createWizard() { - return new ChangeLayoutWizard(this, mDelegate); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutWizard.java deleted file mode 100644 index f5582712f..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeLayoutWizard.java +++ /dev/null @@ -1,195 +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.FQCN_GRID_LAYOUT; -import static com.android.SdkConstants.FQCN_RELATIVE_LAYOUT; -import static com.android.SdkConstants.GRID_LAYOUT; -import static com.android.SdkConstants.RELATIVE_LAYOUT; -import static com.android.SdkConstants.VIEW_FRAGMENT; -import static com.android.SdkConstants.VIEW_INCLUDE; -import static com.android.SdkConstants.VIEW_MERGE; - -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IProject; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -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.Label; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -class ChangeLayoutWizard extends VisualRefactoringWizard { - - public ChangeLayoutWizard(ChangeLayoutRefactoring ref, LayoutEditorDelegate editor) { - super(ref, editor); - setDefaultPageTitle("Change Layout"); - } - - @Override - protected void addUserInputPages() { - ChangeLayoutRefactoring ref = (ChangeLayoutRefactoring) getRefactoring(); - String oldType = ref.getOldType(); - addPage(new InputPage(mDelegate.getEditor().getProject(), oldType)); - } - - /** Wizard page which inputs parameters for the {@link ChangeLayoutRefactoring} operation */ - private static class InputPage extends VisualRefactoringInputPage { - private final IProject mProject; - private final String mOldType; - private Combo mTypeCombo; - private Button mFlatten; - private List<Pair<String, ViewElementDescriptor>> mClassNames; - - public InputPage(IProject project, String oldType) { - super("ChangeLayoutInputPage"); //$NON-NLS-1$ - mProject = project; - mOldType = oldType; - } - - @Override - public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - - Label fromLabel = new Label(composite, SWT.NONE); - fromLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); - String oldTypeBase = mOldType.substring(mOldType.lastIndexOf('.') + 1); - fromLabel.setText(String.format("Change from %1$s", oldTypeBase)); - - Label typeLabel = new Label(composite, SWT.NONE); - typeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - typeLabel.setText("New Layout Type:"); - - mTypeCombo = new Combo(composite, SWT.READ_ONLY); - mTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - SelectionAdapter selectionListener = new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - validatePage(); - // Hierarchy flattening only works for relative layout (and any future - // layouts that can also support arbitrary layouts). - String text = mTypeCombo.getText(); - mFlatten.setVisible(text.equals(RELATIVE_LAYOUT) || text.equals(GRID_LAYOUT)); - } - }; - mTypeCombo.addSelectionListener(selectionListener); - mTypeCombo.addSelectionListener(mSelectionValidateListener); - - mFlatten = new Button(composite, SWT.CHECK); - mFlatten.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, - false, false, 2, 1)); - mFlatten.setText("Flatten hierarchy"); - mFlatten.addSelectionListener(selectionListener); - // Should flattening be selected by default? - mFlatten.setSelection(true); - mFlatten.addSelectionListener(mSelectionValidateListener); - - // We don't exclude RelativeLayout even if the current layout is RelativeLayout, - // in case you are trying to flatten the hierarchy for a hierarchy that has a - // RelativeLayout at the root. - Set<String> exclude = new HashSet<String>(); - exclude.add(VIEW_INCLUDE); - exclude.add(VIEW_MERGE); - exclude.add(VIEW_FRAGMENT); - boolean oldIsRelativeLayout = mOldType.equals(FQCN_RELATIVE_LAYOUT); - boolean oldIsGridLayout = mOldType.equals(FQCN_GRID_LAYOUT); - if (oldIsRelativeLayout || oldIsGridLayout) { - exclude.add(mOldType); - } - mClassNames = WrapInWizard.addLayouts(mProject, mOldType, mTypeCombo, exclude, false); - - boolean gridLayoutAvailable = false; - for (int i = 0; i < mTypeCombo.getItemCount(); i++) { - if (mTypeCombo.getItem(i).equals(GRID_LAYOUT)) { - gridLayoutAvailable = true; - break; - } - } - - mTypeCombo.select(0); - // The default should be GridLayout (if available) and if not RelativeLayout, - // if available (and not the old Type) - if (gridLayoutAvailable && !oldIsGridLayout) { - for (int i = 0; i < mTypeCombo.getItemCount(); i++) { - if (mTypeCombo.getItem(i).equals(GRID_LAYOUT)) { - mTypeCombo.select(i); - break; - } - } - } else if (!oldIsRelativeLayout) { - for (int i = 0; i < mTypeCombo.getItemCount(); i++) { - if (mTypeCombo.getItem(i).equals(RELATIVE_LAYOUT)) { - mTypeCombo.select(i); - break; - } - } - } - mFlatten.setVisible(mTypeCombo.getText().equals(RELATIVE_LAYOUT) - || mTypeCombo.getText().equals(GRID_LAYOUT)); - - setControl(composite); - validatePage(); - } - - @Override - protected boolean validatePage() { - boolean ok = true; - - int selectionIndex = mTypeCombo.getSelectionIndex(); - String type = selectionIndex != -1 ? mClassNames.get(selectionIndex).getFirst() : null; - if (type == null) { - setErrorMessage("Select a layout type"); - ok = false; // The user has chosen a separator - } else { - setErrorMessage(null); - - // Record state - ChangeLayoutRefactoring refactoring = - (ChangeLayoutRefactoring) getRefactoring(); - refactoring.setType(type); - refactoring.setFlatten(mFlatten.getSelection()); - - ViewElementDescriptor descriptor = mClassNames.get(selectionIndex).getSecond(); - if (descriptor instanceof PaletteMetadataDescriptor) { - PaletteMetadataDescriptor paletteDescriptor = - (PaletteMetadataDescriptor) descriptor; - String initializedAttributes = paletteDescriptor.getInitializedAttributes(); - if (initializedAttributes != null && initializedAttributes.length() > 0) { - refactoring.setInitializedAttributes(initializedAttributes); - } - } else { - refactoring.setInitializedAttributes(null); - } - } - - setPageComplete(ok); - return ok; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewAction.java deleted file mode 100644 index fa14e5222..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewAction.java +++ /dev/null @@ -1,47 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -import org.eclipse.jface.action.IAction; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; - -/** - * Action executed when the "Change View Type" menu item is invoked. - */ -public class ChangeViewAction extends VisualRefactoringAction { - @Override - public void run(IAction action) { - if ((mTextSelection != null || mTreeSelection != null) && mFile != null) { - ChangeViewRefactoring ref = new ChangeViewRefactoring(mFile, mDelegate, - mTextSelection, mTreeSelection); - RefactoringWizard wizard = new ChangeViewWizard(ref, mDelegate); - RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); - try { - op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); - } catch (InterruptedException e) { - // Interrupted. Pass. - } - } - } - - public static IAction create(LayoutEditorDelegate editorDelegate) { - return create("Change Widget Type...", editorDelegate, ChangeViewAction.class); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewContribution.java deleted file mode 100644 index 7705ed808..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewContribution.java +++ /dev/null @@ -1,40 +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 org.eclipse.ltk.core.refactoring.RefactoringContribution; -import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; - -import java.util.Map; - -public class ChangeViewContribution extends RefactoringContribution { - - @SuppressWarnings("unchecked") - @Override - public RefactoringDescriptor createDescriptor(String id, String project, String description, - String comment, Map arguments, int flags) throws IllegalArgumentException { - return new ChangeViewRefactoring.Descriptor(project, description, comment, arguments); - } - - @SuppressWarnings("unchecked") - @Override - public Map retrieveArgumentMap(RefactoringDescriptor descriptor) { - if (descriptor instanceof ChangeViewRefactoring.Descriptor) { - return ((ChangeViewRefactoring.Descriptor) descriptor).getArguments(); - } - return super.retrieveArgumentMap(descriptor); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoring.java deleted file mode 100644 index 73f5eb149..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewRefactoring.java +++ /dev/null @@ -1,298 +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_URI; -import static com.android.SdkConstants.ANDROID_WIDGET_PREFIX; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_TEXT; -import static com.android.SdkConstants.EXT_XML; -import static com.android.SdkConstants.VIEW_FRAGMENT; -import static com.android.SdkConstants.VIEW_INCLUDE; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -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.MultiTextEdit; -import org.eclipse.text.edits.ReplaceEdit; -import org.eclipse.text.edits.TextEdit; -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.document.ElementImpl; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Changes the type of the given widgets to the given target type - * and updates the attributes if necessary - */ -@SuppressWarnings("restriction") // XML model -public class ChangeViewRefactoring extends VisualRefactoring { - private static final String KEY_TYPE = "type"; //$NON-NLS-1$ - private String mTypeFqcn; - - /** - * This constructor is solely used by {@link Descriptor}, - * to replay a previous refactoring. - * @param arguments argument map created by #createArgumentMap. - */ - ChangeViewRefactoring(Map<String, String> arguments) { - super(arguments); - mTypeFqcn = arguments.get(KEY_TYPE); - } - - public ChangeViewRefactoring( - IFile file, - LayoutEditorDelegate delegate, - ITextSelection selection, - ITreeSelection treeSelection) { - super(file, delegate, selection, treeSelection); - } - - @VisibleForTesting - ChangeViewRefactoring(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 convert"); - return status; - } - - // Make sure the selection is contiguous - if (mTreeSelection != null) { - List<CanvasViewInfo> infos = getSelectedViewInfos(); - if (!validateNotEmpty(infos, status)) { - return status; - } - } - - // Ensures that we have a valid DOM model: - if (mElements.size() == 0) { - status.addFatalError("Nothing to convert"); - 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_TYPE, mTypeFqcn); - - return args; - } - - @Override - public String getName() { - return "Change Widget Type"; - } - - void setType(String typeFqcn) { - mTypeFqcn = typeFqcn; - } - - @Override - protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) { - String name = getViewClass(mTypeFqcn); - - IFile file = mDelegate.getEditor().getInputFile(); - List<Change> changes = new ArrayList<Change>(); - if (file == null) { - return changes; - } - TextFileChange change = new TextFileChange(file.getName(), file); - MultiTextEdit rootEdit = new MultiTextEdit(); - change.setEdit(rootEdit); - change.setTextType(EXT_XML); - changes.add(change); - - for (Element element : getElements()) { - IndexedRegion region = getRegion(element); - String text = getText(region.getStartOffset(), region.getEndOffset()); - String oldName = element.getNodeName(); - int open = text.indexOf(oldName); - int close = text.lastIndexOf(oldName); - if (element instanceof ElementImpl && ((ElementImpl) element).isEmptyTag()) { - close = -1; - } - - if (open != -1) { - int oldLength = oldName.length(); - rootEdit.addChild(new ReplaceEdit(region.getStartOffset() + open, - oldLength, name)); - } - if (close != -1 && close != open) { - int oldLength = oldName.length(); - rootEdit.addChild(new ReplaceEdit(region.getStartOffset() + close, oldLength, - name)); - } - - // Change tag type - String oldId = getId(element); - String newId = ensureIdMatchesType(element, mTypeFqcn, rootEdit); - // Update any layout references to the old id with the new id - if (oldId != null && newId != null) { - IStructuredModel model = mDelegate.getEditor().getModelForRead(); - try { - IStructuredDocument doc = model.getStructuredDocument(); - if (doc != null) { - IndexedRegion range = getRegion(element); - int skipStart = range.getStartOffset(); - int skipEnd = range.getEndOffset(); - List<TextEdit> replaceIds = replaceIds(getAndroidNamespacePrefix(), doc, - skipStart, skipEnd, - oldId, newId); - for (TextEdit edit : replaceIds) { - rootEdit.addChild(edit); - } - } - } finally { - model.releaseFromRead(); - } - } - - // Strip out attributes that no longer make sense - removeUndefinedAttrs(rootEdit, element); - } - - return changes; - } - - /** Removes all the unused attributes after a conversion */ - private void removeUndefinedAttrs(MultiTextEdit rootEdit, Element element) { - ViewElementDescriptor descriptor = getElementDescriptor(mTypeFqcn); - if (descriptor == null) { - return; - } - - Set<String> defined = new HashSet<String>(); - AttributeDescriptor[] layoutAttributes = descriptor.getAttributes(); - for (AttributeDescriptor attribute : layoutAttributes) { - defined.add(attribute.getXmlLocalName()); - } - - List<Attr> attributes = findAttributes(element); - for (Attr attribute : attributes) { - String name = attribute.getLocalName(); - if (!defined.contains(name)) { - // Remove it - removeAttribute(rootEdit, element, attribute.getNamespaceURI(), name); - } - } - - // Set text attribute if it's defined - if (defined.contains(ATTR_TEXT) && !element.hasAttributeNS(ANDROID_URI, ATTR_TEXT)) { - setAttribute(rootEdit, element, ANDROID_URI, getAndroidNamespacePrefix(), - ATTR_TEXT, descriptor.getUiName()); - } - } - - protected List<Attr> findAttributes(Node root) { - List<Attr> result = new ArrayList<Attr>(); - NamedNodeMap attributes = root.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Node attributeNode = attributes.item(i); - - String name = attributeNode.getLocalName(); - if (!name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && ANDROID_URI.equals(attributeNode.getNamespaceURI())) { - result.add((Attr) attributeNode); - } - } - - return result; - } - - List<String> getOldTypes() { - List<String> types = new ArrayList<String>(); - for (Element primary : getElements()) { - String oldType = primary.getTagName(); - if (oldType.indexOf('.') == -1 - && !oldType.equals(VIEW_INCLUDE) && !oldType.equals(VIEW_FRAGMENT)) { - oldType = ANDROID_WIDGET_PREFIX + oldType; - } - types.add(oldType); - } - - return types; - } - - @Override - VisualRefactoringWizard createWizard() { - return new ChangeViewWizard(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.changeview", //$NON-NLS-1$ - project, description, comment, arguments); - } - - @Override - protected Refactoring createRefactoring(Map<String, String> args) { - return new ChangeViewRefactoring(args); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewWizard.java deleted file mode 100644 index 0ac7106b3..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ChangeViewWizard.java +++ /dev/null @@ -1,199 +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.REQUEST_FOCUS; -import static com.android.SdkConstants.VIEW_FRAGMENT; -import static com.android.SdkConstants.VIEW_INCLUDE; - -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CustomViewFinder; -import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.sdklib.IAndroidTarget; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IProject; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; - -import java.util.ArrayList; -import java.util.List; - -class ChangeViewWizard extends VisualRefactoringWizard { - private static final String SEPARATOR_LABEL = - "----------------------------------------"; //$NON-NLS-1$ - - public ChangeViewWizard(ChangeViewRefactoring ref, LayoutEditorDelegate editor) { - super(ref, editor); - setDefaultPageTitle("Change Widget Type"); - } - - @Override - protected void addUserInputPages() { - ChangeViewRefactoring ref = (ChangeViewRefactoring) getRefactoring(); - List<String> oldTypes = ref.getOldTypes(); - String oldType = null; - for (String type : oldTypes) { - if (oldType == null) { - oldType = type; - } else if (!oldType.equals(type)) { - // If the types differ, don't offer related categories - oldType = null; - break; - } - } - addPage(new InputPage(mDelegate.getEditor().getProject(), oldType)); - } - - /** Wizard page which inputs parameters for the {@link ChangeViewRefactoring} operation */ - private static class InputPage extends VisualRefactoringInputPage { - private final IProject mProject; - private Combo mTypeCombo; - private final String mOldType; - private List<String> mClassNames; - - public InputPage(IProject project, String oldType) { - super("ChangeViewInputPage"); //$NON-NLS-1$ - mProject = project; - mOldType = oldType; - } - - @Override - public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - - Label typeLabel = new Label(composite, SWT.NONE); - typeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - typeLabel.setText("New Widget Type:"); - - mTypeCombo = new Combo(composite, SWT.READ_ONLY); - mTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mTypeCombo.addSelectionListener(mSelectionValidateListener); - - mClassNames = getWidgetTypes(mOldType, mTypeCombo); - mTypeCombo.select(0); - - setControl(composite); - validatePage(); - - mTypeCombo.setFocus(); - } - - private List<String> getWidgetTypes(String oldType, Combo combo) { - List<String> classNames = new ArrayList<String>(); - - // Populate type combo - Sdk currentSdk = Sdk.getCurrent(); - if (currentSdk != null) { - IAndroidTarget target = currentSdk.getTarget(mProject); - if (target != null) { - // Try to pick "related" widgets to the one you have selected. - // For example, for an AnalogClock, display DigitalClock first. - // For a Text, offer EditText, AutoComplete, etc. - if (oldType != null) { - ViewMetadataRepository repository = ViewMetadataRepository.get(); - List<String> relatedTo = repository.getRelatedTo(oldType); - if (relatedTo.size() > 0) { - for (String className : relatedTo) { - String base = className.substring(className.lastIndexOf('.') + 1); - combo.add(base); - classNames.add(className); - } - combo.add(SEPARATOR_LABEL); - classNames.add(null); - } - } - - Pair<List<String>,List<String>> result = - CustomViewFinder.findViews(mProject, false); - List<String> customViews = result.getFirst(); - List<String> thirdPartyViews = result.getSecond(); - if (customViews.size() > 0) { - for (String view : customViews) { - combo.add(view); - classNames.add(view); - } - combo.add(SEPARATOR_LABEL); - classNames.add(null); - } - - if (thirdPartyViews.size() > 0) { - for (String view : thirdPartyViews) { - combo.add(view); - classNames.add(view); - } - combo.add(SEPARATOR_LABEL); - classNames.add(null); - } - - AndroidTargetData targetData = currentSdk.getTargetData(target); - if (targetData != null) { - // Now add ALL known layout descriptors in case the user has - // a special case - List<ViewElementDescriptor> descriptors = - targetData.getLayoutDescriptors().getViewDescriptors(); - for (ViewElementDescriptor d : descriptors) { - String className = d.getFullClassName(); - if (className.equals(VIEW_INCLUDE) - || className.equals(VIEW_FRAGMENT) - || className.equals(REQUEST_FOCUS)) { - continue; - } - combo.add(d.getUiName()); - classNames.add(className); - - } - } - } - } else { - combo.add("SDK not initialized"); - classNames.add(null); - } - - return classNames; - } - - @Override - protected boolean validatePage() { - boolean ok = true; - int selectionIndex = mTypeCombo.getSelectionIndex(); - String type = selectionIndex != -1 ? mClassNames.get(selectionIndex) : null; - if (type == null) { - setErrorMessage("Select a widget type to convert to"); - ok = false; // The user has chosen a separator - } else { - setErrorMessage(null); - } - - // Record state - ChangeViewRefactoring refactoring = - (ChangeViewRefactoring) getRefactoring(); - refactoring.setType(type); - - setPageComplete(ok); - return ok; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeAction.java deleted file mode 100644 index 6f96fe489..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeAction.java +++ /dev/null @@ -1,47 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -import org.eclipse.jface.action.IAction; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; - -/** - * Action executed when the "Extract as Include" menu item is invoked. - */ -public class ExtractIncludeAction extends VisualRefactoringAction { - @Override - public void run(IAction action) { - if ((mTextSelection != null || mTreeSelection != null) && mFile != null) { - ExtractIncludeRefactoring ref = new ExtractIncludeRefactoring(mFile, mDelegate, - mTextSelection, mTreeSelection); - RefactoringWizard wizard = new ExtractIncludeWizard(ref, mDelegate); - RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); - try { - op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); - } catch (InterruptedException e) { - // Interrupted. Pass. - } - } - } - - public static IAction create(LayoutEditorDelegate editorDelegate) { - return create("Extract Include...", editorDelegate, ExtractIncludeAction.class); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeContribution.java deleted file mode 100644 index 5903812ea..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeContribution.java +++ /dev/null @@ -1,40 +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 org.eclipse.ltk.core.refactoring.RefactoringContribution; -import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; - -import java.util.Map; - -public class ExtractIncludeContribution extends RefactoringContribution { - - @SuppressWarnings("unchecked") - @Override - public RefactoringDescriptor createDescriptor(String id, String project, String description, - String comment, Map arguments, int flags) throws IllegalArgumentException { - return new ExtractIncludeRefactoring.Descriptor(project, description, comment, arguments); - } - - @SuppressWarnings("unchecked") - @Override - public Map retrieveArgumentMap(RefactoringDescriptor descriptor) { - if (descriptor instanceof ExtractIncludeRefactoring.Descriptor) { - return ((ExtractIncludeRefactoring.Descriptor) descriptor).getArguments(); - } - return super.retrieveArgumentMap(descriptor); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java deleted file mode 100644 index f58ac5501..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeRefactoring.java +++ /dev/null @@ -1,670 +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_URI; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.EXT_XML; -import static com.android.SdkConstants.FD_RES; -import static com.android.SdkConstants.FD_RESOURCES; -import static com.android.SdkConstants.FD_RES_LAYOUT; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.SdkConstants.VALUE_WRAP_CONTENT; -import static com.android.SdkConstants.VIEW_INCLUDE; -import static com.android.SdkConstants.XMLNS; -import static com.android.SdkConstants.XMLNS_PREFIX; -import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP; -import static com.android.resources.ResourceType.LAYOUT; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.ide.common.xml.XmlFormatStyle; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlFormatPreferences; -import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlPrettyPrinter; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; -import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator; -import com.android.utils.XmlUtils; - -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IFile; -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.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.core.runtime.Path; -import org.eclipse.jface.dialogs.IInputValidator; -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.NullChange; -import org.eclipse.ltk.core.refactoring.Refactoring; -import org.eclipse.ltk.core.refactoring.RefactoringStatus; -import org.eclipse.ltk.core.refactoring.TextFileChange; -import org.eclipse.swt.widgets.Display; -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.ui.IWorkbenchPage; -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.Document; -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.List; -import java.util.Locale; -import java.util.Map; - -/** - * 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. - */ -@SuppressWarnings("restriction") // XML model -public class ExtractIncludeRefactoring extends VisualRefactoring { - private static final String KEY_NAME = "name"; //$NON-NLS-1$ - private static final String KEY_OCCURRENCES = "all-occurrences"; //$NON-NLS-1$ - private String mLayoutName; - private boolean mReplaceOccurrences; - - /** - * This constructor is solely used by {@link Descriptor}, - * to replay a previous refactoring. - * @param arguments argument map created by #createArgumentMap. - */ - ExtractIncludeRefactoring(Map<String, String> arguments) { - super(arguments); - mLayoutName = arguments.get(KEY_NAME); - mReplaceOccurrences = Boolean.parseBoolean(arguments.get(KEY_OCCURRENCES)); - } - - public ExtractIncludeRefactoring( - IFile file, - LayoutEditorDelegate delegate, - ITextSelection selection, - ITreeSelection treeSelection) { - super(file, delegate, selection, treeSelection); - } - - @VisibleForTesting - ExtractIncludeRefactoring(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; - } - - // Make sure the selection is contiguous - if (mTreeSelection != null) { - // TODO - don't do this if we based the selection on text. In this case, - // make sure we're -balanced-. - List<CanvasViewInfo> infos = getSelectedViewInfos(); - if (!validateNotEmpty(infos, status)) { - return status; - } - - if (!validateNotRoot(infos, status)) { - return status; - } - - // Disable if you've selected a single include tag - if (infos.size() == 1) { - UiViewElementNode uiNode = infos.get(0).getUiViewNode(); - if (uiNode != null) { - Node xmlNode = uiNode.getXmlNode(); - if (xmlNode.getLocalName().equals(VIEW_INCLUDE)) { - status.addWarning("No point in refactoring a single include tag"); - } - } - } - - // Enforce that the selection is -contiguous- - if (!validateContiguous(infos, status)) { - 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, mLayoutName); - args.put(KEY_OCCURRENCES, Boolean.toString(mReplaceOccurrences)); - - return args; - } - - @Override - public String getName() { - return "Extract as Include"; - } - - void setLayoutName(String layoutName) { - mLayoutName = layoutName; - } - - void setReplaceOccurrences(boolean selection) { - mReplaceOccurrences = selection; - } - - // ---- Actual implementation of Extract as Include modification computation ---- - - @Override - protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) { - String extractedText = getExtractedText(); - - String namespaceDeclarations = computeNamespaceDeclarations(); - - // Insert namespace: - extractedText = insertNamespace(extractedText, namespaceDeclarations); - - StringBuilder sb = new StringBuilder(); - sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); //$NON-NLS-1$ - sb.append(extractedText); - sb.append('\n'); - - List<Change> changes = new ArrayList<Change>(); - - String newFileName = mLayoutName + DOT_XML; - IProject project = mDelegate.getEditor().getProject(); - IFile sourceFile = mDelegate.getEditor().getInputFile(); - if (sourceFile == null) { - return changes; - } - - // Replace extracted elements by <include> tag - handleIncludingFile(changes, sourceFile, mSelectionStart, mSelectionEnd, - getDomDocument(), getPrimaryElement()); - - // Also extract in other variations of the same file (landscape/portrait, etc) - boolean haveVariations = false; - if (mReplaceOccurrences) { - List<IFile> layouts = getOtherLayouts(sourceFile); - for (IFile file : layouts) { - IModelManager modelManager = StructuredModelManager.getModelManager(); - IStructuredModel model = null; - // We could enhance this with a SubMonitor to make the progress bar move as - // well. - monitor.subTask(String.format("Looking for duplicates in %1$s", - file.getProjectRelativePath())); - if (monitor.isCanceled()) { - throw new OperationCanceledException(); - } - - try { - model = modelManager.getModelForRead(file); - if (model instanceof IDOMModel) { - IDOMModel domModel = (IDOMModel) model; - IDOMDocument otherDocument = domModel.getDocument(); - List<Element> otherElements = new ArrayList<Element>(); - Element otherPrimary = null; - - for (Element element : getElements()) { - Element other = DomUtilities.findCorresponding(element, - otherDocument); - if (other != null) { - // See if the structure is similar to what we have in this - // document - if (DomUtilities.isEquivalent(element, other)) { - otherElements.add(other); - if (element == getPrimaryElement()) { - otherPrimary = other; - } - } - } - } - - // Only perform extract in the other file if we find a match for - // ALL of elements being extracted, and if they too are contiguous - if (otherElements.size() == getElements().size() && - DomUtilities.isContiguous(otherElements)) { - // Find the range - int begin = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - for (Element element : otherElements) { - // Yes!! Extract this one as well! - IndexedRegion region = getRegion(element); - end = Math.max(end, region.getEndOffset()); - begin = Math.min(begin, region.getStartOffset()); - } - handleIncludingFile(changes, file, begin, - end, otherDocument, otherPrimary); - haveVariations = true; - } - } - } catch (IOException e) { - AdtPlugin.log(e, null); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } finally { - if (model != null) { - model.releaseFromRead(); - } - } - } - } - - // Add change to create the new file - IContainer parent = sourceFile.getParent(); - if (haveVariations) { - // If we're extracting from multiple configuration folders, then we need to - // place the extracted include in the base layout folder (if not it goes next to - // the including file) - parent = mProject.getFolder(FD_RES).getFolder(FD_RES_LAYOUT); - } - IPath parentPath = parent.getProjectRelativePath(); - final IFile file = project.getFile(new Path(parentPath + WS_SEP + newFileName)); - TextFileChange addFile = new TextFileChange("Create new separate layout", file); - addFile.setTextType(EXT_XML); - changes.add(addFile); - - String newFile = sb.toString(); - if (AdtPrefs.getPrefs().getFormatGuiXml()) { - newFile = EclipseXmlPrettyPrinter.prettyPrint(newFile, - EclipseXmlFormatPreferences.create(), XmlFormatStyle.LAYOUT, - null /*lineSeparator*/); - } - addFile.setEdit(new InsertEdit(0, newFile)); - - Change finishHook = createFinishHook(file); - changes.add(finishHook); - - return changes; - } - - private void handleIncludingFile(List<Change> changes, - IFile sourceFile, int begin, int end, Document document, Element primary) { - TextFileChange change = new TextFileChange(sourceFile.getName(), sourceFile); - MultiTextEdit rootEdit = new MultiTextEdit(); - change.setTextType(EXT_XML); - changes.add(change); - - String referenceId = getReferenceId(); - // Replace existing elements in the source file and insert <include> - String androidNsPrefix = getAndroidNamespacePrefix(document); - String include = computeIncludeString(primary, mLayoutName, androidNsPrefix, referenceId); - int length = end - begin; - ReplaceEdit replace = new ReplaceEdit(begin, length, include); - rootEdit.addChild(replace); - - // Update any layout references to the old id with the new id - if (referenceId != null && primary != null) { - String rootId = getId(primary); - IStructuredModel model = null; - try { - model = StructuredModelManager.getModelManager().getModelForRead(sourceFile); - IStructuredDocument doc = model.getStructuredDocument(); - if (doc != null && rootId != null) { - List<TextEdit> replaceIds = replaceIds(androidNsPrefix, doc, begin, - end, rootId, referenceId); - for (TextEdit edit : replaceIds) { - rootEdit.addChild(edit); - } - - if (AdtPrefs.getPrefs().getFormatGuiXml()) { - MultiTextEdit formatted = reformat(doc.get(), rootEdit, - XmlFormatStyle.LAYOUT); - if (formatted != null) { - rootEdit = formatted; - } - } - } - } catch (IOException e) { - AdtPlugin.log(e, null); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } finally { - if (model != null) { - model.releaseFromRead(); - } - } - } - - change.setEdit(rootEdit); - } - - /** - * Returns a list of all the other layouts (in all configurations) in the project other - * than the given source layout where the refactoring was initiated. Never null. - */ - private List<IFile> getOtherLayouts(IFile sourceFile) { - List<IFile> layouts = new ArrayList<IFile>(100); - IPath sourcePath = sourceFile.getProjectRelativePath(); - IFolder resources = mProject.getFolder(FD_RESOURCES); - try { - for (IResource folder : resources.members()) { - if (folder.getName().startsWith(FD_RES_LAYOUT) && - folder instanceof IFolder) { - IFolder layoutFolder = (IFolder) folder; - for (IResource file : layoutFolder.members()) { - if (file.getName().endsWith(EXT_XML) - && file instanceof IFile) { - if (!file.getProjectRelativePath().equals(sourcePath)) { - layouts.add((IFile) file); - } - } - } - } - } - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - - return layouts; - } - - String getInitialName() { - String defaultName = ""; //$NON-NLS-1$ - Element primary = getPrimaryElement(); - if (primary != null) { - String id = primary.getAttributeNS(ANDROID_URI, ATTR_ID); - // id null check for https://bugs.eclipse.org/bugs/show_bug.cgi?id=272378 - if (id != null && (id.startsWith(ID_PREFIX) || id.startsWith(NEW_ID_PREFIX))) { - // Use everything following the id/, and make it lowercase since that is - // the convention for layouts (and use Locale.US to ensure that "Image" becomes - // "image" etc) - defaultName = id.substring(id.indexOf('/') + 1).toLowerCase(Locale.US); - - IInputValidator validator = ResourceNameValidator.create(true, mProject, LAYOUT); - - if (validator.isValid(defaultName) != null) { // Already exists? - defaultName = ""; //$NON-NLS-1$ - } - } - } - - return defaultName; - } - - IFile getSourceFile() { - return mFile; - } - - private Change createFinishHook(final IFile file) { - return new NullChange("Open extracted layout and refresh resources") { - @Override - public Change perform(IProgressMonitor pm) throws CoreException { - Display display = AdtPlugin.getDisplay(); - display.asyncExec(new Runnable() { - @Override - public void run() { - openFile(file); - mDelegate.getGraphicalEditor().refreshProjectResources(); - // Save file to trigger include finder scanning (as well as making - // the - // actual show-include feature work since it relies on reading - // files from - // disk, not a live buffer) - IWorkbenchPage page = mDelegate.getEditor().getEditorSite().getPage(); - page.saveEditor(mDelegate.getEditor(), false); - } - }); - - // Not undoable: just return null instead of an undo-change. - return null; - } - }; - } - - private String computeNamespaceDeclarations() { - String androidNsPrefix = null; - String namespaceDeclarations = null; - - StringBuilder sb = new StringBuilder(); - List<Attr> attributeNodes = findNamespaceAttributes(); - for (Node attributeNode : attributeNodes) { - String prefix = attributeNode.getPrefix(); - if (XMLNS.equals(prefix)) { - sb.append(' '); - String name = attributeNode.getNodeName(); - sb.append(name); - sb.append('=').append('"'); - - String value = attributeNode.getNodeValue(); - if (value.equals(ANDROID_URI)) { - androidNsPrefix = name; - if (androidNsPrefix.startsWith(XMLNS_PREFIX)) { - androidNsPrefix = androidNsPrefix.substring(XMLNS_PREFIX.length()); - } - } - sb.append(XmlUtils.toXmlAttributeValue(value)); - sb.append('"'); - } - } - namespaceDeclarations = sb.toString(); - - if (androidNsPrefix == null) { - androidNsPrefix = ANDROID_NS_NAME; - } - - if (namespaceDeclarations.length() == 0) { - sb.setLength(0); - sb.append(' '); - sb.append(XMLNS_PREFIX); - sb.append(androidNsPrefix); - sb.append('=').append('"'); - sb.append(ANDROID_URI); - sb.append('"'); - namespaceDeclarations = sb.toString(); - } - - return namespaceDeclarations; - } - - /** Returns the id to be used for the include tag itself (may be null) */ - private String getReferenceId() { - String rootId = getRootId(); - if (rootId != null) { - return rootId + "_ref"; - } - - return null; - } - - /** - * Compute the actual {@code <include>} string to be inserted in place of the old - * selection - */ - private static String computeIncludeString(Element primaryNode, String newName, - String androidNsPrefix, String referenceId) { - StringBuilder sb = new StringBuilder(); - sb.append("<include layout=\"@layout/"); //$NON-NLS-1$ - sb.append(newName); - sb.append('"'); - sb.append(' '); - - // Create new id for the include itself - if (referenceId != null) { - sb.append(androidNsPrefix); - sb.append(':'); - sb.append(ATTR_ID); - sb.append('=').append('"'); - sb.append(referenceId); - sb.append('"').append(' '); - } - - // Add id string, unless it's a <merge>, since we may need to adjust any layout - // references to apply to the <include> tag instead - - // I should move all the layout_ attributes as well - // I also need to duplicate and modify the id and then replace - // everything else in the file with this new id... - - // HACK: see issue 13494: We must duplicate the width/height attributes on the - // <include> statement for designtime rendering only - String width = null; - String height = null; - if (primaryNode == null) { - // Multiple selection - in that case we will be creating an outer <merge> - // so we need to set our own width/height on it - width = height = VALUE_WRAP_CONTENT; - } else { - if (!primaryNode.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH)) { - width = VALUE_WRAP_CONTENT; - } else { - width = primaryNode.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - } - if (!primaryNode.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT)) { - height = VALUE_WRAP_CONTENT; - } else { - height = primaryNode.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT); - } - } - if (width != null) { - sb.append(' '); - sb.append(androidNsPrefix); - sb.append(':'); - sb.append(ATTR_LAYOUT_WIDTH); - sb.append('=').append('"'); - sb.append(XmlUtils.toXmlAttributeValue(width)); - sb.append('"'); - } - if (height != null) { - sb.append(' '); - sb.append(androidNsPrefix); - sb.append(':'); - sb.append(ATTR_LAYOUT_HEIGHT); - sb.append('=').append('"'); - sb.append(XmlUtils.toXmlAttributeValue(height)); - sb.append('"'); - } - - // Duplicate all the other layout attributes as well - if (primaryNode != null) { - NamedNodeMap attributes = primaryNode.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Node attr = attributes.item(i); - String name = attr.getLocalName(); - if (name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && ANDROID_URI.equals(attr.getNamespaceURI())) { - if (name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) { - // Already handled - continue; - } - - sb.append(' '); - sb.append(androidNsPrefix); - sb.append(':'); - sb.append(name); - sb.append('=').append('"'); - sb.append(XmlUtils.toXmlAttributeValue(attr.getNodeValue())); - sb.append('"'); - } - } - } - - sb.append("/>"); - return sb.toString(); - } - - /** Return the text in the document in the range start to end */ - private String getExtractedText() { - String xml = getText(mSelectionStart, mSelectionEnd); - Element primaryNode = getPrimaryElement(); - xml = stripTopLayoutAttributes(primaryNode, mSelectionStart, xml); - xml = dedent(xml); - - // Wrap siblings in <merge>? - if (primaryNode == null) { - StringBuilder sb = new StringBuilder(); - sb.append("<merge>\n"); //$NON-NLS-1$ - // indent an extra level - for (String line : xml.split("\n")) { //$NON-NLS-1$ - sb.append(" "); //$NON-NLS-1$ - sb.append(line).append('\n'); - } - sb.append("</merge>\n"); //$NON-NLS-1$ - xml = sb.toString(); - } - - return xml; - } - - @Override - VisualRefactoringWizard createWizard() { - return new ExtractIncludeWizard(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.include", //$NON-NLS-1$ - project, description, comment, arguments); - } - - @Override - protected Refactoring createRefactoring(Map<String, String> args) { - return new ExtractIncludeRefactoring(args); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeWizard.java deleted file mode 100644 index f3ac3f1b3..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractIncludeWizard.java +++ /dev/null @@ -1,126 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator; -import com.android.resources.ResourceType; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -class ExtractIncludeWizard extends VisualRefactoringWizard { - public ExtractIncludeWizard(ExtractIncludeRefactoring ref, LayoutEditorDelegate editor) { - super(ref, editor); - setDefaultPageTitle(ref.getName()); - } - - @Override - protected void addUserInputPages() { - ExtractIncludeRefactoring ref = (ExtractIncludeRefactoring) getRefactoring(); - String initialName = ref.getInitialName(); - IFile sourceFile = ref.getSourceFile(); - addPage(new InputPage(mDelegate.getEditor().getProject(), sourceFile, initialName)); - } - - /** Wizard page which inputs parameters for the {@link ExtractIncludeRefactoring} operation */ - private static class InputPage extends VisualRefactoringInputPage { - private final IProject mProject; - private final IFile mSourceFile; - private final String mSuggestedName; - private Text mNameText; - private Button mReplaceAllOccurrences; - - public InputPage(IProject project, IFile sourceFile, String suggestedName) { - super("ExtractIncludeInputPage"); - mProject = project; - mSourceFile = sourceFile; - mSuggestedName = suggestedName; - } - - @Override - public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - - Label nameLabel = new Label(composite, SWT.NONE); - nameLabel.setText("New Layout Name:"); - nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - - mNameText = new Text(composite, SWT.BORDER); - mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mNameText.addModifyListener(mModifyValidateListener); - - mReplaceAllOccurrences = new Button(composite, SWT.CHECK); - mReplaceAllOccurrences.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, - false, false, 2, 1)); - mReplaceAllOccurrences.setText( - "Replace occurrences in all layouts with include to new layout"); - mReplaceAllOccurrences.setEnabled(true); - mReplaceAllOccurrences.setSelection(true); - mReplaceAllOccurrences.addSelectionListener(mSelectionValidateListener); - - // Initialize UI: - if (mSuggestedName != null) { - mNameText.setText(mSuggestedName); - } - - setControl(composite); - validatePage(); - } - - @Override - protected boolean validatePage() { - boolean ok = true; - - String text = mNameText.getText().trim(); - - if (text.length() == 0) { - setErrorMessage("Provide a name for the new layout"); - ok = false; - } else { - ResourceNameValidator validator = ResourceNameValidator.create(false, mProject, - ResourceType.LAYOUT); - String message = validator.isValid(text); - if (message != null) { - setErrorMessage(message); - ok = false; - } - } - - if (ok) { - setErrorMessage(null); - - // Record state - ExtractIncludeRefactoring refactoring = - (ExtractIncludeRefactoring) getRefactoring(); - refactoring.setLayoutName(text); - refactoring.setReplaceOccurrences(mReplaceAllOccurrences.getSelection()); - } - - setPageComplete(ok); - return ok; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java deleted file mode 100644 index 4a498637d..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleAction.java +++ /dev/null @@ -1,47 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -import org.eclipse.jface.action.IAction; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; - -/** - * Action executed when the "Extract Style" menu item is invoked. - */ -public class ExtractStyleAction extends VisualRefactoringAction { - @Override - public void run(IAction action) { - if ((mTextSelection != null || mTreeSelection != null) && mFile != null) { - ExtractStyleRefactoring ref = new ExtractStyleRefactoring(mFile, mDelegate, - mTextSelection, mTreeSelection); - RefactoringWizard wizard = new ExtractStyleWizard(ref, mDelegate); - RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); - try { - op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); - } catch (InterruptedException e) { - // Interrupted. Pass. - } - } - } - - public static IAction create(LayoutEditorDelegate editorDelegate) { - return create("Extract Style...", editorDelegate, ExtractStyleAction.class); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java deleted file mode 100644 index 95fbdbc43..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleContribution.java +++ /dev/null @@ -1,40 +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 org.eclipse.ltk.core.refactoring.RefactoringContribution; -import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; - -import java.util.Map; - -public class ExtractStyleContribution extends RefactoringContribution { - - @SuppressWarnings("unchecked") - @Override - public RefactoringDescriptor createDescriptor(String id, String project, String description, - String comment, Map arguments, int flags) throws IllegalArgumentException { - return new ExtractStyleRefactoring.Descriptor(project, description, comment, arguments); - } - - @SuppressWarnings("unchecked") - @Override - public Map retrieveArgumentMap(RefactoringDescriptor descriptor) { - if (descriptor instanceof ExtractStyleRefactoring.Descriptor) { - return ((ExtractStyleRefactoring.Descriptor) descriptor).getArguments(); - } - return super.retrieveArgumentMap(descriptor); - } -} 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; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java deleted file mode 100644 index 187452d21..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/ExtractStyleWizard.java +++ /dev/null @@ -1,440 +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 org.eclipse.jface.viewers.StyledString.DECORATIONS_STYLER; -import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER; - -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator; -import com.android.resources.ResourceType; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.viewers.CheckStateChangedEvent; -import org.eclipse.jface.viewers.CheckboxTableViewer; -import org.eclipse.jface.viewers.ICheckStateListener; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.StyledCellLabelProvider; -import org.eclipse.jface.viewers.StyledString; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerCell; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; -import org.w3c.dom.Attr; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -class ExtractStyleWizard extends VisualRefactoringWizard { - public ExtractStyleWizard(ExtractStyleRefactoring ref, LayoutEditorDelegate editor) { - super(ref, editor); - setDefaultPageTitle(ref.getName()); - } - - @Override - protected void addUserInputPages() { - String initialName = "styleName"; - addPage(new InputPage(mDelegate.getEditor().getProject(), initialName)); - } - - /** - * Wizard page which inputs parameters for the {@link ExtractStyleRefactoring} - * operation - */ - private static class InputPage extends VisualRefactoringInputPage { - private final IProject mProject; - private final String mSuggestedName; - private Text mNameText; - private Table mTable; - private Button mRemoveExtracted; - private Button mSetStyle; - private Button mRemoveAll; - private Button mExtend; - private CheckboxTableViewer mCheckedView; - - private String mParentStyle; - private Set<Attr> mInSelection; - private List<Attr> mAllAttributes; - private int mElementCount; - private Map<Attr, Integer> mFrequencyCount; - private Set<Attr> mShown; - private List<Attr> mInitialChecked; - private List<Attr> mAllChecked; - private List<Map.Entry<String, List<Attr>>> mRoot; - private Map<String, List<Attr>> mAvailableAttributes; - - public InputPage(IProject project, String suggestedName) { - super("ExtractStyleInputPage"); - mProject = project; - mSuggestedName = suggestedName; - } - - @Override - public void createControl(Composite parent) { - initialize(); - - Composite composite = new Composite(parent, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - - Label nameLabel = new Label(composite, SWT.NONE); - nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - nameLabel.setText("Style Name:"); - - mNameText = new Text(composite, SWT.BORDER); - mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mNameText.addModifyListener(mModifyValidateListener); - - mRemoveExtracted = new Button(composite, SWT.CHECK); - mRemoveExtracted.setSelection(true); - mRemoveExtracted.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1)); - mRemoveExtracted.setText("Remove extracted attributes"); - mRemoveExtracted.addSelectionListener(mSelectionValidateListener); - - mRemoveAll = new Button(composite, SWT.CHECK); - mRemoveAll.setSelection(false); - mRemoveAll.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1)); - mRemoveAll.setText("Remove all extracted attributes regardless of value"); - mRemoveAll.addSelectionListener(mSelectionValidateListener); - - boolean defaultSetStyle = false; - if (mParentStyle != null) { - mExtend = new Button(composite, SWT.CHECK); - mExtend.setSelection(true); - mExtend.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1)); - mExtend.setText(String.format("Extend %1$s", mParentStyle)); - mExtend.addSelectionListener(mSelectionValidateListener); - defaultSetStyle = true; - } - - mSetStyle = new Button(composite, SWT.CHECK); - mSetStyle.setSelection(defaultSetStyle); - mSetStyle.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1)); - mSetStyle.setText("Set style attribute on extracted elements"); - mSetStyle.addSelectionListener(mSelectionValidateListener); - - new Label(composite, SWT.NONE); - new Label(composite, SWT.NONE); - - Label tableLabel = new Label(composite, SWT.NONE); - tableLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); - tableLabel.setText("Choose style attributes to extract:"); - - mCheckedView = CheckboxTableViewer.newCheckList(composite, SWT.BORDER - | SWT.FULL_SELECTION | SWT.HIDE_SELECTION); - mTable = mCheckedView.getTable(); - mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 2)); - ((GridData) mTable.getLayoutData()).heightHint = 200; - - mCheckedView.setContentProvider(new ArgumentContentProvider()); - mCheckedView.setLabelProvider(new ArgumentLabelProvider()); - mCheckedView.setInput(mRoot); - final Object[] initialSelection = mInitialChecked.toArray(); - mCheckedView.setCheckedElements(initialSelection); - - mCheckedView.addCheckStateListener(new ICheckStateListener() { - @Override - public void checkStateChanged(CheckStateChangedEvent event) { - // Try to disable other elements that conflict with this - boolean isChecked = event.getChecked(); - if (isChecked) { - Attr attribute = (Attr) event.getElement(); - List<Attr> list = mAvailableAttributes.get(attribute.getLocalName()); - for (Attr other : list) { - if (other != attribute && mShown.contains(other)) { - mCheckedView.setChecked(other, false); - } - } - } - - validatePage(); - } - }); - - // Select All / Deselect All - Composite buttonForm = new Composite(composite, SWT.NONE); - buttonForm.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); - RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL); - rowLayout.marginTop = 0; - rowLayout.marginLeft = 0; - buttonForm.setLayout(rowLayout); - Button checkAllButton = new Button(buttonForm, SWT.FLAT); - checkAllButton.setText("Select All"); - checkAllButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - // Select "all" (but not conflicting settings) - mCheckedView.setCheckedElements(mAllChecked.toArray()); - validatePage(); - } - }); - Button uncheckAllButton = new Button(buttonForm, SWT.FLAT); - uncheckAllButton.setText("Deselect All"); - uncheckAllButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - mCheckedView.setAllChecked(false); - validatePage(); - } - }); - - // Initialize UI: - if (mSuggestedName != null) { - mNameText.setText(mSuggestedName); - } - - setControl(composite); - validatePage(); - } - - private void initialize() { - ExtractStyleRefactoring ref = (ExtractStyleRefactoring) getRefactoring(); - - mElementCount = ref.getElements().size(); - - mParentStyle = ref.getParentStyle(); - - // Set up data structures needed by the wizard -- to compute the actual - // attributes to list in the wizard (there could be multiple attributes - // of the same name (on different elements) and we only want to show one, etc.) - - Pair<Map<String, List<Attr>>, Set<Attr>> result = ref.getAvailableAttributes(); - // List of all available attributes on the selected elements - mAvailableAttributes = result.getFirst(); - // Set of attributes that overlap the text selection, or all attributes if - // wizard is invoked from GUI context - mInSelection = result.getSecond(); - - // The root data structure, which we set as the table root. The content provider - // will produce children from it. This is the entry set of a map from - // attribute name to list of attribute nodes for that attribute name. - mRoot = new ArrayList<Map.Entry<String, List<Attr>>>( - mAvailableAttributes.entrySet()); - - // Sort the items by attribute name -- the attribute name is the key - // in the entry set above. - Collections.sort(mRoot, new Comparator<Map.Entry<String, List<Attr>>>() { - @Override - public int compare(Map.Entry<String, List<Attr>> e1, - Map.Entry<String, List<Attr>> e2) { - return e1.getKey().compareTo(e2.getKey()); - } - }); - - // Set of attributes actually included in the list shown to the user. - // (There could be many additional "aliasing" nodes on other elements - // with the same name.) Note however that we DO show multiple attribute - // occurrences of the same attribute name: precisely one for each unique -value- - // of that attribute. - mShown = new HashSet<Attr>(); - - // The list of initially checked attributes. - mInitialChecked = new ArrayList<Attr>(); - - // The list of attributes to be checked if "Select All" is chosen (this is not - // the same as *all* attributes, since we need to exclude any conflicts) - mAllChecked = new ArrayList<Attr>(); - - // All attributes. - mAllAttributes = new ArrayList<Attr>(); - - // Frequency count, from attribute to integer. Attributes that do not - // appear in the list have frequency 1, not 0. - mFrequencyCount = new HashMap<Attr, Integer>(); - - for (Map.Entry<String, List<Attr>> entry : mRoot) { - // Iterate over all attributes of the same name, and sort them - // by value. This will make it easy to list each -unique- value in the - // wizard. - List<Attr> attrList = entry.getValue(); - Collections.sort(attrList, new Comparator<Attr>() { - @Override - public int compare(Attr a1, Attr a2) { - return a1.getValue().compareTo(a2.getValue()); - } - }); - - // We need to compute a couple of things: the frequency for all identical - // values (and stash them in the frequency map), and record the first - // attribute with a particular value into the list of attributes to - // be shown. - Attr prevAttr = null; - String prev = null; - List<Attr> uniqueValueAttrs = new ArrayList<Attr>(); - for (Attr attr : attrList) { - String value = attr.getValue(); - if (value.equals(prev)) { - Integer count = mFrequencyCount.get(prevAttr); - if (count == null) { - count = Integer.valueOf(2); - } else { - count = Integer.valueOf(count.intValue() + 1); - } - mFrequencyCount.put(prevAttr, count); - } else { - uniqueValueAttrs.add(attr); - prev = value; - prevAttr = attr; - } - } - - // Sort the values by frequency (and for equal frequencies, alphabetically - // by value) - Collections.sort(uniqueValueAttrs, new Comparator<Attr>() { - @Override - public int compare(Attr a1, Attr a2) { - Integer f1 = mFrequencyCount.get(a1); - Integer f2 = mFrequencyCount.get(a2); - if (f1 == null) { - f1 = Integer.valueOf(1); - } - if (f2 == null) { - f2 = Integer.valueOf(1); - } - int delta = f2.intValue() - f1.intValue(); - if (delta != 0) { - return delta; - } else { - return a1.getValue().compareTo(a2.getValue()); - } - } - }); - - // Add the items in order, and select those attributes that overlap - // the selection - mAllAttributes.addAll(uniqueValueAttrs); - mShown.addAll(uniqueValueAttrs); - Attr first = uniqueValueAttrs.get(0); - mAllChecked.add(first); - if (mInSelection.contains(first)) { - mInitialChecked.add(first); - } - } - } - - @Override - protected boolean validatePage() { - boolean ok = true; - - String text = mNameText.getText().trim(); - - if (text.length() == 0) { - setErrorMessage("Provide a name for the new style"); - ok = false; - } else { - ResourceNameValidator validator = ResourceNameValidator.create(false, mProject, - ResourceType.STYLE); - String message = validator.isValid(text); - if (message != null) { - setErrorMessage(message); - ok = false; - } - } - - Object[] checkedElements = mCheckedView.getCheckedElements(); - if (checkedElements.length == 0) { - setErrorMessage("Choose at least one attribute to extract"); - ok = false; - } - - if (ok) { - setErrorMessage(null); - - // Record state - ExtractStyleRefactoring refactoring = (ExtractStyleRefactoring) getRefactoring(); - refactoring.setStyleName(text); - refactoring.setRemoveExtracted(mRemoveExtracted.getSelection()); - refactoring.setRemoveAll(mRemoveAll.getSelection()); - refactoring.setApplyStyle(mSetStyle.getSelection()); - if (mExtend != null && mExtend.getSelection()) { - refactoring.setParent(mParentStyle); - } - List<Attr> attributes = new ArrayList<Attr>(); - for (Object o : checkedElements) { - attributes.add((Attr) o); - } - refactoring.setChosenAttributes(attributes); - } - - setPageComplete(ok); - return ok; - } - - private class ArgumentLabelProvider extends StyledCellLabelProvider { - public ArgumentLabelProvider() { - } - - @Override - public void update(ViewerCell cell) { - Object element = cell.getElement(); - Attr attribute = (Attr) element; - - StyledString styledString = new StyledString(); - styledString.append(attribute.getLocalName()); - styledString.append(" = ", QUALIFIER_STYLER); - styledString.append(attribute.getValue()); - - if (mElementCount > 1) { - Integer f = mFrequencyCount.get(attribute); - String s = String.format(" (in %d/%d elements)", - f != null ? f.intValue(): 1, mElementCount); - styledString.append(s, DECORATIONS_STYLER); - } - cell.setText(styledString.toString()); - cell.setStyleRanges(styledString.getStyleRanges()); - super.update(cell); - } - } - - private class ArgumentContentProvider implements IStructuredContentProvider { - public ArgumentContentProvider() { - } - - @Override - public Object[] getElements(Object inputElement) { - if (inputElement == mRoot) { - return mAllAttributes.toArray(); - } - - return new Object[0]; - } - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/GridLayoutConverter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/GridLayoutConverter.java deleted file mode 100644 index fe673a5b7..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/GridLayoutConverter.java +++ /dev/null @@ -1,988 +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_URI; -import static com.android.SdkConstants.ATTR_BACKGROUND; -import static com.android.SdkConstants.ATTR_COLUMN_COUNT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN; -import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN_SPAN; -import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_LAYOUT_ROW; -import static com.android.SdkConstants.ATTR_LAYOUT_ROW_SPAN; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_ORIENTATION; -import static com.android.SdkConstants.FQCN_GRID_LAYOUT; -import static com.android.SdkConstants.FQCN_SPACE; -import static com.android.SdkConstants.GRAVITY_VALUE_FILL; -import static com.android.SdkConstants.GRAVITY_VALUE_FILL_HORIZONTAL; -import static com.android.SdkConstants.GRAVITY_VALUE_FILL_VERTICAL; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.SdkConstants.RADIO_GROUP; -import static com.android.SdkConstants.RELATIVE_LAYOUT; -import static com.android.SdkConstants.SPACE; -import static com.android.SdkConstants.TABLE_LAYOUT; -import static com.android.SdkConstants.TABLE_ROW; -import static com.android.SdkConstants.VALUE_FILL_PARENT; -import static com.android.SdkConstants.VALUE_HORIZONTAL; -import static com.android.SdkConstants.VALUE_MATCH_PARENT; -import static com.android.SdkConstants.VALUE_VERTICAL; -import static com.android.SdkConstants.VALUE_WRAP_CONTENT; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_HORIZ_MASK; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_VERT_MASK; - -import com.android.ide.common.api.IViewMetadata.FillPreference; -import com.android.ide.common.layout.BaseLayoutRule; -import com.android.ide.common.layout.GravityHelper; -import com.android.ide.common.layout.GridLayoutRule; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; -import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository; -import com.android.ide.eclipse.adt.internal.project.SupportLibraryHelper; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.text.edits.InsertEdit; -import org.eclipse.text.edits.MalformedTreeException; -import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Helper class which performs the bulk of the layout conversion to grid layout - * <p> - * Future enhancements: - * <ul> - * <li>Render the layout at multiple screen sizes and analyze how the widget bounds - * change and use this to infer gravity - * <li> Use the layout_width and layout_height attributes on views to infer column and - * row flexibility (and as mentioned above, possibly layout_weight). - * move and stretch and use that to add in additional constraints - * <li> Take into account existing margins and add/subtract those from the - * bounds computations and either clear or update them. - * <li>Try to reorder elements into their natural order - * <li> Try to preserve spacing? Right now everything gets converted into a compact - * grid with no spacing between the views; consider inserting {@code <Space>} views - * with dimensions based on existing distances. - * </ul> - */ -@SuppressWarnings("restriction") // DOM model access -class GridLayoutConverter { - private final MultiTextEdit mRootEdit; - private final boolean mFlatten; - private final Element mLayout; - private final ChangeLayoutRefactoring mRefactoring; - private final CanvasViewInfo mRootView; - - private List<View> mViews; - private String mNamespace; - private int mColumnCount; - - /** Creates a new {@link GridLayoutConverter} */ - GridLayoutConverter(ChangeLayoutRefactoring refactoring, - Element layout, boolean flatten, MultiTextEdit rootEdit, CanvasViewInfo rootView) { - mRefactoring = refactoring; - mLayout = layout; - mFlatten = flatten; - mRootEdit = rootEdit; - mRootView = rootView; - } - - /** Performs conversion from any layout to a RelativeLayout */ - public void convertToGridLayout() { - if (mRootView == null) { - return; - } - - // Locate the view for the layout - CanvasViewInfo layoutView = findViewForElement(mRootView, mLayout); - if (layoutView == null || layoutView.getChildren().size() == 0) { - // No children. THAT was an easy conversion! - return; - } - - // Study the layout and get information about how to place individual elements - GridModel gridModel = new GridModel(layoutView, mLayout, mFlatten); - mViews = gridModel.getViews(); - mColumnCount = gridModel.computeColumnCount(); - - deleteRemovedElements(gridModel.getDeletedElements()); - mNamespace = mRefactoring.getAndroidNamespacePrefix(); - - processGravities(); - - // Insert space views if necessary - insertStretchableSpans(); - - // Create/update relative layout constraints - assignGridAttributes(); - - removeUndefinedAttrs(); - - if (mColumnCount > 0) { - mRefactoring.setAttribute(mRootEdit, mLayout, ANDROID_URI, - mNamespace, ATTR_COLUMN_COUNT, Integer.toString(mColumnCount)); - } - } - - private void insertStretchableSpans() { - // Look at the rows and columns and determine if we need to have a stretchable - // row and/or a stretchable column in the layout. - // In a GridLayout, a row or column is stretchable if it defines a gravity (regardless - // of what the gravity is -- in other words, a column is not just stretchable if it - // has gravity=fill but also if it has gravity=left). Furthermore, ALL the elements - // in the row/column have to be stretchable for the overall row/column to be - // considered stretchable. - - // Map from row index to boolean for "is the row fixed/inflexible?" - Map<Integer, Boolean> rowFixed = new HashMap<Integer, Boolean>(); - Map<Integer, Boolean> columnFixed = new HashMap<Integer, Boolean>(); - for (View view : mViews) { - if (view.mElement == mLayout) { - continue; - } - - int gravity = GravityHelper.getGravity(view.mGravity, 0); - if ((gravity & GRAVITY_HORIZ_MASK) == 0) { - columnFixed.put(view.mCol, true); - } else if (!columnFixed.containsKey(view.mCol)) { - columnFixed.put(view.mCol, false); - } - if ((gravity & GRAVITY_VERT_MASK) == 0) { - rowFixed.put(view.mRow, true); - } else if (!rowFixed.containsKey(view.mRow)) { - rowFixed.put(view.mRow, false); - } - } - - boolean hasStretchableRow = false; - boolean hasStretchableColumn = false; - for (boolean fixed : rowFixed.values()) { - if (!fixed) { - hasStretchableRow = true; - } - } - for (boolean fixed : columnFixed.values()) { - if (!fixed) { - hasStretchableColumn = true; - } - } - - if (!hasStretchableRow || !hasStretchableColumn) { - // Insert <Space> to hold stretchable space - // TODO: May also have to increment column count! - int offset = 0; // WHERE? - - String gridLayout = mLayout.getTagName(); - if (mLayout instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) mLayout; - int end = region.getEndOffset(); - // TODO: Look backwards for the "</" - // (and can it ever be <foo/>) ? - end -= (gridLayout.length() + 3); // 3: <, /, > - offset = end; - } - - int row = rowFixed.size(); - int column = columnFixed.size(); - StringBuilder sb = new StringBuilder(64); - String spaceTag = SPACE; - IFile file = mRefactoring.getFile(); - if (file != null) { - spaceTag = SupportLibraryHelper.getTagFor(file.getProject(), FQCN_SPACE); - if (spaceTag.equals(FQCN_SPACE)) { - spaceTag = SPACE; - } - } - - sb.append('<').append(spaceTag).append(' '); - String gravity; - if (!hasStretchableRow && !hasStretchableColumn) { - gravity = GRAVITY_VALUE_FILL; - } else if (!hasStretchableRow) { - gravity = GRAVITY_VALUE_FILL_VERTICAL; - } else { - assert !hasStretchableColumn; - gravity = GRAVITY_VALUE_FILL_HORIZONTAL; - } - - sb.append(mNamespace).append(':'); - sb.append(ATTR_LAYOUT_GRAVITY).append('=').append('"').append(gravity); - sb.append('"').append(' '); - - sb.append(mNamespace).append(':'); - sb.append(ATTR_LAYOUT_ROW).append('=').append('"').append(Integer.toString(row)); - sb.append('"').append(' '); - - sb.append(mNamespace).append(':'); - sb.append(ATTR_LAYOUT_COLUMN).append('=').append('"').append(Integer.toString(column)); - sb.append('"').append('/').append('>'); - - String space = sb.toString(); - InsertEdit replace = new InsertEdit(offset, space); - mRootEdit.addChild(replace); - - mColumnCount++; - } - } - - private void removeUndefinedAttrs() { - ViewElementDescriptor descriptor = mRefactoring.getElementDescriptor(FQCN_GRID_LAYOUT); - if (descriptor == null) { - return; - } - - Set<String> defined = new HashSet<String>(); - AttributeDescriptor[] layoutAttributes = descriptor.getLayoutAttributes(); - for (AttributeDescriptor attribute : layoutAttributes) { - defined.add(attribute.getXmlLocalName()); - } - - for (View view : mViews) { - Element child = view.mElement; - - List<Attr> attributes = mRefactoring.findLayoutAttributes(child); - for (Attr attribute : attributes) { - String name = attribute.getLocalName(); - if (!defined.contains(name)) { - // Remove it - try { - mRefactoring.removeAttribute(mRootEdit, child, attribute.getNamespaceURI(), - name); - } catch (MalformedTreeException mte) { - // Sometimes refactoring has modified attribute; not - // removing - // it is non-fatal so just warn instead of letting - // refactoring - // operation abort - AdtPlugin.log(IStatus.WARNING, - "Could not remove unsupported attribute %1$s; " + //$NON-NLS-1$ - "already modified during refactoring?", //$NON-NLS-1$ - attribute.getLocalName()); - } - } - } - } - } - - /** Removes any elements targeted for deletion */ - private void deleteRemovedElements(List<Element> delete) { - if (mFlatten && delete.size() > 0) { - for (Element element : delete) { - mRefactoring.removeElementTags(mRootEdit, element, delete, - false /*changeIndentation*/); - } - } - } - - /** - * Creates refactoring edits which adds or updates the grid attributes - */ - private void assignGridAttributes() { - // We always convert to horizontal grid layouts for now - mRefactoring.setAttribute(mRootEdit, mLayout, ANDROID_URI, - mNamespace, ATTR_ORIENTATION, VALUE_HORIZONTAL); - - assignCellAttributes(); - } - - /** - * Assign cell attributes to the table, skipping those that will be implied - * by the grid model - */ - private void assignCellAttributes() { - int implicitRow = 0; - int implicitColumn = 0; - int nextRow = 0; - for (View view : mViews) { - Element element = view.getElement(); - if (element == mLayout) { - continue; - } - - int row = view.getRow(); - int column = view.getColumn(); - - if (column != implicitColumn && (implicitColumn > 0 || implicitRow > 0)) { - mRefactoring.setAttribute(mRootEdit, element, ANDROID_URI, - mNamespace, ATTR_LAYOUT_COLUMN, Integer.toString(column)); - if (column < implicitColumn) { - implicitRow++; - } - implicitColumn = column; - } - if (row != implicitRow) { - mRefactoring.setAttribute(mRootEdit, element, ANDROID_URI, - mNamespace, ATTR_LAYOUT_ROW, Integer.toString(row)); - implicitRow = row; - } - - int rowSpan = view.getRowSpan(); - int columnSpan = view.getColumnSpan(); - assert columnSpan >= 1; - - if (rowSpan > 1) { - mRefactoring.setAttribute(mRootEdit, element, ANDROID_URI, - mNamespace, ATTR_LAYOUT_ROW_SPAN, Integer.toString(rowSpan)); - } - if (columnSpan > 1) { - mRefactoring.setAttribute(mRootEdit, element, ANDROID_URI, - mNamespace, ATTR_LAYOUT_COLUMN_SPAN, - Integer.toString(columnSpan)); - } - nextRow = Math.max(nextRow, row + rowSpan); - - // wrap_content is redundant in GridLayouts - Attr width = element.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - if (width != null && VALUE_WRAP_CONTENT.equals(width.getValue())) { - mRefactoring.removeAttribute(mRootEdit, width); - } - Attr height = element.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT); - if (height != null && VALUE_WRAP_CONTENT.equals(height.getValue())) { - mRefactoring.removeAttribute(mRootEdit, height); - } - - // Fix up children moved from LinearLayouts that have "invalid" sizes that - // was intended for layout weight handling in their old parent - if (LINEAR_LAYOUT.equals(element.getParentNode().getNodeName())) { - convert0dipToWrapContent(element); - } - - implicitColumn += columnSpan; - if (implicitColumn >= mColumnCount) { - implicitColumn = 0; - assert nextRow > implicitRow; - implicitRow = nextRow; - } - } - } - - private void processGravities() { - for (View view : mViews) { - Element element = view.getElement(); - if (element == mLayout) { - continue; - } - - Attr width = element.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - Attr height = element.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT); - String gravity = element.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_GRAVITY); - String newGravity = null; - if (width != null && (VALUE_MATCH_PARENT.equals(width.getValue()) || - VALUE_FILL_PARENT.equals(width.getValue()))) { - mRefactoring.removeAttribute(mRootEdit, width); - newGravity = gravity = GRAVITY_VALUE_FILL_HORIZONTAL; - } - if (height != null && (VALUE_MATCH_PARENT.equals(height.getValue()) || - VALUE_FILL_PARENT.equals(height.getValue()))) { - mRefactoring.removeAttribute(mRootEdit, height); - if (newGravity == GRAVITY_VALUE_FILL_HORIZONTAL) { - newGravity = GRAVITY_VALUE_FILL; - } else { - newGravity = GRAVITY_VALUE_FILL_VERTICAL; - } - gravity = newGravity; - } - - if (gravity == null || gravity.length() == 0) { - ElementDescriptor descriptor = view.mInfo.getUiViewNode().getDescriptor(); - if (descriptor instanceof ViewElementDescriptor) { - ViewElementDescriptor viewDescriptor = (ViewElementDescriptor) descriptor; - String fqcn = viewDescriptor.getFullClassName(); - FillPreference fill = ViewMetadataRepository.get().getFillPreference(fqcn); - gravity = GridLayoutRule.computeDefaultGravity(fill); - if (gravity != null) { - newGravity = gravity; - } - } - } - - if (newGravity != null) { - mRefactoring.setAttribute(mRootEdit, element, ANDROID_URI, - mNamespace, ATTR_LAYOUT_GRAVITY, newGravity); - } - - view.mGravity = newGravity != null ? newGravity : gravity; - } - } - - - /** Converts 0dip values in layout_width and layout_height to wrap_content instead */ - private void convert0dipToWrapContent(Element child) { - // Must convert layout_height="0dip" to layout_height="wrap_content". - // (And since wrap_content is the default, what we really do is remove - // the attribute completely.) - // 0dip is a special trick used in linear layouts in the presence of - // weights where 0dip ensures that the height of the view is not taken - // into account when distributing the weights. However, when converted - // to RelativeLayout this will instead cause the view to actually be assigned - // 0 height. - Attr height = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT); - // 0dip, 0dp, 0px, etc - if (height != null && height.getValue().startsWith("0")) { //$NON-NLS-1$ - mRefactoring.removeAttribute(mRootEdit, height); - } - Attr width = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - if (width != null && width.getValue().startsWith("0")) { //$NON-NLS-1$ - mRefactoring.removeAttribute(mRootEdit, width); - } - } - - /** - * Searches a view hierarchy and locates the {@link CanvasViewInfo} for the given - * {@link Element} - * - * @param info the root {@link CanvasViewInfo} to search below - * @param element the target element - * @return the {@link CanvasViewInfo} which corresponds to the given element - */ - private CanvasViewInfo findViewForElement(CanvasViewInfo info, Element element) { - if (getElement(info) == element) { - return info; - } - - for (CanvasViewInfo child : info.getChildren()) { - CanvasViewInfo result = findViewForElement(child, element); - if (result != null) { - return result; - } - } - - return null; - } - - /** Returns the {@link Element} for the given {@link CanvasViewInfo} */ - private static Element getElement(CanvasViewInfo info) { - Node node = info.getUiViewNode().getXmlNode(); - if (node instanceof Element) { - return (Element) node; - } - - return null; - } - - - /** Holds layout information about an individual view */ - private static class View { - private final Element mElement; - private int mRow = -1; - private int mCol = -1; - private int mRowSpan = -1; - private int mColSpan = -1; - private int mX1; - private int mY1; - private int mX2; - private int mY2; - private CanvasViewInfo mInfo; - private String mGravity; - - public View(CanvasViewInfo view, Element element) { - mInfo = view; - mElement = element; - - Rectangle b = mInfo.getAbsRect(); - mX1 = b.x; - mX2 = b.x + b.width; - mY1 = b.y; - mY2 = b.y + b.height; - } - - /** - * Returns the element for this view - * - * @return the element for the view - */ - public Element getElement() { - return mElement; - } - - /** - * The assigned row for this view - * - * @return the assigned row - */ - public int getRow() { - return mRow; - } - - /** - * The assigned column for this view - * - * @return the assigned column - */ - public int getColumn() { - return mCol; - } - - /** - * The assigned row span for this view - * - * @return the assigned row span - */ - public int getRowSpan() { - return mRowSpan; - } - - /** - * The assigned column span for this view - * - * @return the assigned column span - */ - public int getColumnSpan() { - return mColSpan; - } - - /** - * The left edge of the view to be used for placement - * - * @return the left edge x coordinate - */ - public int getLeftEdge() { - return mX1; - } - - /** - * The top edge of the view to be used for placement - * - * @return the top edge y coordinate - */ - public int getTopEdge() { - return mY1; - } - - /** - * The right edge of the view to be used for placement - * - * @return the right edge x coordinate - */ - public int getRightEdge() { - return mX2; - } - - /** - * The bottom edge of the view to be used for placement - * - * @return the bottom edge y coordinate - */ - public int getBottomEdge() { - return mY2; - } - - @Override - public String toString() { - return "View(" + VisualRefactoring.getId(mElement) + ": " + mX1 + "," + mY1 + ")"; - } - } - - /** Grid model for the views found in the view hierarchy, partitioned into rows and columns */ - private static class GridModel { - private final List<View> mViews = new ArrayList<View>(); - private final List<Element> mDelete = new ArrayList<Element>(); - private final Map<Element, View> mElementToView = new HashMap<Element, View>(); - private Element mLayout; - private boolean mFlatten; - - GridModel(CanvasViewInfo view, Element layout, boolean flatten) { - mLayout = layout; - mFlatten = flatten; - - scan(view, true); - analyzeKnownLayouts(); - initializeColumns(); - initializeRows(); - mDelete.remove(getElement(view)); - } - - /** - * Returns the {@link View} objects to be placed in the grid - * - * @return list of {@link View} objects, never null but possibly empty - */ - public List<View> getViews() { - return mViews; - } - - /** - * Returns the list of elements that are scheduled for deletion in the - * flattening operation - * - * @return elements to be deleted, never null but possibly empty - */ - public List<Element> getDeletedElements() { - return mDelete; - } - - /** - * Compute and return column count - * - * @return the column count - */ - public int computeColumnCount() { - int columnCount = 0; - for (View view : mViews) { - if (view.getElement() == mLayout) { - continue; - } - - int column = view.getColumn(); - int columnSpan = view.getColumnSpan(); - if (column + columnSpan > columnCount) { - columnCount = column + columnSpan; - } - } - return columnCount; - } - - /** - * Initializes the column and columnSpan attributes of the views - */ - private void initializeColumns() { - // Now initialize table view row, column and spans - Map<Integer, List<View>> mColumnViews = new HashMap<Integer, List<View>>(); - for (View view : mViews) { - if (view.mElement == mLayout) { - continue; - } - int x = view.getLeftEdge(); - List<View> list = mColumnViews.get(x); - if (list == null) { - list = new ArrayList<View>(); - mColumnViews.put(x, list); - } - list.add(view); - } - - List<Integer> columnOffsets = new ArrayList<Integer>(mColumnViews.keySet()); - Collections.sort(columnOffsets); - - int columnIndex = 0; - for (Integer column : columnOffsets) { - List<View> views = mColumnViews.get(column); - if (views != null) { - for (View view : views) { - view.mCol = columnIndex; - } - } - columnIndex++; - } - // Initialize column spans - for (View view : mViews) { - if (view.mElement == mLayout) { - continue; - } - int index = Collections.binarySearch(columnOffsets, view.getRightEdge()); - int column; - if (index == -1) { - // Smaller than the first element; just use the first column - column = 0; - } else if (index < 0) { - column = -(index + 2); - } else { - column = index; - } - - if (column < view.mCol) { - column = view.mCol; - } - - view.mColSpan = column - view.mCol + 1; - } - } - - /** - * Initializes the row and rowSpan attributes of the views - */ - private void initializeRows() { - Map<Integer, List<View>> mRowViews = new HashMap<Integer, List<View>>(); - for (View view : mViews) { - if (view.mElement == mLayout) { - continue; - } - int y = view.getTopEdge(); - List<View> list = mRowViews.get(y); - if (list == null) { - list = new ArrayList<View>(); - mRowViews.put(y, list); - } - list.add(view); - } - - List<Integer> rowOffsets = new ArrayList<Integer>(mRowViews.keySet()); - Collections.sort(rowOffsets); - - int rowIndex = 0; - for (Integer row : rowOffsets) { - List<View> views = mRowViews.get(row); - if (views != null) { - for (View view : views) { - view.mRow = rowIndex; - } - } - rowIndex++; - } - - // Initialize row spans - for (View view : mViews) { - if (view.mElement == mLayout) { - continue; - } - int index = Collections.binarySearch(rowOffsets, view.getBottomEdge()); - int row; - if (index == -1) { - // Smaller than the first element; just use the first row - row = 0; - } else if (index < 0) { - row = -(index + 2); - } else { - row = index; - } - - if (row < view.mRow) { - row = view.mRow; - } - - view.mRowSpan = row - view.mRow + 1; - } - } - - /** - * Walks over a given view hierarchy and locates views to be placed in - * the grid layout (or deleted if we are flattening the hierarchy) - * - * @param view the view to analyze - * @param isRoot whether this view is the root (which cannot be removed) - * @return the {@link View} object for the {@link CanvasViewInfo} - * hierarchy we just analyzed, or null - */ - private View scan(CanvasViewInfo view, boolean isRoot) { - View added = null; - if (!mFlatten || !isRemovableLayout(view)) { - added = add(view); - if (!isRoot) { - return added; - } - } else { - mDelete.add(getElement(view)); - } - - // Build up a table model of the view - for (CanvasViewInfo child : view.getChildren()) { - Element childElement = getElement(child); - - // See if this view shares the edge with the removed - // parent layout, and if so, record that such that we can - // later handle attachments to the removed parent edges - - if (mFlatten && isRemovableLayout(child)) { - // When flattening, we want to disregard all layouts and instead - // add their children! - for (CanvasViewInfo childView : child.getChildren()) { - scan(childView, false); - } - mDelete.add(childElement); - } else { - scan(child, false); - } - } - - return added; - } - - /** Adds the given {@link CanvasViewInfo} into our internal view list */ - private View add(CanvasViewInfo info) { - Element element = getElement(info); - View view = new View(info, element); - mViews.add(view); - mElementToView.put(element, view); - return view; - } - - private void analyzeKnownLayouts() { - Set<Element> parents = new HashSet<Element>(); - for (View view : mViews) { - Node parent = view.getElement().getParentNode(); - if (parent instanceof Element) { - parents.add((Element) parent); - } - } - - List<Collection<View>> rowGroups = new ArrayList<Collection<View>>(); - List<Collection<View>> columnGroups = new ArrayList<Collection<View>>(); - for (Element parent : parents) { - String tagName = parent.getTagName(); - if (tagName.equals(LINEAR_LAYOUT) || tagName.equals(TABLE_LAYOUT) || - tagName.equals(TABLE_ROW) || tagName.equals(RADIO_GROUP)) { - Set<View> group = new HashSet<View>(); - for (Element child : DomUtilities.getChildren(parent)) { - View view = mElementToView.get(child); - if (view != null) { - group.add(view); - } - } - if (group.size() > 1) { - boolean isVertical = VALUE_VERTICAL.equals(parent.getAttributeNS( - ANDROID_URI, ATTR_ORIENTATION)); - if (tagName.equals(TABLE_LAYOUT)) { - isVertical = true; - } else if (tagName.equals(TABLE_ROW)) { - isVertical = false; - } - if (isVertical) { - columnGroups.add(group); - } else { - rowGroups.add(group); - } - } - } else if (tagName.equals(RELATIVE_LAYOUT)) { - List<Element> children = DomUtilities.getChildren(parent); - for (Element child : children) { - View view = mElementToView.get(child); - if (view == null) { - continue; - } - NamedNodeMap attributes = child.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attr = (Attr) attributes.item(i); - String name = attr.getLocalName(); - if (name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)) { - boolean alignVertical = - name.equals(ATTR_LAYOUT_ALIGN_TOP) || - name.equals(ATTR_LAYOUT_ALIGN_BOTTOM) || - name.equals(ATTR_LAYOUT_ALIGN_BASELINE); - boolean alignHorizontal = - name.equals(ATTR_LAYOUT_ALIGN_LEFT) || - name.equals(ATTR_LAYOUT_ALIGN_RIGHT); - if (!alignVertical && !alignHorizontal) { - continue; - } - String value = attr.getValue(); - if (value.startsWith(ID_PREFIX) - || value.startsWith(NEW_ID_PREFIX)) { - String targetName = BaseLayoutRule.stripIdPrefix(value); - Element target = null; - for (Element c : children) { - String id = VisualRefactoring.getId(c); - if (targetName.equals(BaseLayoutRule.stripIdPrefix(id))) { - target = c; - break; - } - } - View targetView = mElementToView.get(target); - if (targetView != null) { - List<View> group = new ArrayList<View>(2); - group.add(view); - group.add(targetView); - if (alignHorizontal) { - columnGroups.add(group); - } else { - assert alignVertical; - rowGroups.add(group); - } - } - } - } - } - } - } else { - // TODO: Consider looking for interesting metadata from other layouts - } - } - - // Assign the same top or left coordinates to the groups to ensure that they - // all get positioned in the same row or column - for (Collection<View> rowGroup : rowGroups) { - // Find the smallest one - Iterator<View> iterator = rowGroup.iterator(); - int smallest = iterator.next().mY1; - while (iterator.hasNext()) { - smallest = Math.min(smallest, iterator.next().mY1); - } - for (View view : rowGroup) { - view.mY2 -= (view.mY1 - smallest); - view.mY1 = smallest; - } - } - for (Collection<View> columnGroup : columnGroups) { - Iterator<View> iterator = columnGroup.iterator(); - int smallest = iterator.next().mX1; - while (iterator.hasNext()) { - smallest = Math.min(smallest, iterator.next().mX1); - } - for (View view : columnGroup) { - view.mX2 -= (view.mX1 - smallest); - view.mX1 = smallest; - } - } - } - - /** - * Returns true if the given {@link CanvasViewInfo} represents an element we - * should remove in a flattening conversion. We don't want to remove non-layout - * views, or layout views that for example contain drawables on their own. - */ - private boolean isRemovableLayout(CanvasViewInfo child) { - // The element being converted is NOT removable! - Element element = getElement(child); - if (element == mLayout) { - return false; - } - - ElementDescriptor descriptor = child.getUiViewNode().getDescriptor(); - String name = descriptor.getXmlLocalName(); - if (name.equals(LINEAR_LAYOUT) || name.equals(RELATIVE_LAYOUT) - || name.equals(TABLE_LAYOUT) || name.equals(TABLE_ROW)) { - // Don't delete layouts that provide a background image or gradient - if (element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND)) { - AdtPlugin.log(IStatus.WARNING, - "Did not flatten layout %1$s because it defines a '%2$s' attribute", - VisualRefactoring.getId(element), ATTR_BACKGROUND); - return false; - } - - return true; - } - - return false; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/JavaQuickAssistant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/JavaQuickAssistant.java deleted file mode 100644 index df5d9eaf3..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/JavaQuickAssistant.java +++ /dev/null @@ -1,73 +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 com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; -import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringProposal; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.ui.text.java.IInvocationContext; -import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; -import org.eclipse.jdt.ui.text.java.IProblemLocation; - -/** - * Quick Assistant for Java files in Android projects - */ -public class JavaQuickAssistant implements org.eclipse.jdt.ui.text.java.IQuickAssistProcessor { - public JavaQuickAssistant() { - } - - @Override - public boolean hasAssists(IInvocationContext context) throws CoreException { - return true; - } - - @Override - public IJavaCompletionProposal[] getAssists(IInvocationContext context, - IProblemLocation[] locations) throws CoreException { - // We should only offer Android quick assists within Android projects. - // This can be done by adding this logic to the extension registration: - // - // <enablement> - // <with variable="projectNatures"> - // <iterate operator="or"> - // <equals value="com.android.ide.eclipse.adt.AndroidNature"/> - // </iterate> - // </with> - // </enablement> - // - // However, this causes some errors to be dumped to the log, so instead we filter - // out non Android projects programmatically: - - IProject project = context.getCompilationUnit().getJavaProject().getProject(); - if (project == null || !BaseProjectHelper.isAndroidProject(project)) { - return null; - } - - ASTNode coveringNode = context.getCoveringNode(); - if (coveringNode != null && coveringNode.getNodeType() == ASTNode.STRING_LITERAL - && coveringNode.getLength() > 2) { // don't extract empty strings (includes quotes) - return new IJavaCompletionProposal[] { - new ExtractStringProposal(context) - }; - } - - return null; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java deleted file mode 100644 index aa8c11999..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RefactoringAssistant.java +++ /dev/null @@ -1,336 +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 com.android.ide.common.resources.ResourceUrl; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; -import com.android.ide.eclipse.adt.internal.refactorings.core.RenameResourceProcessor; -import com.android.ide.eclipse.adt.internal.refactorings.core.RenameResourceWizard; -import com.android.ide.eclipse.adt.internal.refactorings.core.RenameResourceXmlTextAction; -import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring; -import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard; -import com.android.resources.ResourceType; - -import org.eclipse.core.resources.IFile; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.ITextSelection; -import org.eclipse.jface.text.TextSelection; -import org.eclipse.jface.text.contentassist.ICompletionProposal; -import org.eclipse.jface.text.contentassist.IContextInformation; -import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; -import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; -import org.eclipse.jface.text.source.Annotation; -import org.eclipse.jface.text.source.ISourceViewer; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.ltk.core.refactoring.Refactoring; -import org.eclipse.ltk.core.refactoring.participants.RenameRefactoring; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.PlatformUI; -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.sse.core.internal.provisional.text.IStructuredDocumentRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; -import org.eclipse.wst.sse.ui.StructuredTextEditor; -import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; -import org.w3c.dom.Node; - -import java.util.ArrayList; -import java.util.List; - -/** - * QuickAssistProcessor which helps invoke refactoring operations on text elements. - */ -@SuppressWarnings("restriction") // XML model -public class RefactoringAssistant implements IQuickAssistProcessor { - - /** - * Creates a new {@link RefactoringAssistant} - */ - public RefactoringAssistant() { - } - - @Override - public boolean canAssist(IQuickAssistInvocationContext invocationContext) { - return true; - } - - @Override - public boolean canFix(Annotation annotation) { - return true; - } - - @Override - public ICompletionProposal[] computeQuickAssistProposals( - IQuickAssistInvocationContext invocationContext) { - - ISourceViewer sourceViewer = invocationContext.getSourceViewer(); - AndroidXmlEditor xmlEditor = AndroidXmlEditor.fromTextViewer(sourceViewer); - if (xmlEditor == null) { - return null; - } - - IFile file = xmlEditor.getInputFile(); - if (file == null) { - return null; - } - int offset = invocationContext.getOffset(); - - // Ensure that we are over a tag name (for element-based refactoring - // operations) or a value (for the extract include refactoring) - - boolean isValue = false; - boolean isReferenceValue = false; - boolean isTagName = false; - boolean isAttributeName = false; - boolean isStylableAttribute = false; - ResourceUrl resource = null; - IStructuredModel model = null; - try { - model = xmlEditor.getModelForRead(); - IStructuredDocument doc = model.getStructuredDocument(); - IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(offset); - ITextRegion subRegion = region.getRegionAtCharacterOffset(offset); - if (subRegion != null) { - String type = subRegion.getType(); - if (type.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)) { - String value = region.getText(subRegion); - // Only extract values that aren't already resources - // (and value includes leading ' or ") - isValue = true; - if (value.startsWith("'@") || value.startsWith("\"@")) { //$NON-NLS-1$ //$NON-NLS-2$ - isReferenceValue = true; - resource = RenameResourceXmlTextAction.findResource(doc, offset); - } - } else if (type.equals(DOMRegionContext.XML_TAG_NAME) - || type.equals(DOMRegionContext.XML_TAG_OPEN) - || type.equals(DOMRegionContext.XML_TAG_CLOSE)) { - isTagName = true; - } else if (type.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) ) { - isAttributeName = true; - String name = region.getText(subRegion); - int index = name.indexOf(':'); - if (index != -1) { - name = name.substring(index + 1); - } - isStylableAttribute = ExtractStyleRefactoring.isStylableAttribute(name); - } else if (type.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS)) { - // On the edge of an attribute name and an attribute value - isAttributeName = true; - isStylableAttribute = true; - } else if (type.equals(DOMRegionContext.XML_CONTENT)) { - resource = RenameResourceXmlTextAction.findResource(doc, offset); - } - } - } finally { - if (model != null) { - model.releaseFromRead(); - } - } - - List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); - if (isTagName || isAttributeName || isValue || resource != null) { - StructuredTextEditor structuredEditor = xmlEditor.getStructuredTextEditor(); - ISelectionProvider provider = structuredEditor.getSelectionProvider(); - ISelection selection = provider.getSelection(); - if (selection instanceof ITextSelection) { - ITextSelection textSelection = (ITextSelection) selection; - - ITextSelection originalSelection = textSelection; - - // Most of the visual refactorings do not work on text ranges - // ...except for Extract Style where the actual attributes overlapping - // the selection is going to be the set of eligible attributes - boolean selectionOkay = false; - - if (textSelection.getLength() == 0 && !isValue) { - selectionOkay = true; - ISourceViewer textViewer = xmlEditor.getStructuredSourceViewer(); - int caretOffset = textViewer.getTextWidget().getCaretOffset(); - if (caretOffset >= 0) { - Node node = DomUtilities.getNode(textViewer.getDocument(), caretOffset); - if (node instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) node; - int startOffset = region.getStartOffset(); - int length = region.getEndOffset() - region.getStartOffset(); - textSelection = new TextSelection(startOffset, length); - } - } - } - - if (isValue && !isReferenceValue) { - proposals.add(new RefactoringProposal(xmlEditor, - new ExtractStringRefactoring(file, xmlEditor, textSelection))); - } else if (resource != null) { - RenameResourceProcessor processor = new RenameResourceProcessor( - file.getProject(), resource.type, resource.name, null); - RenameRefactoring refactoring = new RenameRefactoring(processor); - proposals.add(new RefactoringProposal(xmlEditor, refactoring)); - } - - LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(xmlEditor); - if (delegate != null) { - boolean showStyleFirst = isValue || (isAttributeName && isStylableAttribute); - if (showStyleFirst) { - proposals.add(new RefactoringProposal( - xmlEditor, - new ExtractStyleRefactoring( - file, - delegate, - originalSelection, - null))); - } - - if (selectionOkay) { - proposals.add(new RefactoringProposal( - xmlEditor, - new WrapInRefactoring( - file, - delegate, - textSelection, - null))); - proposals.add(new RefactoringProposal( - xmlEditor, - new UnwrapRefactoring( - file, - delegate, - textSelection, - null))); - proposals.add(new RefactoringProposal( - xmlEditor, - new ChangeViewRefactoring( - file, - delegate, - textSelection, - null))); - proposals.add(new RefactoringProposal( - xmlEditor, - new ChangeLayoutRefactoring( - file, - delegate, - textSelection, - null))); - } - - // Extract Include must always have an actual block to be extracted - if (textSelection.getLength() > 0) { - proposals.add(new RefactoringProposal( - xmlEditor, - new ExtractIncludeRefactoring( - file, - delegate, - textSelection, - null))); - } - - // If it's not a value or attribute name, don't place it on top - if (!showStyleFirst) { - proposals.add(new RefactoringProposal( - xmlEditor, - new ExtractStyleRefactoring( - file, - delegate, - originalSelection, - null))); - } - } - } - } - - if (proposals.size() == 0) { - return null; - } else { - return proposals.toArray(new ICompletionProposal[proposals.size()]); - } - } - - @Override - public String getErrorMessage() { - return null; - } - - private static class RefactoringProposal - implements ICompletionProposal { - private final AndroidXmlEditor mEditor; - private final Refactoring mRefactoring; - - RefactoringProposal(AndroidXmlEditor editor, Refactoring refactoring) { - super(); - mEditor = editor; - mRefactoring = refactoring; - } - - @Override - public void apply(IDocument document) { - RefactoringWizard wizard = null; - if (mRefactoring instanceof VisualRefactoring) { - wizard = ((VisualRefactoring) mRefactoring).createWizard(); - } else if (mRefactoring instanceof ExtractStringRefactoring) { - wizard = new ExtractStringWizard((ExtractStringRefactoring) mRefactoring, - mEditor.getProject()); - } else if (mRefactoring instanceof RenameRefactoring) { - RenameRefactoring refactoring = (RenameRefactoring) mRefactoring; - RenameResourceProcessor processor = - (RenameResourceProcessor) refactoring.getProcessor(); - ResourceType type = processor.getType(); - wizard = new RenameResourceWizard((RenameRefactoring) mRefactoring, type, false); - } else { - throw new IllegalArgumentException(); - } - - RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); - try { - IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); - op.run(window.getShell(), wizard.getDefaultPageTitle()); - } catch (InterruptedException e) { - } - } - - @Override - public String getAdditionalProposalInfo() { - return String.format("Initiates the \"%1$s\" refactoring", mRefactoring.getName()); - } - - @Override - public IContextInformation getContextInformation() { - return null; - } - - @Override - public String getDisplayString() { - return mRefactoring.getName(); - } - - @Override - public Image getImage() { - return AdtPlugin.getAndroidLogo(); - } - - @Override - public Point getSelection(IDocument document) { - return null; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RelativeLayoutConversionHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RelativeLayoutConversionHelper.java deleted file mode 100644 index e0d6313bf..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/RelativeLayoutConversionHelper.java +++ /dev/null @@ -1,1633 +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_URI; -import static com.android.SdkConstants.ATTR_BACKGROUND; -import static com.android.SdkConstants.ATTR_BASELINE_ALIGNED; -import static com.android.SdkConstants.ATTR_LAYOUT_ABOVE; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BASELINE; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING; -import static com.android.SdkConstants.ATTR_LAYOUT_BELOW; -import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_HORIZONTAL; -import static com.android.SdkConstants.ATTR_LAYOUT_CENTER_VERTICAL; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF; -import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF; -import static com.android.SdkConstants.ATTR_LAYOUT_WEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_ORIENTATION; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.SdkConstants.RELATIVE_LAYOUT; -import static com.android.SdkConstants.VALUE_FALSE; -import static com.android.SdkConstants.VALUE_N_DP; -import static com.android.SdkConstants.VALUE_TRUE; -import static com.android.SdkConstants.VALUE_VERTICAL; -import static com.android.SdkConstants.VALUE_WRAP_CONTENT; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_BOTTOM; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_CENTER_HORIZ; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_CENTER_VERT; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_FILL_HORIZ; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_FILL_VERT; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_LEFT; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_RIGHT; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_TOP; -import static com.android.ide.common.layout.GravityHelper.GRAVITY_VERT_MASK; - -import com.android.ide.common.layout.GravityHelper; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.utils.Pair; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.text.edits.MultiTextEdit; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Helper class which performs the bulk of the layout conversion to relative layout - * <p> - * Future enhancements: - * <ul> - * <li>Render the layout at multiple screen sizes and analyze how the widgets move and - * stretch and use that to add in additional constraints - * <li> Adapt the LinearLayout analysis code to work with TableLayouts and TableRows as well - * (just need to tweak the "isVertical" interpretation to account for the different defaults, - * and perhaps do something about column size properties. - * <li> We need to take into account existing margins and clear/update them - * </ul> - */ -class RelativeLayoutConversionHelper { - private final MultiTextEdit mRootEdit; - private final boolean mFlatten; - private final Element mLayout; - private final ChangeLayoutRefactoring mRefactoring; - private final CanvasViewInfo mRootView; - private List<Element> mDeletedElements; - - RelativeLayoutConversionHelper(ChangeLayoutRefactoring refactoring, - Element layout, boolean flatten, MultiTextEdit rootEdit, CanvasViewInfo rootView) { - mRefactoring = refactoring; - mLayout = layout; - mFlatten = flatten; - mRootEdit = rootEdit; - mRootView = rootView; - } - - /** Performs conversion from any layout to a RelativeLayout */ - public void convertToRelative() { - if (mRootView == null) { - return; - } - - // Locate the view for the layout - CanvasViewInfo layoutView = findViewForElement(mRootView, mLayout); - if (layoutView == null || layoutView.getChildren().size() == 0) { - // No children. THAT was an easy conversion! - return; - } - - // Study the layout and get information about how to place individual elements - List<View> views = analyzeLayout(layoutView); - - // Create/update relative layout constraints - createAttachments(views); - } - - /** Returns the elements that were deleted, or null */ - List<Element> getDeletedElements() { - return mDeletedElements; - } - - /** - * Analyzes the given view hierarchy and produces a list of {@link View} objects which - * contain placement information for each element - */ - private List<View> analyzeLayout(CanvasViewInfo layoutView) { - EdgeList edgeList = new EdgeList(layoutView); - mDeletedElements = edgeList.getDeletedElements(); - deleteRemovedElements(mDeletedElements); - - List<Integer> columnOffsets = edgeList.getColumnOffsets(); - List<Integer> rowOffsets = edgeList.getRowOffsets(); - - // Compute x/y offsets for each row/column index - int[] left = new int[columnOffsets.size()]; - int[] top = new int[rowOffsets.size()]; - - Map<Integer, Integer> xToCol = new HashMap<Integer, Integer>(); - int columnIndex = 0; - for (Integer offset : columnOffsets) { - left[columnIndex] = offset; - xToCol.put(offset, columnIndex++); - } - Map<Integer, Integer> yToRow = new HashMap<Integer, Integer>(); - int rowIndex = 0; - for (Integer offset : rowOffsets) { - top[rowIndex] = offset; - yToRow.put(offset, rowIndex++); - } - - // Create a complete list of view objects - List<View> views = createViews(edgeList, columnOffsets); - initializeSpans(edgeList, columnOffsets, rowOffsets, xToCol, yToRow); - - // Sanity check - for (View view : views) { - assert view.getLeftEdge() == left[view.mCol]; - assert view.getTopEdge() == top[view.mRow]; - assert view.getRightEdge() == left[view.mCol+view.mColSpan]; - assert view.getBottomEdge() == top[view.mRow+view.mRowSpan]; - } - - // Ensure that every view has a proper id such that it can be referred to - // with a constraint - initializeIds(edgeList, views); - - // Attempt to lay the views out in a grid with constraints (though not that widgets - // can overlap as well) - Grid grid = new Grid(views, left, top); - computeKnownConstraints(views, edgeList); - computeHorizontalConstraints(grid); - computeVerticalConstraints(grid); - - return views; - } - - /** Produces a list of {@link View} objects from an {@link EdgeList} */ - private List<View> createViews(EdgeList edgeList, List<Integer> columnOffsets) { - List<View> views = new ArrayList<View>(); - for (Integer offset : columnOffsets) { - List<View> leftEdgeViews = edgeList.getLeftEdgeViews(offset); - if (leftEdgeViews == null) { - // must have been a right edge - continue; - } - for (View view : leftEdgeViews) { - views.add(view); - } - } - return views; - } - - /** Removes any elements targeted for deletion */ - private void deleteRemovedElements(List<Element> delete) { - if (mFlatten && delete.size() > 0) { - for (Element element : delete) { - mRefactoring.removeElementTags(mRootEdit, element, delete, - !AdtPrefs.getPrefs().getFormatGuiXml() /*changeIndentation*/); - } - } - } - - /** Ensures that every element has an id such that it can be referenced from a constraint */ - private void initializeIds(EdgeList edgeList, List<View> views) { - // Ensure that all views have a valid id - for (View view : views) { - String id = mRefactoring.ensureHasId(mRootEdit, view.mElement, null); - edgeList.setIdAttributeValue(view, id); - } - } - - /** - * Initializes the column and row indices, as well as any column span and row span - * values - */ - private void initializeSpans(EdgeList edgeList, List<Integer> columnOffsets, - List<Integer> rowOffsets, Map<Integer, Integer> xToCol, Map<Integer, Integer> yToRow) { - // Now initialize table view row, column and spans - for (Integer offset : columnOffsets) { - List<View> leftEdgeViews = edgeList.getLeftEdgeViews(offset); - if (leftEdgeViews == null) { - // must have been a right edge - continue; - } - for (View view : leftEdgeViews) { - Integer col = xToCol.get(view.getLeftEdge()); - assert col != null; - Integer end = xToCol.get(view.getRightEdge()); - assert end != null; - - view.mCol = col; - view.mColSpan = end - col; - } - } - - for (Integer offset : rowOffsets) { - List<View> topEdgeViews = edgeList.getTopEdgeViews(offset); - if (topEdgeViews == null) { - // must have been a bottom edge - continue; - } - for (View view : topEdgeViews) { - Integer row = yToRow.get(view.getTopEdge()); - assert row != null; - Integer end = yToRow.get(view.getBottomEdge()); - assert end != null; - - view.mRow = row; - view.mRowSpan = end - row; - } - } - } - - /** - * Creates refactoring edits which adds or updates constraints for the given list of - * views - */ - private void createAttachments(List<View> views) { - // Make the attachments - String namespace = mRefactoring.getAndroidNamespacePrefix(); - for (View view : views) { - for (Pair<String, String> constraint : view.getHorizConstraints()) { - mRefactoring.setAttribute(mRootEdit, view.mElement, ANDROID_URI, - namespace, constraint.getFirst(), constraint.getSecond()); - } - for (Pair<String, String> constraint : view.getVerticalConstraints()) { - mRefactoring.setAttribute(mRootEdit, view.mElement, ANDROID_URI, - namespace, constraint.getFirst(), constraint.getSecond()); - } - } - } - - /** - * Analyzes the existing layouts and layout parameter objects in the document to infer - * constraints for layout types that we know about - such as LinearLayout baseline - * alignment, weights, gravity, etc. - */ - private void computeKnownConstraints(List<View> views, EdgeList edgeList) { - // List of parent layout elements we've already processed. We iterate through all - // the -children-, and we ask each for its element parent (which won't have a view) - // and we look at the parent's layout attributes and its children layout constraints, - // and then we stash away constraints that we can infer. This means that we will - // encounter the same parent for every sibling, so that's why there's a map to - // prevent duplicate work. - Set<Node> seen = new HashSet<Node>(); - - for (View view : views) { - Element element = view.getElement(); - Node parent = element.getParentNode(); - if (seen.contains(parent)) { - continue; - } - seen.add(parent); - - if (parent.getNodeType() != Node.ELEMENT_NODE) { - continue; - } - Element layout = (Element) parent; - String layoutName = layout.getTagName(); - - if (LINEAR_LAYOUT.equals(layoutName)) { - analyzeLinearLayout(edgeList, layout); - } else if (RELATIVE_LAYOUT.equals(layoutName)) { - analyzeRelativeLayout(edgeList, layout); - } else { - // Some other layout -- add more conditional handling here - // for framelayout, tables, etc. - } - } - } - - /** - * Returns the layout weight of of the given child of a LinearLayout, or 0.0 if it - * does not define a weight - */ - private float getWeight(Element linearLayoutChild) { - String weight = linearLayoutChild.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT); - if (weight != null && weight.length() > 0) { - try { - return Float.parseFloat(weight); - } catch (NumberFormatException nfe) { - AdtPlugin.log(nfe, "Invalid weight %1$s", weight); - } - } - - return 0.0f; - } - - /** - * Returns the sum of all the layout weights of the children in the given LinearLayout - * - * @param linearLayout the layout to compute the total sum for - * @return the total sum of all the layout weights in the given layout - */ - private float getWeightSum(Element linearLayout) { - float sum = 0; - for (Element child : DomUtilities.getChildren(linearLayout)) { - sum += getWeight(child); - } - - return sum; - } - - /** - * Analyzes the given LinearLayout and updates the constraints to reflect - * relationships it can infer - based on baseline alignment, gravity, order and - * weights. This method also removes "0dip" as a special width/height used in - * LinearLayouts with weight distribution. - */ - private void analyzeLinearLayout(EdgeList edgeList, Element layout) { - boolean isVertical = VALUE_VERTICAL.equals(layout.getAttributeNS(ANDROID_URI, - ATTR_ORIENTATION)); - View baselineRef = null; - if (!isVertical && - !VALUE_FALSE.equals(layout.getAttributeNS(ANDROID_URI, ATTR_BASELINE_ALIGNED))) { - // Baseline alignment. Find the tallest child and set it as the baseline reference. - int tallestHeight = 0; - View tallest = null; - for (Element child : DomUtilities.getChildren(layout)) { - View view = edgeList.getView(child); - if (view != null && view.getHeight() > tallestHeight) { - tallestHeight = view.getHeight(); - tallest = view; - } - } - if (tallest != null) { - baselineRef = tallest; - } - } - - float weightSum = getWeightSum(layout); - float cumulativeWeight = 0; - - List<Element> children = DomUtilities.getChildren(layout); - String prevId = null; - boolean isFirstChild = true; - boolean linkBackwards = true; - boolean linkForwards = false; - - for (int index = 0, childCount = children.size(); index < childCount; index++) { - Element child = children.get(index); - - View childView = edgeList.getView(child); - if (childView == null) { - // Could be a nested layout that is being removed etc - prevId = null; - isFirstChild = false; - continue; - } - - // Look at the layout_weight attributes and determine whether we should be - // attached on the bottom/right or on the top/left - if (weightSum > 0.0f) { - float weight = getWeight(child); - - // We can't emulate a LinearLayout where multiple children have positive - // weights. However, we CAN support the common scenario where a single - // child has a non-zero weight, and all children after it are pushed - // to the end and the weighted child fills the remaining space. - if (cumulativeWeight == 0 && weight > 0) { - // See if we have a bottom/right edge to attach the forwards link to - // (at the end of the forwards chains). Only if so can we link forwards. - View referenced; - if (isVertical) { - referenced = edgeList.getSharedBottomEdge(layout); - } else { - referenced = edgeList.getSharedRightEdge(layout); - } - if (referenced != null) { - linkForwards = true; - } - } else if (cumulativeWeight > 0) { - linkBackwards = false; - } - - cumulativeWeight += weight; - } - - analyzeGravity(edgeList, layout, isVertical, child, childView); - convert0dipToWrapContent(child); - - // Chain elements together in the flow direction of the linear layout - if (prevId != null) { // No constraint for first child - if (linkBackwards) { - if (isVertical) { - childView.addVerticalConstraint(ATTR_LAYOUT_BELOW, prevId); - } else { - childView.addHorizConstraint(ATTR_LAYOUT_TO_RIGHT_OF, prevId); - } - } - } else if (isFirstChild) { - assert linkBackwards; - - // First element; attach it to the parent if we can - if (isVertical) { - View referenced = edgeList.getSharedTopEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_TOP, - VALUE_TRUE); - } else { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP, - referenced.getId()); - } - } - } else { - View referenced = edgeList.getSharedLeftEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_LEFT, - VALUE_TRUE); - } else { - childView.addHorizConstraint( - ATTR_LAYOUT_ALIGN_LEFT, referenced.getId()); - } - } - } - } - - if (linkForwards) { - if (index < (childCount - 1)) { - Element nextChild = children.get(index + 1); - String nextId = mRefactoring.ensureHasId(mRootEdit, nextChild, null); - if (nextId != null) { - if (isVertical) { - childView.addVerticalConstraint(ATTR_LAYOUT_ABOVE, nextId); - } else { - childView.addHorizConstraint(ATTR_LAYOUT_TO_LEFT_OF, nextId); - } - } - } else { - // Attach to right/bottom edge of the layout - if (isVertical) { - View referenced = edgeList.getSharedBottomEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, - VALUE_TRUE); - } else { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_BOTTOM, - referenced.getId()); - } - } - } else { - View referenced = edgeList.getSharedRightEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_RIGHT, - VALUE_TRUE); - } else { - childView.addHorizConstraint( - ATTR_LAYOUT_ALIGN_RIGHT, referenced.getId()); - } - } - } - } - } - - if (baselineRef != null && baselineRef.getId() != null - && !baselineRef.getId().equals(childView.getId())) { - assert !isVertical; - // Only align if they share the same gravity - if ((childView.getGravity() & GRAVITY_VERT_MASK) == - (baselineRef.getGravity() & GRAVITY_VERT_MASK)) { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_BASELINE, baselineRef.getId()); - } - } - - prevId = mRefactoring.ensureHasId(mRootEdit, child, null); - isFirstChild = false; - } - } - - /** - * Checks the layout "gravity" value for the given child and updates the constraints - * to account for the gravity - */ - private int analyzeGravity(EdgeList edgeList, Element layout, boolean isVertical, - Element child, View childView) { - // Use gravity to constrain elements in the axis orthogonal to the - // direction of the layout - int gravity = childView.getGravity(); - if (isVertical) { - if ((gravity & GRAVITY_RIGHT) != 0) { - View referenced = edgeList.getSharedRightEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_RIGHT, - VALUE_TRUE); - } else { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_RIGHT, - referenced.getId()); - } - } - } else if ((gravity & GRAVITY_CENTER_HORIZ) != 0) { - View referenced1 = edgeList.getSharedLeftEdge(layout); - View referenced2 = edgeList.getSharedRightEdge(layout); - if (referenced1 != null && referenced2 == referenced1) { - if (isAncestor(referenced1.getElement(), child)) { - childView.addHorizConstraint(ATTR_LAYOUT_CENTER_HORIZONTAL, - VALUE_TRUE); - } - } - } else if ((gravity & GRAVITY_FILL_HORIZ) != 0) { - View referenced1 = edgeList.getSharedLeftEdge(layout); - View referenced2 = edgeList.getSharedRightEdge(layout); - if (referenced1 != null && referenced2 == referenced1) { - if (isAncestor(referenced1.getElement(), child)) { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_LEFT, - VALUE_TRUE); - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_RIGHT, - VALUE_TRUE); - } else { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_LEFT, - referenced1.getId()); - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_RIGHT, - referenced2.getId()); - } - } - } else if ((gravity & GRAVITY_LEFT) != 0) { - View referenced = edgeList.getSharedLeftEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_PARENT_LEFT, - VALUE_TRUE); - } else { - childView.addHorizConstraint(ATTR_LAYOUT_ALIGN_LEFT, - referenced.getId()); - } - } - } - } else { - // Handle horizontal layout: perform vertical gravity attachments - if ((gravity & GRAVITY_BOTTOM) != 0) { - View referenced = edgeList.getSharedBottomEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, - VALUE_TRUE); - } else { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_BOTTOM, - referenced.getId()); - } - } - } else if ((gravity & GRAVITY_CENTER_VERT) != 0) { - View referenced1 = edgeList.getSharedTopEdge(layout); - View referenced2 = edgeList.getSharedBottomEdge(layout); - if (referenced1 != null && referenced2 == referenced1) { - if (isAncestor(referenced1.getElement(), child)) { - childView.addVerticalConstraint(ATTR_LAYOUT_CENTER_VERTICAL, - VALUE_TRUE); - } - } - } else if ((gravity & GRAVITY_FILL_VERT) != 0) { - View referenced1 = edgeList.getSharedTopEdge(layout); - View referenced2 = edgeList.getSharedBottomEdge(layout); - if (referenced1 != null && referenced2 == referenced1) { - if (isAncestor(referenced1.getElement(), child)) { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_TOP, - VALUE_TRUE); - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, - VALUE_TRUE); - } else { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP, - referenced1.getId()); - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_BOTTOM, - referenced2.getId()); - } - } - } else if ((gravity & GRAVITY_TOP) != 0) { - View referenced = edgeList.getSharedTopEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_PARENT_TOP, - VALUE_TRUE); - } else { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP, - referenced.getId()); - } - } - } - } - return gravity; - } - - /** Converts 0dip values in layout_width and layout_height to wrap_content instead */ - private void convert0dipToWrapContent(Element child) { - // Must convert layout_height="0dip" to layout_height="wrap_content". - // 0dip is a special trick used in linear layouts in the presence of - // weights where 0dip ensures that the height of the view is not taken - // into account when distributing the weights. However, when converted - // to RelativeLayout this will instead cause the view to actually be assigned - // 0 height. - String height = child.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT); - // 0dip, 0dp, 0px, etc - if (height != null && height.startsWith("0")) { //$NON-NLS-1$ - mRefactoring.setAttribute(mRootEdit, child, ANDROID_URI, - mRefactoring.getAndroidNamespacePrefix(), ATTR_LAYOUT_HEIGHT, - VALUE_WRAP_CONTENT); - } - String width = child.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - if (width != null && width.startsWith("0")) { //$NON-NLS-1$ - mRefactoring.setAttribute(mRootEdit, child, ANDROID_URI, - mRefactoring.getAndroidNamespacePrefix(), ATTR_LAYOUT_WIDTH, - VALUE_WRAP_CONTENT); - } - } - - /** - * Analyzes an embedded RelativeLayout within a layout hierarchy and updates the - * constraints in the EdgeList with those relationships which can continue in the - * outer single RelativeLayout. - */ - private void analyzeRelativeLayout(EdgeList edgeList, Element layout) { - NodeList children = layout.getChildNodes(); - for (int i = 0, n = children.getLength(); i < n; i++) { - Node node = children.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element child = (Element) node; - View childView = edgeList.getView(child); - if (childView == null) { - // Could be a nested layout that is being removed etc - continue; - } - - NamedNodeMap attributes = child.getAttributes(); - for (int j = 0, m = attributes.getLength(); j < m; j++) { - Attr attribute = (Attr) attributes.item(j); - String name = attribute.getLocalName(); - String value = attribute.getValue(); - if (name.equals(ATTR_LAYOUT_WIDTH) - || name.equals(ATTR_LAYOUT_HEIGHT)) { - // Ignore these for now - } else if (name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && ANDROID_URI.equals(attribute.getNamespaceURI())) { - // Determine if the reference is to a known edge - String id = getIdBasename(value); - if (id != null) { - View referenced = edgeList.getView(id); - if (referenced != null) { - // This is a valid reference, so preserve - // the attribute - if (name.equals(ATTR_LAYOUT_BELOW) || - name.equals(ATTR_LAYOUT_ABOVE) || - name.equals(ATTR_LAYOUT_ALIGN_TOP) || - name.equals(ATTR_LAYOUT_ALIGN_BOTTOM) || - name.equals(ATTR_LAYOUT_ALIGN_BASELINE)) { - // Vertical constraint - childView.addVerticalConstraint(name, value); - } else if (name.equals(ATTR_LAYOUT_ALIGN_LEFT) || - name.equals(ATTR_LAYOUT_TO_LEFT_OF) || - name.equals(ATTR_LAYOUT_TO_RIGHT_OF) || - name.equals(ATTR_LAYOUT_ALIGN_RIGHT)) { - // Horizontal constraint - childView.addHorizConstraint(name, value); - } else { - // We don't expect this - assert false : name; - } - } else { - // Reference to some layout that is not included here. - // TODO: See if the given layout has an edge - // that corresponds to one of our known views - // so we can adjust the constraints and keep it after all. - } - } else { - // It's a parent-relative constraint (such - // as aligning with a parent edge, or centering - // in the parent view) - boolean remove = true; - if (name.equals(ATTR_LAYOUT_ALIGN_PARENT_LEFT)) { - View referenced = edgeList.getSharedLeftEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addHorizConstraint(name, VALUE_TRUE); - } else { - childView.addHorizConstraint( - ATTR_LAYOUT_ALIGN_LEFT, referenced.getId()); - } - remove = false; - } - } else if (name.equals(ATTR_LAYOUT_ALIGN_PARENT_RIGHT)) { - View referenced = edgeList.getSharedRightEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addHorizConstraint(name, VALUE_TRUE); - } else { - childView.addHorizConstraint( - ATTR_LAYOUT_ALIGN_RIGHT, referenced.getId()); - } - remove = false; - } - } else if (name.equals(ATTR_LAYOUT_ALIGN_PARENT_TOP)) { - View referenced = edgeList.getSharedTopEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addVerticalConstraint(name, VALUE_TRUE); - } else { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP, - referenced.getId()); - } - remove = false; - } - } else if (name.equals(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM)) { - View referenced = edgeList.getSharedBottomEdge(layout); - if (referenced != null) { - if (isAncestor(referenced.getElement(), child)) { - childView.addVerticalConstraint(name, VALUE_TRUE); - } else { - childView.addVerticalConstraint(ATTR_LAYOUT_ALIGN_BOTTOM, - referenced.getId()); - } - remove = false; - } - } - - boolean alignWithParent = - name.equals(ATTR_LAYOUT_ALIGN_WITH_PARENT_MISSING); - if (remove && alignWithParent) { - // TODO - look for this one AFTER we have processed - // everything else, and then set constraints as necessary - // IF there are no other conflicting constraints! - } - - // Otherwise it's some kind of centering which we don't support - // yet. - - // TODO: Find a way to determine whether we have - // a corresponding edge for the parent (e.g. if - // the ViewInfo bounds match our outer parent or - // some other edge) and if so, substitute for that - // id. - // For example, if this element was centered - // horizontally in a RelativeLayout that actually - // occupies the entire width of our outer layout, - // then it can be preserved after all! - - if (remove) { - if (name.startsWith("layout_margin")) { //$NON-NLS-1$ - continue; - } - - // Remove unknown attributes? - // It's too early to do this, because we may later want - // to *set* this value and it would result in an overlapping edits - // exception. Therefore, we need to RECORD which attributes should - // be removed, which lines should have its indentation adjusted - // etc and finally process it all at the end! - //mRefactoring.removeAttribute(mRootEdit, child, - // attribute.getNamespaceURI(), name); - } - } - } - } - } - } - } - - /** - * Given {@code @id/foo} or {@code @+id/foo}, returns foo. Note that given foo it will - * return null. - */ - private static String getIdBasename(String id) { - if (id.startsWith(NEW_ID_PREFIX)) { - return id.substring(NEW_ID_PREFIX.length()); - } else if (id.startsWith(ID_PREFIX)) { - return id.substring(ID_PREFIX.length()); - } - - return null; - } - - /** Returns true if the given second argument is a descendant of the first argument */ - private static boolean isAncestor(Node ancestor, Node node) { - while (node != null) { - if (node == ancestor) { - return true; - } - node = node.getParentNode(); - } - return false; - } - - /** - * Computes horizontal constraints for the views in the grid for any remaining views - * that do not have constraints (as the result of the analysis of known layouts). This - * will look at the rendered layout coordinates and attempt to connect elements based - * on a spatial layout in the grid. - */ - private void computeHorizontalConstraints(Grid grid) { - int columns = grid.getColumns(); - - String attachLeftProperty = ATTR_LAYOUT_ALIGN_PARENT_LEFT; - String attachLeftValue = VALUE_TRUE; - int marginLeft = 0; - for (int col = 0; col < columns; col++) { - if (!grid.colContainsTopLeftCorner(col)) { - // Just accumulate margins for the next column - marginLeft += grid.getColumnWidth(col); - } else { - // Add horizontal attachments - String firstId = null; - for (View view : grid.viewsStartingInCol(col, true)) { - assert view.getId() != null; - if (firstId == null) { - firstId = view.getId(); - if (view.isConstrainedHorizontally()) { - // Nothing to do -- we already have an accurate position for - // this view - } else if (attachLeftProperty != null) { - view.addHorizConstraint(attachLeftProperty, attachLeftValue); - if (marginLeft > 0) { - view.addHorizConstraint(ATTR_LAYOUT_MARGIN_LEFT, - String.format(VALUE_N_DP, marginLeft)); - marginLeft = 0; - } - } else { - assert false; - } - } else if (!view.isConstrainedHorizontally()) { - view.addHorizConstraint(ATTR_LAYOUT_ALIGN_LEFT, firstId); - } - } - } - - // Figure out edge for the next column - View view = grid.findRightEdgeView(col); - if (view != null) { - assert view.getId() != null; - attachLeftProperty = ATTR_LAYOUT_TO_RIGHT_OF; - attachLeftValue = view.getId(); - - marginLeft = 0; - } else if (marginLeft == 0) { - marginLeft = grid.getColumnWidth(col); - } - } - } - - /** - * Performs vertical layout just like the {@link #computeHorizontalConstraints} method - * did horizontally - */ - private void computeVerticalConstraints(Grid grid) { - int rows = grid.getRows(); - - String attachTopProperty = ATTR_LAYOUT_ALIGN_PARENT_TOP; - String attachTopValue = VALUE_TRUE; - int marginTop = 0; - for (int row = 0; row < rows; row++) { - if (!grid.rowContainsTopLeftCorner(row)) { - // Just accumulate margins for the next column - marginTop += grid.getRowHeight(row); - } else { - // Add horizontal attachments - String firstId = null; - for (View view : grid.viewsStartingInRow(row, true)) { - assert view.getId() != null; - if (firstId == null) { - firstId = view.getId(); - if (view.isConstrainedVertically()) { - // Nothing to do -- we already have an accurate position for - // this view - } else if (attachTopProperty != null) { - view.addVerticalConstraint(attachTopProperty, attachTopValue); - if (marginTop > 0) { - view.addVerticalConstraint(ATTR_LAYOUT_MARGIN_TOP, - String.format(VALUE_N_DP, marginTop)); - marginTop = 0; - } - } else { - assert false; - } - } else if (!view.isConstrainedVertically()) { - view.addVerticalConstraint(ATTR_LAYOUT_ALIGN_TOP, firstId); - } - } - } - - // Figure out edge for the next row - View view = grid.findBottomEdgeView(row); - if (view != null) { - assert view.getId() != null; - attachTopProperty = ATTR_LAYOUT_BELOW; - attachTopValue = view.getId(); - marginTop = 0; - } else if (marginTop == 0) { - marginTop = grid.getRowHeight(row); - } - } - } - - /** - * Searches a view hierarchy and locates the {@link CanvasViewInfo} for the given - * {@link Element} - * - * @param info the root {@link CanvasViewInfo} to search below - * @param element the target element - * @return the {@link CanvasViewInfo} which corresponds to the given element - */ - private CanvasViewInfo findViewForElement(CanvasViewInfo info, Element element) { - if (getElement(info) == element) { - return info; - } - - for (CanvasViewInfo child : info.getChildren()) { - CanvasViewInfo result = findViewForElement(child, element); - if (result != null) { - return result; - } - } - - return null; - } - - /** Returns the {@link Element} for the given {@link CanvasViewInfo} */ - private static Element getElement(CanvasViewInfo info) { - Node node = info.getUiViewNode().getXmlNode(); - if (node instanceof Element) { - return (Element) node; - } - - return null; - } - - /** - * A grid of cells which can contain views, used to infer spatial relationships when - * computing constraints. Note that a view can appear in than one cell; they will - * appear in all cells that their bounds overlap with! - */ - private class Grid { - private final int[] mLeft; - private final int[] mTop; - // A list from row to column to cell, where a cell is a list of views - private final List<List<List<View>>> mRowList; - private int mRowCount; - private int mColCount; - - Grid(List<View> views, int[] left, int[] top) { - mLeft = left; - mTop = top; - - // The left/top arrays should include the ending point too - mColCount = left.length - 1; - mRowCount = top.length - 1; - - // Using nested lists rather than arrays to avoid lack of typed arrays - // (can't create List<View>[row][column] arrays) - mRowList = new ArrayList<List<List<View>>>(top.length); - for (int row = 0; row < top.length; row++) { - List<List<View>> columnList = new ArrayList<List<View>>(left.length); - for (int col = 0; col < left.length; col++) { - columnList.add(new ArrayList<View>(4)); - } - mRowList.add(columnList); - } - - for (View view : views) { - // Get rid of the root view; we don't want that in the attachments logic; - // it was there originally such that it would contribute the outermost - // edges. - if (view.mElement == mLayout) { - continue; - } - - for (int i = 0; i < view.mRowSpan; i++) { - for (int j = 0; j < view.mColSpan; j++) { - mRowList.get(view.mRow + i).get(view.mCol + j).add(view); - } - } - } - } - - /** - * Returns the number of rows in the grid - * - * @return the row count - */ - public int getRows() { - return mRowCount; - } - - /** - * Returns the number of columns in the grid - * - * @return the column count - */ - public int getColumns() { - return mColCount; - } - - /** - * Returns the list of views overlapping the given cell - * - * @param row the row of the target cell - * @param col the column of the target cell - * @return a list of views overlapping the given column - */ - public List<View> get(int row, int col) { - return mRowList.get(row).get(col); - } - - /** - * Returns true if the given column contains a top left corner of a view - * - * @param column the column to check - * @return true if one or more views have their top left corner in this column - */ - public boolean colContainsTopLeftCorner(int column) { - for (int row = 0; row < mRowCount; row++) { - View view = getTopLeftCorner(row, column); - if (view != null) { - return true; - } - } - - return false; - } - - /** - * Returns true if the given row contains a top left corner of a view - * - * @param row the row to check - * @return true if one or more views have their top left corner in this row - */ - public boolean rowContainsTopLeftCorner(int row) { - for (int col = 0; col < mColCount; col++) { - View view = getTopLeftCorner(row, col); - if (view != null) { - return true; - } - } - - return false; - } - - /** - * Returns a list of views (optionally sorted by increasing row index) that have - * their left edge starting in the given column - * - * @param col the column to look up views for - * @param sort whether to sort the result in increasing row order - * @return a list of views starting in the given column - */ - public List<View> viewsStartingInCol(int col, boolean sort) { - List<View> views = new ArrayList<View>(); - for (int row = 0; row < mRowCount; row++) { - View view = getTopLeftCorner(row, col); - if (view != null) { - views.add(view); - } - } - - if (sort) { - View.sortByRow(views); - } - - return views; - } - - /** - * Returns a list of views (optionally sorted by increasing column index) that have - * their top edge starting in the given row - * - * @param row the row to look up views for - * @param sort whether to sort the result in increasing column order - * @return a list of views starting in the given row - */ - public List<View> viewsStartingInRow(int row, boolean sort) { - List<View> views = new ArrayList<View>(); - for (int col = 0; col < mColCount; col++) { - View view = getTopLeftCorner(row, col); - if (view != null) { - views.add(view); - } - } - - if (sort) { - View.sortByColumn(views); - } - - return views; - } - - /** - * Returns the pixel width of the given column - * - * @param col the column to look up the width of - * @return the width of the column - */ - public int getColumnWidth(int col) { - return mLeft[col + 1] - mLeft[col]; - } - - /** - * Returns the pixel height of the given row - * - * @param row the row to look up the height of - * @return the height of the row - */ - public int getRowHeight(int row) { - return mTop[row + 1] - mTop[row]; - } - - /** - * Returns the first view found that has its top left corner in the cell given by - * the row and column indexes, or null if not found. - * - * @param row the row of the target cell - * @param col the column of the target cell - * @return a view with its top left corner in the given cell, or null if not found - */ - View getTopLeftCorner(int row, int col) { - List<View> views = get(row, col); - if (views.size() > 0) { - for (View view : views) { - if (view.mRow == row && view.mCol == col) { - return view; - } - } - } - - return null; - } - - public View findRightEdgeView(int col) { - for (int row = 0; row < mRowCount; row++) { - List<View> views = get(row, col); - if (views.size() > 0) { - List<View> result = new ArrayList<View>(); - for (View view : views) { - // Ends on the right edge of this column? - if (view.mCol + view.mColSpan == col + 1) { - result.add(view); - } - } - if (result.size() > 1) { - View.sortByColumn(result); - } - if (result.size() > 0) { - return result.get(0); - } - } - } - - return null; - } - - public View findBottomEdgeView(int row) { - for (int col = 0; col < mColCount; col++) { - List<View> views = get(row, col); - if (views.size() > 0) { - List<View> result = new ArrayList<View>(); - for (View view : views) { - // Ends on the bottom edge of this column? - if (view.mRow + view.mRowSpan == row + 1) { - result.add(view); - } - } - if (result.size() > 1) { - View.sortByRow(result); - } - if (result.size() > 0) { - return result.get(0); - } - - } - } - - return null; - } - - /** - * Produces a display of view contents along with the pixel positions of each row/column, - * like the following (used for diagnostics only) - * <pre> - * |0 |49 |143 |192 |240 - * 36| | |button2 | - * 72| |radioButton1 |button2 | - * 74|button1 |radioButton1 |button2 | - * 108|button1 | |button2 | - * 110| | |button2 | - * 149| | | | - * 320 - * </pre> - */ - @Override - public String toString() { - // Dump out the view table - int cellWidth = 20; - - StringWriter stringWriter = new StringWriter(); - PrintWriter out = new PrintWriter(stringWriter); - out.printf("%" + cellWidth + "s", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - for (int col = 0; col < mColCount + 1; col++) { - out.printf("|%-" + (cellWidth - 1) + "d", mLeft[col]); //$NON-NLS-1$ //$NON-NLS-2$ - } - out.printf("\n"); //$NON-NLS-1$ - for (int row = 0; row < mRowCount + 1; row++) { - out.printf("%" + cellWidth + "d", mTop[row]); //$NON-NLS-1$ //$NON-NLS-2$ - if (row == mRowCount) { - break; - } - for (int col = 0; col < mColCount; col++) { - List<View> views = get(row, col); - StringBuilder sb = new StringBuilder(); - for (View view : views) { - String id = view != null ? view.getId() : ""; //$NON-NLS-1$ - if (id.startsWith(NEW_ID_PREFIX)) { - id = id.substring(NEW_ID_PREFIX.length()); - } - if (id.length() > cellWidth - 2) { - id = id.substring(0, cellWidth - 2); - } - if (sb.length() > 0) { - sb.append(','); - } - sb.append(id); - } - String cellString = sb.toString(); - if (cellString.contains(",") && cellString.length() > cellWidth - 2) { //$NON-NLS-1$ - cellString = cellString.substring(0, cellWidth - 6) + "...,"; //$NON-NLS-1$ - } - out.printf("|%-" + (cellWidth - 2) + "s ", cellString); //$NON-NLS-1$ //$NON-NLS-2$ - } - out.printf("\n"); //$NON-NLS-1$ - } - - out.flush(); - return stringWriter.toString(); - } - } - - /** Holds layout information about an individual view. */ - private static class View { - private final Element mElement; - private int mRow = -1; - private int mCol = -1; - private int mRowSpan = -1; - private int mColSpan = -1; - private CanvasViewInfo mInfo; - private String mId; - private List<Pair<String, String>> mHorizConstraints = - new ArrayList<Pair<String, String>>(4); - private List<Pair<String, String>> mVerticalConstraints = - new ArrayList<Pair<String, String>>(4); - private int mGravity; - - public View(CanvasViewInfo view, Element element) { - mInfo = view; - mElement = element; - mGravity = GravityHelper.getGravity(element); - } - - public int getHeight() { - return mInfo.getAbsRect().height; - } - - public int getGravity() { - return mGravity; - } - - public String getId() { - return mId; - } - - public Element getElement() { - return mElement; - } - - public List<Pair<String, String>> getHorizConstraints() { - return mHorizConstraints; - } - - public List<Pair<String, String>> getVerticalConstraints() { - return mVerticalConstraints; - } - - public boolean isConstrainedHorizontally() { - return mHorizConstraints.size() > 0; - } - - public boolean isConstrainedVertically() { - return mVerticalConstraints.size() > 0; - } - - public void addHorizConstraint(String property, String value) { - assert property != null && value != null; - // TODO - look for duplicates? - mHorizConstraints.add(Pair.of(property, value)); - } - - public void addVerticalConstraint(String property, String value) { - assert property != null && value != null; - mVerticalConstraints.add(Pair.of(property, value)); - } - - public int getLeftEdge() { - return mInfo.getAbsRect().x; - } - - public int getTopEdge() { - return mInfo.getAbsRect().y; - } - - public int getRightEdge() { - Rectangle bounds = mInfo.getAbsRect(); - // +1: make the bounds overlap, so the right edge is the same as the - // left edge of the neighbor etc. Otherwise we end up with lots of 1-pixel wide - // columns between adjacent items. - return bounds.x + bounds.width + 1; - } - - public int getBottomEdge() { - Rectangle bounds = mInfo.getAbsRect(); - return bounds.y + bounds.height + 1; - } - - @Override - public String toString() { - return "View [mId=" + mId + "]"; //$NON-NLS-1$ //$NON-NLS-2$ - } - - public static void sortByRow(List<View> views) { - Collections.sort(views, new ViewComparator(true/*rowSort*/)); - } - - public static void sortByColumn(List<View> views) { - Collections.sort(views, new ViewComparator(false/*rowSort*/)); - } - - /** Comparator to help sort views by row or column index */ - private static class ViewComparator implements Comparator<View> { - boolean mRowSort; - - public ViewComparator(boolean rowSort) { - mRowSort = rowSort; - } - - @Override - public int compare(View view1, View view2) { - if (mRowSort) { - return view1.mRow - view2.mRow; - } else { - return view1.mCol - view2.mCol; - } - } - } - } - - /** - * An edge list takes a hierarchy of elements and records the bounds of each element - * into various lists such that it can answer queries about shared edges, about which - * particular pixels occur as a boundary edge, etc. - */ - private class EdgeList { - private final Map<Element, View> mElementToViewMap = new HashMap<Element, View>(100); - private final Map<String, View> mIdToViewMap = new HashMap<String, View>(100); - private final Map<Integer, List<View>> mLeft = new HashMap<Integer, List<View>>(); - private final Map<Integer, List<View>> mTop = new HashMap<Integer, List<View>>(); - private final Map<Integer, List<View>> mRight = new HashMap<Integer, List<View>>(); - private final Map<Integer, List<View>> mBottom = new HashMap<Integer, List<View>>(); - private final Map<Element, Element> mSharedLeftEdge = new HashMap<Element, Element>(); - private final Map<Element, Element> mSharedTopEdge = new HashMap<Element, Element>(); - private final Map<Element, Element> mSharedRightEdge = new HashMap<Element, Element>(); - private final Map<Element, Element> mSharedBottomEdge = new HashMap<Element, Element>(); - private final List<Element> mDelete = new ArrayList<Element>(); - - EdgeList(CanvasViewInfo view) { - analyze(view, true); - mDelete.remove(getElement(view)); - } - - public void setIdAttributeValue(View view, String id) { - assert id.startsWith(NEW_ID_PREFIX) || id.startsWith(ID_PREFIX); - view.mId = id; - mIdToViewMap.put(getIdBasename(id), view); - } - - public View getView(Element element) { - return mElementToViewMap.get(element); - } - - public View getView(String id) { - return mIdToViewMap.get(id); - } - - public List<View> getTopEdgeViews(Integer topOffset) { - return mTop.get(topOffset); - } - - public List<View> getLeftEdgeViews(Integer leftOffset) { - return mLeft.get(leftOffset); - } - - void record(Map<Integer, List<View>> map, Integer edge, View info) { - List<View> list = map.get(edge); - if (list == null) { - list = new ArrayList<View>(); - map.put(edge, list); - } - list.add(info); - } - - private List<Integer> getOffsets(Set<Integer> first, Set<Integer> second) { - Set<Integer> joined = new HashSet<Integer>(first.size() + second.size()); - joined.addAll(first); - joined.addAll(second); - List<Integer> unique = new ArrayList<Integer>(joined); - Collections.sort(unique); - - return unique; - } - - public List<Element> getDeletedElements() { - return mDelete; - } - - public List<Integer> getColumnOffsets() { - return getOffsets(mLeft.keySet(), mRight.keySet()); - } - public List<Integer> getRowOffsets() { - return getOffsets(mTop.keySet(), mBottom.keySet()); - } - - private View analyze(CanvasViewInfo view, boolean isRoot) { - View added = null; - if (!mFlatten || !isRemovableLayout(view)) { - added = add(view); - if (!isRoot) { - return added; - } - } else { - mDelete.add(getElement(view)); - } - - Element parentElement = getElement(view); - Rectangle parentBounds = view.getAbsRect(); - - // Build up a table model of the view - for (CanvasViewInfo child : view.getChildren()) { - Rectangle childBounds = child.getAbsRect(); - Element childElement = getElement(child); - - // See if this view shares the edge with the removed - // parent layout, and if so, record that such that we can - // later handle attachments to the removed parent edges - if (parentBounds.x == childBounds.x) { - mSharedLeftEdge.put(childElement, parentElement); - } - if (parentBounds.y == childBounds.y) { - mSharedTopEdge.put(childElement, parentElement); - } - if (parentBounds.x + parentBounds.width == childBounds.x + childBounds.width) { - mSharedRightEdge.put(childElement, parentElement); - } - if (parentBounds.y + parentBounds.height == childBounds.y + childBounds.height) { - mSharedBottomEdge.put(childElement, parentElement); - } - - if (mFlatten && isRemovableLayout(child)) { - // When flattening, we want to disregard all layouts and instead - // add their children! - for (CanvasViewInfo childView : child.getChildren()) { - analyze(childView, false); - - Element childViewElement = getElement(childView); - Rectangle childViewBounds = childView.getAbsRect(); - - // See if this view shares the edge with the removed - // parent layout, and if so, record that such that we can - // later handle attachments to the removed parent edges - if (parentBounds.x == childViewBounds.x) { - mSharedLeftEdge.put(childViewElement, parentElement); - } - if (parentBounds.y == childViewBounds.y) { - mSharedTopEdge.put(childViewElement, parentElement); - } - if (parentBounds.x + parentBounds.width == childViewBounds.x - + childViewBounds.width) { - mSharedRightEdge.put(childViewElement, parentElement); - } - if (parentBounds.y + parentBounds.height == childViewBounds.y - + childViewBounds.height) { - mSharedBottomEdge.put(childViewElement, parentElement); - } - } - mDelete.add(childElement); - } else { - analyze(child, false); - } - } - - return added; - } - - public View getSharedLeftEdge(Element element) { - return getSharedEdge(element, mSharedLeftEdge); - } - - public View getSharedRightEdge(Element element) { - return getSharedEdge(element, mSharedRightEdge); - } - - public View getSharedTopEdge(Element element) { - return getSharedEdge(element, mSharedTopEdge); - } - - public View getSharedBottomEdge(Element element) { - return getSharedEdge(element, mSharedBottomEdge); - } - - private View getSharedEdge(Element element, Map<Element, Element> sharedEdgeMap) { - Element original = element; - - while (element != null) { - View view = getView(element); - if (view != null) { - assert isAncestor(element, original); - return view; - } - element = sharedEdgeMap.get(element); - } - - return null; - } - - private View add(CanvasViewInfo info) { - Rectangle bounds = info.getAbsRect(); - Element element = getElement(info); - View view = new View(info, element); - mElementToViewMap.put(element, view); - record(mLeft, Integer.valueOf(bounds.x), view); - record(mTop, Integer.valueOf(bounds.y), view); - record(mRight, Integer.valueOf(view.getRightEdge()), view); - record(mBottom, Integer.valueOf(view.getBottomEdge()), view); - return view; - } - - /** - * Returns true if the given {@link CanvasViewInfo} represents an element we - * should remove in a flattening conversion. We don't want to remove non-layout - * views, or layout views that for example contain drawables on their own. - */ - private boolean isRemovableLayout(CanvasViewInfo child) { - // The element being converted is NOT removable! - Element element = getElement(child); - if (element == mLayout) { - return false; - } - - ElementDescriptor descriptor = child.getUiViewNode().getDescriptor(); - String name = descriptor.getXmlLocalName(); - if (name.equals(LINEAR_LAYOUT) || name.equals(RELATIVE_LAYOUT)) { - // Don't delete layouts that provide a background image or gradient - if (element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND)) { - AdtPlugin.log(IStatus.WARNING, - "Did not flatten layout %1$s because it defines a '%2$s' attribute", - VisualRefactoring.getId(element), ATTR_BACKGROUND); - return false; - } - - return true; - } - - return false; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapAction.java deleted file mode 100644 index 02c2a276c..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapAction.java +++ /dev/null @@ -1,47 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -import org.eclipse.jface.action.IAction; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; - -/** - * Action executed when the "Remove Container" menu item is invoked. - */ -public class UnwrapAction extends VisualRefactoringAction { - @Override - public void run(IAction action) { - if ((mTextSelection != null || mTreeSelection != null) && mFile != null) { - UnwrapRefactoring ref = new UnwrapRefactoring(mFile, mDelegate, - mTextSelection, mTreeSelection); - RefactoringWizard wizard = new UnwrapWizard(ref, mDelegate); - RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); - try { - op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); - } catch (InterruptedException e) { - // Interrupted. Pass. - } - } - } - - public static IAction create(LayoutEditorDelegate editorDelegate) { - return create("Remove Container...", editorDelegate, UnwrapAction.class); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapContribution.java deleted file mode 100644 index 0869fd637..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapContribution.java +++ /dev/null @@ -1,40 +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 org.eclipse.ltk.core.refactoring.RefactoringContribution; -import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; - -import java.util.Map; - -public class UnwrapContribution extends RefactoringContribution { - - @SuppressWarnings("unchecked") - @Override - public RefactoringDescriptor createDescriptor(String id, String project, String description, - String comment, Map arguments, int flags) throws IllegalArgumentException { - return new UnwrapRefactoring.Descriptor(project, description, comment, arguments); - } - - @SuppressWarnings("unchecked") - @Override - public Map retrieveArgumentMap(RefactoringDescriptor descriptor) { - if (descriptor instanceof UnwrapRefactoring.Descriptor) { - return ((UnwrapRefactoring.Descriptor) descriptor).getArguments(); - } - return super.retrieveArgumentMap(descriptor); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java deleted file mode 100644 index 4eff2cde5..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapRefactoring.java +++ /dev/null @@ -1,246 +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_URI; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.EXT_XML; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.ide.common.xml.XmlFormatStyle; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -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.MultiTextEdit; -import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * Removes the layout surrounding the current selection (or if the current selection has - * children, removes the current layout), and migrates namespace and layout attributes. - */ -@SuppressWarnings("restriction") // XML model -public class UnwrapRefactoring extends VisualRefactoring { - private Element mContainer; - - /** - * This constructor is solely used by {@link Descriptor}, - * to replay a previous refactoring. - * @param arguments argument map created by #createArgumentMap. - */ - UnwrapRefactoring(Map<String, String> arguments) { - super(arguments); - } - - public UnwrapRefactoring( - IFile file, - LayoutEditorDelegate delegate, - ITextSelection selection, - ITreeSelection treeSelection) { - super(file, delegate, selection, treeSelection); - } - - @VisibleForTesting - UnwrapRefactoring(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 wrap"); - return status; - } - - // Make sure that the selection all has the same parent? - if (mElements.size() == 0) { - status.addFatalError("Nothing to unwrap"); - return status; - } - - Element first = mElements.get(0); - - // Determine the element of the container to be removed. - // If you've selected a non-container, or you've selected multiple - // elements, then it's the parent which should be removed. Otherwise, - // it's the selection itself which represents the container. - boolean useParent = mElements.size() > 1; - if (!useParent) { - if (DomUtilities.getChildren(first).size() == 0) { - useParent = true; - } - } - Node parent = first.getParentNode(); - if (parent instanceof Document) { - mContainer = first; - List<Element> elements = DomUtilities.getChildren(mContainer); - if (elements.size() == 0) { - status.addFatalError( - "Cannot remove container when it has no children"); - return status; - } - } else if (useParent && (parent instanceof Element)) { - mContainer = (Element) parent; - } else { - mContainer = first; - } - - for (Element element : mElements) { - if (element.getParentNode() != parent) { - status.addFatalError( - "All unwrapped elements must share the same parent element"); - return status; - } - } - - // Ensure that if we are removing the root, that it has only one child - // such that there is a new single root - if (mContainer.getParentNode() instanceof Document) { - if (DomUtilities.getChildren(mContainer).size() > 1) { - status.addFatalError( - "Cannot remove root: it has more than one child " - + "which would result in multiple new roots"); - 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 - public String getName() { - return "Remove Container"; - } - - @Override - protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) { - // (1) If the removed parent is the root container, transfer its - // namespace declarations - // (2) Remove the root element completely - // (3) Transfer layout attributes? - // (4) Check for Java R.file usages? - - IFile file = mDelegate.getEditor().getInputFile(); - List<Change> changes = new ArrayList<Change>(); - if (file == null) { - return changes; - } - MultiTextEdit rootEdit = new MultiTextEdit(); - - // Transfer namespace elements? - if (mContainer.getParentNode() instanceof Document) { - List<Element> elements = DomUtilities.getChildren(mContainer); - assert elements.size() == 1; - Element newRoot = elements.get(0); - - List<Attr> declarations = findNamespaceAttributes(mContainer); - for (Attr attribute : declarations) { - if (attribute instanceof IndexedRegion) { - setAttribute(rootEdit, newRoot, attribute.getNamespaceURI(), - attribute.getPrefix(), attribute.getLocalName(), attribute.getValue()); - } - } - } - - // Transfer layout_ attributes (other than width and height) - List<Element> children = DomUtilities.getChildren(mContainer); - if (children.size() == 1) { - List<Attr> layoutAttributes = findLayoutAttributes(mContainer); - for (Attr attribute : layoutAttributes) { - String name = attribute.getLocalName(); - if ((name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) - && ANDROID_URI.equals(attribute.getNamespaceURI())) { - // Already handled specially - continue; - } - } - } - - // Remove the root - removeElementTags(rootEdit, mContainer, Collections.<Element>emptyList() /* skip */, - false /*changeIndentation*/); - - MultiTextEdit formatted = reformat(rootEdit, XmlFormatStyle.LAYOUT); - if (formatted != null) { - rootEdit = formatted; - } - - TextFileChange change = new TextFileChange(file.getName(), file); - change.setEdit(rootEdit); - change.setTextType(EXT_XML); - changes.add(change); - return changes; - } - - @Override - public VisualRefactoringWizard createWizard() { - return new UnwrapWizard(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.unwrap", //$NON-NLS-1$ - project, description, comment, arguments); - } - - @Override - protected Refactoring createRefactoring(Map<String, String> args) { - return new UnwrapRefactoring(args); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapWizard.java deleted file mode 100644 index 6e3bcf1e7..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UnwrapWizard.java +++ /dev/null @@ -1,31 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -public class UnwrapWizard extends VisualRefactoringWizard { - public UnwrapWizard(UnwrapRefactoring ref, LayoutEditorDelegate editor) { - super(ref, editor); - setDefaultPageTitle("Remove Container"); - } - - @Override - protected void addUserInputPages() { - // This refactoring takes no parameters - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableAction.java deleted file mode 100644 index 84d3e7ee8..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableAction.java +++ /dev/null @@ -1,48 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -import org.eclipse.jface.action.IAction; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; - -/** - * Action executed when the "Convert Layout" menu item is invoked. - */ -public class UseCompoundDrawableAction extends VisualRefactoringAction { - @Override - public void run(IAction action) { - if ((mTextSelection != null || mTreeSelection != null) && mFile != null) { - UseCompoundDrawableRefactoring ref = new UseCompoundDrawableRefactoring( - mFile, mDelegate, mTextSelection, mTreeSelection); - RefactoringWizard wizard = new UseCompoundDrawableWizard(ref, mDelegate); - RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); - try { - op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); - } catch (InterruptedException e) { - // Interrupted. Pass. - } - } - } - - public static IAction create(LayoutEditorDelegate editorDelegate) { - return create("Convert to a Compound Drawable...", editorDelegate, - UseCompoundDrawableAction.class); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableRefactoring.java deleted file mode 100644 index 0e56bdf4d..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableRefactoring.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2012 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_URI; -import static com.android.SdkConstants.ATTR_DRAWABLE_BOTTOM; -import static com.android.SdkConstants.ATTR_DRAWABLE_LEFT; -import static com.android.SdkConstants.ATTR_DRAWABLE_PADDING; -import static com.android.SdkConstants.ATTR_DRAWABLE_RIGHT; -import static com.android.SdkConstants.ATTR_DRAWABLE_TOP; -import static com.android.SdkConstants.ATTR_GRAVITY; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_BOTTOM; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_LEFT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_RIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_TOP; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ATTR_ORIENTATION; -import static com.android.SdkConstants.ATTR_SRC; -import static com.android.SdkConstants.EXT_XML; -import static com.android.SdkConstants.IMAGE_VIEW; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.TEXT_VIEW; -import static com.android.SdkConstants.VALUE_VERTICAL; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.annotations.VisibleForTesting; -import com.android.ide.common.xml.XmlFormatStyle; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlFormatPreferences; -import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlPrettyPrinter; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -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.MultiTextEdit; -import org.eclipse.text.edits.ReplaceEdit; -import org.eclipse.text.edits.TextEdit; -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.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Converts a LinearLayout with exactly a TextView child and an ImageView child into - * a single TextView with a compound drawable. - */ -@SuppressWarnings("restriction") // XML model -public class UseCompoundDrawableRefactoring extends VisualRefactoring { - /** - * Constructs a new {@link UseCompoundDrawableRefactoring} - * - * @param file the file to refactor in - * @param editor the corresponding editor - * @param selection the editor selection, or null - * @param treeSelection the canvas selection, or null - */ - public UseCompoundDrawableRefactoring(IFile file, LayoutEditorDelegate editor, - ITextSelection selection, ITreeSelection treeSelection) { - super(file, editor, selection, treeSelection); - } - - /** - * This constructor is solely used by {@link Descriptor}, to replay a - * previous refactoring. - * - * @param arguments argument map created by #createArgumentMap. - */ - private UseCompoundDrawableRefactoring(Map<String, String> arguments) { - super(arguments); - } - - @VisibleForTesting - UseCompoundDrawableRefactoring(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("Nothing to convert"); - return status; - } - - // Make sure the selection is contiguous - if (mTreeSelection != null) { - List<CanvasViewInfo> infos = getSelectedViewInfos(); - if (!validateNotEmpty(infos, status)) { - return status; - } - - // Enforce that the selection is -contiguous- - if (!validateContiguous(infos, status)) { - return status; - } - } - - // Ensures that we have a valid DOM model: - if (mElements.size() == 0) { - status.addFatalError("Nothing to convert"); - return status; - } - - // Ensure that we have selected precisely one LinearLayout - if (mElements.size() != 1 || - !(mElements.get(0).getTagName().equals(LINEAR_LAYOUT))) { - status.addFatalError("Must select exactly one LinearLayout"); - return status; - } - - Element layout = mElements.get(0); - List<Element> children = DomUtilities.getChildren(layout); - if (children.size() != 2) { - status.addFatalError("The LinearLayout must have exactly two children"); - return status; - } - Element first = children.get(0); - Element second = children.get(1); - boolean haveTextView = - first.getTagName().equals(TEXT_VIEW) - || second.getTagName().equals(TEXT_VIEW); - boolean haveImageView = - first.getTagName().equals(IMAGE_VIEW) - || second.getTagName().equals(IMAGE_VIEW); - if (!(haveTextView && haveImageView)) { - status.addFatalError("The LinearLayout must have exactly one TextView child " + - "and one ImageView child"); - 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() { - return super.createArgumentMap(); - } - - @Override - public String getName() { - return "Convert to Compound Drawable"; - } - - @Override - protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) { - String androidNsPrefix = getAndroidNamespacePrefix(); - IFile file = mDelegate.getEditor().getInputFile(); - List<Change> changes = new ArrayList<Change>(); - if (file == null) { - return changes; - } - TextFileChange change = new TextFileChange(file.getName(), file); - MultiTextEdit rootEdit = new MultiTextEdit(); - change.setTextType(EXT_XML); - - // (1) Build up the contents of the new TextView. This is identical - // to the old contents, but with the addition of a drawableTop/Left/Right/Bottom - // attribute (depending on the orientation and order), as well as any layout - // params from the LinearLayout. - // (2) Delete the linear layout and replace with the text view. - // (3) Reformat. - - // checkInitialConditions has already validated that we have exactly a LinearLayout - // with an ImageView and a TextView child (in either order) - Element layout = mElements.get(0); - List<Element> children = DomUtilities.getChildren(layout); - Element first = children.get(0); - Element second = children.get(1); - final Element text; - final Element image; - if (first.getTagName().equals(TEXT_VIEW)) { - text = first; - image = second; - } else { - text = second; - image = first; - } - - // Horizontal is the default, so if no value is specified it is horizontal. - boolean isVertical = VALUE_VERTICAL.equals(layout.getAttributeNS(ANDROID_URI, - ATTR_ORIENTATION)); - - // The WST DOM implementation doesn't correctly implement cloneNode: this returns - // an empty document instead: - // text.getOwnerDocument().cloneNode(false/*deep*/); - // Luckily we just need to clone a single element, not a nested structure, so it's - // easy enough to do this manually: - Document tempDocument = DomUtilities.createEmptyDocument(); - if (tempDocument == null) { - return changes; - } - Element newTextElement = tempDocument.createElement(text.getTagName()); - tempDocument.appendChild(newTextElement); - - NamedNodeMap attributes = text.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Attr attribute = (Attr) attributes.item(i); - String name = attribute.getLocalName(); - if (name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && ANDROID_URI.equals(attribute.getNamespaceURI()) - && !(name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT))) { - // Ignore layout params: the parent layout is going away - } else { - newTextElement.setAttribute(attribute.getName(), attribute.getValue()); - } - } - - // Apply all layout params from the parent (except width and height), - // as well as android:gravity - List<Attr> layoutAttributes = findLayoutAttributes(layout); - for (Attr attribute : layoutAttributes) { - String name = attribute.getLocalName(); - if ((name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) - && ANDROID_URI.equals(attribute.getNamespaceURI())) { - // Already handled specially - continue; - } - newTextElement.setAttribute(attribute.getName(), attribute.getValue()); - } - String gravity = layout.getAttributeNS(ANDROID_URI, ATTR_GRAVITY); - if (gravity.length() > 0) { - setAndroidAttribute(newTextElement, androidNsPrefix, ATTR_GRAVITY, gravity); - } - - String src = image.getAttributeNS(ANDROID_URI, ATTR_SRC); - - // Set the drawable - String drawableAttribute; - // The space between the image and the text can have margins/padding, both - // from the text's perspective and from the image's perspective. We need to - // combine these. - String padding1 = null; - String padding2 = null; - if (isVertical) { - if (first == image) { - drawableAttribute = ATTR_DRAWABLE_TOP; - padding1 = getPadding(image, ATTR_LAYOUT_MARGIN_BOTTOM); - padding2 = getPadding(text, ATTR_LAYOUT_MARGIN_TOP); - } else { - drawableAttribute = ATTR_DRAWABLE_BOTTOM; - padding1 = getPadding(text, ATTR_LAYOUT_MARGIN_BOTTOM); - padding2 = getPadding(image, ATTR_LAYOUT_MARGIN_TOP); - } - } else { - if (first == image) { - drawableAttribute = ATTR_DRAWABLE_LEFT; - padding1 = getPadding(image, ATTR_LAYOUT_MARGIN_RIGHT); - padding2 = getPadding(text, ATTR_LAYOUT_MARGIN_LEFT); - } else { - drawableAttribute = ATTR_DRAWABLE_RIGHT; - padding1 = getPadding(text, ATTR_LAYOUT_MARGIN_RIGHT); - padding2 = getPadding(image, ATTR_LAYOUT_MARGIN_LEFT); - } - } - - setAndroidAttribute(newTextElement, androidNsPrefix, drawableAttribute, src); - - String padding = combine(padding1, padding2); - if (padding != null) { - setAndroidAttribute(newTextElement, androidNsPrefix, ATTR_DRAWABLE_PADDING, padding); - } - - // If the removed LinearLayout is the root container, transfer its namespace - // declaration to the TextView - if (layout.getParentNode() instanceof Document) { - List<Attr> declarations = findNamespaceAttributes(layout); - for (Attr attribute : declarations) { - if (attribute instanceof IndexedRegion) { - newTextElement.setAttribute(attribute.getName(), attribute.getValue()); - } - } - } - - // Update any layout references to the layout to point to the text view - String layoutId = getId(layout); - if (layoutId.length() > 0) { - String id = getId(text); - if (id.length() == 0) { - id = ensureHasId(rootEdit, text, null, false); - setAndroidAttribute(newTextElement, androidNsPrefix, ATTR_ID, id); - } - - IStructuredModel model = mDelegate.getEditor().getModelForRead(); - try { - IStructuredDocument doc = model.getStructuredDocument(); - if (doc != null) { - List<TextEdit> replaceIds = replaceIds(androidNsPrefix, - doc, mSelectionStart, mSelectionEnd, layoutId, id); - for (TextEdit edit : replaceIds) { - rootEdit.addChild(edit); - } - } - } finally { - model.releaseFromRead(); - } - } - - String xml = EclipseXmlPrettyPrinter.prettyPrint( - tempDocument.getDocumentElement(), - EclipseXmlFormatPreferences.create(), - XmlFormatStyle.LAYOUT, null, false); - - TextEdit replace = new ReplaceEdit(mSelectionStart, mSelectionEnd - mSelectionStart, xml); - rootEdit.addChild(replace); - - if (AdtPrefs.getPrefs().getFormatGuiXml()) { - MultiTextEdit formatted = reformat(rootEdit, XmlFormatStyle.LAYOUT); - if (formatted != null) { - rootEdit = formatted; - } - } - - change.setEdit(rootEdit); - changes.add(change); - return changes; - } - - @Nullable - private static String getPadding(@NonNull Element element, @NonNull String attribute) { - String padding = element.getAttributeNS(ANDROID_URI, attribute); - if (padding != null && padding.isEmpty()) { - padding = null; - } - return padding; - } - - @VisibleForTesting - @Nullable - static String combine(@Nullable String dimension1, @Nullable String dimension2) { - if (dimension1 == null || dimension1.isEmpty()) { - if (dimension2 != null && dimension2.isEmpty()) { - return null; - } - return dimension2; - } else if (dimension2 == null || dimension2.isEmpty()) { - if (dimension1 != null && dimension1.isEmpty()) { - return null; - } - return dimension1; - } else { - // Two dimensions are specified (e.g. marginRight for the left one and marginLeft - // for the right one); we have to add these together. We can only do that if - // they use the same units, and do not use resources. - if (dimension1.startsWith(PREFIX_RESOURCE_REF) - || dimension2.startsWith(PREFIX_RESOURCE_REF)) { - return null; - } - - Pattern p = Pattern.compile("([\\d\\.]+)(.+)"); //$NON-NLS-1$ - Matcher matcher1 = p.matcher(dimension1); - Matcher matcher2 = p.matcher(dimension2); - if (matcher1.matches() && matcher2.matches()) { - String unit = matcher1.group(2); - if (unit.equals(matcher2.group(2))) { - float value1 = Float.parseFloat(matcher1.group(1)); - float value2 = Float.parseFloat(matcher2.group(1)); - return AdtUtils.formatFloatAttribute(value1 + value2) + unit; - } - } - } - - return null; - } - - /** - * Sets an Android attribute (in the Android namespace) on an element - * without a given namespace prefix. This is done when building a new Element - * in a temporary document such that the namespace prefix matches when the element is - * formatted and replaced in the target document. - */ - private static void setAndroidAttribute(Element element, String prefix, String name, - String value) { - element.setAttribute(prefix + ':' + name, value); - } - - @Override - public VisualRefactoringWizard createWizard() { - return new UseCompoundDrawableWizard(this, mDelegate); - } - - @SuppressWarnings("javadoc") - 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.usecompound", //$NON-NLS-1$ - project, description, comment, arguments); - } - - @Override - protected Refactoring createRefactoring(Map<String, String> args) { - return new UseCompoundDrawableRefactoring(args); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableWizard.java deleted file mode 100644 index 3ffd6b5ea..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/UseCompoundDrawableWizard.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -class UseCompoundDrawableWizard extends VisualRefactoringWizard { - UseCompoundDrawableWizard(UseCompoundDrawableRefactoring ref, LayoutEditorDelegate editor) { - super(ref, editor); - setDefaultPageTitle("Use Compound Drawable"); - } - - @Override - protected void addUserInputPages() { - // This refactoring takes no parameters - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java deleted file mode 100644 index 904a3a084..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoring.java +++ /dev/null @@ -1,1403 +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_URI; -import static com.android.SdkConstants.ANDROID_WIDGET_PREFIX; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.ID_PREFIX; -import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.SdkConstants.XMLNS; -import static com.android.SdkConstants.XMLNS_PREFIX; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -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.formatting.EclipseXmlFormatPreferences; -import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlPrettyPrinter; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationDescription; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; -import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.core.runtime.Path; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.ITextSelection; -import org.eclipse.jface.viewers.ITreeSelection; -import org.eclipse.jface.viewers.TreePath; -import org.eclipse.ltk.core.refactoring.Change; -import org.eclipse.ltk.core.refactoring.ChangeDescriptor; -import org.eclipse.ltk.core.refactoring.CompositeChange; -import org.eclipse.ltk.core.refactoring.Refactoring; -import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; -import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; -import org.eclipse.ltk.core.refactoring.RefactoringStatus; -import org.eclipse.text.edits.DeleteEdit; -import org.eclipse.text.edits.InsertEdit; -import org.eclipse.text.edits.MalformedTreeException; -import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.text.edits.ReplaceEdit; -import org.eclipse.text.edits.TextEdit; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.PartInitException; -import org.eclipse.ui.ide.IDE; -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.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.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - * Parent class for the various visual refactoring operations; contains shared - * implementations needed by most of them - */ -@SuppressWarnings("restriction") // XML model -public abstract class VisualRefactoring extends Refactoring { - private static final String KEY_FILE = "file"; //$NON-NLS-1$ - private static final String KEY_PROJECT = "proj"; //$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$ - - protected final IFile mFile; - protected final LayoutEditorDelegate mDelegate; - protected final IProject mProject; - protected int mSelectionStart = -1; - protected int mSelectionEnd = -1; - protected final List<Element> mElements; - protected final ITreeSelection mTreeSelection; - protected final ITextSelection mSelection; - /** Same as {@link #mSelectionStart} but not adjusted to element edges */ - protected int mOriginalSelectionStart = -1; - /** Same as {@link #mSelectionEnd} but not adjusted to element edges */ - protected int mOriginalSelectionEnd = -1; - - protected final Map<Element, String> mGeneratedIdMap = new HashMap<Element, String>(); - protected final Set<String> mGeneratedIds = new HashSet<String>(); - - protected List<Change> mChanges; - private String mAndroidNamespacePrefix; - - /** - * This constructor is solely used by {@link VisualRefactoringDescriptor}, - * to replay a previous refactoring. - * @param arguments argument map created by #createArgumentMap. - */ - VisualRefactoring(Map<String, String> arguments) { - IPath path = Path.fromPortableString(arguments.get(KEY_PROJECT)); - mProject = (IProject) ResourcesPlugin.getWorkspace().getRoot().findMember(path); - path = Path.fromPortableString(arguments.get(KEY_FILE)); - mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path); - mSelectionStart = Integer.parseInt(arguments.get(KEY_SEL_START)); - mSelectionEnd = Integer.parseInt(arguments.get(KEY_SEL_END)); - mOriginalSelectionStart = mSelectionStart; - mOriginalSelectionEnd = mSelectionEnd; - mDelegate = null; - mElements = null; - mSelection = null; - mTreeSelection = null; - } - - @VisibleForTesting - VisualRefactoring(List<Element> elements, LayoutEditorDelegate delegate) { - mElements = elements; - mDelegate = delegate; - - mFile = delegate != null ? delegate.getEditor().getInputFile() : null; - mProject = delegate != null ? delegate.getEditor().getProject() : null; - mSelectionStart = 0; - mSelectionEnd = 0; - mOriginalSelectionStart = 0; - mOriginalSelectionEnd = 0; - mSelection = null; - mTreeSelection = null; - - int end = Integer.MIN_VALUE; - int start = Integer.MAX_VALUE; - for (Element element : elements) { - if (element instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) element; - start = Math.min(start, region.getStartOffset()); - end = Math.max(end, region.getEndOffset()); - } - } - if (start >= 0) { - mSelectionStart = start; - mSelectionEnd = end; - mOriginalSelectionStart = start; - mOriginalSelectionEnd = end; - } - } - - public VisualRefactoring(IFile file, LayoutEditorDelegate editor, ITextSelection selection, - ITreeSelection treeSelection) { - mFile = file; - mDelegate = editor; - mProject = file.getProject(); - mSelection = selection; - mTreeSelection = treeSelection; - - // Initialize mSelectionStart and mSelectionEnd based on the selection context, which - // is either a treeSelection (when invoked from the layout editor or the outline), or - // a selection (when invoked from an XML editor) - if (treeSelection != null) { - int end = Integer.MIN_VALUE; - int start = Integer.MAX_VALUE; - for (TreePath path : treeSelection.getPaths()) { - Object lastSegment = path.getLastSegment(); - if (lastSegment instanceof CanvasViewInfo) { - CanvasViewInfo viewInfo = (CanvasViewInfo) lastSegment; - UiViewElementNode uiNode = viewInfo.getUiViewNode(); - if (uiNode == null) { - continue; - } - Node xmlNode = uiNode.getXmlNode(); - if (xmlNode instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) xmlNode; - - start = Math.min(start, region.getStartOffset()); - end = Math.max(end, region.getEndOffset()); - } - } - } - if (start >= 0) { - mSelectionStart = start; - mSelectionEnd = end; - mOriginalSelectionStart = mSelectionStart; - mOriginalSelectionEnd = mSelectionEnd; - } - if (selection != null) { - mOriginalSelectionStart = selection.getOffset(); - mOriginalSelectionEnd = mOriginalSelectionStart + selection.getLength(); - } - } else if (selection != null) { - // TODO: update selection to boundaries! - mSelectionStart = selection.getOffset(); - mSelectionEnd = mSelectionStart + selection.getLength(); - mOriginalSelectionStart = mSelectionStart; - mOriginalSelectionEnd = mSelectionEnd; - } - - mElements = initElements(); - } - - @NonNull - protected abstract List<Change> computeChanges(IProgressMonitor monitor); - - @Override - public RefactoringStatus checkFinalConditions(IProgressMonitor monitor) throws CoreException, - OperationCanceledException { - RefactoringStatus status = new RefactoringStatus(); - mChanges = new ArrayList<Change>(); - try { - monitor.beginTask("Checking post-conditions...", 5); - - // Reset state for each computeChanges call, in case the user goes back - // and forth in the refactoring wizard - mGeneratedIdMap.clear(); - mGeneratedIds.clear(); - List<Change> changes = computeChanges(monitor); - mChanges.addAll(changes); - - monitor.worked(1); - } finally { - monitor.done(); - } - - return status; - } - - @Override - public Change createChange(IProgressMonitor monitor) throws CoreException, - OperationCanceledException { - try { - monitor.beginTask("Applying changes...", 1); - - CompositeChange change = new CompositeChange( - getName(), - mChanges.toArray(new Change[mChanges.size()])) { - @Override - public ChangeDescriptor getDescriptor() { - VisualRefactoringDescriptor desc = createDescriptor(); - return new RefactoringChangeDescriptor(desc); - } - }; - - monitor.worked(1); - return change; - - } finally { - monitor.done(); - } - } - - protected abstract VisualRefactoringDescriptor createDescriptor(); - - protected Map<String, String> createArgumentMap() { - HashMap<String, String> args = new HashMap<String, String>(); - args.put(KEY_PROJECT, mProject.getFullPath().toPortableString()); - args.put(KEY_FILE, mFile.getFullPath().toPortableString()); - args.put(KEY_SEL_START, Integer.toString(mSelectionStart)); - args.put(KEY_SEL_END, Integer.toString(mSelectionEnd)); - - return args; - } - - IFile getFile() { - return mFile; - } - - // ---- Shared functionality ---- - - - protected void openFile(IFile file) { - GraphicalEditorPart graphicalEditor = mDelegate.getGraphicalEditor(); - IFile leavingFile = graphicalEditor.getEditedFile(); - - try { - // Duplicate the current state into the newly created file - String state = ConfigurationDescription.getDescription(leavingFile); - - // TODO: Look for a ".NoTitleBar.Fullscreen" theme version of the current - // theme to show. - - file.setSessionProperty(GraphicalEditorPart.NAME_INITIAL_STATE, state); - } catch (CoreException e) { - // pass - } - - /* TBD: "Show Included In" if supported. - * Not sure if this is a good idea. - if (graphicalEditor.renderingSupports(Capability.EMBEDDED_LAYOUT)) { - try { - Reference include = Reference.create(graphicalEditor.getEditedFile()); - file.setSessionProperty(GraphicalEditorPart.NAME_INCLUDE, include); - } catch (CoreException e) { - // pass - worst that can happen is that we don't start with inclusion - } - } - */ - - try { - IEditorPart part = - IDE.openEditor(mDelegate.getEditor().getEditorSite().getPage(), file); - if (part instanceof AndroidXmlEditor && AdtPrefs.getPrefs().getFormatGuiXml()) { - AndroidXmlEditor newEditor = (AndroidXmlEditor) part; - newEditor.reformatDocument(); - } - } catch (PartInitException e) { - AdtPlugin.log(e, "Can't open new included layout"); - } - } - - - /** Produce a list of edits to replace references to the given id with the given new id */ - protected static List<TextEdit> replaceIds(String androidNamePrefix, - IStructuredDocument doc, int skipStart, int skipEnd, - String rootId, String referenceId) { - if (rootId == null) { - return Collections.emptyList(); - } - - // We need to search for either @+id/ or @id/ - String match1 = rootId; - String match2; - if (match1.startsWith(ID_PREFIX)) { - match2 = '"' + NEW_ID_PREFIX + match1.substring(ID_PREFIX.length()) + '"'; - match1 = '"' + match1 + '"'; - } else if (match1.startsWith(NEW_ID_PREFIX)) { - match2 = '"' + ID_PREFIX + match1.substring(NEW_ID_PREFIX.length()) + '"'; - match1 = '"' + match1 + '"'; - } else { - return Collections.emptyList(); - } - - String namePrefix = androidNamePrefix + ':' + ATTR_LAYOUT_RESOURCE_PREFIX; - List<TextEdit> edits = new ArrayList<TextEdit>(); - - IStructuredDocumentRegion region = doc.getFirstStructuredDocumentRegion(); - for (; region != null; region = region.getNext()) { - ITextRegionList list = region.getRegions(); - int regionStart = region.getStart(); - - // Look at all attribute values and look for an id reference match - String attributeName = ""; //$NON-NLS-1$ - for (int j = 0; j < region.getNumberOfRegions(); j++) { - ITextRegion subRegion = list.get(j); - String type = subRegion.getType(); - if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(type)) { - attributeName = region.getText(subRegion); - } else if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(type)) { - // Only replace references in layout attributes - if (!attributeName.startsWith(namePrefix)) { - continue; - } - // Skip occurrences in the given skip range - int subRegionStart = regionStart + subRegion.getStart(); - if (subRegionStart >= skipStart && subRegionStart <= skipEnd) { - continue; - } - - String attributeValue = region.getText(subRegion); - if (attributeValue.equals(match1) || attributeValue.equals(match2)) { - int start = subRegionStart + 1; // skip quote - int end = start + rootId.length(); - - edits.add(new ReplaceEdit(start, end - start, referenceId)); - } - } - } - } - - return edits; - } - - /** Get the id of the root selected element, if any */ - protected String getRootId() { - Element primary = getPrimaryElement(); - if (primary != null) { - String oldId = primary.getAttributeNS(ANDROID_URI, ATTR_ID); - // id null check for https://bugs.eclipse.org/bugs/show_bug.cgi?id=272378 - if (oldId != null && oldId.length() > 0) { - return oldId; - } - } - - return null; - } - - protected String getAndroidNamespacePrefix() { - if (mAndroidNamespacePrefix == null) { - List<Attr> attributeNodes = findNamespaceAttributes(); - for (Node attributeNode : attributeNodes) { - String prefix = attributeNode.getPrefix(); - if (XMLNS.equals(prefix)) { - String name = attributeNode.getNodeName(); - String value = attributeNode.getNodeValue(); - if (value.equals(ANDROID_URI)) { - mAndroidNamespacePrefix = name; - if (mAndroidNamespacePrefix.startsWith(XMLNS_PREFIX)) { - mAndroidNamespacePrefix = - mAndroidNamespacePrefix.substring(XMLNS_PREFIX.length()); - } - } - } - } - - if (mAndroidNamespacePrefix == null) { - mAndroidNamespacePrefix = ANDROID_NS_NAME; - } - } - - return mAndroidNamespacePrefix; - } - - protected static String getAndroidNamespacePrefix(Document document) { - String nsPrefix = null; - List<Attr> attributeNodes = findNamespaceAttributes(document); - for (Node attributeNode : attributeNodes) { - String prefix = attributeNode.getPrefix(); - if (XMLNS.equals(prefix)) { - String name = attributeNode.getNodeName(); - String value = attributeNode.getNodeValue(); - if (value.equals(ANDROID_URI)) { - nsPrefix = name; - if (nsPrefix.startsWith(XMLNS_PREFIX)) { - nsPrefix = - nsPrefix.substring(XMLNS_PREFIX.length()); - } - } - } - } - - if (nsPrefix == null) { - nsPrefix = ANDROID_NS_NAME; - } - - return nsPrefix; - } - - protected List<Attr> findNamespaceAttributes() { - Document document = getDomDocument(); - return findNamespaceAttributes(document); - } - - protected static List<Attr> findNamespaceAttributes(Document document) { - if (document != null) { - Element root = document.getDocumentElement(); - return findNamespaceAttributes(root); - } - - return Collections.emptyList(); - } - - protected static List<Attr> findNamespaceAttributes(Node root) { - List<Attr> result = new ArrayList<Attr>(); - NamedNodeMap attributes = root.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Node attributeNode = attributes.item(i); - - String prefix = attributeNode.getPrefix(); - if (XMLNS.equals(prefix)) { - result.add((Attr) attributeNode); - } - } - - return result; - } - - protected List<Attr> findLayoutAttributes(Node root) { - List<Attr> result = new ArrayList<Attr>(); - NamedNodeMap attributes = root.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Node attributeNode = attributes.item(i); - - String name = attributeNode.getLocalName(); - if (name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && ANDROID_URI.equals(attributeNode.getNamespaceURI())) { - result.add((Attr) attributeNode); - } - } - - return result; - } - - protected String insertNamespace(String xmlText, String namespaceDeclarations) { - // Insert namespace declarations into the extracted XML fragment - int firstSpace = xmlText.indexOf(' '); - int elementEnd = xmlText.indexOf('>'); - int insertAt; - if (firstSpace != -1 && firstSpace < elementEnd) { - insertAt = firstSpace; - } else { - insertAt = elementEnd; - } - xmlText = xmlText.substring(0, insertAt) + namespaceDeclarations - + xmlText.substring(insertAt); - - return xmlText; - } - - /** Remove sections of the document that correspond to top level layout attributes; - * these are placed on the include element instead */ - protected String stripTopLayoutAttributes(Element primary, int start, String xml) { - if (primary != null) { - // List of attributes to remove - List<IndexedRegion> skip = new ArrayList<IndexedRegion>(); - NamedNodeMap attributes = primary.getAttributes(); - for (int i = 0, n = attributes.getLength(); i < n; i++) { - Node attr = attributes.item(i); - String name = attr.getLocalName(); - if (name.startsWith(ATTR_LAYOUT_RESOURCE_PREFIX) - && ANDROID_URI.equals(attr.getNamespaceURI())) { - if (name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) { - // These are special and are left in - continue; - } - - if (attr instanceof IndexedRegion) { - skip.add((IndexedRegion) attr); - } - } - } - if (skip.size() > 0) { - Collections.sort(skip, new Comparator<IndexedRegion>() { - // Sort in start order - @Override - public int compare(IndexedRegion r1, IndexedRegion r2) { - return r1.getStartOffset() - r2.getStartOffset(); - } - }); - - // Successively cut out the various layout attributes - // TODO remove adjacent whitespace too (but not newlines, unless they - // are newly adjacent) - StringBuilder sb = new StringBuilder(xml.length()); - int nextStart = 0; - - // Copy out all the sections except the skip sections - for (IndexedRegion r : skip) { - int regionStart = r.getStartOffset(); - // Adjust to string offsets since we've copied the string out of - // the document - regionStart -= start; - - sb.append(xml.substring(nextStart, regionStart)); - - nextStart = regionStart + r.getLength(); - } - if (nextStart < xml.length()) { - sb.append(xml.substring(nextStart)); - } - - return sb.toString(); - } - } - - return xml; - } - - protected static String getIndent(String line, int max) { - int i = 0; - int n = Math.min(max, line.length()); - for (; i < n; i++) { - char c = line.charAt(i); - if (!Character.isWhitespace(c)) { - return line.substring(0, i); - } - } - - if (n < line.length()) { - return line.substring(0, n); - } else { - return line; - } - } - - protected static String dedent(String xml) { - String[] lines = xml.split("\n"); //$NON-NLS-1$ - if (lines.length < 2) { - // The first line never has any indentation since we copy it out from the - // element start index - return xml; - } - - String indentPrefix = getIndent(lines[1], lines[1].length()); - for (int i = 2, n = lines.length; i < n; i++) { - String line = lines[i]; - - // Ignore blank lines - if (line.trim().length() == 0) { - continue; - } - - indentPrefix = getIndent(line, indentPrefix.length()); - - if (indentPrefix.length() == 0) { - return xml; - } - } - - StringBuilder sb = new StringBuilder(); - for (String line : lines) { - if (line.startsWith(indentPrefix)) { - sb.append(line.substring(indentPrefix.length())); - } else { - sb.append(line); - } - sb.append('\n'); - } - return sb.toString(); - } - - protected String getText(int start, int end) { - try { - IStructuredDocument document = mDelegate.getEditor().getStructuredDocument(); - return document.get(start, end - start); - } catch (BadLocationException e) { - // the region offset was invalid. ignore. - return null; - } - } - - protected List<Element> getElements() { - return mElements; - } - - protected List<Element> initElements() { - List<Element> nodes = new ArrayList<Element>(); - - assert mTreeSelection == null || mSelection == null : - "treeSel= " + mTreeSelection + ", sel=" + mSelection; - - // Initialize mSelectionStart and mSelectionEnd based on the selection context, which - // is either a treeSelection (when invoked from the layout editor or the outline), or - // a selection (when invoked from an XML editor) - if (mTreeSelection != null) { - int end = Integer.MIN_VALUE; - int start = Integer.MAX_VALUE; - for (TreePath path : mTreeSelection.getPaths()) { - Object lastSegment = path.getLastSegment(); - if (lastSegment instanceof CanvasViewInfo) { - CanvasViewInfo viewInfo = (CanvasViewInfo) lastSegment; - UiViewElementNode uiNode = viewInfo.getUiViewNode(); - if (uiNode == null) { - continue; - } - Node xmlNode = uiNode.getXmlNode(); - if (xmlNode instanceof Element) { - Element element = (Element) xmlNode; - nodes.add(element); - IndexedRegion region = getRegion(element); - start = Math.min(start, region.getStartOffset()); - end = Math.max(end, region.getEndOffset()); - } - } - } - if (start >= 0) { - mSelectionStart = start; - mSelectionEnd = end; - } - } else if (mSelection != null) { - mSelectionStart = mSelection.getOffset(); - mSelectionEnd = mSelectionStart + mSelection.getLength(); - mOriginalSelectionStart = mSelectionStart; - mOriginalSelectionEnd = mSelectionEnd; - - // Figure out the range of selected nodes from the document offsets - IStructuredDocument doc = mDelegate.getEditor().getStructuredDocument(); - Pair<Element, Element> range = DomUtilities.getElementRange(doc, - mSelectionStart, mSelectionEnd); - if (range != null) { - Element first = range.getFirst(); - Element last = range.getSecond(); - - // Adjust offsets to get rid of surrounding text nodes (if you happened - // to select a text range and included whitespace on either end etc) - mSelectionStart = getRegion(first).getStartOffset(); - mSelectionEnd = getRegion(last).getEndOffset(); - - if (mSelectionStart > mSelectionEnd) { - int tmp = mSelectionStart; - mSelectionStart = mSelectionEnd; - mSelectionEnd = tmp; - } - - if (first == last) { - nodes.add(first); - } else if (first.getParentNode() == last.getParentNode()) { - // Add the range - Node node = first; - while (node != null) { - if (node instanceof Element) { - nodes.add((Element) node); - } - if (node == last) { - break; - } - node = node.getNextSibling(); - } - } else { - // Different parents: this means we have an uneven selection, selecting - // elements from different levels. We can't extract ranges like that. - } - } - } else { - assert false; - } - - // Make sure that the list of elements is unique - //Set<Element> seen = new HashSet<Element>(); - //for (Element element : nodes) { - // assert !seen.contains(element) : element; - // seen.add(element); - //} - - return nodes; - } - - protected Element getPrimaryElement() { - List<Element> elements = getElements(); - if (elements != null && elements.size() == 1) { - return elements.get(0); - } - - return null; - } - - protected Document getDomDocument() { - if (mDelegate.getUiRootNode() != null) { - return mDelegate.getUiRootNode().getXmlDocument(); - } else { - return getElements().get(0).getOwnerDocument(); - } - } - - protected List<CanvasViewInfo> getSelectedViewInfos() { - List<CanvasViewInfo> infos = new ArrayList<CanvasViewInfo>(); - if (mTreeSelection != null) { - for (TreePath path : mTreeSelection.getPaths()) { - Object lastSegment = path.getLastSegment(); - if (lastSegment instanceof CanvasViewInfo) { - infos.add((CanvasViewInfo) lastSegment); - } - } - } - return infos; - } - - protected boolean validateNotEmpty(List<CanvasViewInfo> infos, RefactoringStatus status) { - if (infos.size() == 0) { - status.addFatalError("No selection to extract"); - return false; - } - - return true; - } - - protected boolean validateNotRoot(List<CanvasViewInfo> infos, RefactoringStatus status) { - for (CanvasViewInfo info : infos) { - if (info.isRoot()) { - status.addFatalError("Cannot refactor the root"); - return false; - } - } - - return true; - } - - protected boolean validateContiguous(List<CanvasViewInfo> infos, RefactoringStatus status) { - if (infos.size() > 1) { - // All elements must be siblings (e.g. same parent) - List<UiViewElementNode> nodes = new ArrayList<UiViewElementNode>(infos - .size()); - for (CanvasViewInfo info : infos) { - UiViewElementNode node = info.getUiViewNode(); - if (node != null) { - nodes.add(node); - } - } - if (nodes.size() == 0) { - status.addFatalError("No selected views"); - return false; - } - - UiElementNode parent = nodes.get(0).getUiParent(); - for (UiViewElementNode node : nodes) { - if (parent != node.getUiParent()) { - status.addFatalError("The selected elements must be adjacent"); - return false; - } - } - // Ensure that the siblings are contiguous; no gaps. - // If we've selected all the children of the parent then we don't need - // to look. - List<UiElementNode> siblings = parent.getUiChildren(); - if (siblings.size() != nodes.size()) { - Set<UiViewElementNode> nodeSet = new HashSet<UiViewElementNode>(nodes); - boolean inRange = false; - int remaining = nodes.size(); - for (UiElementNode node : siblings) { - boolean in = nodeSet.contains(node); - if (in) { - remaining--; - if (remaining == 0) { - break; - } - inRange = true; - } else if (inRange) { - status.addFatalError("The selected elements must be adjacent"); - return false; - } - } - } - } - - return true; - } - - /** - * Updates the given element with a new name if the current id reflects the old - * element type. If the name was changed, it will return the new name. - */ - protected String ensureIdMatchesType(Element element, String newType, MultiTextEdit rootEdit) { - String oldType = element.getTagName(); - if (oldType.indexOf('.') == -1) { - oldType = ANDROID_WIDGET_PREFIX + oldType; - } - String oldTypeBase = oldType.substring(oldType.lastIndexOf('.') + 1); - String id = getId(element); - if (id == null || id.length() == 0 - || id.toLowerCase(Locale.US).contains(oldTypeBase.toLowerCase(Locale.US))) { - String newTypeBase = newType.substring(newType.lastIndexOf('.') + 1); - return ensureHasId(rootEdit, element, newTypeBase); - } - - return null; - } - - /** - * Returns the {@link IndexedRegion} for the given node - * - * @param node the node to look up the region for - * @return the corresponding region, or null - */ - public static IndexedRegion getRegion(Node node) { - if (node instanceof IndexedRegion) { - return (IndexedRegion) node; - } - - return null; - } - - protected String ensureHasId(MultiTextEdit rootEdit, Element element, String prefix) { - return ensureHasId(rootEdit, element, prefix, true); - } - - protected String ensureHasId(MultiTextEdit rootEdit, Element element, String prefix, - boolean apply) { - String id = mGeneratedIdMap.get(element); - if (id != null) { - return NEW_ID_PREFIX + id; - } - - if (!element.hasAttributeNS(ANDROID_URI, ATTR_ID) - || (prefix != null && !getId(element).startsWith(prefix))) { - id = DomUtilities.getFreeWidgetId(element, mGeneratedIds, prefix); - // Make sure we don't use this one again - mGeneratedIds.add(id); - mGeneratedIdMap.put(element, id); - id = NEW_ID_PREFIX + id; - if (apply) { - setAttribute(rootEdit, element, - ANDROID_URI, getAndroidNamespacePrefix(), ATTR_ID, id); - } - return id; - } - - return getId(element); - } - - protected int getFirstAttributeOffset(Element element) { - IndexedRegion region = getRegion(element); - if (region != null) { - int startOffset = region.getStartOffset(); - int endOffset = region.getEndOffset(); - String text = getText(startOffset, endOffset); - String name = element.getLocalName(); - int nameOffset = text.indexOf(name); - if (nameOffset != -1) { - return startOffset + nameOffset + name.length(); - } - } - - return -1; - } - - /** - * Returns the id of the given element - * - * @param element the element to look up the id for - * @return the corresponding id, or an empty string (should not be null - * according to the DOM API, but has been observed to be null on - * some versions of Eclipse) - */ - public static String getId(Element element) { - return element.getAttributeNS(ANDROID_URI, ATTR_ID); - } - - protected String ensureNewId(String id) { - if (id != null && id.length() > 0) { - if (id.startsWith(ID_PREFIX)) { - id = NEW_ID_PREFIX + id.substring(ID_PREFIX.length()); - } else if (!id.startsWith(NEW_ID_PREFIX)) { - id = NEW_ID_PREFIX + id; - } - } else { - id = null; - } - - return id; - } - - protected String getViewClass(String fqcn) { - // Don't include android.widget. as a package prefix in layout files - if (fqcn.startsWith(ANDROID_WIDGET_PREFIX)) { - fqcn = fqcn.substring(ANDROID_WIDGET_PREFIX.length()); - } - - return fqcn; - } - - protected void setAttribute(MultiTextEdit rootEdit, Element element, - String attributeUri, - String attributePrefix, String attributeName, String attributeValue) { - int offset = getFirstAttributeOffset(element); - if (offset != -1) { - if (element.hasAttributeNS(attributeUri, attributeName)) { - replaceAttributeDeclaration(rootEdit, offset, element, attributePrefix, - attributeUri, attributeName, attributeValue); - } else { - addAttributeDeclaration(rootEdit, offset, attributePrefix, attributeName, - attributeValue); - } - } - } - - private void addAttributeDeclaration(MultiTextEdit rootEdit, int offset, - String attributePrefix, String attributeName, String attributeValue) { - StringBuilder sb = new StringBuilder(); - sb.append(' '); - - if (attributePrefix != null) { - sb.append(attributePrefix).append(':'); - } - sb.append(attributeName).append('=').append('"'); - sb.append(attributeValue).append('"'); - - InsertEdit setAttribute = new InsertEdit(offset, sb.toString()); - rootEdit.addChild(setAttribute); - } - - /** Replaces the value declaration of the given attribute */ - private void replaceAttributeDeclaration(MultiTextEdit rootEdit, int offset, - Element element, String attributePrefix, String attributeUri, - String attributeName, String attributeValue) { - // Find attribute value and replace it - IStructuredModel model = mDelegate.getEditor().getModelForRead(); - try { - IStructuredDocument doc = model.getStructuredDocument(); - - IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(offset); - ITextRegionList list = region.getRegions(); - int regionStart = region.getStart(); - - int valueStart = -1; - boolean useNextValue = false; - String targetName = attributePrefix != null - ? attributePrefix + ':' + attributeName : attributeName; - - // Look at all attribute values and look for an id reference match - for (int j = 0; j < region.getNumberOfRegions(); j++) { - ITextRegion subRegion = list.get(j); - String type = subRegion.getType(); - if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(type)) { - // What about prefix? - if (targetName.equals(region.getText(subRegion))) { - useNextValue = true; - } - } else if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(type)) { - if (useNextValue) { - valueStart = regionStart + subRegion.getStart(); - break; - } - } - } - - if (valueStart != -1) { - String oldValue = element.getAttributeNS(attributeUri, attributeName); - int start = valueStart + 1; // Skip opening " - ReplaceEdit setAttribute = new ReplaceEdit(start, oldValue.length(), - attributeValue); - try { - rootEdit.addChild(setAttribute); - } catch (MalformedTreeException mte) { - AdtPlugin.log(mte, "Could not replace attribute %1$s with %2$s", - attributeName, attributeValue); - throw mte; - } - } - } finally { - model.releaseFromRead(); - } - } - - /** Strips out the given attribute, if defined */ - protected void removeAttribute(MultiTextEdit rootEdit, Element element, String uri, - String attributeName) { - if (element.hasAttributeNS(uri, attributeName)) { - Attr attribute = element.getAttributeNodeNS(uri, attributeName); - removeAttribute(rootEdit, attribute); - } - } - - /** Strips out the given attribute, if defined */ - protected void removeAttribute(MultiTextEdit rootEdit, Attr attribute) { - IndexedRegion region = getRegion(attribute); - if (region != null) { - int startOffset = region.getStartOffset(); - int endOffset = region.getEndOffset(); - DeleteEdit deletion = new DeleteEdit(startOffset, endOffset - startOffset); - rootEdit.addChild(deletion); - } - } - - - /** - * Removes the given element's opening and closing tags (including all of its - * attributes) but leaves any children alone - * - * @param rootEdit the multi edit to add the removal operation to - * @param element the element to delete the open and closing tags for - * @param skip a list of elements that should not be modified (for example because they - * are targeted for deletion) - * - * TODO: Rename this to "unwrap" ? And allow for handling nested deletions. - */ - protected void removeElementTags(MultiTextEdit rootEdit, Element element, List<Element> skip, - boolean changeIndentation) { - IndexedRegion elementRegion = getRegion(element); - if (elementRegion == null) { - return; - } - - // Look for the opening tag - IStructuredModel model = mDelegate.getEditor().getModelForRead(); - try { - int startLineInclusive = -1; - int endLineInclusive = -1; - IStructuredDocument doc = model.getStructuredDocument(); - if (doc != null) { - int start = elementRegion.getStartOffset(); - IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(start); - ITextRegionList list = region.getRegions(); - int regionStart = region.getStart(); - int startOffset = regionStart; - for (int j = 0; j < region.getNumberOfRegions(); j++) { - ITextRegion subRegion = list.get(j); - String type = subRegion.getType(); - if (DOMRegionContext.XML_TAG_OPEN.equals(type)) { - startOffset = regionStart + subRegion.getStart(); - } else if (DOMRegionContext.XML_TAG_CLOSE.equals(type)) { - int endOffset = regionStart + subRegion.getStart() + subRegion.getLength(); - - DeleteEdit deletion = createDeletion(doc, startOffset, endOffset); - rootEdit.addChild(deletion); - startLineInclusive = doc.getLineOfOffset(endOffset) + 1; - break; - } - } - - // Find the close tag - // Look at all attribute values and look for an id reference match - region = doc.getRegionAtCharacterOffset(elementRegion.getEndOffset() - - element.getTagName().length() - 1); - list = region.getRegions(); - regionStart = region.getStartOffset(); - startOffset = -1; - for (int j = 0; j < region.getNumberOfRegions(); j++) { - ITextRegion subRegion = list.get(j); - String type = subRegion.getType(); - if (DOMRegionContext.XML_END_TAG_OPEN.equals(type)) { - startOffset = regionStart + subRegion.getStart(); - } else if (DOMRegionContext.XML_TAG_CLOSE.equals(type)) { - int endOffset = regionStart + subRegion.getStart() + subRegion.getLength(); - if (startOffset != -1) { - DeleteEdit deletion = createDeletion(doc, startOffset, endOffset); - rootEdit.addChild(deletion); - endLineInclusive = doc.getLineOfOffset(startOffset) - 1; - } - break; - } - } - } - - // Dedent the contents - if (changeIndentation && startLineInclusive != -1 && endLineInclusive != -1) { - String indent = AndroidXmlEditor.getIndentAtOffset(doc, getRegion(element) - .getStartOffset()); - setIndentation(rootEdit, indent, doc, startLineInclusive, endLineInclusive, - element, skip); - } - } finally { - model.releaseFromRead(); - } - } - - protected void removeIndentation(MultiTextEdit rootEdit, String removeIndent, - IStructuredDocument doc, int startLineInclusive, int endLineInclusive, - Element element, List<Element> skip) { - if (startLineInclusive > endLineInclusive) { - return; - } - int indentLength = removeIndent.length(); - if (indentLength == 0) { - return; - } - - try { - for (int line = startLineInclusive; line <= endLineInclusive; line++) { - IRegion info = doc.getLineInformation(line); - int lineStart = info.getOffset(); - int lineLength = info.getLength(); - int lineEnd = lineStart + lineLength; - if (overlaps(lineStart, lineEnd, element, skip)) { - continue; - } - String lineText = getText(lineStart, - lineStart + Math.min(lineLength, indentLength)); - if (lineText.startsWith(removeIndent)) { - rootEdit.addChild(new DeleteEdit(lineStart, indentLength)); - } - } - } catch (BadLocationException e) { - AdtPlugin.log(e, null); - } - } - - protected void setIndentation(MultiTextEdit rootEdit, String indent, - IStructuredDocument doc, int startLineInclusive, int endLineInclusive, - Element element, List<Element> skip) { - if (startLineInclusive > endLineInclusive) { - return; - } - int indentLength = indent.length(); - if (indentLength == 0) { - return; - } - - try { - for (int line = startLineInclusive; line <= endLineInclusive; line++) { - IRegion info = doc.getLineInformation(line); - int lineStart = info.getOffset(); - int lineLength = info.getLength(); - int lineEnd = lineStart + lineLength; - if (overlaps(lineStart, lineEnd, element, skip)) { - continue; - } - String lineText = getText(lineStart, lineStart + lineLength); - int indentEnd = getFirstNonSpace(lineText); - rootEdit.addChild(new ReplaceEdit(lineStart, indentEnd, indent)); - } - } catch (BadLocationException e) { - AdtPlugin.log(e, null); - } - } - - private int getFirstNonSpace(String s) { - for (int i = 0; i < s.length(); i++) { - if (!Character.isWhitespace(s.charAt(i))) { - return i; - } - } - - return s.length(); - } - - /** Returns true if the given line overlaps any of the given elements */ - private static boolean overlaps(int startOffset, int endOffset, - Element element, List<Element> overlaps) { - for (Element e : overlaps) { - if (e == element) { - continue; - } - - IndexedRegion region = getRegion(e); - if (region.getEndOffset() >= startOffset && region.getStartOffset() <= endOffset) { - return true; - } - } - return false; - } - - protected DeleteEdit createDeletion(IStructuredDocument doc, int startOffset, int endOffset) { - // Expand to delete the whole line? - try { - IRegion info = doc.getLineInformationOfOffset(startOffset); - int lineBegin = info.getOffset(); - // Is the text on the line leading up to the deletion region, - // and the text following it, all whitespace? - boolean deleteLine = true; - if (lineBegin < startOffset) { - String prefix = getText(lineBegin, startOffset); - if (prefix.trim().length() > 0) { - deleteLine = false; - } - } - info = doc.getLineInformationOfOffset(endOffset); - int lineEnd = info.getOffset() + info.getLength(); - if (lineEnd > endOffset) { - String suffix = getText(endOffset, lineEnd); - if (suffix.trim().length() > 0) { - deleteLine = false; - } - } - if (deleteLine) { - startOffset = lineBegin; - endOffset = Math.min(doc.getLength(), lineEnd + 1); - } - } catch (BadLocationException e) { - AdtPlugin.log(e, null); - } - - - return new DeleteEdit(startOffset, endOffset - startOffset); - } - - /** - * Rewrite the edits in the given {@link MultiTextEdit} such that same edits are - * applied, but the resulting range is also formatted - */ - protected MultiTextEdit reformat(MultiTextEdit edit, XmlFormatStyle style) { - String xml = mDelegate.getEditor().getStructuredDocument().get(); - return reformat(xml, edit, style); - } - - /** - * Rewrite the edits in the given {@link MultiTextEdit} such that same edits are - * applied, but the resulting range is also formatted - * - * @param oldContents the original contents that should be edited by a - * {@link MultiTextEdit} - * @param edit the {@link MultiTextEdit} to be applied to some string - * @param style the formatting style to use - * @return a new {@link MultiTextEdit} which performs the same edits as the input edit - * but also reformats the text - */ - public static MultiTextEdit reformat(String oldContents, MultiTextEdit edit, - XmlFormatStyle style) { - IDocument document = new org.eclipse.jface.text.Document(); - document.set(oldContents); - - try { - edit.apply(document); - } catch (MalformedTreeException e) { - AdtPlugin.log(e, null); - return null; // Abort formatting - } catch (BadLocationException e) { - AdtPlugin.log(e, null); - return null; // Abort formatting - } - - String actual = document.get(); - - // TODO: Try to format only the affected portion of the document. - // To do that we need to find out what the affected offsets are; we know - // the MultiTextEdit's affected range, but that is referring to offsets - // in the old document. Use that to compute offsets in the new document. - //int distanceFromEnd = actual.length() - edit.getExclusiveEnd(); - //IStructuredModel model = DomUtilities.createStructuredModel(actual); - //int start = edit.getOffset(); - //int end = actual.length() - distanceFromEnd; - //int length = end - start; - //TextEdit format = AndroidXmlFormattingStrategy.format(model, start, length); - EclipseXmlFormatPreferences formatPrefs = EclipseXmlFormatPreferences.create(); - String formatted = EclipseXmlPrettyPrinter.prettyPrint(actual, formatPrefs, style, - null /*lineSeparator*/); - - - // Figure out how much of the before and after strings are identical and narrow - // the replacement scope - boolean foundDifference = false; - int firstDifference = 0; - int lastDifference = formatted.length(); - int start = 0; - int end = oldContents.length(); - - for (int i = 0, j = start; i < formatted.length() && j < end; i++, j++) { - if (formatted.charAt(i) != oldContents.charAt(j)) { - firstDifference = i; - foundDifference = true; - break; - } - } - - if (!foundDifference) { - // No differences - the document is already formatted, nothing to do - return null; - } - - lastDifference = firstDifference + 1; - for (int i = formatted.length() - 1, j = end - 1; - i > firstDifference && j > start; - i--, j--) { - if (formatted.charAt(i) != oldContents.charAt(j)) { - lastDifference = i + 1; - break; - } - } - - start += firstDifference; - end -= (formatted.length() - lastDifference); - end = Math.max(start, end); - formatted = formatted.substring(firstDifference, lastDifference); - - ReplaceEdit format = new ReplaceEdit(start, end - start, - formatted); - - MultiTextEdit newEdit = new MultiTextEdit(); - newEdit.addChild(format); - - return newEdit; - } - - protected ViewElementDescriptor getElementDescriptor(String fqcn) { - AndroidTargetData data = mDelegate.getEditor().getTargetData(); - if (data != null) { - return data.getLayoutDescriptors().findDescriptorByClass(fqcn); - } - - return null; - } - - /** Create a wizard for this refactoring */ - abstract VisualRefactoringWizard createWizard(); - - public abstract static class VisualRefactoringDescriptor extends RefactoringDescriptor { - private final Map<String, String> mArguments; - - public VisualRefactoringDescriptor( - String id, String project, String description, String comment, - Map<String, String> arguments) { - super(id, project, description, comment, STRUCTURAL_CHANGE | MULTI_CHANGE); - mArguments = arguments; - } - - public Map<String, String> getArguments() { - return mArguments; - } - - protected abstract Refactoring createRefactoring(Map<String, String> args); - - @Override - public Refactoring createRefactoring(RefactoringStatus status) throws CoreException { - try { - return createRefactoring(mArguments); - } catch (NullPointerException e) { - status.addFatalError("Failed to recreate refactoring from descriptor"); - return null; - } - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringAction.java deleted file mode 100644 index f1cc988d7..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringAction.java +++ /dev/null @@ -1,170 +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 com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.text.ITextSelection; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ITreeSelection; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IEditorSite; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.IWorkbenchWindowActionDelegate; -import org.eclipse.ui.part.FileEditorInput; - -abstract class VisualRefactoringAction implements IWorkbenchWindowActionDelegate { - protected IWorkbenchWindow mWindow; - protected ITextSelection mTextSelection; - protected ITreeSelection mTreeSelection; - protected LayoutEditorDelegate mDelegate; - protected IFile mFile; - - /** - * Keep track of the current workbench window. - */ - @Override - public void init(IWorkbenchWindow window) { - mWindow = window; - } - - @Override - public void dispose() { - } - - /** - * Examine the selection to determine if the action should be enabled or not. - * <p/> - * Keep a link to the relevant selection structure - */ - @Override - public void selectionChanged(IAction action, ISelection selection) { - // Look for selections in XML and in the layout UI editor - - // Note, two kinds of selections are returned here: - // ITextSelection on a Java source window - // IStructuredSelection in the outline or navigator - // This simply deals with the refactoring based on a non-empty selection. - // At that point, just enable the action and later decide if it's valid when it actually - // runs since we don't have access to the AST yet. - - mTextSelection = null; - mTreeSelection = null; - mFile = null; - - IEditorPart editor = null; - - if (selection instanceof ITextSelection) { - mTextSelection = (ITextSelection) selection; - editor = AdtUtils.getActiveEditor(); - mFile = getSelectedFile(editor); - } else if (selection instanceof ITreeSelection) { - Object firstElement = ((ITreeSelection)selection).getFirstElement(); - if (firstElement instanceof CanvasViewInfo) { - mTreeSelection = (ITreeSelection) selection; - editor = AdtUtils.getActiveEditor(); - mFile = getSelectedFile(editor); - } - } - - mDelegate = LayoutEditorDelegate.fromEditor(editor); - - action.setEnabled((mTextSelection != null || mTreeSelection != null) - && mFile != null && mDelegate != null); - } - - /** - * Create a new instance of our refactoring and a wizard to configure it. - */ - @Override - public abstract void run(IAction action); - - /** - * 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. - * <p/> - * At that point we do not try to analyze if the selection nor the file is suitable - * for the refactoring. This check is performed when the refactoring is invoked since - * it can then produce meaningful error messages as needed. - */ - private IFile getSelectedFile(IEditorPart editor) { - if (editor != null) { - IEditorInput input = editor.getEditorInput(); - - if (input instanceof FileEditorInput) { - FileEditorInput fi = (FileEditorInput) input; - IFile file = fi.getFile(); - if (file.exists()) { - IProject proj = file.getProject(); - try { - if (proj != null && proj.hasNature(AdtConstants.NATURE_DEFAULT)) { - return file; - } - } catch (CoreException e) { - // ignore - } - } - } - } - - return null; - } - - public static IAction create(String title, LayoutEditorDelegate editorDelegate, - Class<? extends VisualRefactoringAction> clz) { - return new ActionWrapper(title, editorDelegate, clz); - } - - private static class ActionWrapper extends Action { - private Class<? extends VisualRefactoringAction> mClass; - private LayoutEditorDelegate mEditorDelegate; - - ActionWrapper(String title, LayoutEditorDelegate editorDelegate, - Class<? extends VisualRefactoringAction> clz) { - super(title); - mEditorDelegate = editorDelegate; - mClass = clz; - } - - @Override - public void run() { - VisualRefactoringAction action; - try { - action = mClass.newInstance(); - } catch (Exception e) { - AdtPlugin.log(e, null); - return; - } - IEditorSite site = mEditorDelegate.getEditor().getEditorSite(); - action.init(site.getWorkbenchWindow()); - ISelection selection = site.getSelectionProvider().getSelection(); - action.selectionChanged(ActionWrapper.this, selection); - if (isEnabled()) { - action.run(ActionWrapper.this); - } - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringWizard.java deleted file mode 100644 index c103e47dc..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/VisualRefactoringWizard.java +++ /dev/null @@ -1,76 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -import org.eclipse.ltk.core.refactoring.Refactoring; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; - -public abstract class VisualRefactoringWizard extends RefactoringWizard { - protected final LayoutEditorDelegate mDelegate; - - public VisualRefactoringWizard(Refactoring refactoring, LayoutEditorDelegate editor) { - super(refactoring, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE); - mDelegate = editor; - } - - @Override - public boolean performFinish() { - mDelegate.getEditor().setIgnoreXmlUpdate(true); - try { - return super.performFinish(); - } finally { - mDelegate.getEditor().setIgnoreXmlUpdate(false); - mDelegate.refreshXmlModel(); - } - } - - protected abstract static class VisualRefactoringInputPage extends UserInputWizardPage { - public VisualRefactoringInputPage(String name) { - super(name); - } - - /** - * Listener which can be attached on any widget in the wizard page to force - * modifications of the associated widget to validate the page again - */ - protected ModifyListener mModifyValidateListener = new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - validatePage(); - } - }; - - /** - * Listener which can be attached on any widget in the wizard page to force - * selection changes of the associated widget to validate the page again - */ - protected SelectionAdapter mSelectionValidateListener = new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - validatePage(); - } - }; - - protected abstract boolean validatePage(); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInAction.java deleted file mode 100644 index 1cd66596b..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInAction.java +++ /dev/null @@ -1,47 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; - -import org.eclipse.jface.action.IAction; -import org.eclipse.ltk.ui.refactoring.RefactoringWizard; -import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; - -/** - * Action executed when the "Wrap In" menu item is invoked. - */ -public class WrapInAction extends VisualRefactoringAction { - @Override - public void run(IAction action) { - if ((mTextSelection != null || mTreeSelection != null) && mFile != null) { - WrapInRefactoring ref = new WrapInRefactoring(mFile, mDelegate, - mTextSelection, mTreeSelection); - RefactoringWizard wizard = new WrapInWizard(ref, mDelegate); - RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); - try { - op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); - } catch (InterruptedException e) { - // Interrupted. Pass. - } - } - } - - public static IAction create(LayoutEditorDelegate editorDelegate) { - return create("Wrap in Container...", editorDelegate, WrapInAction.class); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInContribution.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInContribution.java deleted file mode 100644 index 61d7987d7..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInContribution.java +++ /dev/null @@ -1,40 +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 org.eclipse.ltk.core.refactoring.RefactoringContribution; -import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; - -import java.util.Map; - -public class WrapInContribution extends RefactoringContribution { - - @SuppressWarnings("unchecked") - @Override - public RefactoringDescriptor createDescriptor(String id, String project, String description, - String comment, Map arguments, int flags) throws IllegalArgumentException { - return new WrapInRefactoring.Descriptor(project, description, comment, arguments); - } - - @SuppressWarnings("unchecked") - @Override - public Map retrieveArgumentMap(RefactoringDescriptor descriptor) { - if (descriptor instanceof WrapInRefactoring.Descriptor) { - return ((WrapInRefactoring.Descriptor) descriptor).getArguments(); - } - return super.retrieveArgumentMap(descriptor); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoring.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoring.java deleted file mode 100644 index 07b00b8da..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInRefactoring.java +++ /dev/null @@ -1,439 +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_PREFIX; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ANDROID_WIDGET_PREFIX; -import static com.android.SdkConstants.ATTR_ID; -import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; -import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; -import static com.android.SdkConstants.EXT_XML; -import static com.android.SdkConstants.VALUE_FILL_PARENT; -import static com.android.SdkConstants.VALUE_MATCH_PARENT; -import static com.android.SdkConstants.VALUE_WRAP_CONTENT; - -import com.android.annotations.NonNull; -import com.android.annotations.VisibleForTesting; -import com.android.ide.common.xml.XmlFormatStyle; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -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.DeleteEdit; -import org.eclipse.text.edits.InsertEdit; -import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.text.edits.TextEdit; -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.w3c.dom.Attr; -import org.w3c.dom.Element; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Inserts a new layout surrounding the current selection, migrates namespace - * attributes (if wrapping the root node), and optionally migrates layout - * attributes and updates references elsewhere. - */ -@SuppressWarnings("restriction") // XML model -public class WrapInRefactoring extends VisualRefactoring { - private static final String KEY_ID = "name"; //$NON-NLS-1$ - private static final String KEY_TYPE = "type"; //$NON-NLS-1$ - - private String mId; - private String mTypeFqcn; - private String mInitializedAttributes; - - /** - * This constructor is solely used by {@link Descriptor}, - * to replay a previous refactoring. - * @param arguments argument map created by #createArgumentMap. - */ - WrapInRefactoring(Map<String, String> arguments) { - super(arguments); - mId = arguments.get(KEY_ID); - mTypeFqcn = arguments.get(KEY_TYPE); - } - - public WrapInRefactoring( - IFile file, - LayoutEditorDelegate delegate, - ITextSelection selection, - ITreeSelection treeSelection) { - super(file, delegate, selection, treeSelection); - } - - @VisibleForTesting - WrapInRefactoring(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 wrap"); - return status; - } - - // Make sure the selection is contiguous - if (mTreeSelection != null) { - // TODO - don't do this if we based the selection on text. In this case, - // make sure we're -balanced-. - - List<CanvasViewInfo> infos = getSelectedViewInfos(); - if (!validateNotEmpty(infos, status)) { - return status; - } - - // Enforce that the selection is -contiguous- - if (!validateContiguous(infos, status)) { - return status; - } - } - - // Ensures that we have a valid DOM model: - if (mElements.size() == 0) { - status.addFatalError("Nothing to wrap"); - 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_TYPE, mTypeFqcn); - args.put(KEY_ID, mId); - - return args; - } - - @Override - public String getName() { - return "Wrap in Container"; - } - - void setId(String id) { - mId = id; - } - - void setType(String typeFqcn) { - mTypeFqcn = typeFqcn; - } - - void setInitializedAttributes(String initializedAttributes) { - mInitializedAttributes = initializedAttributes; - } - - @Override - protected @NonNull List<Change> computeChanges(IProgressMonitor monitor) { - // (1) Insert the new container in front of the beginning of the - // first wrapped view - // (2) If the container is the new root, transfer namespace declarations - // to it - // (3) Insert the closing tag of the new container at the end of the - // last wrapped view - // (4) Reindent the wrapped views - // (5) If the user requested it, update all layout references to the - // wrapped views with the new container? - // For that matter, does RelativeLayout even require it? Probably not, - // it can point inside the current layout... - - // Add indent to all lines between mSelectionStart and mEnd - // TODO: Figure out the indentation amount? - // For now, use 4 spaces - String indentUnit = " "; //$NON-NLS-1$ - boolean separateAttributes = true; - IStructuredDocument document = mDelegate.getEditor().getStructuredDocument(); - String startIndent = AndroidXmlEditor.getIndentAtOffset(document, mSelectionStart); - - String viewClass = getViewClass(mTypeFqcn); - String androidNsPrefix = getAndroidNamespacePrefix(); - - - IFile file = mDelegate.getEditor().getInputFile(); - List<Change> changes = new ArrayList<Change>(); - if (file == null) { - return changes; - } - TextFileChange change = new TextFileChange(file.getName(), file); - MultiTextEdit rootEdit = new MultiTextEdit(); - change.setTextType(EXT_XML); - - String id = ensureNewId(mId); - - // Update any layout references to the old id with the new id - if (id != null) { - String rootId = getRootId(); - IStructuredModel model = mDelegate.getEditor().getModelForRead(); - try { - IStructuredDocument doc = model.getStructuredDocument(); - if (doc != null) { - List<TextEdit> replaceIds = replaceIds(androidNsPrefix, - doc, mSelectionStart, mSelectionEnd, rootId, id); - for (TextEdit edit : replaceIds) { - rootEdit.addChild(edit); - } - } - } finally { - model.releaseFromRead(); - } - } - - // Insert namespace elements? - StringBuilder namespace = null; - List<DeleteEdit> deletions = new ArrayList<DeleteEdit>(); - Element primary = getPrimaryElement(); - if (primary != null && getDomDocument().getDocumentElement() == primary) { - namespace = new StringBuilder(); - - List<Attr> declarations = findNamespaceAttributes(primary); - for (Attr attribute : declarations) { - if (attribute instanceof IndexedRegion) { - // Delete the namespace declaration in the node which is no longer the root - IndexedRegion region = (IndexedRegion) attribute; - int startOffset = region.getStartOffset(); - int endOffset = region.getEndOffset(); - String text = getText(startOffset, endOffset); - DeleteEdit deletion = new DeleteEdit(startOffset, endOffset - startOffset); - deletions.add(deletion); - rootEdit.addChild(deletion); - text = text.trim(); - - // Insert the namespace declaration in the new root - if (separateAttributes) { - namespace.append('\n').append(startIndent).append(indentUnit); - } else { - namespace.append(' '); - } - namespace.append(text); - } - } - } - - // Insert begin tag: <type ...> - StringBuilder sb = new StringBuilder(); - sb.append('<'); - sb.append(viewClass); - - if (namespace != null) { - sb.append(namespace); - } - - // Set the ID if any - if (id != null) { - if (separateAttributes) { - sb.append('\n').append(startIndent).append(indentUnit); - } else { - sb.append(' '); - } - sb.append(androidNsPrefix).append(':'); - sb.append(ATTR_ID).append('=').append('"').append(id).append('"'); - } - - // If any of the elements are fill/match parent, use that instead - String width = VALUE_WRAP_CONTENT; - String height = VALUE_WRAP_CONTENT; - - for (Element element : getElements()) { - String oldWidth = element.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); - String oldHeight = element.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT); - - if (VALUE_MATCH_PARENT.equals(oldWidth) || VALUE_FILL_PARENT.equals(oldWidth)) { - width = oldWidth; - } - if (VALUE_MATCH_PARENT.equals(oldHeight) || VALUE_FILL_PARENT.equals(oldHeight)) { - height = oldHeight; - } - } - - // Add in width/height. - if (separateAttributes) { - sb.append('\n').append(startIndent).append(indentUnit); - } else { - sb.append(' '); - } - sb.append(androidNsPrefix).append(':'); - sb.append(ATTR_LAYOUT_WIDTH).append('=').append('"').append(width).append('"'); - - if (separateAttributes) { - sb.append('\n').append(startIndent).append(indentUnit); - } else { - sb.append(' '); - } - sb.append(androidNsPrefix).append(':'); - sb.append(ATTR_LAYOUT_HEIGHT).append('=').append('"').append(height).append('"'); - - if (mInitializedAttributes != null && mInitializedAttributes.length() > 0) { - for (String s : mInitializedAttributes.split(",")) { //$NON-NLS-1$ - sb.append(' '); - String[] nameValue = s.split("="); //$NON-NLS-1$ - String name = nameValue[0]; - String value = nameValue[1]; - if (name.startsWith(ANDROID_NS_NAME_PREFIX)) { - name = name.substring(ANDROID_NS_NAME_PREFIX.length()); - sb.append(androidNsPrefix).append(':'); - } - sb.append(name).append('=').append('"').append(value).append('"'); - } - } - - // Transfer layout_ attributes (other than width and height) - if (primary != null) { - List<Attr> layoutAttributes = findLayoutAttributes(primary); - for (Attr attribute : layoutAttributes) { - String name = attribute.getLocalName(); - if ((name.equals(ATTR_LAYOUT_WIDTH) || name.equals(ATTR_LAYOUT_HEIGHT)) - && ANDROID_URI.equals(attribute.getNamespaceURI())) { - // Already handled specially - continue; - } - - if (attribute instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) attribute; - int startOffset = region.getStartOffset(); - int endOffset = region.getEndOffset(); - String text = getText(startOffset, endOffset); - DeleteEdit deletion = new DeleteEdit(startOffset, endOffset - startOffset); - rootEdit.addChild(deletion); - deletions.add(deletion); - - if (separateAttributes) { - sb.append('\n').append(startIndent).append(indentUnit); - } else { - sb.append(' '); - } - sb.append(text.trim()); - } - } - } - - // Finish open tag: - sb.append('>'); - sb.append('\n').append(startIndent).append(indentUnit); - - InsertEdit beginEdit = new InsertEdit(mSelectionStart, sb.toString()); - rootEdit.addChild(beginEdit); - - String nested = getText(mSelectionStart, mSelectionEnd); - int index = 0; - while (index != -1) { - index = nested.indexOf('\n', index); - if (index != -1) { - index++; - InsertEdit newline = new InsertEdit(mSelectionStart + index, indentUnit); - // Some of the deleted namespaces may have had newlines - be careful - // not to overlap edits - boolean covered = false; - for (DeleteEdit deletion : deletions) { - if (deletion.covers(newline)) { - covered = true; - break; - } - } - if (!covered) { - rootEdit.addChild(newline); - } - } - } - - // Insert end tag: </type> - sb.setLength(0); - sb.append('\n').append(startIndent); - sb.append('<').append('/').append(viewClass).append('>'); - InsertEdit endEdit = new InsertEdit(mSelectionEnd, sb.toString()); - rootEdit.addChild(endEdit); - - if (AdtPrefs.getPrefs().getFormatGuiXml()) { - MultiTextEdit formatted = reformat(rootEdit, XmlFormatStyle.LAYOUT); - if (formatted != null) { - rootEdit = formatted; - } - } - - change.setEdit(rootEdit); - changes.add(change); - return changes; - } - - String getOldType() { - Element primary = getPrimaryElement(); - if (primary != null) { - String oldType = primary.getTagName(); - if (oldType.indexOf('.') == -1) { - oldType = ANDROID_WIDGET_PREFIX + oldType; - } - return oldType; - } - - return null; - } - - @Override - VisualRefactoringWizard createWizard() { - return new WrapInWizard(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.wrapin", //$NON-NLS-1$ - project, description, comment, arguments); - } - - @Override - protected Refactoring createRefactoring(Map<String, String> args) { - return new WrapInRefactoring(args); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInWizard.java deleted file mode 100644 index 2e06a3bbd..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/WrapInWizard.java +++ /dev/null @@ -1,268 +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.FQCN_GESTURE_OVERLAY_VIEW; -import static com.android.SdkConstants.FQCN_LINEAR_LAYOUT; -import static com.android.SdkConstants.FQCN_RADIO_BUTTON; -import static com.android.SdkConstants.GESTURE_OVERLAY_VIEW; -import static com.android.SdkConstants.RADIO_GROUP; -import static com.android.SdkConstants.VIEW_INCLUDE; - -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CustomViewFinder; -import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository; -import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.resources.ResourceType; -import com.android.sdklib.IAndroidTarget; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IProject; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -public class WrapInWizard extends VisualRefactoringWizard { - private static final String SEPARATOR_LABEL = - "----------------------------------------"; //$NON-NLS-1$ - - public WrapInWizard(WrapInRefactoring ref, LayoutEditorDelegate editor) { - super(ref, editor); - setDefaultPageTitle("Wrap in Container"); - } - - @Override - protected void addUserInputPages() { - WrapInRefactoring ref = (WrapInRefactoring) getRefactoring(); - String oldType = ref.getOldType(); - addPage(new InputPage(mDelegate.getEditor().getProject(), oldType)); - } - - /** Wizard page which inputs parameters for the {@link WrapInRefactoring} operation */ - private static class InputPage extends VisualRefactoringInputPage { - private final IProject mProject; - private final String mOldType; - private Text mIdText; - private Combo mTypeCombo; - private List<Pair<String, ViewElementDescriptor>> mClassNames; - - public InputPage(IProject project, String oldType) { - super("WrapInInputPage"); //$NON-NLS-1$ - mProject = project; - mOldType = oldType; - } - - @Override - public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - - Label typeLabel = new Label(composite, SWT.NONE); - typeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - typeLabel.setText("Type of Container:"); - - mTypeCombo = new Combo(composite, SWT.READ_ONLY); - mTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mTypeCombo.addSelectionListener(mSelectionValidateListener); - - Label idLabel = new Label(composite, SWT.NONE); - idLabel.setText("New Layout Id:"); - idLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - - mIdText = new Text(composite, SWT.BORDER); - mIdText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mIdText.addModifyListener(mModifyValidateListener); - - Set<String> exclude = Collections.singleton(VIEW_INCLUDE); - mClassNames = addLayouts(mProject, mOldType, mTypeCombo, exclude, true); - mTypeCombo.select(0); - - setControl(composite); - validatePage(); - - mTypeCombo.setFocus(); - } - - @Override - protected boolean validatePage() { - boolean ok = true; - - String id = mIdText.getText().trim(); - - if (id.length() == 0) { - setErrorMessage("ID required"); - ok = false; - } else { - // ...but if you do, it has to be valid! - ResourceNameValidator validator = ResourceNameValidator.create(false, mProject, - ResourceType.ID); - String message = validator.isValid(id); - if (message != null) { - setErrorMessage(message); - ok = false; - } - } - - int selectionIndex = mTypeCombo.getSelectionIndex(); - String type = selectionIndex != -1 ? mClassNames.get(selectionIndex).getFirst() : null; - if (type == null) { - setErrorMessage("Select a container type"); - ok = false; // The user has chosen a separator - } - - if (ok) { - setErrorMessage(null); - - // Record state - WrapInRefactoring refactoring = - (WrapInRefactoring) getRefactoring(); - refactoring.setId(id); - refactoring.setType(type); - - ViewElementDescriptor descriptor = mClassNames.get(selectionIndex).getSecond(); - if (descriptor instanceof PaletteMetadataDescriptor) { - PaletteMetadataDescriptor paletteDescriptor = - (PaletteMetadataDescriptor) descriptor; - String initializedAttributes = paletteDescriptor.getInitializedAttributes(); - refactoring.setInitializedAttributes(initializedAttributes); - } else { - refactoring.setInitializedAttributes(null); - } - } - - setPageComplete(ok); - return ok; - } - } - - static List<Pair<String, ViewElementDescriptor>> addLayouts(IProject project, - String oldType, Combo combo, - Set<String> exclude, boolean addGestureOverlay) { - List<Pair<String, ViewElementDescriptor>> classNames = - new ArrayList<Pair<String, ViewElementDescriptor>>(); - - if (oldType != null && oldType.equals(FQCN_RADIO_BUTTON)) { - combo.add(RADIO_GROUP); - // NOT a fully qualified name since android widgets do not include the package - classNames.add(Pair.of(RADIO_GROUP, (ViewElementDescriptor) null)); - - combo.add(SEPARATOR_LABEL); - classNames.add(Pair.<String,ViewElementDescriptor>of(null, null)); - } - - Pair<List<String>,List<String>> result = CustomViewFinder.findViews(project, true); - List<String> customViews = result.getFirst(); - List<String> thirdPartyViews = result.getSecond(); - if (customViews.size() > 0) { - for (String view : customViews) { - combo.add(view); - classNames.add(Pair.of(view, (ViewElementDescriptor) null)); - } - combo.add(SEPARATOR_LABEL); - classNames.add(Pair.<String,ViewElementDescriptor>of(null, null)); - } - - // Populate type combo - Sdk currentSdk = Sdk.getCurrent(); - if (currentSdk != null) { - IAndroidTarget target = currentSdk.getTarget(project); - if (target != null) { - AndroidTargetData targetData = currentSdk.getTargetData(target); - if (targetData != null) { - ViewMetadataRepository repository = ViewMetadataRepository.get(); - List<Pair<String,List<ViewElementDescriptor>>> entries = - repository.getPaletteEntries(targetData, false, true); - // Find the layout category - it contains LinearLayout - List<ViewElementDescriptor> layoutDescriptors = null; - - search: for (Pair<String,List<ViewElementDescriptor>> pair : entries) { - List<ViewElementDescriptor> list = pair.getSecond(); - for (ViewElementDescriptor d : list) { - if (d.getFullClassName().equals(FQCN_LINEAR_LAYOUT)) { - // Found - use this list - layoutDescriptors = list; - break search; - } - } - } - if (layoutDescriptors != null) { - for (ViewElementDescriptor d : layoutDescriptors) { - String className = d.getFullClassName(); - if (exclude == null || !exclude.contains(className)) { - combo.add(d.getUiName()); - classNames.add(Pair.of(className, d)); - } - } - - // SWT does not support separators in combo boxes - combo.add(SEPARATOR_LABEL); - classNames.add(null); - - if (thirdPartyViews.size() > 0) { - for (String view : thirdPartyViews) { - combo.add(view); - classNames.add(Pair.of(view, (ViewElementDescriptor) null)); - } - combo.add(SEPARATOR_LABEL); - classNames.add(null); - } - - if (addGestureOverlay) { - combo.add(GESTURE_OVERLAY_VIEW); - classNames.add(Pair.<String, ViewElementDescriptor> of( - FQCN_GESTURE_OVERLAY_VIEW, null)); - - combo.add(SEPARATOR_LABEL); - classNames.add(Pair.<String,ViewElementDescriptor>of(null, null)); - } - } - - // Now add ALL known layout descriptors in case the user has - // a special case - layoutDescriptors = - targetData.getLayoutDescriptors().getLayoutDescriptors(); - - for (ViewElementDescriptor d : layoutDescriptors) { - String className = d.getFullClassName(); - if (exclude == null || !exclude.contains(className)) { - combo.add(d.getUiName()); - classNames.add(Pair.of(className, d)); - } - } - } - } - } else { - combo.add("SDK not initialized"); - classNames.add(Pair.<String,ViewElementDescriptor>of(null, null)); - } - - return classNames; - } -} |