aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java1
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java1
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java3
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java5
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java55
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java79
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java141
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java5
11 files changed, 277 insertions, 21 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java
index f3582ec..c778271 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java
@@ -16,8 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.INode;
import com.android.ide.common.api.IDragElement.IDragAttribute;
+import com.android.ide.common.api.INode;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
@@ -226,6 +226,7 @@ public class ClipboardSupport {
List<INode> children = entry.getValue();
assert children != null && children.size() > 0;
rulesEngine.callOnRemovingChildren(editor, parent, children);
+ parent.applyPendingChanges();
}
for (SelectionItem cs : selection) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
index c8fab84..45e0234 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
@@ -361,6 +361,7 @@ public class MoveGesture extends DropGesture {
mFeedback,
new Point(canvasPoint.x, canvasPoint.y),
insertType);
+ mTargetNode.applyPendingChanges();
// Clean up drag if applicable
if (event.detail == DND.DROP_MOVE) {
GlobalCanvasDragInfo.getInstance().removeSource();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java
index 5f516d5..cf09160 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java
@@ -130,6 +130,7 @@ import java.util.Set;
Object sourceCanvas = GlobalCanvasDragInfo.getInstance().getSourceCanvas();
boolean createNew = event.detail == DND.DROP_COPY || sourceCanvas != canvas;
BaseLayoutRule.insertAt(targetNode, elements, createNew, indexFinal);
+ targetNode.applyPendingChanges();
// Clean up drag if applicable
if (event.detail == DND.DROP_MOVE) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
index cafcc13..7c0a365 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
@@ -838,6 +838,7 @@ public class OutlinePage extends ContentOutlinePage
canvas.getRulesEngine().setInsertType(insertType);
int index = target.getSecond();
BaseLayoutRule.insertAt(targetNode, elements, false, index);
+ targetNode.applyPendingChanges();
canvas.getClipboardSupport().deleteSelection("Remove", dragSelection);
}
});
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java
index fef1a56..4233208 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java
@@ -16,7 +16,6 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
-import com.android.ide.common.api.IDragElement.IDragAttribute;
import com.android.ide.common.api.INode.IAttribute;
import java.util.regex.Matcher;
@@ -33,7 +32,7 @@ import java.util.regex.Pattern;
* For a more detailed explanation of the purpose of this class,
* please see {@link SimpleXmlTransfer}.
*/
-public class SimpleAttribute implements IDragAttribute, IAttribute {
+public class SimpleAttribute implements IAttribute {
private final String mName;
private final String mValue;
private final String mUri;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java
index 20a8433..cf42077 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java
@@ -20,6 +20,7 @@ import com.android.ide.common.api.IDragElement;
import com.android.ide.common.api.Rect;
import java.util.ArrayList;
+import java.util.List;
/**
* Represents an XML element with a name, attributes and inner elements.
@@ -39,8 +40,8 @@ public class SimpleElement implements IDragElement {
private final String mParentFqcn;
private final Rect mBounds;
private final Rect mParentBounds;
- private final ArrayList<IDragAttribute> mAttributes = new ArrayList<IDragAttribute>();
- private final ArrayList<IDragElement> mElements = new ArrayList<IDragElement>();
+ private final List<IDragAttribute> mAttributes = new ArrayList<IDragAttribute>();
+ private final List<IDragElement> mElements = new ArrayList<IDragElement>();
private IDragAttribute[] mCachedAttributes = null;
private IDragElement[] mCachedElements = null;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
index c4c050a..b27954e 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
@@ -43,7 +43,9 @@ import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
*
@@ -53,6 +55,8 @@ public class NodeProxy implements INode {
private final UiViewElementNode mNode;
private final Rect mBounds;
private final NodeFactory mFactory;
+ /** Map from URI to Map(key=>value) (where no namespace uses "" as a key) */
+ private Map<String, Map<String, String>> mPendingAttributes;
/**
* Creates a new {@link INode} that wraps an {@link UiViewElementNode} that is
@@ -208,6 +212,7 @@ public class NodeProxy implements INode {
// Finally execute the closure that will act on the XML
c.handle(NodeProxy.this);
+ applyPendingChanges();
}
});
}
@@ -295,9 +300,24 @@ public class NodeProxy implements INode {
public boolean setAttribute(String uri, String name, String value) {
checkEditOK();
-
UiAttributeNode attr = mNode.setAttributeValue(name, uri, value, true /* override */);
- mNode.commitDirtyAttributesToXml();
+
+ if (uri == null) {
+ uri = ""; //$NON-NLS-1$
+ }
+
+ Map<String, String> map = null;
+ if (mPendingAttributes == null) {
+ // Small initial size: we don't expect many different namespaces
+ mPendingAttributes = new HashMap<String, Map<String, String>>(3);
+ } else {
+ map = mPendingAttributes.get(uri);
+ }
+ if (map == null) {
+ map = new HashMap<String, String>();
+ mPendingAttributes.put(uri, map);
+ }
+ map.put(name, value);
return attr != null;
}
@@ -309,6 +329,16 @@ public class NodeProxy implements INode {
return null;
}
+ if (mPendingAttributes != null) {
+ Map<String, String> map = mPendingAttributes.get(uri == null ? "" : uri); //$NON-NLS-1$
+ if (map != null) {
+ String value = map.get(attrName);
+ if (value != null) {
+ return value;
+ }
+ }
+ }
+
if (uiNode.getXmlNode() != null) {
Node xmlNode = uiNode.getXmlNode();
if (xmlNode != null) {
@@ -413,4 +443,25 @@ public class NodeProxy implements INode {
);
}
+ /**
+ * If there are any pending changes in these nodes, apply them now
+ *
+ * @return true if any modifications were made
+ */
+ public boolean applyPendingChanges() {
+ boolean modified = false;
+
+ // Flush all pending attributes
+ if (mPendingAttributes != null) {
+ mNode.commitDirtyAttributesToXml();
+ modified = true;
+ mPendingAttributes = null;
+
+ }
+ for (INode child : getChildren()) {
+ modified |= ((NodeProxy) child).applyPendingChanges();
+ }
+
+ return modified;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
index deb566e..5b2d430 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
@@ -555,6 +555,10 @@ public class RulesEngine {
if (childRule != null) {
childRule.onCreate(newNode, parentNode, insertType);
}
+
+ if (parentNode != null) {
+ ((NodeProxy) parentNode).applyPendingChanges();
+ }
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
index 9c822f6..94cea34 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
@@ -16,6 +16,12 @@
package com.android.ide.eclipse.adt.internal.editors.uimodel;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_STYLE;
+
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
@@ -23,6 +29,8 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.IManagedForm;
import org.w3c.dom.Node;
+import java.util.Comparator;
+
/**
* Represents an XML attribute that can be modified by the XML editor's user interface.
* <p/>
@@ -32,7 +40,7 @@ import org.w3c.dom.Node;
* This is an abstract class. Derived classes must implement the creation of the UI
* and manage its synchronization with the XML.
*/
-public abstract class UiAttributeNode {
+public abstract class UiAttributeNode implements Comparable<UiAttributeNode> {
private AttributeDescriptor mDescriptor;
private UiElementNode mUiParent;
@@ -73,12 +81,14 @@ public abstract class UiAttributeNode {
* <p/>
* Subclasses should set the to true as a result of user interaction with the widgets in
* the section and then should set to false when the commit() method completed.
+ *
+ * @param isDirty the new value to set the dirty-flag to
*/
public void setDirty(boolean isDirty) {
- boolean old_value = mIsDirty;
+ boolean wasDirty = mIsDirty;
mIsDirty = isDirty;
// TODO: for unknown attributes, getParent() != null && getParent().getEditor() != null
- if (old_value != isDirty) {
+ if (wasDirty != isDirty) {
AndroidXmlEditor editor = getUiParent().getEditor();
if (editor != null) {
editor.editorDirtyStateChanged();
@@ -141,9 +151,9 @@ public abstract class UiAttributeNode {
* so it will call this to refresh the attribute anyway. It's up to the
* UI implementation to minimize refreshes.
*
- * @param xml_attribute_node
+ * @param node the node to read the value from
*/
- public abstract void updateValue(Node xml_attribute_node);
+ public abstract void updateValue(Node node);
/**
* Called by the user interface when the editor is saved or its state changed
@@ -160,4 +170,63 @@ public abstract class UiAttributeNode {
* </ul>
*/
public abstract void commit();
+
+ // ---- Implements Comparable ----
+ public int compareTo(UiAttributeNode o) {
+ return compareAttributes(mDescriptor.getXmlLocalName(), o.mDescriptor.getXmlLocalName());
+ }
+
+ /**
+ * Returns {@link Comparator} values for ordering attributes in the following
+ * order:
+ * <ul>
+ * <li> id
+ * <li> style
+ * <li> layout_width
+ * <li> layout_height
+ * <li> other layout params, sorted alphabetically
+ * <li> other attributes, sorted alphabetically
+ * </ul>
+ *
+ * @param name1 the first attribute name to compare
+ * @param name2 the second attribute name to compare
+ * @return a negative number if name1 should be ordered before name2
+ */
+ public static int compareAttributes(String name1, String name2) {
+ int priority1 = getAttributePriority(name1);
+ int priority2 = getAttributePriority(name2);
+ if (priority1 != priority2) {
+ return priority1 - priority2;
+ }
+
+ // Sort remaining attributes alphabetically
+ return name1.compareTo(name2);
+ }
+
+ /** Returns a sorting priority for the given attribute name */
+ private static int getAttributePriority(String name) {
+ if (ATTR_ID.equals(name)) {
+ return 10;
+ }
+
+ if (ATTR_STYLE.equals(name)) {
+ return 20;
+ }
+
+ if (name.startsWith(ATTR_LAYOUT_PREFIX)) {
+ // Width and height are special cased because we (a) want width and height
+ // before the other layout attributes, and (b) we want width to sort before height
+ // even though it comes after it alphabetically.
+ if (name.equals(ATTR_LAYOUT_WIDTH)) {
+ return 30;
+ }
+ if (name.equals(ATTR_LAYOUT_HEIGHT)) {
+ return 40;
+ }
+
+ return 50;
+ }
+
+ return 60;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
index 3f7fadd..37273eb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
@@ -1045,6 +1045,8 @@ public class UiElementNode implements IPropertySource {
// Set all initial attributes in the XML node if they are not empty.
// Iterate on the descriptor list to get the desired order and then use the
// internal values, if any.
+ List<UiAttributeNode> addAttributes = new ArrayList<UiAttributeNode>();
+
for (AttributeDescriptor attrDesc : getAttributeDescriptors()) {
if (attrDesc instanceof XmlnsAttributeDescriptor) {
XmlnsAttributeDescriptor desc = (XmlnsAttributeDescriptor) attrDesc;
@@ -1055,10 +1057,31 @@ public class UiElementNode implements IPropertySource {
mXmlNode.getAttributes().setNamedItemNS(attr);
} else {
UiAttributeNode uiAttr = getInternalUiAttributes().get(attrDesc);
- commitAttributeToXml(uiAttr, uiAttr.getCurrentValue());
+
+ // Don't apply the attribute immediately, instead record this attribute
+ // such that we can gather all attributes and sort them first.
+ // This is necessary because the XML model will *append* all attributes
+ // so we want to add them in a particular order.
+ // (Note that we only have to worry about UiAttributeNodes with non null
+ // values, since this is a new node and we therefore don't need to attempt
+ // to remove existing attributes)
+ String value = uiAttr.getCurrentValue();
+ if (value != null && value.length() > 0) {
+ addAttributes.add(uiAttr);
+ }
}
}
+ // Sort and apply the attributes in order, because the Eclipse XML model will always
+ // append the XML attributes, so by inserting them in our desired order they will
+ // appear that way in the XML
+ Collections.sort(addAttributes);
+
+ for (UiAttributeNode node : addAttributes) {
+ commitAttributeToXml(node, node.getCurrentValue());
+ node.setDirty(false);
+ }
+
if (mUiParent != null) {
mUiParent.formatOnInsert(this);
}
@@ -1518,12 +1541,18 @@ public class UiElementNode implements IPropertySource {
if (doc != null) {
Attr attr;
if (attrNsUri != null && attrNsUri.length() > 0) {
- attr = doc.createAttributeNS(attrNsUri, attrLocalName);
- attr.setPrefix(lookupNamespacePrefix(element, attrNsUri));
- attrMap.setNamedItemNS(attr);
+ attr = (Attr) attrMap.getNamedItemNS(attrNsUri, attrLocalName);
+ if (attr == null) {
+ attr = doc.createAttributeNS(attrNsUri, attrLocalName);
+ attr.setPrefix(lookupNamespacePrefix(element, attrNsUri));
+ attrMap.setNamedItemNS(attr);
+ }
} else {
- attr = doc.createAttribute(attrLocalName);
- attrMap.setNamedItem(attr);
+ attr = (Attr) attrMap.getNamedItem(attrLocalName);
+ if (attr == null) {
+ attr = doc.createAttribute(attrLocalName);
+ attrMap.setNamedItem(attr);
+ }
}
attr.setValue(newValue);
return true;
@@ -1549,14 +1578,110 @@ public class UiElementNode implements IPropertySource {
* @return True if one or more values were actually modified or removed,
* false if nothing changed.
*/
+ @SuppressWarnings("null") // Eclipse is confused by the logic and gets it wrong
public boolean commitDirtyAttributesToXml() {
boolean result = false;
+ List<UiAttributeNode> dirtyAttributes = new ArrayList<UiAttributeNode>();
for (UiAttributeNode uiAttr : getAllUiAttributes()) {
if (uiAttr.isDirty()) {
- result |= commitAttributeToXml(uiAttr, uiAttr.getCurrentValue());
- uiAttr.setDirty(false);
+ String value = uiAttr.getCurrentValue();
+ if (value != null && value.length() > 0) {
+ // Defer the new attributes: set these last and in order
+ dirtyAttributes.add(uiAttr);
+ } else {
+ result |= commitAttributeToXml(uiAttr, value);
+ uiAttr.setDirty(false);
+ }
}
}
+ if (dirtyAttributes.size() > 0) {
+ result = true;
+
+ Collections.sort(dirtyAttributes);
+
+ // The Eclipse XML model will *always* append new attributes.
+ // Therefore, if any of the dirty attributes are new, they will appear
+ // after any existing, clean attributes on the element. To fix this,
+ // we need to first remove any of these attributes, then insert them
+ // back in the right order.
+ Node element = prepareCommit();
+ if (element == null) {
+ return result;
+ }
+
+ String firstName = dirtyAttributes.get(0).getDescriptor().getXmlLocalName();
+ NamedNodeMap attributes = ((Element) element).getAttributes();
+ List<Attr> move = new ArrayList<Attr>();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Attr attribute = (Attr) attributes.item(i);
+ if (UiAttributeNode.compareAttributes(attribute.getLocalName(), firstName) > 0) {
+ move.add(attribute);
+ }
+ }
+
+ for (Attr attribute : move) {
+ if (attribute.getNamespaceURI() != null) {
+ attributes.removeNamedItemNS(attribute.getNamespaceURI(),
+ attribute.getLocalName());
+ } else {
+ attributes.removeNamedItem(attribute.getName());
+ }
+ }
+
+ // Merge back the removed DOM attribute nodes and the new UI attribute nodes.
+ // In cases where the attribute DOM name and the UI attribute names equal,
+ // skip the DOM nodes and just apply the UI attributes.
+ int domAttributeIndex = 0;
+ int domAttributeIndexMax = move.size();
+ int uiAttributeIndex = 0;
+ int uiAttributeIndexMax = dirtyAttributes.size();
+
+ while (true) {
+ Attr domAttribute;
+ UiAttributeNode uiAttribute;
+
+ int compare;
+ if (uiAttributeIndex < uiAttributeIndexMax) {
+ if (domAttributeIndex < domAttributeIndexMax) {
+ domAttribute = move.get(domAttributeIndex);
+ uiAttribute = dirtyAttributes.get(uiAttributeIndex);
+
+ String domAttributeName = domAttribute.getLocalName();
+ String uiAttributeName = uiAttribute.getDescriptor().getXmlLocalName();
+ compare = UiAttributeNode.compareAttributes(domAttributeName,
+ uiAttributeName);
+ } else {
+ compare = 1;
+ uiAttribute = dirtyAttributes.get(uiAttributeIndex);
+ domAttribute = null;
+ }
+ } else if (domAttributeIndex < domAttributeIndexMax) {
+ compare = -1;
+ domAttribute = move.get(domAttributeIndex);
+ uiAttribute = null;
+ } else {
+ break;
+ }
+
+ if (compare < 0) {
+ if (domAttribute.getNamespaceURI() != null) {
+ attributes.setNamedItemNS(domAttribute);
+ } else {
+ attributes.setNamedItem(domAttribute);
+ }
+ domAttributeIndex++;
+ } else {
+ assert compare >= 0;
+ if (compare == 0) {
+ domAttributeIndex++;
+ }
+ commitAttributeToXml(uiAttribute, uiAttribute.getCurrentValue());
+ uiAttribute.setDirty(false);
+ uiAttributeIndex++;
+ }
+ }
+ }
+
return result;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java
index fc59ba8..7ff425f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java
@@ -17,6 +17,7 @@ package com.android.ide.common.layout;
import com.android.ide.common.api.IDragElement.IDragAttribute;
import com.android.ide.common.api.INode.IAttribute;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
/** Test/mock implementation of {@link IAttribute} and {@link IDragAttribute} */
public class TestAttribute implements IAttribute, IDragAttribute {
@@ -50,5 +51,7 @@ public class TestAttribute implements IAttribute, IDragAttribute {
return "TestAttribute [name=" + mName + ", uri=" + mUri + ", value=" + mValue + "]";
}
-
+ public int compareTo(IDragAttribute o) {
+ return UiAttributeNode.compareAttributes(mName, o.getName());
+ }
} \ No newline at end of file