Merge "SDK: wrap NPE in getAttributeNS when editing XML files."
diff --git a/.gitignore b/.gitignore
index 0a98c64..80b6f7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,12 +11,7 @@
 eclipse/final*Versions*.properties
 eclipse/plugins/com.android.ide.eclipse.*/@dot*
 eclipse/plugins/com.android.*/javaCompiler...args
-eclipse/plugins/com.android.ide.eclipse.adt/build.xml
-eclipse/plugins/com.android.ide.eclipse.adt.ndk/build.xml
-eclipse/plugins/com.android.ide.eclipse.adt.overlay/build.xml
-eclipse/plugins/com.android.ide.eclipse.ddms/build.xml
-eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/build.xml
-eclipse/plugins/com.android.ide.eclipse.traceview/build.xml
+eclipse/plugins/com.android.ide.eclipse.*/build.xml
 eclipse/features/com.android.ide.eclipse.*/build.xml
 eclipse/features/com.android.ide.eclipse.*/*.zip
 /eclipse/v[0-9-]*-[0-9]*
diff --git a/attribute_stats/src/Analyzer.java b/attribute_stats/src/Analyzer.java
index a6bbb4a..8da53ea 100644
--- a/attribute_stats/src/Analyzer.java
+++ b/attribute_stats/src/Analyzer.java
@@ -34,8 +34,11 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -59,6 +62,7 @@
 
     private List<File> mDirectories;
     private File mCurrentFile;
+    private boolean mListAdvanced;
 
     /** Map from view id to map from attribute to frequency count */
     private Map<String, Map<String, Usage>> mFrequencies =
@@ -74,9 +78,10 @@
     private int mLayoutFileCount;
     private File mXmlMetadataFile;
 
-    private Analyzer(List<File> directories, File xmlMetadataFile) {
+    private Analyzer(List<File> directories, File xmlMetadataFile, boolean listAdvanced) {
         mDirectories = directories;
         mXmlMetadataFile = xmlMetadataFile;
+        mListAdvanced = listAdvanced;
     }
 
     public static void main(String[] args) {
@@ -90,14 +95,21 @@
 
         File metadataFile = null;
         List<File> directories = new ArrayList<File>();
+        boolean listAdvanced = false;
         for (int i = 0, n = args.length; i < n; i++) {
             String arg = args[i];
 
+            if (arg.equals("--list")) {
+                // List ALL encountered attributes
+                listAdvanced = true;
+                continue;
+            }
+
             // The -metadata flag takes a pointer to an ADT extra-view-metadata.xml file
             // and attempts to insert topAttrs attributes into it (and saves it as same
             // file +.mod as an extension). This isn't listed on the usage flag because
             // it's pretty brittle and requires some manual fixups to the file afterwards.
-            if (arg.equals("-metadata")) {
+            if (arg.equals("--metadata")) {
                 i++;
                 File file = new File(args[i]);
                 if (!file.exists()) {
@@ -125,13 +137,18 @@
             directories.add(directory);
         }
 
-        new Analyzer(directories, metadataFile).analyze();
+        new Analyzer(directories, metadataFile, listAdvanced).analyze();
     }
 
     private void analyze() {
         for (File directory : mDirectories) {
             scanDirectory(directory);
         }
+
+        if (mListAdvanced) {
+            listAdvanced();
+        }
+
         printStatistics();
 
         if (mXmlMetadataFile != null) {
@@ -523,6 +540,69 @@
         System.out.println("Done - wrote " + output.getPath());
     }
 
+    //private File mPublicFile = new File(location, "data/res/values/public.xml");
+    private File mPublicFile = new File("/Volumes/AndroidWork/git/frameworks/base/core/res/res/values/public.xml");
+
+    private void listAdvanced() {
+        Set<String> keys = new HashSet<String>(1000);
+
+        // Merged usages across view types
+        Map<String, Usage> mergedUsages = new HashMap<String, Usage>(100);
+
+        for (Entry<String,Map<String,Usage>> entry : mFrequencies.entrySet()) {
+            String view = entry.getKey();
+            if (view.indexOf('.') != -1 && !view.startsWith("android.")) {
+                // Skip custom views etc
+                continue;
+            }
+            Map<String, Usage> map = entry.getValue();
+            for (Usage usage : map.values()) {
+//                if (usage.count == 1) {
+//                    System.out.println("Only found *one* usage of " + usage.attribute);
+//                }
+//                if (usage.count < 4) {
+//                    System.out.println("Only found " + usage.count + " usage of " + usage.attribute);
+//                }
+
+                String attribute = usage.attribute;
+                int index = attribute.indexOf(':');
+                if (index == -1 || attribute.startsWith("android:")) {
+                    Usage merged = mergedUsages.get(attribute);
+                    if (merged == null) {
+                        merged = new Usage(attribute);
+                        merged.count = usage.count;
+                        mergedUsages.put(attribute, merged);
+                    } else {
+                        merged.count += usage.count;
+                    }
+                }
+            }
+        }
+
+        for (Usage usage : mergedUsages.values())  {
+            String attribute = usage.attribute;
+            if (usage.count < 4) {
+                System.out.println("Only found " + usage.count + " usage of " + usage.attribute);
+                continue;
+            }
+            int index = attribute.indexOf(':');
+            if (index != -1) {
+                attribute = attribute.substring(index + 1); // +1: skip ':'
+            }
+            keys.add(attribute);
+        }
+
+        List<String> sorted = new ArrayList<String>(keys);
+        Collections.sort(sorted);
+        System.out.println("\nEncountered Attributes");
+        System.out.println("-----------------------------");
+        for (String attribute : sorted) {
+            System.out.println(attribute);
+        }
+
+        System.out.println();
+    }
+
     private static class Usage implements Comparable<Usage> {
         public String attribute;
         public int count;
@@ -539,6 +619,7 @@
             count++;
         }
 
+        @Override
         public int compareTo(Usage o) {
             // Sort by decreasing frequency, then sort alphabetically
             int frequencyDelta = o.count - count;
diff --git a/build/tools.atree b/build/tools.atree
index 54164a9..89401e2 100644
--- a/build/tools.atree
+++ b/build/tools.atree
@@ -121,9 +121,9 @@
 framework/jcommon-1.0.12.jar                                  tools/lib/jcommon-1.0.12.jar
 framework/jfreechart-1.0.9.jar                                tools/lib/jfreechart-1.0.9.jar
 framework/jfreechart-1.0.9-swt.jar                            tools/lib/jfreechart-1.0.9-swt.jar
-framework/org.eclipse.core.commands_3.4.0.I20080509-2000.jar  tools/lib/org.eclipse.core.commands_3.4.0.I20080509-2000.jar
-framework/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar tools/lib/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar
-framework/org.eclipse.jface_3.4.2.M20090107-0800.jar          tools/lib/org.eclipse.jface_3.4.2.M20090107-0800.jar
+framework/org.eclipse.core.commands_3.6.0.I20100512-1500.jar  tools/lib/org.eclipse.core.commands_3.6.0.I20100512-1500.jar
+framework/org.eclipse.equinox.common_3.6.0.v20100503.jar      tools/lib/org.eclipse.equinox.common_3.6.0.v20100503.jar
+framework/org.eclipse.jface_3.6.2.M20110210-1200.jar          tools/lib/org.eclipse.jface_3.6.2.M20110210-1200.jar
 framework/osgi.jar                                            tools/lib/osgi.jar
 framework/swing-worker-1.1.jar                                tools/lib/swing-worker-1.1.jar
 prebuilts/tools/common/asm-tools/asm-4.0.jar                  tools/lib/asm-4.0.jar
diff --git a/ddms/app/.classpath b/ddms/app/.classpath
index 6caf176..f8f8a9c 100644
--- a/ddms/app/.classpath
+++ b/ddms/app/.classpath
@@ -2,12 +2,15 @@
 <classpath>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_OSGI"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/SdkStatsService"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/>
 	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/osgi/osgi.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/ddms/app/Android.mk b/ddms/app/Android.mk
index d1e4a52..74d0f6f 100644
--- a/ddms/app/Android.mk
+++ b/ddms/app/Android.mk
@@ -18,9 +18,9 @@
 	ddmuilib \
 	swt \
 	swtmenubar \
-	org.eclipse.jface_3.4.2.M20090107-0800 \
-	org.eclipse.equinox.common_3.4.0.v20080421-2006 \
-	org.eclipse.core.commands_3.4.0.I20080509-2000
+	org.eclipse.jface_3.6.2.M20110210-1200 \
+	org.eclipse.equinox.common_3.6.0.v20100503 \
+	org.eclipse.core.commands_3.6.0.I20100512-1500
 LOCAL_MODULE := ddms
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/ddms/app/README b/ddms/app/README
index 42efb7b..0d9bbc4 100644
--- a/ddms/app/README
+++ b/ddms/app/README
@@ -45,7 +45,7 @@
 - Create a new user library named ANDROID_OSGI
 - Add the following JAR file:
 
-  - prebuilt/common/osgi/osgi.jar
+  - prebuilt/common/eclipse/org.eclipse.osgi_3.*.jar
 
 
 -------
diff --git a/ddms/app/etc/manifest.txt b/ddms/app/etc/manifest.txt
index 8c6ab23..3907767 100644
--- a/ddms/app/etc/manifest.txt
+++ b/ddms/app/etc/manifest.txt
@@ -1,3 +1,3 @@
 Main-Class: com.android.ddms.Main
-Class-Path: androidprefs.jar sdkstats.jar ddmlib.jar ddmuilib.jar swtmenubar.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar
+Class-Path: androidprefs.jar sdkstats.jar ddmlib.jar ddmuilib.jar swtmenubar.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar
 
diff --git a/ddms/libs/ddmuilib/.classpath b/ddms/libs/ddmuilib/.classpath
index 839e2be..027a79c 100644
--- a/ddms/libs/ddmuilib/.classpath
+++ b/ddms/libs/ddmuilib/.classpath
@@ -3,7 +3,12 @@
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_JFREECHART"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/jfreechart/jcommon-1.0.12.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/jfreechart/jfreechart-1.0.9-swt.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/jfreechart/jfreechart-1.0.9.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/ddms/libs/ddmuilib/Android.mk b/ddms/libs/ddmuilib/Android.mk
index d35b861..4b34500 100644
--- a/ddms/libs/ddmuilib/Android.mk
+++ b/ddms/libs/ddmuilib/Android.mk
@@ -15,9 +15,9 @@
 LOCAL_JAVA_LIBRARIES := \
 	ddmlib \
 	swt \
-	org.eclipse.jface_3.4.2.M20090107-0800 \
-	org.eclipse.equinox.common_3.4.0.v20080421-2006 \
-	org.eclipse.core.commands_3.4.0.I20080509-2000 \
+	org.eclipse.jface_3.6.2.M20110210-1200 \
+	org.eclipse.equinox.common_3.6.0.v20100503 \
+	org.eclipse.core.commands_3.6.0.I20100512-1500 \
 	jcommon-1.0.12 \
 	jfreechart-1.0.9 \
 	jfreechart-1.0.9-swt
diff --git a/ddms/libs/ddmuilib/etc/manifest.txt b/ddms/libs/ddmuilib/etc/manifest.txt
index 1db70de..b74fd14 100644
--- a/ddms/libs/ddmuilib/etc/manifest.txt
+++ b/ddms/libs/ddmuilib/etc/manifest.txt
@@ -1 +1 @@
-Class-Path: ddmlib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar
\ No newline at end of file
+Class-Path: ddmlib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar
diff --git a/ddms/libs/ddmuilib/tests/Android.mk b/ddms/libs/ddmuilib/tests/Android.mk
index dc187ec..6bbff34 100644
--- a/ddms/libs/ddmuilib/tests/Android.mk
+++ b/ddms/libs/ddmuilib/tests/Android.mk
@@ -26,9 +26,9 @@
 	ddmlib \
 	ddmuilib \
 	swt \
-	org.eclipse.jface_3.4.2.M20090107-0800 \
-	org.eclipse.equinox.common_3.4.0.v20080421-2006 \
-	org.eclipse.core.commands_3.4.0.I20080509-2000 \
+	org.eclipse.jface_3.6.2.M20110210-1200 \
+	org.eclipse.equinox.common_3.6.0.v20100503 \
+	org.eclipse.core.commands_3.6.0.I20100512-1500 \
 	junit
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt
index c1640c0..c4d9746 100644
--- a/eclipse/dictionary.txt
+++ b/eclipse/dictionary.txt
@@ -36,6 +36,7 @@
 carlo
 cf
 changeset
+charset
 checkbox
 checkboxes
 classloader
@@ -174,6 +175,7 @@
 namespace
 namespaces
 newfound
+nexus
 ninepatch
 nodpi
 num
@@ -205,6 +207,7 @@
 programmatic
 programmatically
 proguard
+propertysheet
 proxies
 proxy
 pulldown
@@ -268,6 +271,7 @@
 subclassing
 submenu
 subregion
+superclasses
 supertype
 symlinks
 syncs
@@ -278,6 +282,7 @@
 textfields
 thematically
 themed
+thumbnail
 timestamp
 tmp
 toolbar
diff --git a/eclipse/features/com.android.ide.eclipse.adt/.project b/eclipse/features/com.android.ide.eclipse.adt/.project
index beca599..513b14b 100644
--- a/eclipse/features/com.android.ide.eclipse.adt/.project
+++ b/eclipse/features/com.android.ide.eclipse.adt/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>adt-feature</name>
+	<name>feature-adt</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/features/com.android.ide.eclipse.ddms/.project b/eclipse/features/com.android.ide.eclipse.ddms/.project
index f80ff60..3a74819 100644
--- a/eclipse/features/com.android.ide.eclipse.ddms/.project
+++ b/eclipse/features/com.android.ide.eclipse.ddms/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>ddms-feature</name>
+	<name>feature-ddms</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/features/com.android.ide.eclipse.gldebugger/.project b/eclipse/features/com.android.ide.eclipse.gldebugger/.project
index 28a7928..067a8f4 100644
--- a/eclipse/features/com.android.ide.eclipse.gldebugger/.project
+++ b/eclipse/features/com.android.ide.eclipse.gldebugger/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>gldebugger-feature</name>
+	<name>feature-gldebugger</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/features/com.android.ide.eclipse.hierarchyviewer/.project b/eclipse/features/com.android.ide.eclipse.hierarchyviewer/.project
index 6f5c27e..0c7a096 100644
--- a/eclipse/features/com.android.ide.eclipse.hierarchyviewer/.project
+++ b/eclipse/features/com.android.ide.eclipse.hierarchyviewer/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>hierarchyviewer-feature</name>
+	<name>feature-hierarchyviewer</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/features/com.android.ide.eclipse.ndk/.project b/eclipse/features/com.android.ide.eclipse.ndk/.project
index cca5cae..42d1891 100644
--- a/eclipse/features/com.android.ide.eclipse.ndk/.project
+++ b/eclipse/features/com.android.ide.eclipse.ndk/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>ndk-feature</name>
+	<name>feature-ndk</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/features/com.android.ide.eclipse.pdt/.project b/eclipse/features/com.android.ide.eclipse.pdt/.project
index 769d8a7..8aa2663 100644
--- a/eclipse/features/com.android.ide.eclipse.pdt/.project
+++ b/eclipse/features/com.android.ide.eclipse.pdt/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>pdt-feature</name>
+	<name>feature-pdt</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/features/com.android.ide.eclipse.tests/.project b/eclipse/features/com.android.ide.eclipse.tests/.project
index 6a16276..e9a6867 100644
--- a/eclipse/features/com.android.ide.eclipse.tests/.project
+++ b/eclipse/features/com.android.ide.eclipse.tests/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>adt-tests-feature</name>
+	<name>feature-tests</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/features/com.android.ide.eclipse.traceview/.project b/eclipse/features/com.android.ide.eclipse.traceview/.project
index 12c788b..c6cf73c 100644
--- a/eclipse/features/com.android.ide.eclipse.traceview/.project
+++ b/eclipse/features/com.android.ide.eclipse.traceview/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>traceview-feature</name>
+	<name>feature-traceview</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.overlay/.project b/eclipse/plugins/com.android.ide.eclipse.adt.overlay/.project
index 4b8c4f6..b24d832 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt.overlay/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.overlay/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>adt-overlay</name>
+	<name>plugin-adt-overlay</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath
index f874a9e..a3376bd 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath
@@ -14,6 +14,7 @@
 	<classpathentry kind="lib" path="libs/assetstudio.jar" sourcepath="/assetstudio"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
+	<classpathentry kind="var" path="ANDROID_SRC/sdk/eclipse/plugins/com.android.ide.eclipse.adt/libs/propertysheet.jar" sourcepath="/ANDROID_SRC/external/eclipse-windowbuilder/propertysheet/src"/>
 	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/>
 	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-tree-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/>
 	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/lombok-ast/lombok-ast-0.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/lombok-ast/src.zip"/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.project b/eclipse/plugins/com.android.ide.eclipse.adt/.project
index c7b1ad4..0bc58f9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>adt</name>
+	<name>plugin-adt</name>
 	<comment></comment>
 	<projects>
 		<project>SdkLib</project>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index 0b40004..dcef3c9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -15,7 +15,8 @@
  libs/lint_checks.jar,
  libs/lombok-ast-0.2.jar,
  libs/asm-4.0.jar,
- libs/asm-tree-4.0.jar
+ libs/asm-tree-4.0.jar,
+ libs/propertysheet.jar
 Bundle-Activator: com.android.ide.eclipse.adt.AdtPlugin
 Bundle-Vendor: The Android Open Source Project
 Require-Bundle: com.android.ide.eclipse.base,
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_design.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_design.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_source.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_page_source.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_palette.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/editor_palette.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/filter_advanced_properties.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/filter_advanced_properties.png
new file mode 100644
index 0000000..5f5b078
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/filter_advanced_properties.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/goto_definition.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/goto_definition.png
new file mode 100644
index 0000000..daac537
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/goto_definition.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/properties_default.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/properties_default.png
new file mode 100644
index 0000000..5696a31
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/properties_default.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/sdk_manager.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/sdk_manager.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/sort_alpha.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/sort_alpha.png
new file mode 100644
index 0000000..112e05b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/sort_alpha.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index f6fe09f..f8f6311 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -16,6 +16,11 @@
 
 package com.android.ide.eclipse.adt;
 
+import static com.android.sdklib.SdkConstants.CURRENT_PLATFORM;
+import static com.android.sdklib.SdkConstants.PLATFORM_DARWIN;
+import static com.android.sdklib.SdkConstants.PLATFORM_LINUX;
+import static com.android.sdklib.SdkConstants.PLATFORM_WINDOWS;
+
 import com.android.AndroidConstants;
 import com.android.ide.common.log.ILogger;
 import com.android.ide.common.resources.ResourceFile;
@@ -94,6 +99,7 @@
 import org.eclipse.ui.ide.IDE;
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.eclipse.ui.texteditor.AbstractTextEditor;
+import org.eclipse.wb.internal.core.DesignerPlugin;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
@@ -257,6 +263,14 @@
         // load preferences.
         AdtPrefs.getPrefs().loadValues(null /*event*/);
 
+        // initialize property-sheet library
+        DesignerPlugin.initialize(
+                this,
+                PLUGIN_ID,
+                CURRENT_PLATFORM == PLATFORM_WINDOWS,
+                CURRENT_PLATFORM == PLATFORM_DARWIN,
+                CURRENT_PLATFORM == PLATFORM_LINUX);
+
         // initialize editors
         startEditors();
 
@@ -286,6 +300,8 @@
         stopEditors();
         IncludeFinder.stop();
 
+        DesignerPlugin.dispose();
+
         mRed.dispose();
         synchronized (AdtPlugin.class) {
             sPlugin = null;
@@ -668,18 +684,19 @@
     }
 
     /**
-     * Reads the contents of an {@link InputStreamReader} and return it as a String
+     * Reads the contents of a {@link Reader} and return it as a String. This
+     * method will close the input reader.
      *
-     * @param inputStream the input stream to be read from
-     * @return the String read from the stream, or null if there was an error
+     * @param reader the reader to be read from
+     * @return the String read from reader, or null if there was an error
      */
-    public static String readFile(Reader inputStream) {
-        BufferedReader reader = null;
+    public static String readFile(Reader reader) {
+        BufferedReader bufferedReader = null;
         try {
-            reader = new BufferedReader(inputStream);
+            bufferedReader = new BufferedReader(reader);
             StringBuilder sb = new StringBuilder(2000);
             while (true) {
-                int c = reader.read();
+                int c = bufferedReader.read();
                 if (c == -1) {
                     return sb.toString();
                 } else {
@@ -690,8 +707,8 @@
             // pass -- ignore files we can't read
         } finally {
             try {
-                if (reader != null) {
-                    reader.close();
+                if (bufferedReader != null) {
+                    bufferedReader.close();
                 }
             } catch (IOException e) {
                 AdtPlugin.log(e, "Can't read input stream"); //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
index 08bb7d8..7e2a44d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
@@ -103,6 +103,32 @@
     }
 
     /**
+     * Returns true if the given string starts with the given prefix, using a
+     * case-insensitive comparison.
+     *
+     * @param string the full string to be checked
+     * @param prefix the prefix to be checked for
+     * @return true if the string case-insensitively starts with the given prefix
+     */
+    public static boolean startsWithIgnoreCase(String string, String prefix) {
+        return string.regionMatches(true /* ignoreCase */, 0, prefix, 0, prefix.length());
+    }
+
+    /**
+     * Returns true if the given string starts at the given offset with the
+     * given prefix, case insensitively.
+     *
+     * @param string the full string to be checked
+     * @param offset the offset in the string to start looking
+     * @param prefix the prefix to be checked for
+     * @return true if the string case-insensitively starts at the given offset
+     *         with the given prefix
+     */
+    public static boolean startsWith(String string, int offset, String prefix) {
+        return string.regionMatches(true /* ignoreCase */, offset, prefix, 0, prefix.length());
+    }
+
+    /**
      * Strips the whitespace from the given string
      *
      * @param string the string to be cleaned up
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java
index 22aa687..0a12ba4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java
@@ -373,5 +373,4 @@
             return data;
         }
     }
-
 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java
index 4ccab2d..61db9f3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java
@@ -110,7 +110,7 @@
     @Override
     public void addSelectionChangedListener(ISelectionChangedListener listener) {
         if (mListeners == null) {
-            mListeners = new ArrayList<ISelectionChangedListener>();
+            mListeners = new ArrayList<ISelectionChangedListener>(2);
         }
         mListeners.add(listener);
     }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java
index 20096f4..b00656e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java
@@ -40,6 +40,7 @@
 import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_INCLUDE;
 import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE;
 
+import com.android.annotations.NonNull;
 import com.android.ide.common.api.IAttributeInfo.Format;
 import com.android.ide.common.resources.platform.AttributeInfo;
 import com.android.ide.eclipse.adt.AdtConstants;
@@ -308,6 +309,66 @@
     }
 
     /**
+     * Similar to {@link #prettyAttributeUiName(String)}, but it will capitalize
+     * all words, not just the first one.
+     * <p/>
+     * The original xml name starts with a lower case and is camel-case, e.g.
+     * "maxWidthForView". The corresponding return value is
+     * "Max Width For View".
+     *
+     * @param name the attribute name, which should be a camel case name, e.g.
+     *            "maxWidth"
+     * @return the corresponding display name, e.g. "Max Width"
+     */
+    @NonNull
+    public static String capitalize(@NonNull String name) {
+        if (name.isEmpty()) {
+            return name;
+        }
+        StringBuilder buf = new StringBuilder(2 * name.length());
+
+        char c = name.charAt(0);
+        // Use upper case initial letter
+        buf.append(Character.toUpperCase(c));
+        int len = name.length();
+        for (int i = 1; i < len; i++) {
+            c = name.charAt(i);
+            if (Character.isUpperCase(c)) {
+                // Break camel case into separate words
+                buf.append(' ');
+                // Use a lower case initial letter for the next word, except if the
+                // word is solely X, Y or Z.
+                buf.append(c);
+            } else if (c == '_') {
+                buf.append(' ');
+                if (i < len -1 && Character.isLowerCase(name.charAt(i + 1))) {
+                    buf.append(Character.toUpperCase(name.charAt(i + 1)));
+                    i++;
+                }
+            } else {
+                buf.append(c);
+            }
+        }
+
+        name = buf.toString();
+
+        // Replace these acronyms by upper-case versions
+        // - (?<=^| ) means "if preceded by a space or beginning of string"
+        // - (?=$| )  means "if followed by a space or end of string"
+        if (name.contains("Sdk")) {
+            name = name.replaceAll("(?<=^| )Sdk(?=$| )", "SDK");
+        }
+        if (name.contains("Uri")) {
+            name = name.replaceAll("(?<=^| )Uri(?=$| )", "URI");
+        }
+        if (name.contains("Ime")) {
+            name = name.replaceAll("(?<=^| )Ime(?=$| )", "IME");
+        }
+
+        return name;
+    }
+
+    /**
      * Formats the javadoc tooltip to be usable in a tooltip.
      */
     public static String formatTooltip(String javadoc) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java
index df39a3d..5f2b79b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java
@@ -36,8 +36,9 @@
 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutActionBar;
 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas;
 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.OutlinePage;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.PropertySheetPage;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionManager;
 import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
+import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertySheetPage;
 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
@@ -54,6 +55,8 @@
 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
 import org.eclipse.jface.text.source.ISourceViewer;
 import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IEditorPart;
@@ -476,6 +479,16 @@
             // https://bugs.eclipse.org/bugs/show_bug.cgi?id=1917
             if (mMultiOutline == null || mMultiOutline.isDisposed()) {
                 mMultiOutline = new XmlEditorMultiOutline();
+                mMultiOutline.addSelectionChangedListener(new ISelectionChangedListener() {
+                    @Override
+                    public void selectionChanged(SelectionChangedEvent event) {
+                        ISelection selection = event.getSelection();
+                        getEditor().getSite().getSelectionProvider().setSelection(selection);
+                        SelectionManager manager =
+                                mGraphicalEditor.getCanvasControl().getSelectionManager();
+                        manager.setSelection(selection);
+                    }
+                });
                 updateOutline(getEditor().getActivePageInstance());
             }
 
@@ -484,7 +497,7 @@
 
         if (IPropertySheetPage.class == adapter && mGraphicalEditor != null) {
             if (mPropertyPage == null) {
-                mPropertyPage = new PropertySheetPage();
+                mPropertyPage = new PropertySheetPage(mGraphicalEditor);
             }
 
             return mPropertyPage;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java
index 10e1a4d..2133abd 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java
@@ -305,8 +305,6 @@
             AttributeInfo[] attrList = link.getAttributes();
             if (attrList.length > 0) {
                 attributeSources.add(link.getFullClassName());
-                attributes.add(new SeparatorAttributeDescriptor(
-                        String.format("Attributes from %1$s", link.getShortClassName())));
                 DescriptorsUtils.appendAttributes(attributes,
                         null, // elementName
                         SdkConstants.NS_RESOURCES,
@@ -321,28 +319,11 @@
         LayoutParamsInfo layoutParams = info.getLayoutData();
 
         for(; layoutParams != null; layoutParams = layoutParams.getSuperClass()) {
-            boolean needSeparator = true;
             for (AttributeInfo attrInfo : layoutParams.getAttributes()) {
                 if (DescriptorsUtils.containsAttribute(layoutAttributes,
                         SdkConstants.NS_RESOURCES, attrInfo)) {
                     continue;
                 }
-                if (needSeparator) {
-                    ViewClassInfo viewLayoutClass = layoutParams.getViewLayoutClass();
-                    String title;
-                    String shortClassName = viewLayoutClass.getShortClassName();
-                    if (layoutParams.getShortClassName().equals(
-                            SdkConstants.CLASS_NAME_LAYOUTPARAMS)) {
-                        title = String.format("Layout Attributes from %1$s",
-                                    shortClassName);
-                    } else {
-                        title = String.format("Layout Attributes from %1$s (%2$s)",
-                                shortClassName,
-                                layoutParams.getShortClassName());
-                    }
-                    layoutAttributes.add(new SeparatorAttributeDescriptor(title));
-                    needSeparator = false;
-                }
                 DescriptorsUtils.appendAttribute(layoutAttributes,
                         null, // elementName
                         SdkConstants.NS_RESOURCES,
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
index dd103c5..c8497da 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
@@ -21,6 +21,8 @@
 import static com.android.ide.common.layout.LayoutConstants.GESTURE_OVERLAY_VIEW;
 import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE;
 
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.ide.common.api.Margins;
 import com.android.ide.common.api.Rect;
 import com.android.ide.common.layout.GridLayoutRule;
@@ -70,8 +72,7 @@
     /**
      * Minimal size of the selection, in case an empty view or layout is selected.
      */
-    private static final int SELECTION_MIN_SIZE = 6;
-
+    public static final int SELECTION_MIN_SIZE = 6;
 
     private final Rectangle mAbsRect;
     private final Rectangle mSelectionRect;
@@ -121,17 +122,21 @@
      *
      * @return the bounding box in absolute coordinates
      */
+    @NonNull
     public Rectangle getAbsRect() {
         return mAbsRect;
     }
 
-    /*
-    * Returns the absolute selection bounds of the view info as a rectangle.
-    * The selection bounds will always have a size greater or equal to
-    * {@link #SELECTION_MIN_SIZE}.
-    * The width/height is inclusive (i.e. width = right-left-1).
-    * This is in absolute "screen" coordinates (relative to the rendered bitmap).
-    */
+    /**
+     * Returns the absolute selection bounds of the view info as a rectangle.
+     * The selection bounds will always have a size greater or equal to
+     * {@link #SELECTION_MIN_SIZE}.
+     * The width/height is inclusive (i.e. width = right-left-1).
+     * This is in absolute "screen" coordinates (relative to the rendered bitmap).
+     *
+     * @return the absolute selection bounds
+     */
+    @NonNull
     public Rectangle getSelectionRect() {
         return mSelectionRect;
     }
@@ -141,6 +146,7 @@
      * @return An {@link UiViewElementNode} that uniquely identifies the object in the XML model.
      * @see ViewInfo#getCookie()
      */
+    @Nullable
     public UiViewElementNode getUiViewNode() {
         return mUiViewNode;
     }
@@ -151,6 +157,7 @@
      *
      * @return the parent {@link CanvasViewInfo}, which can be null
      */
+    @Nullable
     public CanvasViewInfo getParent() {
         return mParent;
     }
@@ -162,6 +169,7 @@
      *
      * @return the children, never null
      */
+    @NonNull
     public List<CanvasViewInfo> getChildren() {
         return mChildren;
     }
@@ -171,6 +179,7 @@
      * children of a {@code <merge>} tag included into a separate layout, return the
      * "primary" view, the first view that is rendered
      */
+    @Nullable
     private CanvasViewInfo getPrimaryNodeSibling() {
         if (mNodeSiblings == null || mNodeSiblings.size() == 0) {
             return null;
@@ -200,6 +209,7 @@
      *
      * @return a non-empty list of siblings (including this), or null
      */
+    @Nullable
     public List<CanvasViewInfo> getNodeSiblings() {
         return mNodeSiblings;
     }
@@ -218,6 +228,7 @@
      * @return list of {@link CanvasViewInfo} objects that are children of this view,
      *         never null
      */
+    @NonNull
     public List<CanvasViewInfo> getUniqueChildren() {
         boolean haveHidden = false;
 
@@ -261,10 +272,7 @@
      * @param potentialParent the view info to check
      * @return true if the given info is a parent of this view
      */
-    public boolean isParent(CanvasViewInfo potentialParent) {
-        if (potentialParent == null) {
-
-        }
+    public boolean isParent(@NonNull CanvasViewInfo potentialParent) {
         CanvasViewInfo p = mParent;
         while (p != null) {
             if (p == potentialParent) {
@@ -281,10 +289,11 @@
      * Experience shows this is the full qualified Java name of the View.
      * TODO: Rename this method to getFqcn.
      *
-     * @return the name of the view info, or null
+     * @return the name of the view info
      *
      * @see ViewInfo#getClassName()
      */
+    @NonNull
     public String getName() {
         return mName;
     }
@@ -293,10 +302,16 @@
      * Returns the View object associated with the {@link CanvasViewInfo}.
      * @return the view object or null.
      */
+    @Nullable
     public Object getViewObject() {
         return mViewObject;
     }
 
+    /**
+     * Returns the baseline of this object, or -1 if it does not support a baseline
+     *
+     * @return the baseline or -1
+     */
     public int getBaseline() {
         if (mViewInfo != null) {
             int baseline = mViewInfo.getBaseLine();
@@ -313,6 +328,7 @@
      *
      * @return the {@link Margins} for this {@link CanvasViewInfo}
      */
+    @Nullable
     public Margins getMargins() {
         if (mViewInfo != null) {
             int leftMargin = mViewInfo.getLeftMargin();
@@ -331,6 +347,7 @@
     }
 
     // ---- Implementation of IPropertySource
+    // TODO: Get rid of this once the old propertysheet implementation is fully gone
 
     @Override
     public Object getEditableValue() {
@@ -390,6 +407,7 @@
      *
      * @return The XML node corresponding to this info object, or null
      */
+    @Nullable
     public Node getXmlNode() {
         UiViewElementNode uiView = getUiViewNode();
         if (uiView != null) {
@@ -480,7 +498,7 @@
      *
      * @param exploded New value of the exploded property to mark this info with.
      */
-    /* package */ void setExploded(boolean exploded) {
+    void setExploded(boolean exploded) {
         this.mExploded = exploded;
     }
 
@@ -489,7 +507,8 @@
      *
      * @return A {@link SimpleElement} wrapping this info.
      */
-    /* package */ SimpleElement toSimpleElement() {
+    @NonNull
+    SimpleElement toSimpleElement() {
 
         UiViewElementNode uiNode = getUiViewNode();
 
@@ -538,6 +557,7 @@
      * @return the layout url attribute value for the surrounding include tag, or null if
      *         not applicable
      */
+    @Nullable
     public String getIncludeUrl() {
         CanvasViewInfo curr = this;
         while (curr != null) {
@@ -568,12 +588,12 @@
     }
 
     /** Adds the given {@link CanvasViewInfo} as a new last child of this view */
-    private void addChild(CanvasViewInfo child) {
+    private void addChild(@NonNull CanvasViewInfo child) {
         mChildren.add(child);
     }
 
     /** Adds the given {@link CanvasViewInfo} as a child at the given index */
-    private void addChildAt(int index, CanvasViewInfo child) {
+    private void addChildAt(int index, @NonNull CanvasViewInfo child) {
         mChildren.add(index, child);
     }
 
@@ -584,7 +604,7 @@
      * @param child the child to be removed
      * @return true if it was a child and was removed
      */
-    public boolean removeChild(CanvasViewInfo child) {
+    public boolean removeChild(@NonNull CanvasViewInfo child) {
         return mChildren.remove(child);
     }
 
@@ -623,6 +643,7 @@
      * @param root the root {@link ViewInfo} to build from
      * @return a {@link CanvasViewInfo} hierarchy
      */
+    @NonNull
     public static Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root, boolean layoutlib5) {
         return new Builder(layoutlib5).create(root);
     }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java
deleted file mode 100644
index d785faf..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PropertySheetPage.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2010 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.gle2;
-
-import static com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor.DEPRECATED_CATEGORY;
-
-import org.eclipse.jface.action.IStatusLineManager;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeItem;
-import org.eclipse.ui.IActionBars;
-import org.eclipse.ui.views.properties.IPropertySheetEntry;
-import org.eclipse.ui.views.properties.PropertySheetEntry;
-import org.eclipse.ui.views.properties.PropertySheetSorter;
-
-/**
- * A customized property sheet page for the graphical layout editor v2.
- * <p/>
- * Currently it just provides a custom tooltip to display attributes javadocs.
- * <p/>
- * The property sheet is linked to the current site's selection service.
- * <p/>
- * Note: this is an exact copy of GLE1's UiPropertySheetPage implementation.
- * The idea is that eventually GLE1 will go away and we'll upgrade this to be
- * a more robust property editor (it currently lacks on so many levels, it's not
- * even worth listing the flaws.)
- *
- * @since GLE2
- */
-public class PropertySheetPage extends org.eclipse.ui.views.properties.PropertySheetPage {
-    private static final String MISC_CATEGORY = "Misc";
-
-    public PropertySheetPage() {
-        super();
-
-        setSorter(new PropertySheetSorter() {
-            @Override
-            public int compareCategories(String categoryA, String categoryB) {
-                // Sort the "Deprecated" category to the bottom, and the "Misc"
-                // category second to last.
-                if (categoryA.equals(DEPRECATED_CATEGORY)) {
-                    return 1;
-                } else if (categoryB.equals(DEPRECATED_CATEGORY)) {
-                    return -1;
-                }
-                if (categoryA.equals(MISC_CATEGORY)) {
-                    return 1;
-                } else if (categoryB.equals(MISC_CATEGORY)) {
-                    return -1;
-                }
-
-                return super.compareCategories(categoryA, categoryB);
-            }
-        });
-    }
-
-    @Override
-    public void createControl(Composite parent) {
-        super.createControl(parent);
-
-        setupTooltip();
-
-        // Override parent class' "set status message" behavior. The parent will set
-        // the status message to the property's "getDescription()" field. That field
-        // may contain newlines, which means the text gets cut off. We want to instead
-        // show ALL the text, fit on a single line, and since we don't get to subclass
-        // the viewer we will just replace the status message with our own, which works
-        // since our mouse listener is registered later so runs later.
-        final Tree tree = (Tree) getControl();
-        tree.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseDown(MouseEvent event) {
-                Point pt = new Point(event.x, event.y);
-                TreeItem item = tree.getItem(pt);
-                if (item != null) {
-                    Object object = item.getData();
-                    if (object instanceof IPropertySheetEntry) {
-                        IPropertySheetEntry entry = (IPropertySheetEntry) object;
-                        String help = entry.getDescription();
-                        if (help != null) {
-                            // Strip out newlines to make this a single line entry
-                            help = help.replace('\n', ' ');
-                            // Remove repeated spaces in case there were trailing spaces
-                            help = help.replaceAll("  ", " "); //$NON-NLS-1$ //$NON-NLS-2$
-                            IActionBars actionBars = getSite().getActionBars();
-                            IStatusLineManager status = actionBars.getStatusLineManager();
-                            status.setMessage(help);
-                        }
-                    }
-                }
-            }
-        });
-
-        // Fix the selection background. In Eclipse 3.5 and 3.6, the selection color
-        // is white, painted on top of a white or light blue background (table striping),
-        // which is practically unreadable.  This is fixed in 3.7M3, but we need a workaround
-        // for earlier releases. This just paints a solid color under the current line in
-        // the left column.
-        tree.addListener(SWT.EraseItem, new Listener() {
-            @Override
-            public void handleEvent(Event event) {
-                if ((event.detail & SWT.SELECTED) != 0 && event.index == 0) {
-                    GC gc = event.gc;
-                    Rectangle rect = event.getBounds();
-                    Color background = gc.getBackground();
-                    Display display = tree.getDisplay();
-                    gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION));
-                    gc.fillRectangle(rect.x, rect.y, rect.width, rect.height);
-                    gc.setBackground(background);
-                }
-            }
-        });
-    }
-
-    /**
-     * Sets up a custom tooltip when hovering over tree items.
-     * <p/>
-     * The tooltip will display the element's javadoc, if any, or the item's getText otherwise.
-     */
-    private void setupTooltip() {
-        final Tree tree = (Tree) getControl();
-
-        /*
-         * Reference:
-         * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
-         */
-
-        final Listener listener = new Listener() {
-            Shell tip = null;
-            Label label  = null;
-
-            @Override
-            public void handleEvent(Event event) {
-                switch(event.type) {
-                case SWT.Dispose:
-                case SWT.KeyDown:
-                case SWT.MouseExit:
-                case SWT.MouseDown:
-                case SWT.MouseMove:
-                    if (tip != null) {
-                        tip.dispose();
-                        tip = null;
-                        label = null;
-                    }
-                    break;
-                case SWT.MouseHover:
-                    if (tip != null) {
-                        tip.dispose();
-                        tip = null;
-                        label = null;
-                    }
-
-                    String tooltip = null;
-
-                    TreeItem item = tree.getItem(new Point(event.x, event.y));
-                    if (item != null) {
-                        Object data = item.getData();
-                        if (data instanceof PropertySheetEntry) {
-                            tooltip = ((PropertySheetEntry) data).getDescription();
-                        }
-
-                        if (tooltip == null) {
-                            tooltip = item.getText();
-                        } else {
-                            tooltip = item.getText() + ":\r" + tooltip;
-                        }
-
-                        if (tooltip != null) {
-                            Shell shell = tree.getShell();
-                            Display display = tree.getDisplay();
-
-                            tip = new Shell(shell, SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
-                            tip.setBackground(display .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
-                            FillLayout layout = new FillLayout();
-                            layout.marginWidth = 2;
-                            tip.setLayout(layout);
-                            label = new Label(tip, SWT.NONE);
-                            label.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
-                            label.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
-                            label.setData("_TABLEITEM", item);
-                            label.setText(tooltip);
-                            label.addListener(SWT.MouseExit, this);
-                            label.addListener(SWT.MouseDown, this);
-                            Point size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT);
-                            Rectangle rect = item.getBounds(0);
-                            // Display the tooltip on the same line as the property,
-                            // but offset to the right of wherever the mouse cursor was,
-                            // such that it does not obscure the list of properties.
-                            Point pt = tree.toDisplay(event.x + 15, rect.y);
-                            tip.setBounds(pt.x, pt.y, size.x, size.y);
-                            tip.setVisible(true);
-                        }
-                    }
-                }
-            }
-        };
-
-        tree.addListener(SWT.Dispose, listener);
-        tree.addListener(SWT.KeyDown, listener);
-        tree.addListener(SWT.MouseMove, listener);
-        tree.addListener(SWT.MouseHover, listener);
-
-    }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
index 7c2f7f5..5d49426 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
@@ -16,6 +16,8 @@
 
 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
 
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.ide.common.api.ResizePolicy;
 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
@@ -97,11 +99,22 @@
      *
      * @return the selected view info. Cannot be null.
      */
+    @NonNull
     public CanvasViewInfo getViewInfo() {
         return mCanvasViewInfo;
     }
 
     /**
+     * Returns the selected node.
+     *
+     * @return the selected node, or null
+     */
+    @Nullable
+    public UiViewElementNode getUiNode() {
+        return mCanvasViewInfo.getUiViewNode();
+    }
+
+    /**
      * Returns the selection border rectangle. Cannot be null.
      *
      * @return the selection border rectangle, never null
@@ -111,11 +124,13 @@
     }
 
     /** Returns the node associated with this selection (may be null) */
+    @Nullable
     NodeProxy getNode() {
         return mNodeProxy;
     }
 
     /** Returns the canvas associated with this selection (never null) */
+    @NonNull
     LayoutCanvas getCanvas() {
         return mCanvas;
     }
@@ -126,6 +141,7 @@
      * Gets the XML text from the given selection for a text transfer.
      * The returned string can be empty but not null.
      */
+    @NonNull
     static String getAsText(LayoutCanvas canvas, List<SelectionItem> selection) {
         StringBuilder sb = new StringBuilder();
 
@@ -152,6 +168,7 @@
      * @param items Items to wrap in elements
      * @return An array of wrapper elements. Never null.
      */
+    @NonNull
     static SimpleElement[] getAsElements(List<SelectionItem> items) {
         ArrayList<SimpleElement> elements = new ArrayList<SimpleElement>();
 
@@ -184,6 +201,7 @@
      *
      * @return the {@link SelectionHandles} for this {@link SelectionItem}, never null
      */
+    @NonNull
     public SelectionHandles getSelectionHandles() {
         if (mHandles == null) {
             mHandles = new SelectionHandles(this);
@@ -197,6 +215,7 @@
      *
      * @return the {@link ResizePolicy} for this item, never null
      */
+    @NonNull
     public ResizePolicy getResizePolicy() {
         if (mResizePolicy == null && mNodeProxy != null) {
             mResizePolicy = ViewMetadataRepository.get().getResizePolicy(mNodeProxy.getFqcn());
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
index e2c573a..1450768 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
@@ -20,6 +20,7 @@
 import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_MARGIN;
 import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_RADIUS;
 
+import com.android.annotations.NonNull;
 import com.android.ide.common.api.INode;
 import com.android.ide.common.layout.GridLayoutRule;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
@@ -122,6 +123,7 @@
      *
      * @return An immutable list of {@link SelectionItem}. Can be empty but not null.
      */
+    @NonNull
     List<SelectionItem> getSelections() {
         return mUnmodifiableSelection;
     }
@@ -132,7 +134,12 @@
      *
      * @return A copy of the current selection. Never null.
      */
-    /* package */ List<SelectionItem> getSnapshot() {
+    @NonNull
+    public List<SelectionItem> getSnapshot() {
+        if (mSelectionListeners.isEmpty()) {
+            return Collections.emptyList();
+        }
+
         return new ArrayList<SelectionItem>(mSelections);
     }
 
@@ -190,6 +197,7 @@
             return;
         }
 
+        boolean changed = false;
         try {
             mInsideUpdateSelection = true;
 
@@ -211,7 +219,6 @@
                     return;
                 }
 
-                boolean changed = false;
                 boolean redoLayout = false;
 
                 // Create a list of all currently selected view infos
@@ -242,6 +249,10 @@
                         if (newVi.isInvisible()) {
                             redoLayout = true;
                         }
+                    } else {
+                        // Unrelated selection (e.g. user clicked in the Project Explorer
+                        // or something) -- just ignore these
+                        return;
                     }
                 }
 
@@ -257,15 +268,16 @@
                 if (redoLayout) {
                     mCanvas.getEditorDelegate().recomputeLayout();
                 }
-                if (changed) {
-                    redraw();
-                    updateActionsFromSelection();
-                }
-
             }
         } finally {
             mInsideUpdateSelection = false;
         }
+
+        if (changed) {
+            redraw();
+            fireSelectionChanged();
+            updateActionsFromSelection();
+        }
     }
 
     /**
@@ -699,7 +711,7 @@
     }
 
     /** Sync the selection with an updated view info tree */
-    /* package */ void sync() {
+    void sync() {
         // Check if the selection is still the same (based on the object keys)
         // and eventually recompute their bounds.
         for (ListIterator<SelectionItem> it = mSelections.listIterator(); it.hasNext(); ) {
@@ -714,6 +726,9 @@
             // we need to recompute its bounds in case it moved so we'll insert a new one
             // at the same place.
             it.remove();
+            if (vi == null) {
+                vi = findCorresponding(s.getViewInfo(), viewHierarchy.getRoot());
+            }
             if (vi != null) {
                 it.add(createSelection(vi));
             }
@@ -724,6 +739,39 @@
         mAltSelection = null;
     }
 
+    /** Finds the corresponding {@link CanvasViewInfo} in the new hierarchy */
+    private CanvasViewInfo findCorresponding(CanvasViewInfo old, CanvasViewInfo newRoot) {
+        CanvasViewInfo oldParent = old.getParent();
+        if (oldParent != null) {
+            CanvasViewInfo newParent = findCorresponding(oldParent, newRoot);
+            if (newParent == null) {
+                return null;
+            }
+
+            List<CanvasViewInfo> oldSiblings = oldParent.getChildren();
+            List<CanvasViewInfo> newSiblings = newParent.getChildren();
+            Iterator<CanvasViewInfo> oldIterator = oldSiblings.iterator();
+            Iterator<CanvasViewInfo> newIterator = newSiblings.iterator();
+            while (oldIterator.hasNext() && newIterator.hasNext()) {
+                CanvasViewInfo oldSibling = oldIterator.next();
+                CanvasViewInfo newSibling = newIterator.next();
+
+                if (oldSibling.getName().equals(newSibling.getName())) {
+                    // Structure has changed: can't do a proper search
+                    return null;
+                }
+
+                if (oldSibling == old) {
+                    return newSibling;
+                }
+            }
+        } else {
+            return newRoot;
+        }
+
+        return null;
+    }
+
     /**
      * Notifies listeners that the selection has changed.
      */
@@ -884,9 +932,13 @@
                 newChildren.add(viewInfo);
             }
         }
-        mCanvas.getSelectionManager().selectMultiple(newChildren);
+        boolean found = nodes.size() == newChildren.size();
 
-        return nodes.size() == newChildren.size();
+        if (found || newChildren.size() > 0) {
+            mCanvas.getSelectionManager().selectMultiple(newChildren);
+        }
+
+        return found;
     }
 
     /**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java
index d0e957e..99d6505 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java
@@ -18,6 +18,7 @@
 import com.android.ide.common.api.Rect;
 
 import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Device;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.FontMetrics;
 import org.eclipse.swt.graphics.GC;
@@ -123,7 +124,7 @@
      * @return A new SWT {@link Image} with the same contents as the source
      *         {@link BufferedImage}
      */
-    public static Image convertToSwt(Display display, BufferedImage awtImage,
+    public static Image convertToSwt(Device display, BufferedImage awtImage,
             boolean transferAlpha, int globalAlpha) {
         if (!isSupportedPaletteType(awtImage.getType())) {
             awtImage = convertToCompatibleFormat(awtImage);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
index f6437fc..579ef44 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
@@ -18,6 +18,8 @@
 
 import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE;
 
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.ide.common.api.INode;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.ViewInfo;
@@ -179,7 +181,7 @@
                     if (root != null) {
                         infos = CanvasViewInfo.create(root, layoutlib5);
                         if (DUMP_INFO) {
-                            dump(root, 0);
+                            dump(session, root, 0);
                         }
                     } else {
                         infos = null;
@@ -693,12 +695,27 @@
     }
 
     /**
+     * Returns a map of the default properties for the given view object in this session
+     *
+     * @param viewObject the object to look up the properties map for
+     * @return the map of properties, or null if not found
+     */
+    @Nullable
+    public Map<String, String> getDefaultProperties(@NonNull Object viewObject) {
+        if (mSession != null) {
+            return mSession.getDefaultProperties(viewObject);
+        }
+
+        return null;
+    }
+
+    /**
      * Dumps a {@link ViewInfo} hierarchy to stdout
      *
      * @param info the {@link ViewInfo} object to dump
      * @param depth the depth to indent it to
      */
-    public static void dump(ViewInfo info, int depth) {
+    public static void dump(RenderSession session, ViewInfo info, int depth) {
         if (DUMP_INFO) {
             StringBuilder sb = new StringBuilder();
             for (int i = 0; i < depth; i++) {
@@ -724,11 +741,19 @@
             } else if (cookie != null) {
                 sb.append(" " + cookie); //$NON-NLS-1$
             }
+            /* Display defaults?
+            if (info.getViewObject() != null) {
+                Map<String, String> defaults = session.getDefaultProperties(info.getCookie());
+                sb.append(" - defaults: "); //$NON-NLS-1$
+                sb.append(defaults);
+                sb.append('\n');
+            }
+            */
 
             System.out.println(sb.toString());
 
             for (ViewInfo child : info.getChildren()) {
-                dump(child, depth + 1);
+                dump(session, child, depth + 1);
             }
         }
     }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java
index 3380a38..d6d5cd9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/PaletteMetadataDescriptor.java
@@ -47,6 +47,7 @@
                 descriptor.getChildren(), descriptor.getMandatory() == Mandatory.MANDATORY);
         mInitString = initString;
         mIconName = iconName;
+        setSuperClass(descriptor.getSuperClassDesc());
     }
 
     /**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/BooleanXmlPropertyEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/BooleanXmlPropertyEditor.java
new file mode 100644
index 0000000..32b1192
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/BooleanXmlPropertyEditor.java
@@ -0,0 +1,118 @@
+/*
+ * 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.properties;
+
+import static com.android.ide.common.layout.LayoutConstants.VALUE_FALSE;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_TRUE;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.wb.internal.core.DesignerPlugin;
+import org.eclipse.wb.internal.core.model.property.Property;
+import org.eclipse.wb.internal.core.model.property.table.PropertyTable;
+import org.eclipse.wb.internal.core.utils.ui.DrawUtils;
+
+/**
+ * Handle an XML property which represents booleans.
+ *
+ * Similar to the WindowBuilder PropertyEditor, but operates on Strings rather
+ * than Booleans (which means it is a tri-state boolean: true, false, not set)
+ */
+public class BooleanXmlPropertyEditor extends XmlPropertyEditor {
+    public static final BooleanXmlPropertyEditor INSTANCE = new BooleanXmlPropertyEditor();
+
+    private static final Image mTrueImage = DesignerPlugin.getImage("properties/true.png");
+    private static final Image mFalseImage = DesignerPlugin.getImage("properties/false.png");
+    private static final Image mNullImage =
+            DesignerPlugin.getImage("properties/BooleanNull.png");
+    private static final Image mUnknownImage =
+            DesignerPlugin.getImage("properties/BooleanUnknown.png");
+
+    private BooleanXmlPropertyEditor() {
+    }
+
+    @Override
+    public void paint(Property property, GC gc, int x, int y, int width, int height)
+            throws Exception {
+        Object value = property.getValue();
+        assert value == null || value instanceof String;
+        if (value == null || value instanceof String) {
+            String text = (String) value;
+            Image image;
+            if (VALUE_TRUE.equals(text)) {
+                image = mTrueImage;
+            } else if (VALUE_FALSE.equals(text)) {
+                image = mFalseImage;
+            } else if (text == null) {
+                image = mNullImage;
+            } else {
+                // Probably something like a reference, e.g. @boolean/foo
+                image = mUnknownImage;
+            }
+
+            // draw image
+            DrawUtils.drawImageCV(gc, image, x, y, height);
+
+            // prepare new position/width
+            int imageWidth = image.getBounds().width + 2;
+            width -= imageWidth;
+
+            // draw text
+            if (text != null) {
+                x += imageWidth;
+                DrawUtils.drawStringCV(gc, text, x, y, width, height);
+            }
+        }
+    }
+
+    @Override
+    public boolean activate(PropertyTable propertyTable, Property property, Point location)
+            throws Exception {
+        // check that user clicked on image
+        if (location == null || location.x < mTrueImage.getBounds().width + 2) {
+            cycleValue(property);
+        }
+        // don't activate
+        return false;
+    }
+
+    @Override
+    public void doubleClick(Property property, Point location) throws Exception {
+        cycleValue(property);
+    }
+
+    /**
+     * Cycles through the values
+     */
+    private void cycleValue(Property property) throws Exception {
+        Object value = property.getValue();
+        if (value == null || value instanceof String) {
+            // Cycle null => true => false => null
+            String text = (String) value;
+            if (VALUE_TRUE.equals(text)) {
+                property.setValue(VALUE_FALSE);
+            } else if (VALUE_FALSE.equals(text)) {
+                property.setValue(null);
+            } else {
+                property.setValue(VALUE_TRUE);
+            }
+        } else {
+            assert false;
+        }
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/EnumXmlPropertyEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/EnumXmlPropertyEditor.java
new file mode 100644
index 0000000..f1a3f2a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/EnumXmlPropertyEditor.java
@@ -0,0 +1,77 @@
+/*
+ * 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.properties;
+
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ListAttributeDescriptor;
+
+import org.eclipse.wb.core.controls.CCombo3;
+import org.eclipse.wb.internal.core.model.property.Property;
+import org.eclipse.wb.internal.core.model.property.editor.AbstractComboPropertyEditor;
+import org.eclipse.wb.internal.core.model.property.editor.ITextValuePropertyEditor;
+
+class EnumXmlPropertyEditor extends AbstractComboPropertyEditor implements
+        ITextValuePropertyEditor {
+    public static final EnumXmlPropertyEditor INSTANCE = new EnumXmlPropertyEditor();
+
+    private EnumXmlPropertyEditor() {
+    }
+
+    @Override
+    protected String getText(Property property) throws Exception {
+        Object value = property.getValue();
+        if (value == null) {
+            return "";
+        } else if (value instanceof String) {
+            return (String) value;
+        } else if (value == Property.UNKNOWN_VALUE) {
+            return "<varies>";
+        } else {
+            return "";
+        }
+    }
+
+    private String[] getItems(Property property) {
+        XmlProperty xmlProperty = (XmlProperty) property;
+        AttributeDescriptor descriptor = xmlProperty.getDescriptor();
+        assert descriptor instanceof ListAttributeDescriptor;
+        ListAttributeDescriptor list = (ListAttributeDescriptor) descriptor;
+        return list.getValues();
+    }
+
+    @Override
+    protected void addItems(Property property, CCombo3 combo) throws Exception {
+        for (String item : getItems(property)) {
+            combo.add(item);
+        }
+    }
+
+    @Override
+    protected void selectItem(Property property, CCombo3 combo) throws Exception {
+        combo.setText(getText(property));
+    }
+
+    @Override
+    protected void toPropertyEx(Property property, CCombo3 combo, int index) throws Exception {
+        property.setValue(getItems(property)[index]);
+    }
+
+    @Override
+    public void setText(Property property, String text) throws Exception {
+        property.setValue(text);
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagValueCompleter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagValueCompleter.java
new file mode 100644
index 0000000..d3707a5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagValueCompleter.java
@@ -0,0 +1,80 @@
+/*
+ * 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.properties;
+
+import com.android.ide.eclipse.adt.AdtUtils;
+
+import org.eclipse.jface.fieldassist.ContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Resource value completion for the given property */
+class FlagValueCompleter implements IContentProposalProvider {
+    protected final XmlProperty mProperty;
+    private String[] mValues;
+
+    FlagValueCompleter(XmlProperty property, String[] values) {
+        mProperty = property;
+        mValues = values;
+    }
+
+    @Override
+    public IContentProposal[] getProposals(String contents, int position) {
+        List<IContentProposal> proposals = new ArrayList<IContentProposal>(mValues.length);
+        String prefix = contents;
+        int flagStart = prefix.lastIndexOf('|');
+        String prepend = null;
+        if (flagStart != -1) {
+            prepend = prefix.substring(0, flagStart + 1);
+            prefix = prefix.substring(flagStart + 1).trim();
+        }
+
+        boolean exactMatch = false;
+        for (String value : mValues) {
+            if (prefix.equals(value)) {
+                exactMatch = true;
+                proposals.add(new ContentProposal(contents));
+
+                break;
+            }
+        }
+
+        if (exactMatch) {
+            prepend = contents + '|';
+            prefix = "";
+        }
+
+        for (String value : mValues) {
+            if (AdtUtils.startsWithIgnoreCase(value, prefix)) {
+                if (prepend != null && prepend.contains(value)) {
+                    continue;
+                }
+                String match;
+                if (prepend != null) {
+                    match = prepend + value;
+                } else {
+                    match = value;
+                }
+                proposals.add(new ContentProposal(match));
+            }
+        }
+
+        return proposals.toArray(new IContentProposal[proposals.size()]);
+    }
+}
\ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagXmlPropertyDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagXmlPropertyDialog.java
new file mode 100644
index 0000000..0276b6c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/FlagXmlPropertyDialog.java
@@ -0,0 +1,212 @@
+/*
+ * 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.properties;
+
+import com.android.annotations.NonNull;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.google.common.base.Splitter;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+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.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
+import org.eclipse.wb.internal.core.utils.execution.RunnableEx;
+import org.eclipse.wb.internal.core.utils.ui.dialogs.ResizableDialog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class FlagXmlPropertyDialog extends ResizableDialog
+implements IStructuredContentProvider, ICheckStateListener, SelectionListener, KeyListener {
+    private final String mTitle;
+    private final XmlProperty mProperty;
+    private final String[] mFlags;
+    private final boolean mIsRadio;
+
+    private Table mTable;
+    private CheckboxTableViewer mViewer;
+
+    FlagXmlPropertyDialog(
+            @NonNull Shell parentShell,
+            @NonNull String title,
+            boolean isRadio,
+            @NonNull String[] flags,
+            @NonNull XmlProperty property) {
+        super(parentShell, AdtPlugin.getDefault());
+        mTitle = title;
+        mIsRadio = isRadio;
+        mFlags = flags;
+        mProperty = property;
+    }
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        newShell.setText(mTitle);
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite container = (Composite) super.createDialogArea(parent);
+
+        mViewer = CheckboxTableViewer.newCheckList(container,
+                SWT.BORDER | SWT.FULL_SELECTION | SWT.HIDE_SELECTION);
+        mTable = mViewer.getTable();
+        mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+        mViewer.setContentProvider(this);
+        mViewer.setInput(mFlags);
+
+        String current = mProperty.getStringValue();
+        if (current != null) {
+            Object[] checked = null;
+            if (mIsRadio) {
+                checked = new String[] { current };
+            } else {
+                List<String> flags = new ArrayList<String>();
+                for (String s : Splitter.on('|').omitEmptyStrings().trimResults().split(current)) {
+                    flags.add(s);
+                }
+                checked = flags.toArray(new String[flags.size()]);
+            }
+            mViewer.setCheckedElements(checked);
+        }
+        if (mFlags.length > 0) {
+            mTable.setSelection(0);
+        }
+
+        if (mIsRadio) {
+            // Enforce single-item selection
+            mViewer.addCheckStateListener(this);
+        }
+        mTable.addSelectionListener(this);
+        mTable.addKeyListener(this);
+
+        return container;
+    }
+
+    @Override
+    protected void createButtonsForButtonBar(Composite parent) {
+        createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+    }
+
+    @Override
+    protected Point getDefaultSize() {
+        return new Point(450, 400);
+    }
+
+    @Override
+    protected void okPressed() {
+        // Apply the value
+        ExecutionUtils.runLog(new RunnableEx() {
+            @Override
+            public void run() throws Exception {
+                StringBuilder sb = new StringBuilder(30);
+                for (Object o : mViewer.getCheckedElements()) {
+                    if (sb.length() > 0) {
+                        sb.append('|');
+                    }
+                    sb.append((String) o);
+                }
+                String value = sb.length() > 0 ? sb.toString() : null;
+                mProperty.setValue(value);
+            }
+        });
+
+        // close dialog
+        super.okPressed();
+    }
+
+    // ---- Implements IStructuredContentProvider ----
+
+    @Override
+    public Object[] getElements(Object inputElement) {
+        return (Object []) inputElement;
+    }
+
+    @Override
+    public void dispose() {
+    }
+
+    @Override
+    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+    }
+
+    // ---- Implements ICheckStateListener ----
+
+    @Override
+    public void checkStateChanged(CheckStateChangedEvent event) {
+        // Try to disable other elements that conflict with this
+        boolean isChecked = event.getChecked();
+        if (isChecked) {
+            Object selected = event.getElement();
+            for (Object other : mViewer.getCheckedElements()) {
+                if (other != selected) {
+                    mViewer.setChecked(other, false);
+                }
+            }
+        } else {
+
+        }
+    }
+
+    // ---- Implements SelectionListener ----
+
+    @Override
+    public void widgetSelected(SelectionEvent e) {
+    }
+
+    @Override
+    public void widgetDefaultSelected(SelectionEvent e) {
+        if (e.item instanceof TableItem) {
+            TableItem item = (TableItem) e.item;
+            item.setChecked(!item.getChecked());
+        }
+    }
+
+    // ---- Implements KeyListener ----
+
+    @Override
+    public void keyPressed(KeyEvent e) {
+        // Let space toggle checked state
+        if (e.keyCode == ' ' /* SWT.SPACE requires Eclipse 3.7 */) {
+            if (mTable.getSelectionCount() == 1) {
+                TableItem item = mTable.getSelection()[0];
+                item.setChecked(!item.getChecked());
+            }
+        }
+    }
+
+    @Override
+    public void keyReleased(KeyEvent e) {
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyFactory.java
new file mode 100644
index 0000000..bdd7c29
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyFactory.java
@@ -0,0 +1,690 @@
+/*
+ * 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.properties;
+
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_MARGIN;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+
+import com.android.annotations.Nullable;
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.common.api.IAttributeInfo.Format;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
+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.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+import com.android.tools.lint.detector.api.LintUtils;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+
+import org.eclipse.wb.internal.core.editor.structure.property.PropertyListIntersector;
+import org.eclipse.wb.internal.core.model.property.ComplexProperty;
+import org.eclipse.wb.internal.core.model.property.Property;
+import org.eclipse.wb.internal.core.model.property.category.PropertyCategory;
+import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor;
+import org.eclipse.wb.internal.core.model.property.table.PropertyTable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * The {@link PropertyFactory} creates (and caches) the set of {@link Property}
+ * instances applicable to a given node. It's also responsible for ordering
+ * these, and sometimes combining them into {@link ComplexProperty} category
+ * nodes.
+ * <p>
+ * TODO: For any properties that are *set* in XML, they should NOT be labeled as
+ * advanced (which would make them disappear)
+ */
+class PropertyFactory {
+    /** Disable cache during development only */
+    private static final boolean CACHE_ENABLED = true || !LintUtils.assertionsEnabled();
+    static {
+        if (!CACHE_ENABLED) {
+            System.err.println("WARNING: The property cache is disabled");
+        }
+    }
+
+    private static final Property[] NO_PROPERTIES = new Property[0];
+
+    private static final int PRIO_FIRST = -100000;
+    private static final int PRIO_SECOND = PRIO_FIRST + 10;
+    private static final int PRIO_LAST = 100000;
+
+    private final GraphicalEditorPart mGraphicalEditorPart;
+    private final PropertyTable mPropertyTable;
+    private Map<UiViewElementNode, Property[]> mCache =
+            new WeakHashMap<UiViewElementNode, Property[]>();
+    private UiViewElementNode mCurrentViewCookie;
+
+    /** Sorting orders for the properties */
+    public enum SortingMode {
+        NATURAL,
+        BY_ORIGIN,
+        ALPHABETICAL;
+    }
+
+    /** The default sorting mode */
+    public static final SortingMode DEFAULT_MODE = SortingMode.BY_ORIGIN;
+
+    private SortingMode mSortMode = DEFAULT_MODE;
+    private SortingMode mCacheSortMode;
+
+    PropertyFactory(GraphicalEditorPart graphicalEditorPart, PropertyTable propertyTable) {
+        mGraphicalEditorPart = graphicalEditorPart;
+        mPropertyTable = propertyTable;
+    }
+
+    /**
+     * Get the properties for the given list of selection items.
+     *
+     * @param items the {@link CanvasViewInfo} instances to get an intersected
+     *            property list for
+     * @return the properties for the given items
+     */
+    public Property[] getProperties(List<CanvasViewInfo> items) {
+        mCurrentViewCookie = null;
+
+        if (items == null || items.size() == 0) {
+            return NO_PROPERTIES;
+        } else if (items.size() == 1) {
+            CanvasViewInfo item = items.get(0);
+            mCurrentViewCookie = item.getUiViewNode();
+
+            return getProperties(item);
+        } else {
+            // intersect properties
+            PropertyListIntersector intersector = new PropertyListIntersector();
+            for (CanvasViewInfo node : items) {
+                intersector.intersect(getProperties(node));
+            }
+
+            return intersector.getProperties();
+        }
+    }
+
+    private Property[] getProperties(CanvasViewInfo item) {
+        UiViewElementNode node = item.getUiViewNode();
+        if (node == null) {
+            return NO_PROPERTIES;
+        }
+
+        if (mCacheSortMode != mSortMode) {
+            mCacheSortMode = mSortMode;
+            mCache.clear();
+        }
+
+        Property[] properties = mCache.get(node);
+        if (!CACHE_ENABLED) {
+            properties = null;
+        }
+        if (properties == null) {
+            Collection<? extends Property> propertyList = getProperties(node, mPropertyTable);
+            if (propertyList == null) {
+                properties = new Property[0];
+            } else {
+                properties = propertyList.toArray(new Property[propertyList.size()]);
+            }
+            mCache.put(node, properties);
+        }
+        return properties;
+    }
+
+
+    protected Collection<? extends Property> getProperties(
+            UiViewElementNode node,
+            PropertyTable propertyTable) {
+        ViewMetadataRepository repository = ViewMetadataRepository.get();
+        ViewElementDescriptor viewDescriptor = (ViewElementDescriptor) node.getDescriptor();
+        String fqcn = viewDescriptor.getFullClassName();
+        Set<String> top = new HashSet<String>(repository.getTopAttributes(fqcn));
+        AttributeDescriptor[] attributeDescriptors = node.getAttributeDescriptors();
+
+        List<XmlProperty> properties = new ArrayList<XmlProperty>(attributeDescriptors.length);
+        int priority = 0;
+        for (final AttributeDescriptor descriptor : attributeDescriptors) {
+            // TODO: Filter out non-public properties!!
+            // (They shouldn't be in the descriptors at all)
+
+            assert !(descriptor instanceof SeparatorAttributeDescriptor); // No longer inserted
+            if (descriptor instanceof XmlnsAttributeDescriptor) {
+                continue;
+            }
+
+            PropertyEditor editor = XmlPropertyEditor.INSTANCE;
+            IAttributeInfo info = descriptor.getAttributeInfo();
+            if (info != null) {
+                EnumSet<Format> formats = info.getFormats();
+                if (formats.contains(Format.BOOLEAN)) {
+                    editor = BooleanXmlPropertyEditor.INSTANCE;
+                } else if (formats.contains(Format.ENUM)) {
+                    editor = EnumXmlPropertyEditor.INSTANCE;
+                }
+            }
+
+            XmlProperty property = new XmlProperty(editor, this, node, descriptor);
+            // Assign ids sequentially. This ensures that the properties will mostly keep their
+            // relative order (such as placing width before height), even though we will regroup
+            // some (such as properties in the same category, and the layout params etc)
+            priority += 10;
+
+            PropertyCategory category = PropertyCategory.NORMAL;
+            String name = descriptor.getXmlLocalName();
+            if (top.contains(name) || PropertyMetadata.isPreferred(name)) {
+                category = PropertyCategory.PREFERRED;
+                property.setPriority(PRIO_FIRST + priority);
+            } else {
+                property.setPriority(priority);
+
+                // Prefer attributes defined on the specific type of this
+                // widget
+                // NOTE: This doesn't work very well for TextViews
+               /* IAttributeInfo attributeInfo = descriptor.getAttributeInfo();
+                if (attributeInfo != null && fqcn.equals(attributeInfo.getDefinedBy())) {
+                    category = PropertyCategory.PREFERRED;
+                } else*/ if (PropertyMetadata.isAdvanced(name)) {
+                    category = PropertyCategory.ADVANCED;
+                }
+            }
+            if (category != null) {
+                property.setCategory(category);
+            }
+            properties.add(property);
+        }
+
+        switch (mSortMode) {
+            case BY_ORIGIN:
+                return sortByOrigin(node, properties);
+
+            case ALPHABETICAL:
+                return sortAlphabetically(node, properties);
+
+            default:
+            case NATURAL:
+                return sortNatural(node, properties);
+        }
+    }
+
+    protected Collection<? extends Property> sortAlphabetically(
+            UiViewElementNode node,
+            List<XmlProperty> properties) {
+        Collections.sort(properties, Property.ALPHABETICAL);
+        return properties;
+    }
+
+    protected Collection<? extends Property> sortByOrigin(
+            UiViewElementNode node,
+            List<XmlProperty> properties) {
+        List<Property> collapsed = new ArrayList<Property>(properties.size());
+        List<Property> layoutProperties = Lists.newArrayListWithExpectedSize(20);
+        List<Property> marginProperties = null;
+        List<Property> deprecatedProperties = null;
+        Map<String, ComplexProperty> categoryToProperty = new HashMap<String, ComplexProperty>();
+        Multimap<String, Property> categoryToProperties = ArrayListMultimap.create();
+
+
+        ViewElementDescriptor parent = (ViewElementDescriptor) properties.get(0).getDescriptor()
+                .getParent();
+        Map<String, Integer> categoryPriorities = Maps.newHashMap();
+        int nextCategoryPriority = 100;
+        while (parent != null) {
+            categoryPriorities.put(parent.getFullClassName(), nextCategoryPriority += 100);
+            parent = parent.getSuperClassDesc();
+        }
+
+        for (int i = 0, max = properties.size(); i < max; i++) {
+            XmlProperty property = properties.get(i);
+
+            AttributeDescriptor descriptor = property.getDescriptor();
+            if (descriptor.isDeprecated()) {
+                if (deprecatedProperties == null) {
+                    deprecatedProperties = Lists.newArrayListWithExpectedSize(10);
+                }
+                deprecatedProperties.add(property);
+                continue;
+            }
+
+            String firstName = descriptor.getXmlLocalName();
+            if (firstName.startsWith(ATTR_LAYOUT_PREFIX)) {
+                if (firstName.startsWith(ATTR_LAYOUT_MARGIN)) {
+                    if (marginProperties == null) {
+                        marginProperties = Lists.newArrayListWithExpectedSize(5);
+                    }
+                    marginProperties.add(property);
+                } else {
+                    layoutProperties.add(property);
+                }
+                continue;
+            }
+
+            if (firstName.equals(ATTR_ID)) {
+                // Add id to the front (though the layout parameters will be added to
+                // the front of this at the end)
+                property.setPriority(PRIO_FIRST);
+                collapsed.add(property);
+                continue;
+            }
+
+            if (property.getCategory() == PropertyCategory.PREFERRED) {
+                collapsed.add(property);
+                // Fall through: these are *duplicated* inside their defining categories!
+                // However, create a new instance of the property, such that the propertysheet
+                // doesn't see the same property instance twice (when selected, it will highlight
+                // both, etc.) Also, set the category to Normal such that we don't draw attention
+                // to it again. We want it to appear in both places such that somebody looking
+                // within a category will always find it there, even if for this specific
+                // view type it's a common attribute and replicated up at the top.
+                XmlProperty oldProperty = property;
+                property = new XmlProperty(oldProperty.getEditor(), this, node,
+                        oldProperty.getDescriptor());
+                property.setPriority(oldProperty.getPriority());
+            }
+
+            IAttributeInfo attributeInfo = descriptor.getAttributeInfo();
+            if (attributeInfo != null && attributeInfo.getDefinedBy() != null) {
+                String category = attributeInfo.getDefinedBy();
+                ComplexProperty complex = categoryToProperty.get(category);
+                if (complex == null) {
+                    complex = new ComplexProperty(
+                            category.substring(category.lastIndexOf('.') + 1),
+                            "[]",
+                            null /* properties */);
+                    categoryToProperty.put(category, complex);
+                    Integer categoryPriority = categoryPriorities.get(category);
+                    if (categoryPriority != null) {
+                        complex.setPriority(categoryPriority);
+                    } else {
+                        // Descriptor for an attribute whose definedBy does *not*
+                        // correspond to one of the known superclasses of this widget.
+                        // This sometimes happens; for example, a RatingBar will pull in
+                        // an ImageView's minWidth attribute. Probably an error in the
+                        // metadata, but deal with it gracefully here.
+                        categoryPriorities.put(category, nextCategoryPriority += 100);
+                        complex.setPriority(nextCategoryPriority);
+                    }
+                }
+                categoryToProperties.put(category, property);
+                continue;
+            } else {
+                collapsed.add(property);
+            }
+        }
+
+        // Update the complex properties
+        for (String category : categoryToProperties.keySet()) {
+            Collection<Property> subProperties = categoryToProperties.get(category);
+            if (subProperties.size() > 1) {
+                ComplexProperty complex = categoryToProperty.get(category);
+                assert complex != null : category;
+                Property[] subArray = new Property[subProperties.size()];
+                complex.setProperties(subProperties.toArray(subArray));
+                //complex.setPriority(subArray[0].getPriority());
+
+                collapsed.add(complex);
+
+                boolean allAdvanced = true;
+                boolean isPreferred = false;
+                for (Property p : subProperties) {
+                    PropertyCategory c = p.getCategory();
+                    if (c != PropertyCategory.ADVANCED) {
+                        allAdvanced = false;
+                    }
+                    if (c == PropertyCategory.PREFERRED) {
+                        isPreferred = true;
+                    }
+                }
+                if (isPreferred) {
+                    complex.setCategory(PropertyCategory.PREFERRED);
+                } else if (allAdvanced) {
+                    complex.setCategory(PropertyCategory.ADVANCED);
+                }
+            } else if (subProperties.size() == 1) {
+                collapsed.add(subProperties.iterator().next());
+            }
+        }
+
+        if (layoutProperties.size() > 0 || marginProperties != null) {
+            if (marginProperties != null) {
+                XmlProperty[] m =
+                        marginProperties.toArray(new XmlProperty[marginProperties.size()]);
+                Property marginProperty = new ComplexProperty(
+                        "Margins",
+                        "[]",
+                        m);
+                layoutProperties.add(marginProperty);
+                marginProperty.setPriority(PRIO_LAST);
+
+                for (XmlProperty p : m) {
+                    p.setParent(marginProperty);
+                }
+            }
+            Property[] l = layoutProperties.toArray(new Property[layoutProperties.size()]);
+            Arrays.sort(l, Property.PRIORITY);
+            Property property = new ComplexProperty(
+                    "Layout Parameters",
+                    "[]",
+                    l);
+            for (Property p : l) {
+                if (p instanceof XmlProperty) {
+                    ((XmlProperty) p).setParent(property);
+                }
+            }
+            property.setCategory(PropertyCategory.PREFERRED);
+            collapsed.add(property);
+            property.setPriority(PRIO_SECOND);
+        }
+
+        if (deprecatedProperties != null && deprecatedProperties.size() > 0) {
+            Property property = new ComplexProperty(
+                    "Deprecated",
+                    "(Deprecated Properties)",
+                    deprecatedProperties.toArray(new Property[deprecatedProperties.size()]));
+            property.setPriority(PRIO_LAST);
+            collapsed.add(property);
+        }
+
+        Collections.sort(collapsed, Property.PRIORITY);
+
+        return collapsed;
+    }
+
+    protected Collection<? extends Property> sortNatural(
+            UiViewElementNode node,
+            List<XmlProperty> properties) {
+        Collections.sort(properties, Property.ALPHABETICAL);
+        List<Property> collapsed = new ArrayList<Property>(properties.size());
+        List<Property> layoutProperties = Lists.newArrayListWithExpectedSize(20);
+        List<Property> marginProperties = null;
+        List<Property> deprecatedProperties = null;
+        Map<String, ComplexProperty> categoryToProperty = new HashMap<String, ComplexProperty>();
+        Multimap<String, Property> categoryToProperties = ArrayListMultimap.create();
+
+        for (int i = 0, max = properties.size(); i < max; i++) {
+            XmlProperty property = properties.get(i);
+
+            AttributeDescriptor descriptor = property.getDescriptor();
+            if (descriptor.isDeprecated()) {
+                if (deprecatedProperties == null) {
+                    deprecatedProperties = Lists.newArrayListWithExpectedSize(10);
+                }
+                deprecatedProperties.add(property);
+                continue;
+            }
+
+            String firstName = descriptor.getXmlLocalName();
+            if (firstName.startsWith(ATTR_LAYOUT_PREFIX)) {
+                if (firstName.startsWith(ATTR_LAYOUT_MARGIN)) {
+                    if (marginProperties == null) {
+                        marginProperties = Lists.newArrayListWithExpectedSize(5);
+                    }
+                    marginProperties.add(property);
+                } else {
+                    layoutProperties.add(property);
+                }
+                continue;
+            }
+
+            if (firstName.equals(ATTR_ID)) {
+                // Add id to the front (though the layout parameters will be added to
+                // the front of this at the end)
+                property.setPriority(PRIO_FIRST);
+                collapsed.add(property);
+                continue;
+            }
+
+            String category = PropertyMetadata.getCategory(firstName);
+            if (category != null) {
+                ComplexProperty complex = categoryToProperty.get(category);
+                if (complex == null) {
+                    complex = new ComplexProperty(
+                            category,
+                            "[]",
+                            null /* properties */);
+                    categoryToProperty.put(category, complex);
+                    complex.setPriority(property.getPriority());
+                }
+                categoryToProperties.put(category, property);
+                continue;
+            }
+
+            // Index of second word in the first name, so in fooBar it's 3 (index of 'B')
+            int firstNameIndex = firstName.length();
+            for (int k = 0, kn = firstName.length(); k < kn; k++) {
+                if (Character.isUpperCase(firstName.charAt(k))) {
+                    firstNameIndex = k;
+                    break;
+                }
+            }
+
+            // Scout forwards and see how many properties we can combine
+            int j = i + 1;
+            if (property.getCategory() != PropertyCategory.PREFERRED
+                    && !property.getDescriptor().isDeprecated()) {
+                for (; j < max; j++) {
+                    XmlProperty next = properties.get(j);
+                    String nextName = next.getName();
+                    if (nextName.regionMatches(0, firstName, 0, firstNameIndex)
+                            // Also make sure we begin the second word at the next
+                            // character; if not, we could have something like
+                            // scrollBar
+                            // scrollingBehavior
+                            && nextName.length() > firstNameIndex
+                            && Character.isUpperCase(nextName.charAt(firstNameIndex))) {
+
+                        // Deprecated attributes, and preferred attributes, should not
+                        // be pushed into normal clusters (preferred stay top-level
+                        // and sort to the top, deprecated are all put in the same cluster at
+                        // the end)
+
+                        if (next.getCategory() == PropertyCategory.PREFERRED) {
+                            break;
+                        }
+                        if (next.getDescriptor().isDeprecated()) {
+                            break;
+                        }
+
+                        // This property should be combined with the previous
+                        // property
+                    } else {
+                        break;
+                    }
+                }
+            }
+            if (j - i > 1) {
+                // Combining multiple properties: all the properties from i
+                // through j inclusive
+                XmlProperty[] subprops = new XmlProperty[j - i];
+                for (int k = i, index = 0; k < j; k++, index++) {
+                    subprops[index] = properties.get(k);
+                }
+                Arrays.sort(subprops, Property.PRIORITY);
+
+                // See if we can compute a LONGER base than just the first word.
+                // For example, if we have "lineSpacingExtra" and "lineSpacingMultiplier"
+                // we'd like the base to be "lineSpacing", not "line".
+                int common = firstNameIndex;
+                for (int k = firstNameIndex + 1, n = firstName.length(); k < n; k++) {
+                    if (Character.isUpperCase(firstName.charAt(k))) {
+                        common = k;
+                        break;
+                    }
+                }
+                if (common > firstNameIndex) {
+                    for (int k = 0, n = subprops.length; k < n; k++) {
+                        String nextName = subprops[k].getName();
+                        if (nextName.regionMatches(0, firstName, 0, common)
+                                // Also make sure we begin the second word at the next
+                                // character; if not, we could have something like
+                                // scrollBar
+                                // scrollingBehavior
+                                && nextName.length() > common
+                                && Character.isUpperCase(nextName.charAt(common))) {
+                            // New prefix is okay
+                        } else {
+                            common = firstNameIndex;
+                            break;
+                        }
+                    }
+                    firstNameIndex = common;
+                }
+
+                String base = firstName.substring(0, firstNameIndex);
+                base = DescriptorsUtils.capitalize(base);
+                Property complexProperty = new ComplexProperty(
+                        base,
+                        "[]",
+                        subprops);
+                complexProperty.setPriority(subprops[0].getPriority());
+                //complexProperty.setCategory(PropertyCategory.PREFERRED);
+                collapsed.add(complexProperty);
+                boolean allAdvanced = true;
+                boolean isPreferred = false;
+                for (XmlProperty p : subprops) {
+                    p.setParent(complexProperty);
+                    PropertyCategory c = p.getCategory();
+                    if (c != PropertyCategory.ADVANCED) {
+                        allAdvanced = false;
+                    }
+                    if (c == PropertyCategory.PREFERRED) {
+                        isPreferred = true;
+                    }
+                }
+                if (isPreferred) {
+                    complexProperty.setCategory(PropertyCategory.PREFERRED);
+                } else if (allAdvanced) {
+                    complexProperty.setCategory(PropertyCategory.PREFERRED);
+                }
+            } else {
+                // Add the individual properties (usually 1, sometimes 2
+                for (int k = i; k < j; k++) {
+                    collapsed.add(properties.get(k));
+                }
+            }
+
+            i = j - 1; // -1: compensate in advance for the for-loop adding 1
+        }
+
+        // Update the complex properties
+        for (String category : categoryToProperties.keySet()) {
+            Collection<Property> subProperties = categoryToProperties.get(category);
+            if (subProperties.size() > 1) {
+                ComplexProperty complex = categoryToProperty.get(category);
+                assert complex != null : category;
+                Property[] subArray = new Property[subProperties.size()];
+                complex.setProperties(subProperties.toArray(subArray));
+                complex.setPriority(subArray[0].getPriority());
+                collapsed.add(complex);
+
+                boolean allAdvanced = true;
+                boolean isPreferred = false;
+                for (Property p : subProperties) {
+                    PropertyCategory c = p.getCategory();
+                    if (c != PropertyCategory.ADVANCED) {
+                        allAdvanced = false;
+                    }
+                    if (c == PropertyCategory.PREFERRED) {
+                        isPreferred = true;
+                    }
+                }
+                if (isPreferred) {
+                    complex.setCategory(PropertyCategory.PREFERRED);
+                } else if (allAdvanced) {
+                    complex.setCategory(PropertyCategory.ADVANCED);
+                }
+            } else if (subProperties.size() == 1) {
+                collapsed.add(subProperties.iterator().next());
+            }
+        }
+
+        if (layoutProperties.size() > 0 || marginProperties != null) {
+            if (marginProperties != null) {
+                XmlProperty[] m =
+                        marginProperties.toArray(new XmlProperty[marginProperties.size()]);
+                Property marginProperty = new ComplexProperty(
+                        "Margins",
+                        "[]",
+                        m);
+                layoutProperties.add(marginProperty);
+                marginProperty.setPriority(PRIO_LAST);
+
+                for (XmlProperty p : m) {
+                    p.setParent(marginProperty);
+                }
+            }
+            Property[] l = layoutProperties.toArray(new Property[layoutProperties.size()]);
+            Arrays.sort(l, Property.PRIORITY);
+            Property property = new ComplexProperty(
+                    "Layout Parameters",
+                    "[]",
+                    l);
+            for (Property p : l) {
+                if (p instanceof XmlProperty) {
+                    ((XmlProperty) p).setParent(property);
+                }
+            }
+            property.setCategory(PropertyCategory.PREFERRED);
+            collapsed.add(property);
+            property.setPriority(PRIO_SECOND);
+        }
+
+        if (deprecatedProperties != null && deprecatedProperties.size() > 0) {
+            Property property = new ComplexProperty(
+                    "Deprecated",
+                    "(Deprecated Properties)",
+                    deprecatedProperties.toArray(new Property[deprecatedProperties.size()]));
+            property.setPriority(PRIO_LAST);
+            collapsed.add(property);
+        }
+
+        Collections.sort(collapsed, Property.PRIORITY);
+
+        return collapsed;
+    }
+
+    PropertyTable getPropertyTable() {
+        return mPropertyTable;
+    }
+
+    @Nullable
+    GraphicalEditorPart getGraphicalEditor() {
+        return mGraphicalEditorPart;
+    }
+
+    // HACK: This should be passed into each property instead
+    public Object getCurrentViewObject() {
+        return mCurrentViewCookie;
+    }
+
+    public void setSortingMode(SortingMode sortingMode) {
+        mSortMode = sortingMode;
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyMetadata.java
new file mode 100644
index 0000000..cdf7664
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertyMetadata.java
@@ -0,0 +1,329 @@
+/*
+ * 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.properties;
+
+import static com.android.ide.common.layout.LayoutConstants.ATTR_CONTENT_DESCRIPTION;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_HINT;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** Extra metadata about properties not available from the descriptors (yet) */
+class PropertyMetadata {
+    static boolean isAdvanced(@NonNull String name) {
+        return sAdvanced.contains(name);
+    }
+
+    static boolean isPreferred(@NonNull String name) {
+        return sPreferred.contains(name);
+    }
+
+    @Nullable
+    static String getCategory(@NonNull String name) {
+        //return sCategories.get(name);
+        assert false : "Disabled to save memory since this method is not currently used.";
+        return null;
+    }
+
+    private static final int ADVANCED_MAP_SIZE = 134;
+    private static final Set<String> sAdvanced = new HashSet<String>(ADVANCED_MAP_SIZE);
+    static {
+        // This metadata about which attributes are "advanced" was generated as follows:
+        // First, I ran the sdk/attribute_stats project with the --list argument to dump out
+        // *all* referenced XML attributes found in layouts, run against a bunch of
+        // sample Android code (development/samples, packages/apps, vendor, etc.
+        //
+        // Then I iterated over the LayoutDescriptors' ViewElementDescriptors'
+        // AttributeDescriptors, and basically diffed the two: any attribute descriptor name
+        // which was *not* found in any of the representative layouts is added here
+        // as an advanced property.
+        //
+        // Then I manually edited in some attributes that were referenced in the sample
+        // layouts but which I still consider to be advanced:
+        // -- nothing right now
+
+        // I also manually *removed* some entries from the below list:
+        //     drawableBottom   (the others, drawableTop, drawableLeft and drawableRight were all
+        //                       NOT on the list so keep bottom off for symmetry)
+        //     rating     (useful when you deal with a RatingsBar component)
+
+
+        // Automatically generated, see above:
+        sAdvanced.add("alwaysDrawnWithCache");
+        sAdvanced.add("animationCache");
+        sAdvanced.add("animationDuration");
+        sAdvanced.add("animationResolution");
+        sAdvanced.add("baseline");
+        sAdvanced.add("bufferType");
+        sAdvanced.add("calendarViewShown");
+        sAdvanced.add("completionHint");
+        sAdvanced.add("completionHintView");
+        sAdvanced.add("completionThreshold");
+        sAdvanced.add("cursorVisible");
+        sAdvanced.add("dateTextAppearance");
+        sAdvanced.add("dial");
+        sAdvanced.add("digits");
+        sAdvanced.add("disableChildrenWhenDisabled");
+        sAdvanced.add("disabledAlpha");
+        sAdvanced.add("drawableAlpha");
+        sAdvanced.add("drawableEnd");
+        sAdvanced.add("drawableStart");
+        sAdvanced.add("drawingCacheQuality");
+        sAdvanced.add("dropDownAnchor");
+        sAdvanced.add("dropDownHeight");
+        sAdvanced.add("dropDownHorizontalOffset");
+        sAdvanced.add("dropDownSelector");
+        sAdvanced.add("dropDownVerticalOffset");
+        sAdvanced.add("dropDownWidth");
+        sAdvanced.add("editorExtras");
+        sAdvanced.add("ems");
+        sAdvanced.add("endYear");
+        sAdvanced.add("eventsInterceptionEnabled");
+        sAdvanced.add("fadeDuration");
+        sAdvanced.add("fadeEnabled");
+        sAdvanced.add("fadeOffset");
+        sAdvanced.add("fadeScrollbars");
+        sAdvanced.add("filterTouchesWhenObscured");
+        sAdvanced.add("firstDayOfWeek");
+        sAdvanced.add("flingable");
+        sAdvanced.add("focusedMonthDateColor");
+        sAdvanced.add("foregroundInsidePadding");
+        sAdvanced.add("format");
+        sAdvanced.add("gestureColor");
+        sAdvanced.add("gestureStrokeAngleThreshold");
+        sAdvanced.add("gestureStrokeLengthThreshold");
+        sAdvanced.add("gestureStrokeSquarenessThreshold");
+        sAdvanced.add("gestureStrokeType");
+        sAdvanced.add("gestureStrokeWidth");
+        sAdvanced.add("hand_hour");
+        sAdvanced.add("hand_minute");
+        sAdvanced.add("hapticFeedbackEnabled");
+        sAdvanced.add("id");
+        sAdvanced.add("imeActionId");
+        sAdvanced.add("imeActionLabel");
+        sAdvanced.add("indeterminateDrawable");
+        sAdvanced.add("indeterminateDuration");
+        sAdvanced.add("inputMethod");
+        sAdvanced.add("interpolator");
+        sAdvanced.add("isScrollContainer");
+        sAdvanced.add("keepScreenOn");
+        sAdvanced.add("layerType");
+        sAdvanced.add("layoutDirection");
+        sAdvanced.add("maxDate");
+        sAdvanced.add("minDate");
+        sAdvanced.add("mode");
+        sAdvanced.add("numeric");
+        sAdvanced.add("paddingEnd");
+        sAdvanced.add("paddingStart");
+        sAdvanced.add("persistentDrawingCache");
+        sAdvanced.add("phoneNumber");
+        sAdvanced.add("popupBackground");
+        sAdvanced.add("popupPromptView");
+        sAdvanced.add("privateImeOptions");
+        sAdvanced.add("quickContactWindowSize");
+        //sAdvanced.add("rating");
+        sAdvanced.add("requiresFadingEdge");
+        sAdvanced.add("rotation");
+        sAdvanced.add("rotationX");
+        sAdvanced.add("rotationY");
+        sAdvanced.add("saveEnabled");
+        sAdvanced.add("scaleX");
+        sAdvanced.add("scaleY");
+        sAdvanced.add("scrollX");
+        sAdvanced.add("scrollY");
+        sAdvanced.add("scrollbarAlwaysDrawHorizontalTrack");
+        sAdvanced.add("scrollbarDefaultDelayBeforeFade");
+        sAdvanced.add("scrollbarFadeDuration");
+        sAdvanced.add("scrollbarSize");
+        sAdvanced.add("scrollbarThumbHorizontal");
+        sAdvanced.add("scrollbarThumbVertical");
+        sAdvanced.add("scrollbarTrackHorizontal");
+        sAdvanced.add("scrollbarTrackVertical");
+        sAdvanced.add("secondaryProgress");
+        sAdvanced.add("selectedDateVerticalBar");
+        sAdvanced.add("selectedWeekBackgroundColor");
+        sAdvanced.add("selectionDivider");
+        sAdvanced.add("selectionDividerHeight");
+        sAdvanced.add("showWeekNumber");
+        sAdvanced.add("shownWeekCount");
+        sAdvanced.add("solidColor");
+        sAdvanced.add("soundEffectsEnabled");
+        sAdvanced.add("spinnerMode");
+        sAdvanced.add("spinnersShown");
+        sAdvanced.add("startYear");
+        sAdvanced.add("switchMinWidth");
+        sAdvanced.add("switchPadding");
+        sAdvanced.add("switchTextAppearance");
+        sAdvanced.add("textColorHighlight");
+        sAdvanced.add("textCursorDrawable");
+        sAdvanced.add("textDirection");
+        sAdvanced.add("textEditNoPasteWindowLayout");
+        sAdvanced.add("textEditPasteWindowLayout");
+        sAdvanced.add("textEditSideNoPasteWindowLayout");
+        sAdvanced.add("textEditSidePasteWindowLayout");
+        sAdvanced.add("textEditSuggestionItemLayout");
+        sAdvanced.add("textIsSelectable");
+        sAdvanced.add("textOff");
+        sAdvanced.add("textOn");
+        sAdvanced.add("textScaleX");
+        sAdvanced.add("textSelectHandle");
+        sAdvanced.add("textSelectHandleLeft");
+        sAdvanced.add("textSelectHandleRight");
+        sAdvanced.add("thumbOffset");
+        sAdvanced.add("thumbTextPadding");
+        sAdvanced.add("tint");
+        sAdvanced.add("track");
+        sAdvanced.add("transformPivotX");
+        sAdvanced.add("transformPivotY");
+        sAdvanced.add("translationX");
+        sAdvanced.add("translationY");
+        sAdvanced.add("uncertainGestureColor");
+        sAdvanced.add("unfocusedMonthDateColor");
+        sAdvanced.add("unselectedAlpha");
+        sAdvanced.add("verticalScrollbarPosition");
+        sAdvanced.add("weekDayTextAppearance");
+        sAdvanced.add("weekNumberColor");
+        sAdvanced.add("weekSeparatorLineColor");
+
+        assert sAdvanced.size() == ADVANCED_MAP_SIZE : sAdvanced.size();
+
+    }
+
+    private static final int PREFERRED_MAP_SIZE = 7;
+    private static final Set<String> sPreferred = new HashSet<String>(PREFERRED_MAP_SIZE);
+    static {
+        // Manual registrations of attributes that should be treated as preferred if
+        // they are available on a widget even if they don't show up in the top 10% of
+        // usages (which the view metadata provides)
+        sPreferred.add(ATTR_TEXT);
+        sPreferred.add(ATTR_CONTENT_DESCRIPTION);
+        sPreferred.add(ATTR_HINT);
+        sPreferred.add("indeterminate");
+        sPreferred.add("progress");
+        sPreferred.add("rating");
+        sPreferred.add("max");
+        assert sPreferred.size() == PREFERRED_MAP_SIZE : sPreferred.size();
+    }
+
+    /*
+    private static final int CATEGORY_MAP_SIZE = 62;
+    private static final Map<String, String> sCategories =
+            new HashMap<String, String>(CATEGORY_MAP_SIZE);
+    static {
+        sCategories.put("requiresFadingEdge", "Scrolling");
+        sCategories.put("fadingEdgeLength", "Scrolling");
+        sCategories.put("scrollbarSize", "Scrolling");
+        sCategories.put("scrollbarThumbVertical", "Scrolling");
+        sCategories.put("scrollbarThumbHorizontal", "Scrolling");
+        sCategories.put("scrollbarTrackHorizontal", "Scrolling");
+        sCategories.put("scrollbarTrackVertical", "Scrolling");
+        sCategories.put("scrollbarAlwaysDrawHorizontalTrack", "Scrolling");
+        sCategories.put("scrollbarAlwaysDrawVerticalTrack", "Scrolling");
+        sCategories.put("scrollViewStyle", "Scrolling");
+        sCategories.put("scrollbars", "Scrolling");
+        sCategories.put("scrollingCache", "Scrolling");
+        sCategories.put("scrollHorizontally", "Scrolling");
+        sCategories.put("scrollbarFadeDuration", "Scrolling");
+        sCategories.put("scrollbarDefaultDelayBeforeFade", "Scrolling");
+        sCategories.put("fastScrollEnabled", "Scrolling");
+        sCategories.put("smoothScrollbar", "Scrolling");
+        sCategories.put("isScrollContainer", "Scrolling");
+        sCategories.put("fadeScrollbars", "Scrolling");
+        sCategories.put("overScrollMode", "Scrolling");
+        sCategories.put("overScrollHeader", "Scrolling");
+        sCategories.put("overScrollFooter", "Scrolling");
+        sCategories.put("verticalScrollbarPosition", "Scrolling");
+        sCategories.put("fastScrollAlwaysVisible", "Scrolling");
+        sCategories.put("fastScrollThumbDrawable", "Scrolling");
+        sCategories.put("fastScrollPreviewBackgroundLeft", "Scrolling");
+        sCategories.put("fastScrollPreviewBackgroundRight", "Scrolling");
+        sCategories.put("fastScrollTrackDrawable", "Scrolling");
+        sCategories.put("fastScrollOverlayPosition", "Scrolling");
+        sCategories.put("horizontalScrollViewStyle", "Scrolling");
+        sCategories.put("fastScrollTextColor", "Scrolling");
+        sCategories.put("scrollbarSize", "Scrolling");
+        sCategories.put("scrollbarSize", "Scrolling");
+        sCategories.put("scrollbarSize", "Scrolling");
+        sCategories.put("scrollbarSize", "Scrolling");
+        sCategories.put("scrollbarSize", "Scrolling");
+
+        // TODO: All the styles: radioButtonStyle, ratingBarStyle, progressBarStyle, ...
+
+        sCategories.put("focusable", "Focus");
+        sCategories.put("focusableInTouchMode", "Focus");
+        sCategories.put("nextFocusLeft", "Focus");
+        sCategories.put("nextFocusRight", "Focus");
+        sCategories.put("nextFocusUp", "Focus");
+        sCategories.put("nextFocusDown", "Focus");
+        sCategories.put("descendantFocusability", "Focus");
+        sCategories.put("selectAllOnFocus", "Focus");
+        sCategories.put("nextFocusForward", "Focus");
+        sCategories.put("colorFocusedHighlight", "Focus");
+
+        sCategories.put("rotation", "Transforms");
+        sCategories.put("scrollX", "Transforms");
+        sCategories.put("scrollY", "Transforms");
+        sCategories.put("rotationX", "Transforms");
+        sCategories.put("rotationY", "Transforms");
+        sCategories.put("transformPivotX", "Transforms");
+        sCategories.put("transformPivotY", "Transforms");
+        sCategories.put("translationX", "Transforms");
+        sCategories.put("translationY", "Transforms");
+        sCategories.put("scaleX", "Transforms");
+        sCategories.put("scaleY", "Transforms");
+
+        sCategories.put("width", "Size");
+        sCategories.put("height", "Size");
+        sCategories.put("minWidth", "Size");
+        sCategories.put("minHeight", "Size");
+
+        sCategories.put("longClickable", "Clicks");
+        sCategories.put("onClick", "Clicks");
+        sCategories.put("clickable", "Clicks");
+        sCategories.put("hapticFeedbackEnabled", "Clicks");
+
+        sCategories.put("duplicateParentState", "State");
+        sCategories.put("addStatesFromChildren", "State");
+
+        assert sCategories.size() == CATEGORY_MAP_SIZE : sCategories.size();
+    }
+    */
+
+//    private static final int PRIO_CLZ_LAYOUT = 1000;
+//    private static final int PRIO_CLZ_TEXT = 2000;
+//    private static final int PRIO_CLZ_DRAWABLE = 3000;
+//    private static final int PRIO_CLZ_ANIMATION = 4000;
+//    private static final int PRIO_CLZ_FOCUS = 5000;
+//
+//    private static final int PRIORITY_MAP_SIZE = 100;
+//    private static final Map<String, Integer> sPriorities =
+//            new HashMap<String, Integer>(PRIORITY_MAP_SIZE);
+//    static {
+//        // TODO: I should put all the properties roughly based on their original order: this
+//        // will correspond to the rough order they came in with
+//        // TODO: How can I make similar complex properties show up adjacent; e.g. min and max
+//        sPriorities.put("min", PRIO_CLZ_LAYOUT);
+//        sPriorities.put("max", PRIO_CLZ_LAYOUT);
+//
+//        assert sPriorities.size() == PRIORITY_MAP_SIZE : sPriorities.size();
+//    }
+
+    // TODO: Emit metadata into a file
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertySheetPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertySheetPage.java
new file mode 100644
index 0000000..4a33223
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/PropertySheetPage.java
@@ -0,0 +1,367 @@
+/*
+ * 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.properties;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertyFactory.SortingMode;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.Page;
+import org.eclipse.ui.views.properties.IPropertySheetPage;
+import org.eclipse.wb.internal.core.model.property.Property;
+import org.eclipse.wb.internal.core.model.property.table.IPropertyExceptionHandler;
+import org.eclipse.wb.internal.core.model.property.table.PropertyTable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Property sheet page used when the graphical layout editor is chosen
+ */
+public class PropertySheetPage extends Page implements IPropertySheetPage, IUiUpdateListener {
+    private PropertyTable mPropertyTable;
+    private final GraphicalEditorPart mEditor;
+    private PropertyFactory mPropertyFactory;
+    private Property mActiveProperty;
+    private Action mDefaultValueAction;
+    private Action mShowAdvancedPropertiesAction;
+    private Action mSortAlphaAction;
+    private Action mCollapseAll;
+    private Action mExpandAll;
+    private List<CanvasViewInfo> mSelection;
+
+    private static final String EXPAND_DISABLED_ICON = "expandall-disabled";          //$NON-NLS-1$
+    private static final String EXPAND_ICON = "expandall";                            //$NON-NLS-1$
+    private static final String DEFAULT_ICON = "properties_default";                  //$NON-NLS-1$
+    private static final String ADVANCED_ICON = "filter_advanced_properties";         //$NON-NLS-1$
+    private static final String ALPHA_ICON = "sort_alpha";                            //$NON-NLS-1$
+    // TODO: goto-definition.png
+
+    /**
+     * Constructs a new {@link PropertySheetPage} associated with the given
+     * editor
+     *
+     * @param editor the editor associated with this property sheet page
+     */
+    public PropertySheetPage(GraphicalEditorPart editor) {
+        mEditor = editor;
+    }
+
+    PropertyFactory getPropertyFactory() {
+        if (mPropertyFactory == null) {
+            assert mPropertyTable != null;
+            mPropertyFactory = new PropertyFactory(mEditor, mPropertyTable);
+        }
+
+        return mPropertyFactory;
+    }
+
+    @Override
+    public void createControl(Composite parent) {
+        mPropertyTable = new PropertyTable(parent, SWT.NONE);
+        mPropertyTable.setExceptionHandler(new IPropertyExceptionHandler() {
+            @Override
+            public void handle(Throwable e) {
+                AdtPlugin.log(e, null);
+            }
+        });
+        mPropertyTable.setDefaultCollapsedNames(Arrays.asList(
+                "Deprecated",
+                "Layout Parameters|Margins"));
+
+        createActions();
+        setPropertyTableContextMenu();
+    }
+
+    @Override
+    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+        if (selection instanceof TreeSelection
+                && mPropertyTable != null && !mPropertyTable.isDisposed()) {
+            TreeSelection treeSelection = (TreeSelection) selection;
+
+            stopTrackingSelection();
+
+            if (treeSelection.isEmpty()) {
+                mSelection = Collections.emptyList();
+            } else {
+                int selectionCount = treeSelection.size();
+                List<CanvasViewInfo> newSelection = new ArrayList<CanvasViewInfo>(selectionCount);
+                Iterator<?> iterator = treeSelection.iterator();
+                while (iterator.hasNext()) {
+                    Object next = iterator.next();
+                    if (next instanceof CanvasViewInfo) {
+                        CanvasViewInfo info = (CanvasViewInfo) next;
+                        newSelection.add(info);
+                    }
+                }
+                mSelection = newSelection;
+            }
+
+            startTrackingSelection();
+
+            refreshProperties();
+        }
+    }
+
+    @Override
+    public void dispose() {
+        stopTrackingSelection();
+        super.dispose();
+    }
+
+    private void startTrackingSelection() {
+        if (mSelection != null && !mSelection.isEmpty()) {
+            for (CanvasViewInfo item : mSelection) {
+                UiViewElementNode node = item.getUiViewNode();
+                if (node != null) {
+                    node.addUpdateListener(this);
+                }
+            }
+        }
+    }
+
+    private void stopTrackingSelection() {
+        if (mSelection != null && !mSelection.isEmpty()) {
+            for (CanvasViewInfo item : mSelection) {
+                UiViewElementNode node = item.getUiViewNode();
+                if (node != null) {
+                    node.removeUpdateListener(this);
+                }
+            }
+        }
+        mSelection = null;
+    }
+
+    // Implements IUiUpdateListener
+    @Override
+    public void uiElementNodeUpdated(UiElementNode node, UiUpdateState state) {
+        refreshProperties();
+    }
+
+    @Override
+    public Control getControl() {
+        return mPropertyTable;
+    }
+
+    @Override
+    public void setFocus() {
+        mPropertyTable.setFocus();
+    }
+
+    @Override
+    public void makeContributions(IMenuManager menuManager,
+            IToolBarManager toolBarManager, IStatusLineManager statusLineManager) {
+        toolBarManager.add(mShowAdvancedPropertiesAction);
+        toolBarManager.add(new Separator());
+        toolBarManager.add(mSortAlphaAction);
+        toolBarManager.add(new Separator());
+        toolBarManager.add(mDefaultValueAction);
+        toolBarManager.add(new Separator());
+        toolBarManager.add(mExpandAll);
+        toolBarManager.add(mCollapseAll);
+        toolBarManager.add(new Separator());
+    }
+
+    private void createActions() {
+        ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
+        IconFactory iconFactory = IconFactory.getInstance();
+
+        mExpandAll = new PropertySheetAction(
+                IAction.AS_PUSH_BUTTON,
+                "Expand All",
+                ACTION_EXPAND,
+                iconFactory.getImageDescriptor(EXPAND_ICON),
+                iconFactory.getImageDescriptor(EXPAND_DISABLED_ICON));
+
+        mCollapseAll = new PropertySheetAction(
+                IAction.AS_PUSH_BUTTON,
+                "Collapse All",
+                ACTION_COLLAPSE,
+                sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL),
+                sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL_DISABLED));
+
+        mShowAdvancedPropertiesAction = new PropertySheetAction(
+                IAction.AS_CHECK_BOX,
+                "Show Advanced Properties",
+                ACTION_SHOW_ADVANCED,
+                iconFactory.getImageDescriptor(ADVANCED_ICON),
+                null);
+
+        mSortAlphaAction = new PropertySheetAction(
+                IAction.AS_CHECK_BOX,
+                "Sort Alphabetically",
+                ACTION_SORT_ALPHA,
+                iconFactory.getImageDescriptor(ALPHA_ICON),
+                null);
+
+        mDefaultValueAction = new PropertySheetAction(
+                IAction.AS_PUSH_BUTTON,
+                "Restore Default Value",
+                ACTION_DEFAULT_VALUE,
+                iconFactory.getImageDescriptor(DEFAULT_ICON),
+                null);
+
+        // Listen on the selection in the property sheet so we can update the
+        // Restore Default Value action
+        ISelectionChangedListener listener = new ISelectionChangedListener() {
+            @Override
+            public void selectionChanged(SelectionChangedEvent event) {
+                StructuredSelection selection = (StructuredSelection) event.getSelection();
+                mActiveProperty = (Property) selection.getFirstElement();
+                updateDefaultValueAction();
+            }
+        };
+        mPropertyTable.addSelectionChangedListener(listener);
+    }
+
+    /**
+     * Updates the state of {@link #mDefaultValueAction}.
+     */
+    private void updateDefaultValueAction() {
+        if (mActiveProperty != null) {
+            try {
+                mDefaultValueAction.setEnabled(mActiveProperty.isModified());
+            } catch (Exception e) {
+                AdtPlugin.log(e, null);
+            }
+        } else {
+            mDefaultValueAction.setEnabled(false);
+        }
+    }
+
+    /**
+     * Sets the context menu for {@link #mPropertyTable}.
+     */
+    private void setPropertyTableContextMenu() {
+        final MenuManager manager = new MenuManager();
+        manager.setRemoveAllWhenShown(true);
+        manager.addMenuListener(new IMenuListener() {
+            @Override
+            public void menuAboutToShow(IMenuManager m) {
+                // dispose items to avoid caching
+                for (MenuItem item : manager.getMenu().getItems()) {
+                    item.dispose();
+                }
+                // apply new items
+                fillContextMenu();
+            }
+
+            private void fillContextMenu() {
+                manager.add(mDefaultValueAction);
+                manager.add(mSortAlphaAction);
+                manager.add(mShowAdvancedPropertiesAction);
+            }
+        });
+
+        mPropertyTable.setMenu(manager.createContextMenu(mPropertyTable));
+    }
+
+    /**
+     * Shows {@link Property}'s of current objects.
+     */
+    private void refreshProperties() {
+        PropertyFactory factory = getPropertyFactory();
+        mPropertyTable.setInput(factory.getProperties(mSelection));
+        updateDefaultValueAction();
+    }
+
+    // ---- Actions ----
+
+    private static final int ACTION_DEFAULT_VALUE = 1;
+    private static final int ACTION_SHOW_ADVANCED = 2;
+    private static final int ACTION_COLLAPSE = 3;
+    private static final int ACTION_EXPAND = 4;
+    private static final int ACTION_SORT_ALPHA = 5;
+
+    private class PropertySheetAction extends Action {
+        private final int mAction;
+
+        private PropertySheetAction(int style, String label, int action,
+                ImageDescriptor imageDesc, ImageDescriptor disabledImageDesc) {
+            super(label, style);
+            mAction = action;
+            setImageDescriptor(imageDesc);
+            if (disabledImageDesc != null) {
+                setDisabledImageDescriptor(disabledImageDesc);
+            }
+            setToolTipText(label);
+        }
+
+        @Override
+        public void run() {
+            switch (mAction) {
+                case ACTION_COLLAPSE: {
+                    mPropertyTable.collapseAll();
+                    break;
+                }
+                case ACTION_EXPAND: {
+                    mPropertyTable.expandAll();
+                    break;
+                }
+                case ACTION_SHOW_ADVANCED: {
+                    boolean show = mShowAdvancedPropertiesAction.isChecked();
+                    mPropertyTable.setShowAdvancedProperties(show);
+                    break;
+                }
+                case ACTION_SORT_ALPHA: {
+                    boolean isAlphabetical = mSortAlphaAction.isChecked();
+                    getPropertyFactory().setSortingMode(
+                        isAlphabetical ? SortingMode.ALPHABETICAL : PropertyFactory.DEFAULT_MODE);
+                    refreshProperties();
+                    break;
+                }
+                case ACTION_DEFAULT_VALUE:
+                    try {
+                        mActiveProperty.setValue(Property.UNKNOWN_VALUE);
+                    } catch (Exception e) {
+                        // Ignore warnings from setters
+                    }
+                    break;
+                default:
+                    assert false : mAction;
+            }
+        }
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java
new file mode 100644
index 0000000..19dc0bc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java
@@ -0,0 +1,164 @@
+/*
+ * 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.properties;
+
+import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF;
+import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
+
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.resources.ResourceType;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.fieldassist.ContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Resource value completion for the given property
+ * <p>
+ * TODO:
+ * <ul>
+ * <li>also offer other values seen in the app
+ * <li>also offer previously set values for this property
+ * <li>also complete on properties
+ * </ul>
+ */
+class ResourceValueCompleter implements IContentProposalProvider {
+    protected final XmlProperty xmlProperty;
+
+    ResourceValueCompleter(XmlProperty xmlProperty) {
+        this.xmlProperty = xmlProperty;
+    }
+
+    @Override
+    public IContentProposal[] getProposals(String contents, int position) {
+        if (contents.startsWith(PREFIX_RESOURCE_REF)) {
+            CommonXmlEditor editor = this.xmlProperty.getXmlEditor();
+            if (editor != null) {
+                String[] matches = computeResourceStringMatches(
+                        editor,
+                        this.xmlProperty.mDescriptor, contents.substring(0, position));
+                List<IContentProposal> proposals = null;
+                if (matches != null && matches.length > 0) {
+                    proposals = new ArrayList<IContentProposal>(matches.length);
+                    for (String match : matches) {
+                        proposals.add(new ContentProposal(match));
+                    }
+                    return proposals.toArray(new IContentProposal[proposals.size()]);
+                }
+            }
+        }
+
+        return new IContentProposal[0];
+    }
+
+    /**
+     * Similar to {@link UiResourceAttributeNode#computeResourceStringMatches}
+     * but computes complete results up front rather than dividing it up into
+     * smaller chunks like @{code @android:}, {@code string/}, and {@code ok}.
+     */
+    static String[] computeResourceStringMatches(AndroidXmlEditor editor,
+            AttributeDescriptor attributeDescriptor, String prefix) {
+        List<String> results = new ArrayList<String>(200);
+
+        // System matches: only do this if the value already matches at least @a,
+        // and doesn't start with something that can't possibly be @android
+        if (prefix.startsWith("@a") && //$NON-NLS-1$
+                prefix.regionMatches(true /* ignoreCase */, 0, PREFIX_ANDROID_RESOURCE_REF, 0,
+                        Math.min(prefix.length() - 1, PREFIX_ANDROID_RESOURCE_REF.length()))) {
+            AndroidTargetData data = editor.getTargetData();
+            if (data != null) {
+                ResourceRepository repository = data.getFrameworkResources();
+                addMatches(repository, prefix, true /* isSystem */, results);
+            }
+        }
+
+        // When completing project resources skip framework resources unless
+        // the prefix possibly completes both, such as "@an" which can match
+        // both the project resource @animator as well as @android:string
+        if (!prefix.startsWith("@and")) { //$NON-NLS-1$
+            IProject project = editor.getProject();
+            if (project != null) {
+                // get the resource repository for this project and the system resources.
+                ResourceManager manager = ResourceManager.getInstance();
+                ResourceRepository repository = manager.getProjectResources(project);
+                if (repository != null) {
+                    // We have a style name and a repository. Find all resources that match this
+                    // type and recreate suggestions out of them.
+                    addMatches(repository, prefix, false /* isSystem */, results);
+                }
+
+            }
+        }
+
+        if (attributeDescriptor != null) {
+            UiResourceAttributeNode.sortAttributeChoices(attributeDescriptor, results);
+        } else {
+            Collections.sort(results);
+        }
+
+        return results.toArray(new String[results.size()]);
+    }
+
+    private static void addMatches(ResourceRepository repository, String prefix, boolean isSystem,
+            List<String> results) {
+        int typeStart = isSystem
+                ? PREFIX_ANDROID_RESOURCE_REF.length() : PREFIX_RESOURCE_REF.length();
+
+        for (ResourceType type : repository.getAvailableResourceTypes()) {
+            if (prefix.regionMatches(typeStart, type.getName(), 0,
+                    Math.min(type.getName().length(), prefix.length() - typeStart))) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(PREFIX_RESOURCE_REF);
+
+                if (type == ResourceType.ID && prefix.startsWith(NEW_ID_PREFIX)) {
+                    sb.append('+');
+                }
+
+                if (isSystem) {
+                    sb.append(ANDROID_PKG).append(':');
+                }
+
+                sb.append(type.getName()).append('/');
+                String base = sb.toString();
+
+                int nameStart = typeStart + type.getName().length() + 1; // +1: add "/" divider
+                String namePrefix =
+                        prefix.length() <= nameStart ? "" : prefix.substring(nameStart);
+                for (ResourceItem item : repository.getResourceItemsOfType(type)) {
+                    String name = item.getName();
+                    if (AdtUtils.startsWithIgnoreCase(name, namePrefix)) {
+                        results.add(base + name);
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/StringXmlPropertyDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/StringXmlPropertyDialog.java
new file mode 100644
index 0000000..3fb72a9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/StringXmlPropertyDialog.java
@@ -0,0 +1,31 @@
+/*
+ * 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.properties;
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.wb.internal.core.model.property.Property;
+import org.eclipse.wb.internal.core.model.property.editor.string.StringPropertyDialog;
+
+class StringXmlPropertyDialog extends StringPropertyDialog {
+    StringXmlPropertyDialog(Shell parentShell, Property property) throws Exception {
+        super(parentShell, property);
+    }
+
+    @Override
+    protected boolean isMultiLine() {
+        return false;
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlProperty.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlProperty.java
new file mode 100644
index 0000000..c846ea1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlProperty.java
@@ -0,0 +1,265 @@
+/*
+ * 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.properties;
+
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_MARGIN;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.common.api.IAttributeInfo.Format;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ViewHierarchy;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.wb.internal.core.model.property.Property;
+import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor;
+import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipProvider;
+import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipTextProvider;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+/**
+ * An Android XML property
+ */
+class XmlProperty extends Property {
+    private PropertyFactory mFactory;
+    final AttributeDescriptor mDescriptor;
+    private UiViewElementNode mNode;
+    private Property mParent;
+
+    XmlProperty(
+            @NonNull PropertyEditor editor,
+            @NonNull PropertyFactory factory,
+            @NonNull UiViewElementNode node,
+            @NonNull AttributeDescriptor descriptor) {
+        super(editor);
+        mFactory = factory;
+        mNode = node;
+        mDescriptor = descriptor;
+    }
+
+    public PropertyFactory getFactory() {
+        return mFactory;
+    }
+
+    public UiViewElementNode getNode() {
+        return mNode;
+    }
+
+    public AttributeDescriptor getDescriptor() {
+        return mDescriptor;
+    }
+
+    @Override
+    public String getName() {
+        return mDescriptor.getXmlLocalName();
+    }
+
+    @Override
+    public String getTitle() {
+        String name = mDescriptor.getXmlLocalName();
+        int nameLength = name.length();
+
+        if (name.startsWith(ATTR_LAYOUT_PREFIX)) {
+            if (name.startsWith(ATTR_LAYOUT_MARGIN)
+                    && nameLength > ATTR_LAYOUT_MARGIN.length()) {
+                name = name.substring(ATTR_LAYOUT_MARGIN.length());
+            } else {
+                name = name.substring(ATTR_LAYOUT_PREFIX.length());
+            }
+        }
+
+        // Capitalize
+        name = DescriptorsUtils.capitalize(name);
+
+        // If we're nested within a complex property, say "Line Spacing", don't
+        // include "Line Spacing " as a prefix for each property here
+        if (mParent != null) {
+            String parentTitle = mParent.getTitle();
+            if (name.startsWith(parentTitle)) {
+                int parentTitleLength = parentTitle.length();
+                if (parentTitleLength < nameLength) {
+                    if (nameLength > parentTitleLength &&
+                            Character.isWhitespace(name.charAt(parentTitleLength))) {
+                        parentTitleLength++;
+                    }
+                    name = name.substring(parentTitleLength);
+                }
+            }
+        }
+
+        return name;
+    }
+
+    @Override
+    public <T> T getAdapter(Class<T> adapter) {
+        // tooltip
+        if (adapter == PropertyTooltipProvider.class) {
+            return adapter.cast(new PropertyTooltipTextProvider() {
+                @Override
+                protected String getText(Property p) throws Exception {
+                    if (mDescriptor instanceof IPropertyDescriptor) {
+                        IPropertyDescriptor d = (IPropertyDescriptor) mDescriptor;
+                        return d.getDescription();
+                    }
+
+                    return null;
+                }
+            });
+        } else if (adapter == IContentProposalProvider.class) {
+            IAttributeInfo info = mDescriptor.getAttributeInfo();
+            if (info != null) {
+                EnumSet<Format> formats = info.getFormats();
+                if (formats.contains(Format.FLAG)) {
+                    return adapter.cast(new FlagValueCompleter(this, info.getFlagValues()));
+                } else if (formats.contains(Format.ENUM)) {
+                    return adapter.cast(new FlagValueCompleter(this, info.getEnumValues()));
+                }
+            }
+            // Fallback: complete values on resource values
+            return adapter.cast(new ResourceValueCompleter(this));
+        } else if (adapter == ILabelProvider.class) {
+            return adapter.cast(new LabelProvider() {
+                @Override
+              public Image getImage(Object element) {
+                  return AdtPlugin.getAndroidLogo();
+              }
+
+              @Override
+              public String getText(Object element) {
+                  return ((IContentProposal) element).getLabel();
+              }
+            });
+        }
+        return super.getAdapter(adapter);
+    }
+
+    @Override
+    public boolean isModified() throws Exception {
+        Object s = null;
+        try {
+            Element element = (Element) mNode.getXmlNode();
+            String name = mDescriptor.getXmlLocalName();
+            String uri = mDescriptor.getNamespaceUri();
+            if (uri != null) {
+                return element.hasAttributeNS(uri, name);
+            } else {
+                return element.hasAttribute(name);
+            }
+        } catch (Exception e) {
+            // pass
+        }
+        return s != null && s.toString().length() > 0;
+    }
+
+    public String getStringValue() {
+        Element element = (Element) mNode.getXmlNode();
+        String name = mDescriptor.getXmlLocalName();
+        String uri = mDescriptor.getNamespaceUri();
+        Attr attr;
+        if (uri != null) {
+            attr = element.getAttributeNodeNS(uri, name);
+        } else {
+            attr = element.getAttributeNode(name);
+        }
+        if (attr != null) {
+            return attr.getValue();
+        }
+
+        Object viewObject = getFactory().getCurrentViewObject();
+        if (viewObject != null) {
+            ViewHierarchy views = getGraphicalEditor().getCanvasControl().getViewHierarchy();
+            Map<String, String> defaultProperties = views.getDefaultProperties(viewObject);
+            if (defaultProperties != null) {
+                return defaultProperties.get(name);
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public Object getValue() throws Exception {
+        return getStringValue();
+    }
+
+    @Override
+    public void setValue(Object value) throws Exception {
+        CommonXmlEditor editor = getXmlEditor();
+        if (editor == null) {
+            return;
+        }
+        final String attribute = mDescriptor.getXmlLocalName();
+        final String xmlValue = value != null && value != UNKNOWN_VALUE ? value.toString() : null;
+        editor.wrapUndoEditXmlModel(
+                String.format("Set \"%1$s\" to \"%2$s\"", attribute, xmlValue),
+                new Runnable() {
+            @Override
+            public void run() {
+                mNode.setAttributeValue(attribute,
+                        mDescriptor.getNamespaceUri(), xmlValue, true /*override*/);
+                mNode.commitDirtyAttributesToXml();
+            }
+        });
+    }
+
+    @Override
+    public Property getComposite(Property[] properties) {
+        return XmlPropertyComposite.create(properties);
+    }
+
+    @Nullable
+    GraphicalEditorPart getGraphicalEditor() {
+        return mFactory.getGraphicalEditor();
+    }
+
+    @Nullable CommonXmlEditor getXmlEditor() {
+        GraphicalEditorPart graphicalEditor = getGraphicalEditor();
+        if (graphicalEditor != null) {
+            return graphicalEditor.getEditorDelegate().getEditor();
+        }
+
+        return null;
+    }
+
+    public Property getParent() {
+        return mParent;
+    }
+
+    public void setParent(Property parent) {
+        mParent = parent;
+    }
+
+    @Override
+    public String toString() {
+        return getName() + ":" + getPriority();
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyComposite.java
new file mode 100644
index 0000000..7abc91c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyComposite.java
@@ -0,0 +1,118 @@
+/*
+ * 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.properties;
+
+import com.google.common.base.Objects;
+
+import org.eclipse.wb.internal.core.model.property.Property;
+
+import java.util.Arrays;
+
+/**
+ * Property holding multiple instances of the same {@link XmlProperty} (but
+ * bound to difference objects. This is used when multiple objects are selected
+ * in the layout editor and the common properties are shown; editing a value
+ * will (via {@link #setValue(Object)}) set it on all selected objects.
+ * <p>
+ * Similar to
+ * org.eclipse.wb.internal.core.model.property.GenericPropertyComposite
+ */
+class XmlPropertyComposite extends XmlProperty {
+    private static final Object NO_VALUE = new Object();
+
+    private final XmlProperty[] mProperties;
+
+    public XmlPropertyComposite(XmlProperty primary, XmlProperty[] properties) {
+        super(
+            primary.getEditor(),
+            primary.getFactory(),
+            primary.getNode(),
+            primary.getDescriptor());
+        mProperties = properties;
+    }
+
+    @Override
+    public String getTitle() {
+        return mProperties[0].getTitle();
+    }
+
+    @Override
+    public int hashCode() {
+        return mProperties.length;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj instanceof XmlPropertyComposite) {
+            XmlPropertyComposite property = (XmlPropertyComposite) obj;
+            return Arrays.equals(mProperties, property.mProperties);
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean isModified() throws Exception {
+        for (Property property : mProperties) {
+            if (property.isModified()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public Object getValue() throws Exception {
+        Object value = NO_VALUE;
+        for (Property property : mProperties) {
+            Object propertyValue = property.getValue();
+            if (value == NO_VALUE) {
+                value = propertyValue;
+            } else if (!Objects.equal(value, propertyValue)) {
+                return UNKNOWN_VALUE;
+            }
+        }
+
+        return value;
+    }
+
+    @Override
+    public void setValue(final Object value) throws Exception {
+        // TBD: Wrap in ExecutionUtils.run?
+        for (Property property : mProperties) {
+            property.setValue(value);
+        }
+    }
+
+    public static XmlPropertyComposite create(Property... properties) {
+        // Cast from Property into XmlProperty
+        XmlProperty[] xmlProperties = new XmlProperty[properties.length];
+        for (int i = 0; i < properties.length; i++) {
+            Property property = properties[i];
+            xmlProperties[i] = (XmlProperty) property;
+        }
+
+        XmlPropertyComposite composite = new XmlPropertyComposite(xmlProperties[0], xmlProperties);
+        composite.setCategory(xmlProperties[0].getCategory());
+        return composite;
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyEditor.java
new file mode 100644
index 0000000..628cda6
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/XmlPropertyEditor.java
@@ -0,0 +1,428 @@
+/*
+ * 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.properties;
+
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_PNG;
+import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.common.api.IAttributeInfo.Format;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderService;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.ui.ReferenceChooserDialog;
+import com.android.ide.eclipse.adt.internal.ui.ResourceChooser;
+import com.android.ide.eclipse.adt.internal.ui.ResourcePreviewHelper;
+import com.android.resources.ResourceType;
+import com.google.common.collect.Maps;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.wb.draw2d.IColorConstants;
+import org.eclipse.wb.internal.core.model.property.Property;
+import org.eclipse.wb.internal.core.model.property.editor.AbstractTextPropertyEditor;
+import org.eclipse.wb.internal.core.model.property.editor.presentation.ButtonPropertyEditorPresentation;
+import org.eclipse.wb.internal.core.model.property.editor.presentation.PropertyEditorPresentation;
+import org.eclipse.wb.internal.core.model.property.table.PropertyTable;
+import org.eclipse.wb.internal.core.utils.ui.DrawUtils;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Special property editor used for the {@link XmlProperty} instances which handles
+ * editing the XML properties, rendering defaults by looking up the actual colors and images,
+ */
+class XmlPropertyEditor extends AbstractTextPropertyEditor {
+    public static final XmlPropertyEditor INSTANCE = new XmlPropertyEditor();
+    private static final int SAMPLE_SIZE = 10;
+    private static final int SAMPLE_MARGIN = 3;
+
+    protected XmlPropertyEditor() {
+    }
+
+    private final PropertyEditorPresentation mPresentation =
+            new ButtonPropertyEditorPresentation() {
+        @Override
+        protected void onClick(PropertyTable propertyTable, Property property) throws Exception {
+            openDialog(propertyTable, property);
+        }
+    };
+
+    @Override
+    public PropertyEditorPresentation getPresentation() {
+        return mPresentation;
+    }
+
+    @Override
+    public String getText(Property property) throws Exception {
+        Object value = property.getValue();
+        if (value instanceof String) {
+            return (String) value;
+        }
+        return null;
+    }
+
+    @Override
+    protected String getEditorText(Property property) throws Exception {
+        return getText(property);
+    }
+
+    @Override
+    public void paint(Property property, GC gc, int x, int y, int width, int height)
+            throws Exception {
+        String text = getText(property);
+        if (text != null) {
+            ResourceValue resValue = null;
+            String resolvedText = null;
+
+            // TODO: Use the constants for @, ?, @android: etc
+            if (text.startsWith("@") || text.startsWith("?")) { //$NON-NLS-1$ //$NON-NLS-2$
+                // Yes, try to resolve it in order to show better info
+                XmlProperty xmlProperty = (XmlProperty) property;
+                ResourceResolver resolver = xmlProperty.getGraphicalEditor().getResourceResolver();
+                boolean isFramework = text.startsWith("@android:") || text.startsWith("?android:");
+                resValue = resolver.findResValue(text, isFramework);
+                while (resValue != null && resValue.getValue() != null) {
+                    String value = resValue.getValue();
+                    if (value.startsWith("@") || value.startsWith("?")) {
+                        // TODO: do I have to strip off the @ too?
+                        isFramework = isFramework || value.startsWith("@android:") || value.startsWith("?android:");;
+                        ResourceValue v = resolver.findResValue(text, isFramework);
+                        if (v != null && !value.equals(v.getValue())) {
+                            resValue = v;
+                        } else {
+                            break;
+                        }
+                    } else {
+                        break;
+                    }
+                }
+            } else if (text.startsWith("#") && text.matches("#\\p{XDigit}+")) { //$NON-NLS-1$
+                resValue = new ResourceValue(ResourceType.COLOR, property.getName(), text, false);
+            }
+
+            if (resValue != null && resValue.getValue() != null) {
+                String value = resValue.getValue();
+                // Decide whether it's a color, an image, a nine patch etc
+                // and decide how to render it
+                if (value.startsWith("#") || value.endsWith(DOT_XML) //$NON-NLS-1$
+                        && value.contains("res/color")) { //$NON-NLS-1$ // TBD: File.separator?
+                    XmlProperty xmlProperty = (XmlProperty) property;
+                    ResourceResolver resolver =
+                            xmlProperty.getGraphicalEditor().getResourceResolver();
+                    RGB rgb = ResourceHelper.resolveColor(resolver, resValue);
+                    if (rgb != null) {
+                        Color color = new Color(gc.getDevice(), rgb);
+                        // draw color sample
+                        Color oldBackground = gc.getBackground();
+                        Color oldForeground = gc.getForeground();
+                        try {
+                            int width_c = SAMPLE_SIZE;
+                            int height_c = SAMPLE_SIZE;
+                            int x_c = x;
+                            int y_c = y + (height - height_c) / 2;
+                            // update rest bounds
+                            int delta = SAMPLE_SIZE + SAMPLE_MARGIN;
+                            x += delta;
+                            width -= delta;
+                            // fill
+                            gc.setBackground(color);
+                            gc.fillRectangle(x_c, y_c, width_c, height_c);
+                            // draw line
+                            gc.setForeground(IColorConstants.gray);
+                            gc.drawRectangle(x_c, y_c, width_c, height_c);
+                        } finally {
+                            gc.setBackground(oldBackground);
+                            gc.setForeground(oldForeground);
+                        }
+                        color.dispose();
+                    }
+                } else {
+                    Image swtImage = null;
+                    if (value.endsWith(DOT_XML) && value.contains("res/drawable")) { // TBD: Filesep?
+                        Map<String, Image> cache = getImageCache(property);
+                        swtImage = cache.get(value);
+                        if (swtImage == null) {
+                            XmlProperty xmlProperty = (XmlProperty) property;
+                            GraphicalEditorPart graphicalEditor = xmlProperty.getGraphicalEditor();
+                            RenderService service = RenderService.create(graphicalEditor);
+                            service.setSize(SAMPLE_SIZE, SAMPLE_SIZE);
+                            BufferedImage drawable = service.renderDrawable(resValue);
+                            if (drawable != null) {
+                                swtImage = SwtUtils.convertToSwt(gc.getDevice(), drawable,
+                                        true /*transferAlpha*/, -1);
+                                cache.put(value, swtImage);
+                            }
+                        }
+                    } else if (value.endsWith(DOT_PNG)) {
+                        // TODO: 9-patch handling?
+                        //if (text.endsWith(DOT_9PNG)) {
+                        //    // 9-patch image: How do we paint this?
+                        //    URL url = new File(text).toURI().toURL();
+                        //    NinePatch ninepatch = NinePatch.load(url, false /* ?? */);
+                        //    BufferedImage image = ninepatch.getImage();
+                        //}
+                        Map<String, Image> cache = getImageCache(property);
+                        swtImage = cache.get(value);
+                        if (swtImage == null) {
+                            File file = new File(value);
+                            if (file.exists()) {
+                                try {
+                                    BufferedImage awtImage = ImageIO.read(file);
+                                    if (awtImage != null && awtImage.getWidth() > 0
+                                            && awtImage.getHeight() > 0) {
+                                        awtImage = ImageUtils.cropBlank(awtImage, null);
+                                        if (awtImage != null) {
+                                            // Scale image
+                                            int imageWidth = awtImage.getWidth();
+                                            int imageHeight = awtImage.getHeight();
+                                            int maxWidth = 3 * height;
+
+                                            if (imageWidth > maxWidth || imageHeight > height) {
+                                                double scale = height / (double) imageHeight;
+                                                int scaledWidth = (int) (imageWidth * scale);
+                                                if (scaledWidth > maxWidth) {
+                                                    scale = maxWidth / (double) imageWidth;
+                                                }
+                                                awtImage = ImageUtils.scale(awtImage, scale,
+                                                        scale);
+                                            }
+                                            swtImage = SwtUtils.convertToSwt(gc.getDevice(),
+                                                    awtImage, true /*transferAlpha*/, -1);
+                                        }
+                                    }
+                                } catch (IOException e) {
+                                    AdtPlugin.log(e, value);
+                                }
+                            }
+                            cache.put(value, swtImage);
+                        }
+
+                    } else if (value != null) {
+                        // It's a normal string: if different from the text, paint
+                        // it in parentheses, e.g.
+                        //   @string/foo: Foo Bar (probably cropped)
+                        if (!value.equals(text) && !value.equals("@null")) { //$NON-NLS-1$
+                            resolvedText = value;
+                        }
+                    }
+
+                    if (swtImage != null) {
+                        // Make a square the size of the height
+                        ImageData imageData = swtImage.getImageData();
+                        int imageWidth = imageData.width;
+                        int imageHeight = imageData.height;
+                        if (imageWidth > 0 && imageHeight > 0) {
+                            gc.drawImage(swtImage, x, y + (height - imageHeight) / 2);
+                            int delta = imageWidth + SAMPLE_MARGIN;
+                            x += delta;
+                            width -= delta;
+                        }
+                    }
+                }
+            }
+
+            DrawUtils.drawStringCV(gc, text, x, y, width, height);
+
+            if (resolvedText != null && resolvedText.length() > 0) {
+                Point size = gc.stringExtent(text);
+                x += size.x;
+                width -= size.x;
+
+                x += SAMPLE_MARGIN;
+                width -= SAMPLE_MARGIN;
+
+                if (width > 0) {
+                    Color oldForeground = gc.getForeground();
+                    try {
+                        gc.setForeground(PropertyTable.COLOR_PROPERTY_FG_DEFAULT);
+                        DrawUtils.drawStringCV(gc, '(' + resolvedText + ')', x, y, width, height);
+                    } finally {
+                        gc.setForeground(oldForeground);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    protected boolean setEditorText(Property property, String text) throws Exception {
+        property.setValue(text);
+        return true;
+    }
+
+    private void openDialog(PropertyTable propertyTable, Property property) throws Exception {
+        XmlProperty xmlProperty = (XmlProperty) property;
+        IAttributeInfo attributeInfo = xmlProperty.getDescriptor().getAttributeInfo();
+
+        boolean isId = xmlProperty.getDescriptor().getXmlLocalName().equals(ATTR_ID);
+        if (isId) {
+            // When editing the id attribute, don't offer a resource chooser: usually
+            // you want to enter a *new* id here
+            attributeInfo = null;
+        }
+
+        boolean referenceAllowed = false;
+        if (attributeInfo != null) {
+            EnumSet<Format> formats = attributeInfo.getFormats();
+            ResourceType type = null;
+            List<ResourceType> types = null;
+            if (formats.contains(Format.FLAG)) {
+                FlagXmlPropertyDialog dialog =
+                        new FlagXmlPropertyDialog(propertyTable.getShell(),
+                                "Select Flag Values", false /* radio */,
+                                attributeInfo.getFlagValues(), xmlProperty);
+
+                dialog.open();
+                return;
+
+            } else if (formats.contains(Format.ENUM)) {
+                FlagXmlPropertyDialog dialog =
+                        new FlagXmlPropertyDialog(propertyTable.getShell(),
+                                "Select Enum Value", true /* radio */,
+                                attributeInfo.getEnumValues(), xmlProperty);
+                dialog.open();
+                return;
+            } else {
+                for (Format format : formats) {
+                    ResourceType t = format.getResourceType();
+                    if (t != null) {
+                        if (type != null) {
+                            if (types == null) {
+                                types = new ArrayList<ResourceType>();
+                                types.add(type);
+                            }
+                            types.add(t);
+                        }
+                        type = t;
+                    } else if (format == Format.REFERENCE) {
+                        referenceAllowed = true;
+                    }
+                }
+            }
+            if (types != null || referenceAllowed) {
+                // Multiple resource types (such as string *and* boolean):
+                // just use a reference chooser
+                GraphicalEditorPart graphicalEditor = xmlProperty.getGraphicalEditor();
+                LayoutEditorDelegate delegate = graphicalEditor.getEditorDelegate();
+                IProject project = delegate.getEditor().getProject();
+                if (project != null) {
+                    // get the resource repository for this project and the system resources.
+                    ResourceRepository projectRepository =
+                        ResourceManager.getInstance().getProjectResources(project);
+                    Shell shell = AdtPlugin.getDisplay().getActiveShell();
+                    ReferenceChooserDialog dlg = new ReferenceChooserDialog(
+                            project,
+                            projectRepository,
+                            shell);
+                    dlg.setPreviewHelper(new ResourcePreviewHelper(dlg, graphicalEditor));
+
+                    String currentValue = (String) property.getValue();
+                    dlg.setCurrentResource(currentValue);
+
+                    if (dlg.open() == Window.OK) {
+                        String resource = dlg.getCurrentResource();
+                        if (resource != null) {
+                            // Returns null for cancel, "" for clear and otherwise a new value
+                            if (resource.length() > 0) {
+                                property.setValue(resource);
+                            } else {
+                                property.setValue(null);
+                            }
+                        }
+                    }
+
+                    return;
+                }
+
+            } else if (type != null) {
+                // Single resource type: use a resource chooser
+                GraphicalEditorPart graphicalEditor = xmlProperty.getGraphicalEditor();
+                String currentValue = (String) property.getValue();
+                // TODO: Add validator factory?
+                String resource = ResourceChooser.chooseResource(graphicalEditor,
+                        type, currentValue, null /* validator */);
+                // Returns null for cancel, "" for clear and otherwise a new value
+                if (resource != null) {
+                    if (resource.length() > 0) {
+                        property.setValue(resource);
+                    } else {
+                        property.setValue(null);
+                    }
+                }
+
+                return;
+            }
+        }
+
+        // Fallback: Just use a plain string editor
+        StringXmlPropertyDialog dialog =
+                new StringXmlPropertyDialog(propertyTable.getShell(), property);
+        if (dialog.open() == Window.OK) {
+            // TODO: Do I need to activate?
+        }
+    }
+
+    /** Qualified name for the per-project persistent property include-map */
+    private final static QualifiedName CACHE_NAME = new QualifiedName(AdtPlugin.PLUGIN_ID,
+            "property-images");//$NON-NLS-1$
+
+    @NonNull
+    private static Map<String, Image> getImageCache(@NonNull Property property) {
+        XmlProperty xmlProperty = (XmlProperty) property;
+        IProject project = xmlProperty.getGraphicalEditor().getProject();
+        try {
+            Map<String, Image> cache = (Map<String, Image>) project.getSessionProperty(CACHE_NAME);
+            if (cache == null) {
+                cache = Maps.newHashMap();
+                project.setSessionProperty(CACHE_NAME, cache);
+            }
+
+            return cache;
+        } catch (CoreException e) {
+            AdtPlugin.log(e, null);
+            return Maps.newHashMap();
+        }
+    }
+}
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 e5fe678..219754b 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
@@ -130,7 +130,7 @@
     private Map<String, AttributeDescriptor> mCachedHiddenAttributes;
     /** An optional list of {@link IUiUpdateListener}. Most element nodes will not have any
      *  listeners attached, so the list is only created on demand and can be null. */
-    private ArrayList<IUiUpdateListener> mUiUpdateListeners;
+    private List<IUiUpdateListener> mUiUpdateListeners;
     /** A provider that knows how to create {@link ElementDescriptor} from unmapped XML names.
      *  The default is to have one that creates new {@link ElementDescriptor}. */
     private IUnknownDescriptorProvider mUnknownDescProvider;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java
index 055d432..e2b2094 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java
@@ -309,8 +309,11 @@
      * <p>
      * For example, if you are editing a style attribute, it's likely that among the
      * resource values you would rather see @style or @android than @string.
+     * @param descriptor the descriptor that the resource values are being completed for,
+     *          used to prioritize some of the resource types
+     * @param choices the set of string resource values
      */
-    private static void sortAttributeChoices(AttributeDescriptor descriptor,
+    public static void sortAttributeChoices(AttributeDescriptor descriptor,
             List<String> choices) {
         final IAttributeInfo attributeInfo = descriptor.getAttributeInfo();
         Collections.sort(choices, new Comparator<String>() {
@@ -319,7 +322,7 @@
                 int compare = score(attributeInfo, s1) - score(attributeInfo, s2);
                 if (compare == 0) {
                     // Sort alphabetically as a fallback
-                    compare = s1.compareTo(s2);
+                    compare = s1.compareToIgnoreCase(s2);
                 }
                 return compare;
             }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java
index 6b945f8..1280bc7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java
@@ -312,7 +312,21 @@
         boolean isClassDetector = issue != null && issue.getScope().contains(Scope.CLASS_FILE);
 
         NodeFinder nodeFinder = new NodeFinder(root, offset, length);
-        ASTNode coveringNode = nodeFinder.getCoveringNode();
+        ASTNode coveringNode;
+        if (offset <= 0) {
+            // Error added on the first line of a Java class: typically from a class-based
+            // detector which lacks line information. Map this to the top level class
+            // in the file instead.
+            coveringNode = root;
+            if (root.types() != null && root.types().size() > 0) {
+                Object type = root.types().get(0);
+                if (type instanceof ASTNode) {
+                    coveringNode = (ASTNode) type;
+                }
+            }
+        } else {
+            coveringNode = nodeFinder.getCoveringNode();
+        }
         for (ASTNode body = coveringNode; body != null; body = body.getParent()) {
             if (body instanceof BodyDeclaration) {
                 BodyDeclaration declaration = (BodyDeclaration) body;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
index 3297756..cd077ec 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
@@ -115,7 +115,7 @@
 
                 // Use the non-namespace form of set attribute since we can't
                 // reference the namespace until the model has been reloaded
-                mElement.setAttribute(prefix + ':' + ATTR_IGNORE, mId);
+                mElement.setAttribute(prefix + ':' + ATTR_IGNORE, ignore);
 
                 UiElementNode rootUiNode = mEditor.getUiRootNode();
                 if (rootUiNode != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
index 956ee9d..e9f94cc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintViewPart.java
@@ -229,7 +229,7 @@
         mRemoveAllAction = new LintViewAction("Remove All", ACTION_REMOVE_ALL,
                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL),
                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL_DISABLED));
-        mRefreshAction = new LintViewAction("Refresh", ACTION_REFRESH,
+        mRefreshAction = new LintViewAction("Refresh (& Save Files)", ACTION_REFRESH,
                 iconFactory.getImageDescriptor(REFRESH_ICON), null);
         mRemoveAllAction.setEnabled(true);
         mCollapseAll = new LintViewAction("Collapse All", ACTION_COLLAPSE,
@@ -461,6 +461,11 @@
         public void run() {
             switch (mAction) {
                 case ACTION_REFRESH: {
+                    IWorkbench workbench = PlatformUI.getWorkbench();
+                    if (workbench != null) {
+                        workbench.saveAllEditors(false /*confirm*/);
+                    }
+
                     Job[] jobs = EclipseLintRunner.getCurrentJobs();
                     if (jobs.length > 0) {
                         EclipseLintRunner.cancelCurrentJobs(false);
@@ -471,9 +476,8 @@
                         }
                         Job job = EclipseLintRunner.startLint(resources, null,
                                 false /*fatalOnly*/, false /*show*/);
-                        if (job != null) {
+                        if (job != null && workbench != null) {
                             job.addJobChangeListener(LintViewPart.this);
-                            IWorkbench workbench = PlatformUI.getWorkbench();
                             ISharedImages sharedImages = workbench.getSharedImages();
                             setImageDescriptor(sharedImages.getImageDescriptor(
                                     ISharedImages.IMG_ELCL_STOP));
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
index 54b5716..4906730 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
@@ -135,7 +135,9 @@
         createFilteredTree(top);
 
         // setup the initial selection
-        setupInitialSelection();
+        if (mCurrentResource != null) {
+            setupInitialSelection();
+        }
 
         // create the "New Resource" button
         createNewResButtons(top);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
index 263cc2d..202e1cf 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
@@ -20,6 +20,7 @@
 import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF;
 import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF;
 
+import com.android.annotations.NonNull;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.resources.ResourceItem;
 import com.android.ide.common.resources.ResourceRepository;
@@ -27,6 +28,7 @@
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AdtUtils;
 import com.android.ide.eclipse.adt.internal.assetstudio.OpenCreateAssetSetWizardAction;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
 import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
@@ -770,4 +772,24 @@
             updateStatus(status);
         }
     }
+
+    /**
+     * Open the resource chooser for the given type, associated with the given
+     * editor
+     *
+     * @param graphicalEditor the editor associated with the resource to be
+     *            chosen (used to find the associated Android target to be used
+     *            for framework resources etc)
+     * @param type the resource type to be chosen
+     * @param currentValue the current value, or null
+     * @param validator a validator to be used, or null
+     * @return the chosen resource, null if cancelled and "" if value should be
+     *         cleared
+     */
+    public static String chooseResource(
+            @NonNull GraphicalEditorPart graphicalEditor,
+            @NonNull ResourceType type,
+            String currentValue, IInputValidator validator) {
+        return ResourceChooser.chooseResource(graphicalEditor, type, currentValue, validator);
+    }
 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.base/.project b/eclipse/plugins/com.android.ide.eclipse.base/.project
index 55fe3ee..34e7eda 100644
--- a/eclipse/plugins/com.android.ide.eclipse.base/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.base/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>base</name>
+	<name>plugin-base</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/.project b/eclipse/plugins/com.android.ide.eclipse.ddms/.project
index 2e9f996..f4150a2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.ddms/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>ddms-plugin</name>
+	<name>plugin-ddms</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/.classpath b/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/.classpath
index c594e06..b3968a7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/.classpath
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/.classpath
@@ -3,6 +3,6 @@
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="lib" path="/gldebugger-plugin/libs/host-libprotobuf-java-2.3.0-lite.jar"/>
+	<classpathentry kind="lib" path="/plugin-gldebugger/libs/host-libprotobuf-java-2.3.0-lite.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/.project b/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/.project
index 7582b74..ef19c36 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger.tests/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>gldebugger-tests</name>
+	<name>plugin-gldebugger-tests</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/.project b/eclipse/plugins/com.android.ide.eclipse.gldebugger/.project
index c627d82..0327756 100755
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>gldebugger-plugin</name>
+	<name>plugin-gldebugger</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/.project b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/.project
index 9a33e7b..c9e9696 100644
--- a/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>hierarchyviewer-plugin</name>
+	<name>plugin-hierarchyviewer</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.monitor/.project b/eclipse/plugins/com.android.ide.eclipse.monitor/.project
index 2d33f44..e7ca0c9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.monitor/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.monitor/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>monitor-plugin</name>
+	<name>plugin-monitor</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/.project b/eclipse/plugins/com.android.ide.eclipse.ndk/.project
index 15270be..416aa2e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.ndk/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.ndk/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>ndk</name>
+	<name>plugin-ndk</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.pdt/.project b/eclipse/plugins/com.android.ide.eclipse.pdt/.project
index 6e419bf..091b013 100644
--- a/eclipse/plugins/com.android.ide.eclipse.pdt/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.pdt/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>pdt</name>
+	<name>plugin-pdt</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/.classpath b/eclipse/plugins/com.android.ide.eclipse.tests/.classpath
index 331b89c..47bf7aa 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/.classpath
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/.classpath
@@ -8,14 +8,14 @@
 	<classpathentry kind="lib" path="easymock.jar"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
-	<classpathentry kind="lib" path="/adt/libs/layoutlib_api.jar" sourcepath="/layoutlib_api"/>
-	<classpathentry kind="lib" path="/adt/libs/ide_common.jar" sourcepath="/ide_common"/>
-	<classpathentry kind="lib" path="/adt/libs/ninepatch.jar" sourcepath="/ninepatch"/>
-	<classpathentry kind="lib" path="/base/libs/sdklib.jar" sourcepath="/SdkLib"/>
-	<classpathentry kind="lib" path="/adt/libs/sdkuilib.jar" sourcepath="/SdkUiLib"/>
-	<classpathentry kind="lib" path="/adt/libs/rule_api.jar" sourcepath="/rule_api"/>
-	<classpathentry kind="lib" path="/base/libs/common.jar" sourcepath="/common"/>
-	<classpathentry kind="lib" path="/adt/libs/lint_api.jar" sourcepath="/lint-api"/>
-	<classpathentry kind="lib" path="/adt/libs/lint_checks.jar" sourcepath="/lint-checks"/>
+	<classpathentry kind="lib" path="/plugin-adt/libs/layoutlib_api.jar" sourcepath="/layoutlib_api"/>
+	<classpathentry kind="lib" path="/plugin-adt/libs/ide_common.jar" sourcepath="/ide_common"/>
+	<classpathentry kind="lib" path="/plugin-adt/libs/ninepatch.jar" sourcepath="/ninepatch"/>
+	<classpathentry kind="lib" path="/plugin-base/libs/sdklib.jar" sourcepath="/SdkLib"/>
+	<classpathentry kind="lib" path="/plugin-adt/libs/sdkuilib.jar" sourcepath="/SdkUiLib"/>
+	<classpathentry kind="lib" path="/plugin-adt/libs/rule_api.jar" sourcepath="/rule_api"/>
+	<classpathentry kind="lib" path="/plugin-base/libs/common.jar" sourcepath="/common"/>
+	<classpathentry kind="lib" path="/plugin-adt/libs/lint_api.jar" sourcepath="/lint-api"/>
+	<classpathentry kind="lib" path="/plugin-adt/libs/lint_checks.jar" sourcepath="/lint-checks"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/.project b/eclipse/plugins/com.android.ide.eclipse.tests/.project
index 99e4964..d37e27b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>adt-tests</name>
+	<name>plugin-tests</name>
 	<comment></comment>
 	<projects>
 		<project>SdkLib</project>
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java
index 1eb1b4a..177662d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java
@@ -31,6 +31,28 @@
         assertFalse(AdtUtils.endsWithIgnoreCase("foo", "fo"));
     }
 
+    public void testStartsWithIgnoreCase() {
+        assertTrue(AdtUtils.startsWithIgnoreCase("foo", "foo"));
+        assertTrue(AdtUtils.startsWithIgnoreCase("foo", "Foo"));
+        assertTrue(AdtUtils.startsWithIgnoreCase("foo", "foo"));
+        assertTrue(AdtUtils.startsWithIgnoreCase("barfoo", "bar"));
+        assertTrue(AdtUtils.startsWithIgnoreCase("BarFoo", "bar"));
+        assertTrue(AdtUtils.startsWithIgnoreCase("BarFoo", "bAr"));
+
+        assertFalse(AdtUtils.startsWithIgnoreCase("bfoo", "foo"));
+        assertFalse(AdtUtils.startsWithIgnoreCase("fo", "foo"));
+    }
+
+    public void testStartsWith() {
+        assertTrue(AdtUtils.startsWith("foo", 0, "foo"));
+        assertTrue(AdtUtils.startsWith("foo", 0, "Foo"));
+        assertTrue(AdtUtils.startsWith("Foo", 0, "foo"));
+        assertTrue(AdtUtils.startsWith("aFoo", 1, "foo"));
+
+        assertFalse(AdtUtils.startsWith("aFoo", 0, "foo"));
+        assertFalse(AdtUtils.startsWith("aFoo", 2, "foo"));
+    }
+
     public void testEndsWith() {
         assertTrue(AdtUtils.endsWith("foo", "foo"));
         assertTrue(AdtUtils.endsWith("foobar", "obar"));
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java
index 51781be..447f6de 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java
@@ -58,6 +58,44 @@
         assertEquals("The Y axis", DescriptorsUtils.prettyAttributeUiName("theYAxis"));
         assertEquals("The Z axis", DescriptorsUtils.prettyAttributeUiName("theZAxis"));
         assertEquals("The t axis", DescriptorsUtils.prettyAttributeUiName("theTAxis"));
+
+        // Special cases for "uri" and "sdk" etc
+        assertEquals("Grant URI permission",
+                DescriptorsUtils.prettyAttributeUiName("grantUriPermission"));
+        assertEquals("URI permission",
+                DescriptorsUtils.prettyAttributeUiName("uriPermission"));
+        assertEquals("Min SDK version", DescriptorsUtils.prettyAttributeUiName("minSdkVersion"));
+        assertEquals("SDK version", DescriptorsUtils.prettyAttributeUiName("sdkVersion"));
+        assertEquals("IME action method",
+                DescriptorsUtils.prettyAttributeUiName("imeActionMethod"));
+    }
+
+    public void testCapitalize() {
+        assertEquals("", DescriptorsUtils.capitalize(""));
+
+        assertEquals("Max Width For View",
+                DescriptorsUtils.capitalize("maxWidthForView"));
+
+        assertEquals("Layout Width",
+                DescriptorsUtils.capitalize("layout_width"));
+
+        assertEquals("Axis X", DescriptorsUtils.capitalize("axisX"));
+        assertEquals("Axis Y", DescriptorsUtils.capitalize("axisY"));
+        assertEquals("Axis Z", DescriptorsUtils.capitalize("axisZ"));
+        assertEquals("Axis T", DescriptorsUtils.capitalize("axisT"));
+
+        assertEquals("The X Axis", DescriptorsUtils.capitalize("theXAxis"));
+        assertEquals("The Y Axis", DescriptorsUtils.capitalize("theYAxis"));
+        assertEquals("The Z Axis", DescriptorsUtils.capitalize("theZAxis"));
+        assertEquals("The T Axis", DescriptorsUtils.capitalize("theTAxis"));
+
+        // Special cases for "uri" and "sdk" etc
+        assertEquals("Grant URI Permission", DescriptorsUtils.capitalize("grantUriPermission"));
+        assertEquals("Min SDK Version", DescriptorsUtils.capitalize("minSdkVersion"));
+        assertEquals("IME Action Method", DescriptorsUtils.capitalize("imeActionMethod"));
+        assertEquals("URI Permission", DescriptorsUtils.capitalize("uriPermission"));
+        assertEquals("SDK Version", DescriptorsUtils.capitalize("sdkVersion"));
+        assertEquals("Grant IME", DescriptorsUtils.capitalize("GrantIme"));
     }
 
     public void testFormatTooltip() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
index bca7cae..891e77b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java
@@ -39,6 +39,7 @@
 
 import junit.framework.TestCase;
 
+@SuppressWarnings("javadoc")
 public class CanvasViewInfoTest extends TestCase {
 
     public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.traceview/.project b/eclipse/plugins/com.android.ide.eclipse.traceview/.project
index f4a3b50..0cb5d18 100644
--- a/eclipse/plugins/com.android.ide.eclipse.traceview/.project
+++ b/eclipse/plugins/com.android.ide.eclipse.traceview/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>traceview-plugin</name>
+	<name>plugin-traceview</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/scripts/create_all_symlinks.sh b/eclipse/scripts/create_all_symlinks.sh
index fcc86af..a6874ba 100755
--- a/eclipse/scripts/create_all_symlinks.sh
+++ b/eclipse/scripts/create_all_symlinks.sh
@@ -124,7 +124,7 @@
 ### ADT ###
 
 ADT_DEST="sdk/eclipse/plugins/com.android.ide.eclipse.adt/libs"
-ADT_LIBS="layoutlib_api lint_api lint_checks ide_common rule_api ninepatch sdkuilib assetstudio"
+ADT_LIBS="layoutlib_api lint_api lint_checks ide_common rule_api ninepatch sdkuilib assetstudio propertysheet"
 ADT_PREBUILTS="\
     prebuilt/common/kxml2/kxml2-2.3.0.jar \
     prebuilts/tools/common/asm-tools/asm-4.0.jar \
@@ -239,6 +239,7 @@
     continue
   fi
   if [[ ! -f "$SRC" ]]; then
+    ORIG_SRC="$SRC"
     SRC="out/host/$PLATFORM/framework/$SRC.jar"
   fi
   if [[ -f "$SRC" ]]; then
@@ -248,7 +249,7 @@
 
     cpfile "$SRC" "$DEST"
   else
-    die "## Unknown file '$SRC' to copy in '$DEST'"
+    die "## Unknown source '$ORIG_SRC' to copy in '$DEST'"
   fi
 done
 
diff --git a/eclipse/sites/external/.project b/eclipse/sites/external/.project
index 9916269..3a6f889 100644
--- a/eclipse/sites/external/.project
+++ b/eclipse/sites/external/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>external-site</name>
+	<name>site-external</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/eclipse/sites/internal/.project b/eclipse/sites/internal/.project
index 0bd658d..d1ef73b 100644
--- a/eclipse/sites/internal/.project
+++ b/eclipse/sites/internal/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>internal-site</name>
+	<name>site-internal</name>
 	<comment></comment>
 	<projects>
 	</projects>
diff --git a/hierarchyviewer2/app/.classpath b/hierarchyviewer2/app/.classpath
index c5a657c..51ce9f3 100644
--- a/hierarchyviewer2/app/.classpath
+++ b/hierarchyviewer2/app/.classpath
@@ -2,11 +2,14 @@
 <classpath>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/hierarchyviewerlib"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
 	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/hierarchyviewer2/app/Android.mk b/hierarchyviewer2/app/Android.mk
index 0e00273..076e32b 100644
--- a/hierarchyviewer2/app/Android.mk
+++ b/hierarchyviewer2/app/Android.mk
@@ -25,8 +25,8 @@
     ddmuilib \
     hierarchyviewerlib \
     swt \
-    org.eclipse.jface_3.4.2.M20090107-0800 \
-    org.eclipse.core.commands_3.4.0.I20080509-2000 \
+    org.eclipse.jface_3.6.2.M20110210-1200 \
+    org.eclipse.core.commands_3.6.0.I20100512-1500 \
     sdklib \
     swtmenubar
 
diff --git a/hierarchyviewer2/app/etc/manifest.txt b/hierarchyviewer2/app/etc/manifest.txt
index 52759c9..0caa3c2 100644
--- a/hierarchyviewer2/app/etc/manifest.txt
+++ b/hierarchyviewer2/app/etc/manifest.txt
@@ -1,2 +1,2 @@
 Main-Class: com.android.hierarchyviewer.HierarchyViewerApplication
-Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar sdklib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar
+Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar sdklib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar org.eclipse.equinox.common_3.6.0.v20100503.jar
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/.classpath b/hierarchyviewer2/libs/hierarchyviewerlib/.classpath
index ef57724..105d22e 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/.classpath
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/.classpath
@@ -3,7 +3,10 @@
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt b/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt
index ac6ab55..3805b59 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/manifest.txt
@@ -1 +1 @@
-Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar
+Class-Path: ddmlib.jar ddmuilib.jar hierarchyviewerlib.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar org.eclipse.equinox.common_3.6.0.v20100503.jar
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
index 3ca63dd..1afbc92 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
@@ -23,8 +23,8 @@
 LOCAL_JAVA_LIBRARIES := ddmlib \
     ddmuilib \
     swt \
-    org.eclipse.jface_3.4.2.M20090107-0800 \
-    org.eclipse.core.commands_3.4.0.I20080509-2000
+    org.eclipse.jface_3.6.2.M20110210-1200 \
+    org.eclipse.core.commands_3.6.0.I20100512-1500
 
 LOCAL_MODULE := hierarchyviewerlib
 
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
index ad736c6..e221faa 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
@@ -950,9 +950,17 @@
             if (classDetectors != null && classDetectors.size() > 0 && entries.size() > 0) {
                 mOuterClasses = new ArrayDeque<ClassNode>();
                 for (ClassEntry entry : entries) {
-                    ClassReader reader = new ClassReader(entry.bytes);
-                    ClassNode classNode = new ClassNode();
-                    reader.accept(classNode, 0 /* flags */);
+                    ClassReader reader;
+                    ClassNode classNode;
+                    try {
+                        reader = new ClassReader(entry.bytes);
+                        classNode = new ClassNode();
+                        reader.accept(classNode, 0 /* flags */);
+                    } catch (Throwable t) {
+                        mClient.log(null, "Error processing %1$s: broken class file?",
+                                entry.path());
+                        continue;
+                    }
 
                     ClassNode peek;
                     while ((peek = mOuterClasses.peek()) != null) {
@@ -1019,9 +1027,13 @@
 
     private void addSuperClasses(SuperclassVisitor visitor, List<ClassEntry> entries) {
         for (ClassEntry entry : entries) {
-            ClassReader reader = new ClassReader(entry.bytes);
-            int flags = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
-            reader.accept(visitor, flags);
+            try {
+                ClassReader reader = new ClassReader(entry.bytes);
+                int flags = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
+                reader.accept(visitor, flags);
+            } catch (Throwable t) {
+                mClient.log(null, "Error processing %1$s: broken class file?", entry.path());
+            }
         }
     }
 
@@ -1764,6 +1776,14 @@
             this.bytes = bytes;
         }
 
+        public String path() {
+            if (jarFile != null) {
+                return jarFile.getPath() + ':' + file.getPath();
+            } else {
+                return file.getPath();
+            }
+        }
+
         @Override
         public int compareTo(ClassEntry other) {
             String p1 = file.getPath();
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
index 52eaaa8..05b640d 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java
@@ -345,6 +345,9 @@
                         // each inherited method
                         if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) {
                             owner = context.getDriver().getSuperClass(owner);
+                        } else if (node.getOpcode() == Opcodes.INVOKESTATIC && api == -1) {
+                            // Inherit through static classes as well
+                            owner = context.getDriver().getSuperClass(owner);
                         } else {
                             owner = null;
                         }
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
index dc65f26..04cb9b6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
@@ -179,7 +179,7 @@
                         "Use a %1$s of 0dip instead of %2$s for better performance",
                         dimension, size);
                 context.report(INEFFICIENT_WEIGHT,
-                        element,
+                        weightChild,
                         context.getLocation(sizeNode != null ? sizeNode : weightChild), msg, null);
 
             }
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
index 95fc107..e99005c 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
@@ -124,6 +124,9 @@
     private static final String BOOL = "boolean";                           //$NON-NLS-1$
     private static final String BOOLEAN = "Boolean";                        //$NON-NLS-1$
     private static final String LONG = "Long";                              //$NON-NLS-1$
+    private static final String CHARACTER = "Character";                    //$NON-NLS-1$
+    private static final String DOUBLE = "Double";                          //$NON-NLS-1$
+    private static final String FLOAT = "Float";                            //$NON-NLS-1$
     private static final String HASH_MAP = "HashMap";                       //$NON-NLS-1$
     private static final String CANVAS = "Canvas";                          //$NON-NLS-1$
     private static final String ON_DRAW = "onDraw";                         //$NON-NLS-1$
@@ -204,11 +207,12 @@
                     TypeReference reference = node.astTypeReference();
                     typeName = reference.astParts().last().astIdentifier().astValue();
                 }
-                if ((typeName.equals("Integer")             //$NON-NLS-1$
-                        || typeName.equals("Boolean")       //$NON-NLS-1$
-                        || typeName.equals("Float")         //$NON-NLS-1$
-                        || typeName.equals("Character")     //$NON-NLS-1$
-                        || typeName.equals("Double"))       //$NON-NLS-1$
+                if ((typeName.equals(INTEGER)
+                        || typeName.equals(BOOLEAN)
+                        || typeName.equals(FLOAT)
+                        || typeName.equals(CHARACTER)
+                        || typeName.equals(LONG)
+                        || typeName.equals(DOUBLE))
                         && node.astTypeReference().astParts().size() == 1
                         && node.astArguments().size() == 1) {
                     String argument = node.astArguments().first().toString();
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
index 6a6c1cc..8a611a1 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java
@@ -345,7 +345,7 @@
             if (exception != null) {
                 sb.append(exception.toString());
             }
-            fail(sb.toString());
+            System.err.println(sb);
         }
 
         @Override
@@ -387,7 +387,6 @@
 
             return super.findResource(relativePath);
         }
-
     }
 
     public class TestConfiguration extends Configuration {
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
index 0cde634..01ab449 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java
@@ -183,6 +183,21 @@
                 ));
     }
 
+    public void testInheritStatic() throws Exception {
+        assertEquals(
+            "ApiCallTest5.java:16: Error: Call requires API level 11 (current min is 2): android.view.View#resolveSizeAndState\n" +
+            "ApiCallTest5.java:18: Error: Call requires API level 11 (current min is 2): android.view.View#resolveSizeAndState\n" +
+            "ApiCallTest5.java:20: Error: Call requires API level 11 (current min is 2): android.view.View#combineMeasuredStates\n" +
+            "ApiCallTest5.java:21: Error: Call requires API level 11 (current min is 2): android.view.View#combineMeasuredStates",
+
+            lintProject(
+                "apicheck/classpath=>.classpath",
+                "apicheck/minsdk2.xml=>AndroidManifest.xml",
+                "apicheck/ApiCallTest5.java.txt=>src/foo/bar/ApiCallTest5.java",
+                "apicheck/ApiCallTest5.class.data=>bin/classes/foo/bar/ApiCallTest5.class"
+                ));
+    }
+
     public void testInheritLocal() throws Exception {
         // Test virtual dispatch in a local class which extends some other local class (which
         // in turn extends an Android API)
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
index ccbf26b..95d8b09 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/FieldGetterDetectorTest.java
@@ -71,4 +71,14 @@
                 "bytecode/GetterTest.jar.data=>bin/classes.jar"
                 ));
     }
+
+    public void testTruncatedData() throws Exception {
+        assertEquals(
+                "No warnings.",
+
+                lintProject(
+                    "bytecode/classpath-jar=>.classpath",
+                    "bytecode/GetterTest.jar.data=>bin/test/pkg/bogus.class"
+                    ));
+    }
 }
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/InefficientWeightDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
index 8300cbd..ed820c6 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
@@ -60,4 +60,13 @@
             "baseline_weights3.xml:2: Warning: Set android:baselineAligned=\"false\" on this element for better performance",
             lintFiles("res/layout/baseline_weights3.xml"));
     }
+
+    public void testSuppressed() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintFiles("res/layout/inefficient_weight2.xml"));
+    }
+
+
 }
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
index cfcda6a..100c3f7 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
@@ -34,6 +34,7 @@
             "JavaPerformanceTest.java:116: Warning: Avoid object allocations during draw operations: Use Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() which allocates a temporary Rect\n" +
             "JavaPerformanceTest.java:140: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse instead)\n" +
             "JavaPerformanceTest.java:145: Warning: Use Integer.valueOf(42) instead\n" +
+            "JavaPerformanceTest.java:146: Warning: Use Long.valueOf(42L) instead\n" +
             "JavaPerformanceTest.java:147: Warning: Use Boolean.valueOf(true) instead\n" +
             "JavaPerformanceTest.java:148: Warning: Use Character.valueOf('c') instead\n" +
             "JavaPerformanceTest.java:149: Warning: Use Float.valueOf(1.0f) instead\n" +
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
new file mode 100644
index 0000000..ad0cd48
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
new file mode 100644
index 0000000..4be8258
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
@@ -0,0 +1,23 @@
+package test.pkg;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.view.View;
+
+public class ApiCallTest5 extends View {
+    public ApiCallTest5(Context context) {
+        super(context);
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    @TargetApi(2)
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int measuredWidth = View.resolveSizeAndState(widthMeasureSpec,
+                widthMeasureSpec, 0);
+        int measuredHeight = resolveSizeAndState(heightMeasureSpec,
+                heightMeasureSpec, 0);
+        View.combineMeasuredStates(0, 0);
+        ApiCallTest5.combineMeasuredStates(0, 0);
+    }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
new file mode 100644
index 0000000..0e7f396
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+       <SeekBar
+            android:id="@+id/seekbar"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_weight="1"
+            android:max="100"
+            android:paddingBottom="10dip"
+            android:paddingLeft="15dip"
+            android:paddingRight="15dip"
+            android:paddingTop="10dip"
+            android:secondaryProgress="0"
+            tools:ignore="InefficientWeight" />
+
+</LinearLayout>
diff --git a/rule_api/src/com/android/ide/common/api/IAttributeInfo.java b/rule_api/src/com/android/ide/common/api/IAttributeInfo.java
index 7fd253c..997eeb4 100644
--- a/rule_api/src/com/android/ide/common/api/IAttributeInfo.java
+++ b/rule_api/src/com/android/ide/common/api/IAttributeInfo.java
@@ -18,6 +18,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.resources.ResourceType;
 import com.google.common.annotations.Beta;
 
 import java.util.EnumSet;
@@ -90,6 +91,39 @@
                     return EnumSet.of(this);
             }
         }
+
+        /** Returns the corresponding resource type for this attribute info,
+         * or null if there is no known or corresponding resource type (such as for
+         * enums and flags)
+         *
+         * @return the corresponding resource type, or null
+         */
+        @Nullable
+        public ResourceType getResourceType() {
+            switch (this) {
+                case STRING:
+                    return ResourceType.STRING;
+                case BOOLEAN:
+                    return ResourceType.BOOL;
+                case COLOR:
+                    return ResourceType.COLOR;
+                case DIMENSION:
+                    return ResourceType.DIMEN;
+                case FRACTION:
+                    return ResourceType.FRACTION;
+                case INTEGER:
+                    return ResourceType.INTEGER;
+
+                // No direct corresponding resource type
+                case ENUM:
+                case FLAG:
+                case FLOAT:
+                case REFERENCE:
+                    return null;
+            }
+
+            return null;
+        }
     }
 
     /** Returns the XML Name of the attribute */
diff --git a/sdkmanager/app/.classpath b/sdkmanager/app/.classpath
index 50576bb..6ca79d9 100644
--- a/sdkmanager/app/.classpath
+++ b/sdkmanager/app/.classpath
@@ -7,8 +7,11 @@
     <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
     <classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/>
     <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
-    <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
     <classpathentry combineaccessrules="false" kind="src" path="/common"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/>
     <classpathentry kind="output" path="bin"/>
 </classpath>
 
diff --git a/sdkmanager/app/Android.mk b/sdkmanager/app/Android.mk
index d7b630e..6431695 100644
--- a/sdkmanager/app/Android.mk
+++ b/sdkmanager/app/Android.mk
@@ -20,9 +20,9 @@
 	sdklib \
 	sdkuilib \
 	swt \
-	org.eclipse.jface_3.4.2.M20090107-0800 \
-	org.eclipse.equinox.common_3.4.0.v20080421-2006 \
-	org.eclipse.core.commands_3.4.0.I20080509-2000
+	org.eclipse.jface_3.6.2.M20110210-1200 \
+	org.eclipse.equinox.common_3.6.0.v20100503 \
+	org.eclipse.core.commands_3.6.0.I20100512-1500
 
 LOCAL_MODULE := sdkmanager
 
diff --git a/sdkmanager/app/etc/manifest.txt b/sdkmanager/app/etc/manifest.txt
index bb2e8c4..12d22e5 100644
--- a/sdkmanager/app/etc/manifest.txt
+++ b/sdkmanager/app/etc/manifest.txt
@@ -1,2 +1,2 @@
 Main-Class: com.android.sdkmanager.Main
-Class-Path: androidprefs.jar common.jar sdklib.jar sdkuilib.jar swtmenubar.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar
+Class-Path: androidprefs.jar common.jar sdklib.jar sdkuilib.jar swtmenubar.jar org.eclipse.jface_3.6.2.M20110210-1200.jar org.eclipse.equinox.common_3.6.0.v20100503.jar org.eclipse.core.commands_3.6.0.I20100512-1500.jar
diff --git a/sdkmanager/libs/sdkuilib/.classpath b/sdkmanager/libs/sdkuilib/.classpath
index 9080f57..3517cf7 100644
--- a/sdkmanager/libs/sdkuilib/.classpath
+++ b/sdkmanager/libs/sdkuilib/.classpath
@@ -3,11 +3,14 @@
 	<classpathentry excluding="**/Android.mk" kind="src" path="src"/>
 	<classpathentry excluding="**/Android.mk" kind="src" path="tests"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/common"/>
 	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/sdkmanager/libs/sdkuilib/Android.mk b/sdkmanager/libs/sdkuilib/Android.mk
index 740615e..3ce5c95 100644
--- a/sdkmanager/libs/sdkuilib/Android.mk
+++ b/sdkmanager/libs/sdkuilib/Android.mk
@@ -29,9 +29,9 @@
 	androidprefs \
 	swtmenubar \
 	swt \
-	org.eclipse.jface_3.4.2.M20090107-0800 \
-	org.eclipse.equinox.common_3.4.0.v20080421-2006 \
-	org.eclipse.core.commands_3.4.0.I20080509-2000
+	org.eclipse.jface_3.6.2.M20110210-1200 \
+	org.eclipse.equinox.common_3.6.0.v20100503 \
+	org.eclipse.core.commands_3.6.0.I20100512-1500
 
 LOCAL_MODULE := sdkuilib
 
diff --git a/sdkstats/.classpath b/sdkstats/.classpath
index 73b1af5..e823aa5 100644
--- a/sdkstats/.classpath
+++ b/sdkstats/.classpath
@@ -2,7 +2,10 @@
 <classpath>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/sdkstats/src/Android.mk b/sdkstats/src/Android.mk
index e95e67a..b7100b4 100644
--- a/sdkstats/src/Android.mk
+++ b/sdkstats/src/Android.mk
@@ -7,9 +7,9 @@
 LOCAL_JAVA_LIBRARIES := \
 	androidprefs \
 	swt \
-	org.eclipse.jface_3.4.2.M20090107-0800 \
-	org.eclipse.equinox.common_3.4.0.v20080421-2006 \
-	org.eclipse.core.commands_3.4.0.I20080509-2000
+	org.eclipse.jface_3.6.2.M20110210-1200 \
+	org.eclipse.equinox.common_3.6.0.v20100503 \
+	org.eclipse.core.commands_3.6.0.I20100512-1500
 LOCAL_MODULE := sdkstats
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/swtmenubar/Android.mk b/swtmenubar/Android.mk
index 25e80da..321a78a 100644
--- a/swtmenubar/Android.mk
+++ b/swtmenubar/Android.mk
@@ -29,7 +29,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
 	swt \
-	org.eclipse.jface_3.4.2.M20090107-0800
+	org.eclipse.jface_3.6.2.M20110210-1200
 
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java
deleted file mode 100755
index 45dacfb..0000000
--- a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java
+++ /dev/null
@@ -1,134 +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.menubar.internal;
-
-import com.android.menubar.IMenuBarCallback;
-import com.android.menubar.IMenuBarEnhancer;
-
-import org.eclipse.swt.internal.Callback;
-import org.eclipse.swt.internal.carbon.HICommand;
-import org.eclipse.swt.internal.carbon.OS;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Menu;
-
-
-/**
- * Implementation of IMenuBarEnhancer for MacOS Carbon SWT.
- */
-public final class MenuBarEnhancerCarbon implements IMenuBarEnhancer {
-
-    private static final int kHICommandPreferences = ('p'<<24) + ('r'<<16) + ('e'<<8) + 'f';
-    private static final int kHICommandAbout       = ('a'<<24) + ('b'<<16) + ('o'<<8) + 'u';
-    private static final int kHICommandServices    = ('s'<<24) + ('e'<<16) + ('r'<<8) + 'v';
-
-    public MenuBarEnhancerCarbon() {
-    }
-
-    public MenuBarMode getMenuBarMode() {
-        return MenuBarMode.MAC_OS;
-    }
-
-    public void setupMenu(
-            String appName,
-            Display display,
-            final IMenuBarCallback callbacks) {
-
-        // Callback target
-        Object target = new Object() {
-           @SuppressWarnings("unused")
-           int commandProc(int nextHandler, int theEvent, int userData) {
-              if (OS.GetEventKind(theEvent) == OS.kEventProcessCommand) {
-                 HICommand command = new HICommand();
-                 OS.GetEventParameter(
-                         theEvent,
-                         OS.kEventParamDirectObject,
-                         OS.typeHICommand,
-                         null,
-                         HICommand.sizeof,
-                         null,
-                         command);
-                 switch (command.commandID) {
-                 case kHICommandPreferences:
-                     callbacks.onPreferencesMenuSelected();
-                   return OS.eventNotHandledErr; // TODO wrong
-                 case kHICommandAbout:
-                     callbacks.onAboutMenuSelected();
-                     return OS.eventNotHandledErr;// TODO wrong
-                 default:
-                    break;
-                 }
-              }
-              return OS.eventNotHandledErr;
-           }
-        };
-
-        final Callback commandCallback= new Callback(target, "commandProc", 3); //$NON-NLS-1$
-        int commandProc = commandCallback.getAddress();
-        if (commandProc == 0) {
-           commandCallback.dispose();
-           log(callbacks, "%1$s: commandProc hook failed.", getClass().getSimpleName()); //$NON-NLS-1$
-           return;  // give up
-        }
-
-        // Install event handler for commands
-        int[] mask = new int[] {
-           OS.kEventClassCommand, OS.kEventProcessCommand
-        };
-        OS.InstallEventHandler(
-                OS.GetApplicationEventTarget(), commandProc, mask.length / 2, mask, 0, null);
-
-        // create About Eclipse menu command
-        int[] outMenu = new int[1];
-        short[] outIndex = new short[1];
-        if (OS.GetIndMenuItemWithCommandID(
-                0, kHICommandPreferences, 1, outMenu, outIndex) == OS.noErr && outMenu[0] != 0) {
-           int menu = outMenu[0];
-
-           // add About menu item (which isn't present by default)
-           String about = "About " + appName;
-           int l = about.length();
-           char buffer[] = new char[l];
-           about.getChars(0, l, buffer, 0);
-           int str = OS.CFStringCreateWithCharacters(OS.kCFAllocatorDefault, buffer, l);
-           OS.InsertMenuItemTextWithCFString(menu, str, (short) 0, 0, kHICommandAbout);
-           OS.CFRelease(str);
-
-           // add separator between About & Preferences
-           OS.InsertMenuItemTextWithCFString(menu, 0, (short) 1, OS.kMenuItemAttrSeparator, 0);
-
-           // enable pref menu
-           OS.EnableMenuCommand(menu, kHICommandPreferences);
-
-           // disable services menu
-           OS.DisableMenuCommand(menu, kHICommandServices);
-        }
-
-        // schedule disposal of callback object
-        display.disposeExec(
-           new Runnable() {
-              public void run() {
-                 commandCallback.dispose();
-              }
-           }
-        );
-    }
-
-    private void log(IMenuBarCallback callbacks, String format, Object... args) {
-        callbacks.printError(format , args);
-    }
-
-}
diff --git a/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java
index eb3e817..7ca6471 100644
--- a/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java
+++ b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java
@@ -191,9 +191,7 @@
         IMenuBarEnhancer enhancer = null;
         String p = SWT.getPlatform();
         String className = null;
-        if ("carbon".equals(p)) {                                                 //$NON-NLS-1$
-            className = "com.android.menubar.internal.MenuBarEnhancerCarbon";     //$NON-NLS-1$
-        } else if ("cocoa".equals(p)) {                                           //$NON-NLS-1$
+        if ("cocoa".equals(p)) {                                                  //$NON-NLS-1$
             className = "com.android.menubar.internal.MenuBarEnhancerCocoa";      //$NON-NLS-1$
         }
 
diff --git a/traceview/.classpath b/traceview/.classpath
index 400289f..301f785 100644
--- a/traceview/.classpath
+++ b/traceview/.classpath
@@ -3,7 +3,10 @@
 	<classpathentry excluding="resources/" kind="src" path="src"/>
 	<classpathentry kind="src" path="src/resources"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/SdkStatsService"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/linux-x86/swt/swt.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.core.commands_3.6.0.I20100512-1500.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.equinox.common_3.6.0.v20100503.jar"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/eclipse/org.eclipse.jface_3.6.2.M20110210-1200.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/traceview/src/Android.mk b/traceview/src/Android.mk
index fd901f1..cd63141 100644
--- a/traceview/src/Android.mk
+++ b/traceview/src/Android.mk
@@ -11,9 +11,9 @@
 	androidprefs \
 	sdkstats \
 	swt \
-	org.eclipse.jface_3.4.2.M20090107-0800 \
-	org.eclipse.equinox.common_3.4.0.v20080421-2006 \
-	org.eclipse.core.commands_3.4.0.I20080509-2000
+	org.eclipse.jface_3.6.2.M20110210-1200 \
+	org.eclipse.equinox.common_3.6.0.v20100503 \
+	org.eclipse.core.commands_3.6.0.I20100512-1500
 LOCAL_MODULE := traceview
 
 include $(BUILD_HOST_JAVA_LIBRARY)