OSDN Git Service

Add palette category metadata
authorTor Norbye <tnorbye@google.com>
Thu, 20 Jan 2011 22:25:22 +0000 (14:25 -0800)
committerTor Norbye <tnorbye@google.com>
Thu, 20 Jan 2011 22:25:22 +0000 (14:25 -0800)
This changeset contains some background work for the palette preview
work, separated out to make that changeset smaller.

First, it creates a new metadata XML file, which augments the metadata
provided by the platform, defining things like palette categories, a
natural order for the views within each category. The older code-based
metadata for fill preferences (used to decide how to set the width and
height attributes on drop) are also moved into this XML file. There
will be more metadata added to this file in the next changeset, where
for example XML fragments defining how to render a view for preview
purposes will be defined there.

Second, change the signature on the view and layout descriptor lists
passed around such that we don't have to do instanceof
ViewElementDescriptor in various places.

Fix sentence capitalization for a couple of undo label strings.

Change-Id: I66191ccf4cc0f4105c2331d496f3674ac1ad8b9d

14 files changed:
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewMetadata.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ExplodedRenderingHelper.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/CustomViewDescriptorService.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java [deleted file]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepository.java [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/uimodel/UiViewElementNode.java
eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderLoggerTest.java
eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java [new file with mode: 0644]

index 9397ef8..22fcb50 100644 (file)
@@ -23,13 +23,6 @@ package com.android.ide.common.api;
  */
 public interface IViewMetadata {
     /**
-     * Returns true if this view is a potential parent (e.g. it <b>can</b> have children).
-     *
-     * @return true if this view can have children
-     */
-    public boolean isParent();
-
-    /**
      * Returns the display name views of this type (a name suitable to display to the
      * user, normally capitalized and usually but not necessarily tied to the
      * implementation class). To be clear, a specific view may have an id attribute and a
@@ -42,13 +35,6 @@ public interface IViewMetadata {
     public String getDisplayName();
 
     /**
-     * Returns the tooltip for this view, if any
-     *
-     * @return a tooltip, or null
-     */
-    public String getTooltip();
-
-    /**
      * Returns the {@link FillPreference} of this view
      *
      * @return the {@link FillPreference} of this view
index 3674633..1b0d2bd 100644 (file)
@@ -156,7 +156,7 @@ public class BaseViewRule implements IViewRule {
                 if (fullActionId.equals(WIDTH_ID)) {
                     final String newAttrValue = getValue(valueId, customWidth);
                     if (newAttrValue != null) {
-                        node.editXml("Change attribute " + ATTR_LAYOUT_WIDTH,
+                        node.editXml("Change Attribute " + ATTR_LAYOUT_WIDTH,
                                 new PropertySettingNodeHandler(ANDROID_URI,
                                         ATTR_LAYOUT_WIDTH, newAttrValue));
                     }
@@ -165,7 +165,7 @@ public class BaseViewRule implements IViewRule {
                     // Ask the user
                     final String newAttrValue = getValue(valueId, customHeight);
                     if (newAttrValue != null) {
-                        node.editXml("Change attribute " + ATTR_LAYOUT_HEIGHT,
+                        node.editXml("Change Attribute " + ATTR_LAYOUT_HEIGHT,
                                 new PropertySettingNodeHandler(ANDROID_URI,
                                         ATTR_LAYOUT_HEIGHT, newAttrValue));
                     }
@@ -180,7 +180,7 @@ public class BaseViewRule implements IViewRule {
                         if (!newId.startsWith(NEW_ID_PREFIX)) {
                             newId = NEW_ID_PREFIX + stripIdPrefix(newId);
                         }
-                        node.editXml("Change id", new PropertySettingNodeHandler(ANDROID_URI,
+                        node.editXml("Change ID", new PropertySettingNodeHandler(ANDROID_URI,
                                 ATTR_ID, newId));
                     }
                 } else if (fullActionId.equals(EDIT_TEXT_ID)) {
@@ -188,7 +188,7 @@ public class BaseViewRule implements IViewRule {
                     oldText = ensureValidString(oldText);
                     String newText = mRulesEngine.displayResourceInput("string", oldText); //$NON-NLS-1$
                     if (newText != null) {
-                        node.editXml("Change text", new PropertySettingNodeHandler(ANDROID_URI,
+                        node.editXml("Change Text", new PropertySettingNodeHandler(ANDROID_URI,
                                 ATTR_TEXT, newText));
                     }
                 }
@@ -203,7 +203,7 @@ public class BaseViewRule implements IViewRule {
                         final String customValue = prop.isStringEdit()
                             ? inputAttributeValue(node, actionId) : null;
 
-                        node.editXml("Change attribute " + actionId, new INodeHandler() {
+                        node.editXml("Change Attribute " + actionId, new INodeHandler() {
                             public void handle(INode n) {
                                 if (prop.isToggle()) {
                                     // case of toggle
index e06ad40..0df0fe2 100644 (file)
@@ -16,8 +16,8 @@
 
 package com.android.ide.eclipse.adt.internal.editors.layout;
 
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.sdklib.IAndroidTarget;
@@ -69,8 +69,8 @@ public final class ExplodedRenderingHelper {
         LayoutDescriptors descriptors = data.getLayoutDescriptors();
 
         mLayoutNames = new HashSet<String>();
-        List<ElementDescriptor> layoutDescriptors = descriptors.getLayoutDescriptors();
-        for (ElementDescriptor desc : layoutDescriptors) {
+        List<ViewElementDescriptor> layoutDescriptors = descriptors.getLayoutDescriptors();
+        for (ViewElementDescriptor desc : layoutDescriptors) {
             mLayoutNames.add(desc.getXmlLocalName());
         }
 
index 82e1402..ec77994 100644 (file)
@@ -24,8 +24,8 @@ import static com.android.ide.common.layout.LayoutConstants.VALUE_MATCH_PARENT;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.ResourceDensity;
 import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
@@ -64,7 +64,7 @@ public final class UiElementPullParser extends BasePullParser {
     private final boolean mExplodedRendering;
     private boolean mZeroAttributeIsPadding = false;
     private boolean mIncreaseExistingPadding = false;
-    private List<ElementDescriptor> mLayoutDescriptors;
+    private List<ViewElementDescriptor> mLayoutDescriptors;
     private final int mDensityValue;
     private final float mXdpi;
 
@@ -158,7 +158,7 @@ public final class UiElementPullParser extends BasePullParser {
         if (mExplodedRendering) {
             // first get the node name
             String xml = node.getDescriptor().getXmlLocalName();
-            for (ElementDescriptor descriptor : mLayoutDescriptors) {
+            for (ViewElementDescriptor descriptor : mLayoutDescriptors) {
                 if (xml.equals(descriptor.getXmlLocalName())) {
                     NamedNodeMap attributes = node.getXmlNode().getAttributes();
                     Node padding = attributes.getNamedItemNS(SdkConstants.NS_RESOURCES, "padding");
index 0b1f2b7..e3faf4b 100644 (file)
@@ -18,7 +18,6 @@ package com.android.ide.eclipse.adt.internal.editors.layout.descriptors;
 
 import com.android.ide.common.resources.platform.ViewClassInfo;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.sdklib.IAndroidTarget;
@@ -201,7 +200,7 @@ public final class CustomViewDescriptorService {
     private ViewElementDescriptor createViewDescriptor(IType type, IProject project,
             ITypeHierarchy typeHierarchy) {
         // check if the type is a built-in View class.
-        List<ElementDescriptor> builtInList = null;
+        List<ViewElementDescriptor> builtInList = null;
 
         Sdk currentSdk = Sdk.getCurrent();
         if (currentSdk != null) {
@@ -220,12 +219,9 @@ public final class CustomViewDescriptorService {
         String fqcn = type.getFullyQualifiedName();
 
         if (builtInList != null) {
-            for (ElementDescriptor desc : builtInList) {
-                if (desc instanceof ViewElementDescriptor) {
-                    ViewElementDescriptor viewDescriptor = (ViewElementDescriptor)desc;
-                    if (fqcn.equals(viewDescriptor.getFullClassName())) {
-                        return viewDescriptor;
-                    }
+            for (ViewElementDescriptor viewDescriptor : builtInList) {
+                if (fqcn.equals(viewDescriptor.getFullClassName())) {
+                    return viewDescriptor;
                 }
             }
         }
index 6546b0b..b61c069 100644 (file)
@@ -32,6 +32,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 
 
@@ -69,16 +70,17 @@ public final class LayoutDescriptors implements IDescriptorProvider {
         new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$
 
     /** The list of all known ViewLayout descriptors. */
-    private ArrayList<ElementDescriptor> mLayoutDescriptors = new ArrayList<ElementDescriptor>();
+    private List<ViewElementDescriptor> mLayoutDescriptors =
+        new ArrayList<ViewElementDescriptor>();
 
     /** Read-Only list of View Descriptors. */
-    private List<ElementDescriptor> mROLayoutDescriptors;
+    private List<ViewElementDescriptor> mROLayoutDescriptors;
 
     /** The list of all known View (not ViewLayout) descriptors. */
-    private ArrayList<ElementDescriptor> mViewDescriptors = new ArrayList<ElementDescriptor>();
+    private List<ViewElementDescriptor> mViewDescriptors = new ArrayList<ViewElementDescriptor>();
 
     /** Read-Only list of View Descriptors. */
-    private List<ElementDescriptor> mROViewDescriptors;
+    private List<ViewElementDescriptor> mROViewDescriptors;
 
     /** The descriptor matching android.view.View. */
     private ViewElementDescriptor mBaseViewDescriptor;
@@ -89,12 +91,12 @@ public final class LayoutDescriptors implements IDescriptorProvider {
     }
 
     /** Returns the read-only list of all known ViewLayout descriptors. */
-    public List<ElementDescriptor> getLayoutDescriptors() {
+    public List<ViewElementDescriptor> getLayoutDescriptors() {
         return mROLayoutDescriptors;
     }
 
     /** Returns the read-only list of all known View (not ViewLayout) descriptors. */
-    public List<ElementDescriptor> getViewDescriptors() {
+    public List<ViewElementDescriptor> getViewDescriptors() {
         return mROViewDescriptors;
     }
 
@@ -141,10 +143,10 @@ public final class LayoutDescriptors implements IDescriptorProvider {
         HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap =
             new HashMap<ViewClassInfo, ViewElementDescriptor>();
 
-        ArrayList<ElementDescriptor> newViews = new ArrayList<ElementDescriptor>();
+        ArrayList<ViewElementDescriptor> newViews = new ArrayList<ViewElementDescriptor>();
         if (views != null) {
             for (ViewClassInfo info : views) {
-                ElementDescriptor desc = convertView(info, infoDescMap);
+                ViewElementDescriptor desc = convertView(info, infoDescMap);
                 newViews.add(desc);
             }
         }
@@ -153,20 +155,20 @@ public final class LayoutDescriptors implements IDescriptorProvider {
         // Note: ViewStub is already described by attrs.xml
         insertInclude(newViews);
 
-        ArrayList<ElementDescriptor> newLayouts = new ArrayList<ElementDescriptor>();
+        List<ViewElementDescriptor> newLayouts = new ArrayList<ViewElementDescriptor>();
         if (layouts != null) {
             for (ViewClassInfo info : layouts) {
-                ElementDescriptor desc = convertView(info, infoDescMap);
+                ViewElementDescriptor desc = convertView(info, infoDescMap);
                 newLayouts.add(desc);
             }
         }
 
-        ArrayList<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>();
+        List<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>();
         newDescriptors.addAll(newLayouts);
         newDescriptors.addAll(newViews);
 
         // Link all layouts to everything else here.. recursively
-        for (ElementDescriptor layoutDesc : newLayouts) {
+        for (ViewElementDescriptor layoutDesc : newLayouts) {
             layoutDesc.setChildren(newDescriptors);
         }
 
@@ -174,7 +176,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
 
         // The <merge> tag can only be a root tag, so it is added at the end.
         // It gets everything else as children but it is not made a child itself.
-        ElementDescriptor mergeTag = createMerge(newLayouts);
+        ViewElementDescriptor mergeTag = createMerge(newLayouts);
         mergeTag.setChildren(newDescriptors);  // mergeTag makes a copy of the list
         newDescriptors.add(mergeTag);
         newLayouts.add(mergeTag);
@@ -199,7 +201,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
      * @param infoDescMap This map links every ViewClassInfo to the ElementDescriptor it created.
      *                    It is filled by here and used later to fix the super-class hierarchy.
      */
-    private ElementDescriptor convertView(
+    private ViewElementDescriptor convertView(
             ViewClassInfo info,
             HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap) {
         String xml_name = info.getShortClassName();
@@ -296,7 +298,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
      * @param knownViews A list of view descriptors being populated. Also used to find the
      *   View descriptor and extract its layout attributes.
      */
-    private void insertInclude(ArrayList<ElementDescriptor> knownViews) {
+    private void insertInclude(List<ViewElementDescriptor> knownViews) {
         String xml_name = VIEW_INCLUDE;
 
         // Create the include custom attributes
@@ -344,7 +346,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
      * @param knownLayouts  A list of all known layout view descriptors, used to find the
      *   FrameLayout descriptor and extract its layout attributes.
      */
-    private ElementDescriptor createMerge(ArrayList<ElementDescriptor> knownLayouts) {
+    private ViewElementDescriptor createMerge(List<ViewElementDescriptor> knownLayouts) {
         String xml_name = VIEW_MERGE;
 
         // Find View and inherit all its layout attributes
@@ -370,14 +372,11 @@ public final class LayoutDescriptors implements IDescriptorProvider {
      */
     private AttributeDescriptor[] findViewLayoutAttributes(
             String viewFqcn,
-            ArrayList<ElementDescriptor> knownViews) {
+            List<ViewElementDescriptor> knownViews) {
 
-        for (ElementDescriptor desc : knownViews) {
-            if (desc instanceof ViewElementDescriptor) {
-                ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc;
-                if (viewFqcn.equals(viewDesc.getFullClassName())) {
-                    return viewDesc.getLayoutAttributes();
-                }
+        for (ViewElementDescriptor viewDesc : knownViews) {
+            if (viewFqcn.equals(viewDesc.getFullClassName())) {
+                return viewDesc.getLayoutAttributes();
             }
         }
 
@@ -388,7 +387,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
      * Set the super-class of each {@link ViewElementDescriptor} by using the super-class
      * information available in the {@link ViewClassInfo}.
      */
-    private void fixSuperClasses(HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap) {
+    private void fixSuperClasses(Map<ViewClassInfo, ViewElementDescriptor> infoDescMap) {
 
         for (Entry<ViewClassInfo, ViewElementDescriptor> entry : infoDescMap.entrySet()) {
             ViewClassInfo info = entry.getKey();
index 1349c23..cdd064a 100755 (executable)
@@ -36,6 +36,7 @@ import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescript
 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
@@ -222,6 +223,12 @@ public class PaletteControl extends Composite {
         GridDataBuilder.create(mRoot).hGrab().hFill();
 
         if (targetData != null) {
+            // TODO: Use metadata categories instead:
+            //List<Pair<String,List<ViewElementDescriptor>>> paletteEntries =
+            //    ViewMetadataRepository.get().getPaletteEntries(targetData);
+            //for (Pair<String,List<ViewElementDescriptor>> pair : paletteEntries) {
+            //    addGroup(mRoot, pair.getFirst(), pair.getSecond());
+            //}
             addGroup(mRoot, "Views", targetData.getLayoutDescriptors().getViewDescriptors());
             addGroup(mRoot, "Layouts", targetData.getLayoutDescriptors().getLayoutDescriptors());
         }
@@ -301,7 +308,7 @@ public class PaletteControl extends Composite {
 
     private void addGroup(Composite parent,
             String uiName,
-            List<ElementDescriptor> descriptors) {
+            List<ViewElementDescriptor> descriptors) {
 
         Composite group = new Composite(parent, SWT.NONE);
         GridLayoutBuilder.create(group).columns(1).columnsEqual().spacing(0).noMargins();
@@ -310,7 +317,7 @@ public class PaletteControl extends Composite {
         Toggle toggle = new Toggle(group, uiName);
         GridDataBuilder.create(toggle).hFill().hGrab();
 
-        for (ElementDescriptor desc : descriptors) {
+        for (ViewElementDescriptor desc : descriptors) {
 
             // Exclude the <include> and <merge> tags from the View palette.
             // We don't have drop support for it right now, although someday we should.
index 2bcc32f..afd7848 100755 (executable)
@@ -773,7 +773,16 @@ public class RulesEngine {
         }
 
         public IViewMetadata getMetadata(final String fqcn) {
-            return new ViewMetadata(mEditor.getLayoutEditor(), fqcn);
+            return new IViewMetadata() {
+                public String getDisplayName() {
+                    // This also works when there is no "."
+                    return fqcn.substring(fqcn.lastIndexOf('.') + 1);
+                }
+
+                public FillPreference getFillPreference() {
+                    return ViewMetadataRepository.get().getFillPreference(fqcn);
+                }
+            };
         }
 
         public int getMinApiLevel() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java
deleted file mode 100644 (file)
index 22f1168..0000000
+++ /dev/null
@@ -1,130 +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.gre;
-
-/**
- * An implementation of {@link IViewMetadata} which consults the
- * SDK descriptors to answer metadata questions about views.
- */
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
-
-import java.util.HashMap;
-import java.util.Map;
-
-final class ViewMetadata implements IViewMetadata {
-    /** The {@link ElementDescriptor} for this view, computed lazily */
-    private ElementDescriptor mDescriptor;
-
-    /** The fully qualified class name of the view whose metadata this class represents */
-    private String mFqcn;
-
-    /** The {@link LayoutEditor} associated with the view we're looking up */
-    private LayoutEditor mEditor;
-
-    /**
-     * A map from class names to {@link IViewMetadata.FillPreference} which indicates how each view
-     * prefers to grow when added in various layout contexts
-     */
-    private static final Map<String, FillPreference> mFill = new HashMap<String,FillPreference>();
-    static {
-        // Hardcoded metadata about fill preferences for various known views. We should
-        // work to try to get this into the platform as designtime annotations.
-
-        mFill.put("android.widget.EditText",     FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
-        mFill.put("android.widget.DialerFilter", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
-        mFill.put("android.widget.SeekBar",      FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
-        mFill.put("android.widget.Spinner",      FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
-        mFill.put("android.widget.AutoComplete", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
-        mFill.put("android.widget.ListView",     FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
-        mFill.put("android.widget.GridView",     FillPreference.OPPOSITE);          //$NON-NLS-1$
-        mFill.put("android.widget.Gallery",      FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
-        mFill.put("android.widget.TabWidget",    FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
-        mFill.put("android.widget.MapView",      FillPreference.OPPOSITE);          //$NON-NLS-1$
-        mFill.put("android.widget.WebView",      FillPreference.OPPOSITE);          //$NON-NLS-1$
-
-        // In addition, all layouts are FillPreference.OPPOSITE - these are computed
-        // lazily rather than enumerating them here
-
-        // For any other view, the fallback fill preference is FillPreference.NONE.
-    }
-
-    public ViewMetadata(LayoutEditor editor, String fqcn) {
-        super();
-        mFqcn = fqcn;
-        mEditor = editor;
-    }
-
-    /** Lazily look up the descriptor for the FQCN of this metadata object */
-    private boolean findDescriptor() {
-        if (mDescriptor == null) {
-            // Look up the corresponding view element node. We don't need the graphical part;
-            // we just need the project context. Maybe I should extract this code into
-            // a utility.
-            mDescriptor = mEditor.getFqcnViewDescriptor(mFqcn);
-        }
-
-        return mDescriptor != null;
-    }
-
-    public boolean isParent() {
-        if (findDescriptor()) {
-            return mDescriptor.hasChildren();
-        }
-
-        return false;
-    }
-
-    public String getDisplayName() {
-        if (findDescriptor()) {
-            return mDescriptor.getUiName();
-        }
-
-        return mFqcn.substring(mFqcn.lastIndexOf('.') + 1); // This also works when there is no "."
-    }
-
-    public String getTooltip() {
-        if (findDescriptor()) {
-            return mDescriptor.getTooltip();
-        }
-
-        return null;
-    }
-
-    /** Returns true if this view represents a layout */
-    private boolean isLayout() {
-        if (findDescriptor()) {
-            return mDescriptor.hasChildren();
-        }
-        return false;
-    }
-
-    public FillPreference getFillPreference() {
-        FillPreference fillPreference = mFill.get(mFqcn);
-        if (fillPreference == null) {
-            if (isLayout()) {
-                fillPreference = FillPreference.OPPOSITE;
-            } else {
-                fillPreference = FillPreference.NONE;
-            }
-            mFill.put(mFqcn, fillPreference);
-        }
-
-        return fillPreference;
-    }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepository.java
new file mode 100644 (file)
index 0000000..84fe12d
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.editors.layout.gre;
+
+import static com.android.ide.common.api.IViewMetadata.FillPreference.NONE;
+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.ide.common.api.IViewMetadata.FillPreference;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.sdklib.util.Pair;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * The {@link ViewMetadataRepository} contains additional metadata for Android view
+ * classes
+ */
+public class ViewMetadataRepository {
+    /** Singleton instance */
+    private static ViewMetadataRepository sInstance = new ViewMetadataRepository();
+
+    /**
+     * Returns the singleton instance
+     *
+     * @return the {@link ViewMetadataRepository}
+     */
+    public static ViewMetadataRepository get() {
+        return sInstance;
+    }
+
+    /**
+     * Ever increasing counter used to assign natural ordering numbers to views and
+     * categories
+     */
+    private static int sNextOrdinal = 0;
+
+    /**
+     * List of categories (which contain views); constructed lazily so use
+     * {@link #getCategories()}
+     */
+    private List<CategoryData> mCategories;
+
+    /**
+     * Map from class names to view data objects; constructed lazily so use
+     * {@link #getClassToView}
+     */
+    private Map<String, ViewData> mClassToView;
+
+    /** Hidden constructor: Create via factory {@link #get()} instead */
+    private ViewMetadataRepository() {
+    }
+
+    /** Returns a map from class fully qualified names to {@link ViewData} objects */
+    private Map<String, ViewData> getClassToView() {
+        if (mClassToView == null) {
+            int initialSize = 75;
+            mClassToView = new HashMap<String, ViewData>(initialSize);
+            List<CategoryData> categories = getCategories();
+            for (CategoryData category : categories) {
+                for (ViewData view : category) {
+                    mClassToView.put(view.getFcqn(), view);
+                }
+            }
+            assert mClassToView.size() <= initialSize;
+        }
+
+        return mClassToView;
+    }
+
+    /** Returns an ordered list of categories and views, parsed from a metadata file */
+    private List<CategoryData> getCategories() {
+        if (mCategories == null) {
+            mCategories = new ArrayList<CategoryData>();
+
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            String fileName = "extra-view-metadata.xml"; //$NON-NLS-1$
+            InputStream inputStream = ViewMetadataRepository.class.getResourceAsStream(fileName);
+            InputSource is = new InputSource(new BufferedInputStream(inputStream));
+            try {
+                factory.setNamespaceAware(true);
+                factory.setValidating(false);
+                DocumentBuilder builder = factory.newDocumentBuilder();
+                Document document = builder.parse(is);
+                Map<String, FillPreference> fillTypes = new HashMap<String, FillPreference>();
+                for (FillPreference pref : FillPreference.values()) {
+                    fillTypes.put(pref.toString().toLowerCase(), pref);
+                }
+
+                NodeList categoryNodes = document.getDocumentElement().getChildNodes();
+                for (int i = 0, n = categoryNodes.getLength(); i < n; i++) {
+                    Node node = categoryNodes.item(i);
+                    if (node.getNodeType() == Node.ELEMENT_NODE) {
+                        Element element = (Element) node;
+                        if (element.getNodeName().equals("category")) { //$NON-NLS-1$
+                            String name = element.getAttribute("name"); //$NON-NLS-1$
+                            CategoryData category = new CategoryData(name);
+                            NodeList children = element.getChildNodes();
+                            for (int j = 0, m = children.getLength(); j < m; j++) {
+                                Node childNode = children.item(j);
+                                if (childNode.getNodeType() == Node.ELEMENT_NODE) {
+                                    Element child = (Element) childNode;
+                                    String fqcn = child.getAttribute("class"); //$NON-NLS-1$
+                                    String fill = child.getAttribute("fill"); //$NON-NLS-1$
+                                    FillPreference fillPreference = null;
+                                    if (fill.length() > 0) {
+                                        fillPreference = fillTypes.get(fill);
+                                    }
+                                    if (fillPreference == null) {
+                                        fillPreference = NONE;
+                                    }
+                                    ViewData view = new ViewData(category, fqcn, fillPreference);
+                                    category.addView(view);
+                                }
+                            }
+
+                            mCategories.add(category);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                AdtPlugin.log(e, "Invalid palette metadata"); //$NON-NLS-1$
+            }
+        }
+
+        return mCategories;
+    }
+
+    /**
+     * Computes the palette entries for the given {@link AndroidTargetData}, looking up the
+     * available node descriptors, categorizing and sorting them.
+     *
+     * @param targetData the target data for which to compute palette entries
+     * @return a list of pairs where each pair contains of the category label and an
+     *         ordered list of elements to be included in that category
+     */
+    public List<Pair<String, List<ViewElementDescriptor>>> getPaletteEntries(
+            AndroidTargetData targetData) {
+        List<Pair<String, List<ViewElementDescriptor>>> result =
+            new ArrayList<Pair<String, List<ViewElementDescriptor>>>();
+
+        final Map<String, ViewData> viewMap = getClassToView();
+        Map<CategoryData, List<ViewElementDescriptor>> categories =
+            new TreeMap<CategoryData, List<ViewElementDescriptor>>();
+
+        // Locate the "Other" category
+        CategoryData other = null;
+        for (CategoryData category : getCategories()) {
+            if (category.getViewCount() == 0) {
+                other = category;
+                break;
+            }
+        }
+
+        List<List<ViewElementDescriptor>> lists = new ArrayList<List<ViewElementDescriptor>>(2);
+        LayoutDescriptors layoutDescriptors = targetData.getLayoutDescriptors();
+        lists.add(layoutDescriptors.getViewDescriptors());
+        lists.add(layoutDescriptors.getLayoutDescriptors());
+
+        for (List<ViewElementDescriptor> list : lists) {
+            for (ViewElementDescriptor view : list) {
+                String name = view.getXmlLocalName();
+
+                // Exclude the <include> and <merge> tags from the View palette.
+                // We don't have drop support for it right now, although someday we should.
+                if (VIEW_INCLUDE.equals(name) || VIEW_MERGE.equals(name)) {
+                    continue;
+                }
+
+                ViewData viewData = getClassToView().get(view.getFullClassName());
+                CategoryData category = other;
+                if (viewData != null) {
+                    category = viewData.getCategory();
+                }
+
+                List<ViewElementDescriptor> viewList = categories.get(category);
+                if (viewList == null) {
+                    viewList = new ArrayList<ViewElementDescriptor>();
+                    categories.put(category, viewList);
+                }
+                viewList.add(view);
+
+            }
+        }
+
+        for (Map.Entry<CategoryData, List<ViewElementDescriptor>> entry : categories.entrySet()) {
+            String name = entry.getKey().getName();
+            List<ViewElementDescriptor> items = entry.getValue();
+            if (items == null) {
+                continue; // empty category
+            }
+
+            // Natural sort of the descriptors
+            Comparator<ViewElementDescriptor> comparator = new Comparator<ViewElementDescriptor>() {
+                public int compare(ViewElementDescriptor v1, ViewElementDescriptor v2) {
+                    String fqcn1 = v1.getFullClassName();
+                    String fqcn2 = v2.getFullClassName();
+                    if (fqcn1 == null) {
+                        // <view> and <merge> tags etc
+                        fqcn1 = v1.getUiName();
+                    }
+                    if (fqcn2 == null) {
+                        fqcn2 = v2.getUiName();
+                    }
+                    ViewData d1 = viewMap.get(fqcn1);
+                    ViewData d2 = viewMap.get(fqcn2);
+
+                    // Use natural sorting order of the view data
+                    // Sort unknown views to the end (and alphabetically among themselves)
+                    if (d1 != null) {
+                        if (d2 != null) {
+                            return d1.getOrdinal() - d2.getOrdinal();
+                        } else {
+                            return 1;
+                        }
+                    } else {
+                        if (d2 == null) {
+                            return v1.getUiName().compareTo(v2.getUiName());
+                        } else {
+                            return -1;
+                        }
+                    }
+                }
+            };
+            Collections.sort(items, comparator);
+            result.add(Pair.of(name, items));
+        }
+
+        return result;
+    }
+
+    /**
+     * Metadata holder for a particular category - contains the name of the category, its
+     * ordinal (for natural/logical sorting order) and views contained in the category
+     */
+    private static class CategoryData implements Iterable<ViewData>, Comparable<CategoryData> {
+        /** Category name */
+        private final String mName;
+        /** Views included in this category */
+        private final List<ViewData> mViews = new ArrayList<ViewData>();
+        /** Natural ordering rank */
+        private final int mOrdinal = sNextOrdinal++;
+
+        /** Constructs a new category with the given name */
+        private CategoryData(String name) {
+            super();
+            mName = name;
+        }
+
+        /** Adds a new view into this category */
+        private void addView(ViewData view) {
+            mViews.add(view);
+        }
+
+        private String getName() {
+            return mName;
+        }
+
+        public int getViewCount() {
+            return mViews.size();
+        }
+
+        // Implements Iterable<ViewData> such that we can use for-each on the category to
+        // enumerate its views
+        public Iterator<ViewData> iterator() {
+            return mViews.iterator();
+        }
+
+        // Implements Comparable<CategoryData> such that categories can be naturally sorted
+        public int compareTo(CategoryData other) {
+            return mOrdinal - other.mOrdinal;
+        }
+    }
+
+    /** Metadata holder for a view of a given fully qualified class name */
+    private static class ViewData implements Comparable<ViewData> {
+        /** The fully qualified class name of the view */
+        private final String mFqcn;
+        /** Fill preference of the view */
+        private final FillPreference mFillPreference;
+        /** The category that the view belongs to */
+        private final CategoryData mCategory;
+        /** The relative rank of the view for natural ordering */
+        private final int mOrdinal = sNextOrdinal++;
+
+        /** Constructs a new view data for the given class */
+        private ViewData(CategoryData category, String fqcn, FillPreference fillPreference) {
+            super();
+            mCategory = category;
+            mFqcn = fqcn;
+            mFillPreference = fillPreference;
+        }
+
+        /** Returns the category for views of this type */
+        private CategoryData getCategory() {
+            return mCategory;
+        }
+
+        /** Returns the {@link FillPreference} for views of this type */
+        private FillPreference getFillPreference() {
+            return mFillPreference;
+        }
+
+        /** Fully qualified class name of views of this type */
+        private String getFcqn() {
+            return mFqcn;
+        }
+
+        /** Relative rank of this view type */
+        private int getOrdinal() {
+            return mOrdinal;
+        }
+
+        // Implements Comparable<ViewData> such that views can be sorted naturally
+        public int compareTo(ViewData other) {
+            return mOrdinal - other.mOrdinal;
+        }
+    }
+
+    /**
+     * Returns the {@link FillPreference} for classes with the given fully qualified class
+     * name
+     *
+     * @param fqcn the fully qualified class name of the view
+     * @return a suitable {@link FillPreference} for the given view type
+     */
+    public FillPreference getFillPreference(String fqcn) {
+        ViewData view = getClassToView().get(fqcn);
+        if (view != null) {
+            return view.getFillPreference();
+        }
+
+        return FillPreference.NONE;
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml
new file mode 100644 (file)
index 0000000..31b71aa
--- /dev/null
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Palette Metadata
+
+    This document provides additional designtime metadata for various Android views,
+    such as logical palette categories (as well as a natural ordering of the views within
+    their categories, fill-preferences (how a view will sets its width and height attributes
+    when dropped into other views), and so on.
+ -->
+<!DOCTYPE metadata [
+<!--- The metadata consists of a series of category definitions -->
+<!ELEMENT metadata (category)*>
+<!--- Each category has a name and contains a list of views in order -->
+<!ELEMENT category (view)*>
+<!ATTLIST category name CDATA #IMPLIED>
+<!--- Each view is identified by its full class name and has various
+      other attributes such as a fill preference  -->
+<!ELEMENT view EMPTY>
+<!ATTLIST view
+    class CDATA #REQUIRED
+    previewXml CDATA #IMPLIED
+    fill ( none|both|width|height|opposite|
+          width_in_vertical|height_in_horizontal) "none"
+>
+]>
+<metadata>
+    <category name="Form Widgets">
+        <view class="android.widget.TextView" />
+        <view class="android.widget.Button" />
+        <view class="android.widget.CheckBox" />
+        <view class="android.widget.ToggleButton" />
+        <view class="android.widget.RadioButton" />
+        <view class="android.widget.CheckedTextView" />
+        <view class="android.widget.Spinner" fill="width_in_vertical" />
+        <view class="android.widget.EditText" fill="width_in_vertical" />
+        <view class="android.widget.AutoCompleteTextView" fill="width_in_vertical" />
+        <view class="android.widget.MultiAutoCompleteTextView" fill="width_in_vertical" />
+        <view class="android.widget.ProgressBar" />
+        <view class="android.widget.QuickContactBadge" />
+        <view class="android.widget.RadioGroup" />
+        <view class="android.widget.RatingBar" />
+        <view class="android.widget.SeekBar" fill="width_in_vertical" />
+    </category>
+    <category name="Composite">
+        <view class="android.widget.ListView" fill="width_in_vertical" />
+        <view class="android.widget.ExpandableListView" fill="width_in_vertical" />
+        <view class="android.widget.TwoLineListItem" />
+        <view class="android.widget.GridView" fill="opposite"/>
+        <view class="android.widget.ScrollView" fill="opposite"/>
+        <view class="android.widget.HorizontalScrollView" />
+        <view class="android.widget.SearchView" />
+        <view class="android.widget.SlidingDrawer" />
+        <view class="android.widget.TabHost" fill="width_in_vertical" />
+        <view class="android.widget.TabWidget" />
+        <view class="android.webkit.WebView" fill="opposite"/>
+    </category>
+    <category name="Images &amp; Media">
+        <view class="android.widget.ImageView" />
+        <view class="android.widget.ImageButton" />
+        <view class="android.widget.Gallery" fill="width_in_vertical" />
+        <view class="android.widget.MediaController" />
+        <view class="android.widget.VideoView"  fill="opposite" />
+    </category>
+    <category name="Layouts">
+        <view class="android.widget.LinearLayout" fill="opposite"/>
+        <view class="android.widget.RelativeLayout" fill="opposite"/>
+        <view class="android.widget.FrameLayout" fill="opposite"/>
+        <view class="android.widget.AbsoluteLayout" fill="opposite"/>
+        <view class="android.widget.TableLayout" fill="opposite"/>
+        <view class="android.widget.TableRow" fill="opposite"/>
+    </category>
+    <category name="Time &amp; Date">
+        <view class="android.widget.TimePicker" />
+        <view class="android.widget.DatePicker" />
+        <view class="android.widget.CalendarView" />
+        <view class="android.widget.Chronometer" />
+        <view class="android.widget.AnalogClock" />
+        <view class="android.widget.DigitalClock" />
+    </category>
+    <category name="Transitions">
+        <view class="android.widget.ImageSwitcher" />
+        <view class="android.widget.AdapterViewFlipper" fill="opposite"/>
+        <view class="android.widget.StackView" fill="opposite"/>
+        <view class="android.widget.TextSwitcher" fill="opposite"/>
+        <view class="android.widget.ViewAnimator" fill="opposite"/>
+        <view class="android.widget.ViewFlipper" fill="opposite"/>
+        <view class="android.widget.ViewSwitcher" fill="opposite"/>
+    </category>
+    <category name="Advanced">
+        <view class="android.view.View" />
+        <view class="android.view.ViewStub" />
+        <view class="android.gesture.GestureOverlayView" />
+        <view class="android.view.SurfaceView" />
+        <view class="android.widget.NumberPicker" />
+        <view class="android.widget.ZoomButton" />
+        <view class="android.widget.ZoomControls" />
+        <view class="android.widget.DialerFilter" fill="width_in_vertical" />
+    </category>
+    <category name="Other">
+        <!--  This is the catch-all category which contains unknown views if we encounter any -->
+    </category>
+    <!--  TODO: Add-ons? -->
+</metadata>
index 5354924..f4b10b7 100644 (file)
@@ -18,7 +18,6 @@ package com.android.ide.eclipse.adt.internal.editors.layout.uimodel;
 
 import com.android.ide.common.layout.LayoutConstants;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
@@ -68,7 +67,7 @@ public class UiViewElementNode extends UiElementNode {
             // owned by a FrameLayout.
             // TODO replace by something user-configurable.
 
-            List<ElementDescriptor> layoutDescriptors = null;
+            List<ViewElementDescriptor> layoutDescriptors = null;
             IProject project = getEditor().getProject();
             if (project != null) {
                 Sdk currentSdk = Sdk.getCurrent();
@@ -84,10 +83,9 @@ public class UiViewElementNode extends UiElementNode {
             }
 
             if (layoutDescriptors != null) {
-                for (ElementDescriptor desc : layoutDescriptors) {
-                    if (desc instanceof ViewElementDescriptor &&
-                            desc.getXmlName().equals(SdkConstants.CLASS_NAME_FRAMELAYOUT)) {
-                        layout_attrs = ((ViewElementDescriptor) desc).getLayoutAttributes();
+                for (ViewElementDescriptor desc : layoutDescriptors) {
+                    if (desc.getXmlName().equals(SdkConstants.CLASS_NAME_FRAMELAYOUT)) {
+                        layout_attrs = desc.getLayoutAttributes();
                         need_xmlns = true;
                         break;
                     }
index 2ca5eb2..300dfb6 100644 (file)
@@ -39,7 +39,7 @@ public class RenderLoggerTest extends TestCase {
     public void testLogger3() throws Exception {
         RenderLogger l = new RenderLogger("foo");
         assertFalse(l.hasProblems());
-        l.error("timeout", "Sample Error", new RuntimeException());
+        l.error("timeout", "Sample Error", new RuntimeException(), null);
         l.warning("slow", "Sample warning", null);
         assertTrue(l.hasProblems());
         assertEquals("Sample Error\n" + "Sample warning\n"
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java
new file mode 100644 (file)
index 0000000..d18967d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.editors.layout.gre;
+
+import com.android.ide.common.api.IViewMetadata.FillPreference;
+
+import junit.framework.TestCase;
+
+public class ViewMetadataRepositoryTest extends TestCase {
+    public void testSingleton() throws Exception {
+        assertSame(ViewMetadataRepository.get(), ViewMetadataRepository.get());
+    }
+
+    public void testBasic() throws Exception {
+        ViewMetadataRepository repository = ViewMetadataRepository.get();
+
+        assertEquals(FillPreference.WIDTH_IN_VERTICAL,
+                repository.getFillPreference("android.widget.Spinner"));
+        assertEquals(FillPreference.NONE,
+                repository.getFillPreference("foo.bar"));
+    }
+}