OSDN Git Service

Resource management refactoring and clean-up.
authorXavier Ducrohet <xav@android.com>
Tue, 1 Mar 2011 00:49:37 +0000 (16:49 -0800)
committerXavier Ducrohet <xav@android.com>
Wed, 9 Mar 2011 01:50:49 +0000 (17:50 -0800)
- (I)ResourceRepository is now a common class instead of an
  interface. This contains most of the code to control
  a repository (which was extracted from ProjectResources)
  ProjectResources extends it adding minor features such as
      library support, and inline ID definition.
  FrameworkResources extends it adding support for public
      resources (which used to be duplicated and dispersed
      in weird places).
  Changed the way resources are reloaded on resource change event.
  Instead of marking the resources as modified (using
  Resource.touch()), the resources are now parsed as the files are
  processed during the resource delta visitor. This makes more sense
  as there are now other listeners to the resource changes (hyperlinks)
  that access the resource list in their listeners, which wouldn't work
  previously.
  This also makes the code cleaner as the previous method had to query
  the repo for items and return a list of new ones, which was kinda
  crappy. The new code is much simpler, as is the post update process.

- ResourceItem is now the base class for resource items. It includes
  all the small methods that were added by all the child classes or
  interfaces.
  Project/ConfigurableResourceItem are merged into the based class.
  IIdResourceItem and IdResourceItem are gone and replaced by a
  simpler InlineResourceItem.
  FrameworkResourceItem is a simple override for framework resources.

- Also improved the API of a bit for the resource repository, making
  more use of unmodifiable lists and emptyList/Map()

Change-Id: Ie3ac1995213fed66153c7e7ecbdd170ec257be62

37 files changed:
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.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/uimodel/UiResourceAttributeNode.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IIdResourceItem.java [deleted file]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IResourceRepository.java [deleted file]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceItem.java [deleted file]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Configurable.java [moved from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Resource.java with 65% similarity]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ConfigurableResourceItem.java [deleted file]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FrameworkResourceItem.java [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FrameworkResources.java [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/InlineResourceItem.java [moved from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdResourceItem.java with 53% similarity]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResourceItem.java [deleted file]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceItem.java [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceRepository.java [new file with mode: 0644]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/FrameworkResourceRepository.java [deleted file]
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java
eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
ide_common/src/com/android/ide/common/resources/ResourceResolver.java

index 10ccd77..c28a561 100644 (file)
@@ -37,6 +37,7 @@ import org.eclipse.core.resources.IResourceDelta;
 import org.eclipse.core.runtime.CoreException;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -352,7 +353,7 @@ public final class LayoutReloadMonitor {
 
             // now check that the file is *NOT* a layout file (those automatically trigger a layout
             // reload and we don't want to do it twice.)
-            List<ResourceType> resTypes = file.getResourceTypes();
+            Collection<ResourceType> resTypes = file.getResourceTypes();
 
             // it's unclear why but there has been cases of resTypes being empty!
             if (resTypes.size() > 0) {
index 3c5cea4..5e03b54 100644 (file)
@@ -36,6 +36,7 @@ import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice;
 import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager;
@@ -211,9 +212,9 @@ public class ConfigurationComposite extends Composite {
          */
         void onRenderingTargetPostChange(IAndroidTarget target);
 
-        ProjectResources getProjectResources();
-        ProjectResources getFrameworkResources();
-        ProjectResources getFrameworkResources(IAndroidTarget target);
+        ResourceRepository getProjectResources();
+        ResourceRepository getFrameworkResources();
+        ResourceRepository getFrameworkResources(IAndroidTarget target);
         Map<ResourceType, Map<String, ResourceValue>> getConfiguredProjectResources();
         Map<ResourceType, Map<String, ResourceValue>> getConfiguredFrameworkResources();
     }
@@ -1121,12 +1122,12 @@ public class ConfigurationComposite extends Composite {
             boolean hasLocale = false;
 
             // get the languages from the project.
-            ProjectResources project = mListener.getProjectResources();
+            ResourceRepository projectRes = mListener.getProjectResources();
 
             // in cases where the opened file is not linked to a project, this could be null.
-            if (project != null) {
+            if (projectRes != null) {
                 // now get the languages from the project.
-                languages = project.getLanguages();
+                languages = projectRes.getLanguages();
 
                 for (String language : languages) {
                     hasLocale = true;
@@ -1134,7 +1135,7 @@ public class ConfigurationComposite extends Composite {
                     LanguageQualifier langQual = new LanguageQualifier(language);
 
                     // find the matching regions and add them
-                    SortedSet<String> regions = project.getRegions(language);
+                    SortedSet<String> regions = projectRes.getRegions(language);
                     for (String region : regions) {
                         mLocaleCombo.add(
                                 String.format("%1$s / %2$s", language, region)); //$NON-NLS-1$
@@ -1225,7 +1226,7 @@ public class ConfigurationComposite extends Composite {
             return; // can't do anything w/o it.
         }
 
-        ProjectResources frameworkProject = mListener.getFrameworkResources(getRenderingTarget());
+        ResourceRepository frameworkRes = mListener.getFrameworkResources(getRenderingTarget());
 
         mDisableUpdates++;
 
@@ -1237,10 +1238,10 @@ public class ConfigurationComposite extends Composite {
             ArrayList<String> themes = new ArrayList<String>();
 
             // get the themes, and languages from the Framework.
-            if (frameworkProject != null) {
+            if (frameworkRes != null) {
                 // get the configured resources for the framework
                 Map<ResourceType, Map<String, ResourceValue>> frameworResources =
-                    frameworkProject.getConfiguredResources(getCurrentConfig());
+                    frameworkRes.getConfiguredResources(getCurrentConfig());
 
                 if (frameworResources != null) {
                     // get the styles.
@@ -1269,9 +1270,9 @@ public class ConfigurationComposite extends Composite {
             }
 
             // now get the themes and languages from the project.
-            ProjectResources project = mListener.getProjectResources();
+            ResourceRepository projectRes = mListener.getProjectResources();
             // in cases where the opened file is not linked to a project, this could be null.
-            if (project != null) {
+            if (projectRes != null) {
                 // get the configured resources for the project
                 Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes =
                     mListener.getConfiguredProjectResources();
index 9e0525e..858bfb8 100644 (file)
@@ -60,6 +60,7 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQ
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
@@ -541,7 +542,7 @@ public class GraphicalEditorPart extends EditorPart
 
         public Map<ResourceType, Map<String, ResourceValue>> getConfiguredFrameworkResources() {
             if (mConfiguredFrameworkRes == null && mConfigComposite != null) {
-                ProjectResources frameworkRes = getFrameworkResources();
+                ResourceRepository frameworkRes = getFrameworkResources();
 
                 if (frameworkRes == null) {
                     AdtPlugin.log(IStatus.ERROR, "Failed to get ProjectResource for the framework");
@@ -559,9 +560,6 @@ public class GraphicalEditorPart extends EditorPart
             if (mConfiguredProjectRes == null && mConfigComposite != null) {
                 ProjectResources project = getProjectResources();
 
-                // make sure they are loaded
-                project.loadAll();
-
                 // get the project resource values based on the current config
                 mConfiguredProjectRes = project.getConfiguredResources(
                         mConfigComposite.getCurrentConfig());
@@ -575,7 +573,7 @@ public class GraphicalEditorPart extends EditorPart
          * configuration selection.
          * @return the framework resources or null if not found.
          */
-        public ProjectResources getFrameworkResources() {
+        public ResourceRepository getFrameworkResources() {
             return getFrameworkResources(getRenderingTarget());
         }
 
@@ -585,7 +583,7 @@ public class GraphicalEditorPart extends EditorPart
          * @param target the target for which to return the framework resources.
          * @return the framework resources or null if not found.
          */
-        public ProjectResources getFrameworkResources(IAndroidTarget target) {
+        public ResourceRepository getFrameworkResources(IAndroidTarget target) {
             if (target != null) {
                 AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
 
index f9e103a..3461d18 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
 
+import static com.android.AndroidConstants.FD_RES_LAYOUT;
 import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
 import static com.android.ide.eclipse.adt.AdtConstants.WS_LAYOUTS;
 import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
-import static com.android.AndroidConstants.FD_RES_LAYOUT;
+import static com.android.resources.ResourceType.LAYOUT;
 
 import static org.eclipse.core.resources.IResourceDelta.ADDED;
 import static org.eclipse.core.resources.IResourceDelta.CHANGED;
@@ -30,10 +31,10 @@ import com.android.annotations.VisibleForTesting;
 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.project.BaseProjectHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager.IResourceListener;
 import com.android.ide.eclipse.adt.io.IFileWrapper;
@@ -62,6 +63,7 @@ import org.xml.sax.SAXException;
 import java.io.IOException;
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -396,8 +398,8 @@ public class IncludeFinder {
     private void scanProject() {
         ProjectResources resources = ResourceManager.getInstance().getProjectResources(mProject);
         if (resources != null) {
-            ProjectResourceItem[] layouts = resources.getResources(ResourceType.LAYOUT);
-            for (ProjectResourceItem layout : layouts) {
+            Collection<ResourceItem> layouts = resources.getResourceItemsOfType(LAYOUT);
+            for (ResourceItem layout : layouts) {
                 List<ResourceFile> sources = layout.getSourceFileList();
                 for (ResourceFile source : sources) {
                     updateFileIncludes(source, false);
@@ -419,7 +421,7 @@ public class IncludeFinder {
      * @return true if we updated the includes for the resource file
      */
     private boolean updateFileIncludes(ResourceFile resourceFile, boolean singleUpdate) {
-        List<ResourceType> resourceTypes = resourceFile.getResourceTypes();
+        Collection<ResourceType> resourceTypes = resourceFile.getResourceTypes();
         for (ResourceType type : resourceTypes) {
             if (type == ResourceType.LAYOUT) {
                 ensureInitialized();
index ce299e4..7a1e06e 100755 (executable)
@@ -38,8 +38,8 @@ import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewEleme
 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleElement;
 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
@@ -852,7 +852,7 @@ public class RulesEngine {
             IProject project = editor.getProject();
             if (project != null) {
                 // get the resource repository for this project and the system resources.
-                IResourceRepository projectRepository =
+                ResourceRepository projectRepository =
                     ResourceManager.getInstance().getProjectResources(project);
                 Shell shell = AdtPlugin.getDisplay().getActiveShell();
                 if (shell == null) {
@@ -879,7 +879,7 @@ public class RulesEngine {
             ResourceType type = ResourceType.getEnum(resourceTypeName);
             if (project != null) {
                 // get the resource repository for this project and the system resources.
-                IResourceRepository projectRepository = ResourceManager.getInstance()
+                ResourceRepository projectRepository = ResourceManager.getInstance()
                         .getProjectResources(project);
                 Shell shell = AdtPlugin.getDisplay().getActiveShell();
                 if (shell == null) {
@@ -887,7 +887,7 @@ public class RulesEngine {
                 }
 
                 AndroidTargetData data = editor.getTargetData();
-                IResourceRepository systemRepository = data.getSystemResources();
+                ResourceRepository systemRepository = data.getFrameworkResources();
 
                 // open a resource chooser dialog for specified resource type.
                 ResourceChooser dlg = new ResourceChooser(project, type, projectRepository,
index b10d69b..1af04a8 100644 (file)
@@ -23,9 +23,9 @@ import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescrip
 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.ui.ReferenceChooserDialog;
 import com.android.ide.eclipse.adt.internal.ui.ResourceChooser;
@@ -48,6 +48,8 @@ import org.eclipse.ui.forms.widgets.FormToolkit;
 import org.eclipse.ui.forms.widgets.TableWrapData;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -128,19 +130,19 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
         IProject project = editor.getProject();
         if (project != null) {
             // get the resource repository for this project and the system resources.
-            IResourceRepository projectRepository =
+            ResourceRepository projectRepository =
                 ResourceManager.getInstance().getProjectResources(project);
 
             if (mType != null) {
                 // get the Target Data to get the system resources
                 AndroidTargetData data = editor.getTargetData();
-                IResourceRepository systemRepository = data.getSystemResources();
+                ResourceRepository frameworkRepository = data.getFrameworkResources();
 
                 // open a resource chooser dialog for specified resource type.
                 ResourceChooser dlg = new ResourceChooser(project,
                         mType,
                         projectRepository,
-                        systemRepository,
+                        frameworkRepository,
                         shell);
 
                 dlg.setCurrentResource(currentValue);
@@ -196,7 +198,7 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
      */
     @Override
     public String[] getPossibleValues(String prefix) {
-        IResourceRepository repository = null;
+        ResourceRepository repository = null;
         boolean isSystem = false;
 
         UiElementNode uiNode = getUiParent();
@@ -212,15 +214,15 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
             // If there's a prefix with "android:" in it, use the system resources
             // Non-public framework resources are filtered out later.
             AndroidTargetData data = editor.getTargetData();
-            repository = data.getSystemResources();
+            repository = data.getFrameworkResources();
             isSystem = true;
         }
 
         // Get list of potential resource types, either specific to this project
         // or the generic list.
-        ResourceType[] resTypes = (repository != null) ?
+        Collection<ResourceType> resTypes = (repository != null) ?
                     repository.getAvailableResourceTypes() :
-                    ResourceType.values();
+                    EnumSet.allOf(ResourceType.class);
 
         // Get the type name from the prefix, if any. It's any word before the / if there's one
         String typeName = null;
@@ -265,18 +267,8 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
                 sb.append(typeName).append('/');
                 String base = sb.toString();
 
-                if (isSystem) {
-                    AndroidTargetData targetData = editor.getTargetData();
-                    for (ResourceItem item : repository.getResources(resType)) {
-                        String name = item.getName();
-                        if (targetData == null || targetData.isPublicResource(resType, name)) {
-                            results.add(base + name);
-                        }
-                    }
-                } else {
-                    for (ResourceItem item : repository.getResources(resType)) {
-                        results.add(base + item.getName());
-                    }
+                for (ResourceItem item : repository.getResourceItemsOfType(resType)) {
+                    results.add(base + item.getName());
                 }
             }
         }
index efd95e2..5a2f39f 100644 (file)
@@ -47,6 +47,7 @@ import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.ide.eclipse.adt.io.IFileWrapper;
@@ -790,7 +791,7 @@ public class Hyperlinks {
     }
 
     /** Return either the project resources or the framework resources (or null) */
-    private static ProjectResources getResources(IProject project, boolean framework) {
+    private static ResourceRepository getResources(IProject project, boolean framework) {
         if (framework) {
             IAndroidTarget target = getTarget(project);
             if (target == null) {
@@ -822,7 +823,7 @@ public class Hyperlinks {
         }
 
         // Look in the configuration folder: Search compatible configurations
-        ProjectResources resources = getResources(project, false /* isFramework */);
+        ResourceRepository resources = getResources(project, false /* isFramework */);
         FolderConfiguration configuration = getConfiguration();
         if (configuration != null) { // Not the case when searching from Java files for example
             List<ResourceFolder> folders = resources.getFolders(ResourceFolderType.LAYOUT);
@@ -1128,7 +1129,7 @@ public class Hyperlinks {
 
         boolean isFramework = url.startsWith("@android"); //$NON-NLS-1$
 
-        ProjectResources resources = getResources(project, isFramework);
+        ResourceRepository resources = getResources(project, isFramework);
         if (resources == null) {
             return null;
         }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IIdResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IIdResourceItem.java
deleted file mode 100644 (file)
index acc4cf2..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2008 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.resources;
-
-import com.android.resources.ResourceType;
-
-/**
- * Classes which implements this interface provides a method indicating the state of a resource of
- * type {@link ResourceType#ID}.
- */
-public interface IIdResourceItem {
-    /**
-     * Returns whether the ID resource has been declared inline inside another resource XML file. 
-     */
-    public boolean isDeclaredInline();
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IResourceRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IResourceRepository.java
deleted file mode 100644 (file)
index 1abd9eb..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 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.resources;
-
-import com.android.resources.ResourceType;
-
-/**
- * A repository of resources. This allows access to the resource by {@link ResourceType}.
- */
-public interface IResourceRepository {
-
-    /**
-     * Returns the present {@link ResourceType}s in the project.
-     * @return an array containing all the type of resources existing in the project.
-     */
-    public abstract ResourceType[] getAvailableResourceTypes();
-
-    /**
-     * Returns an array of the existing resource for the specified type.
-     * @param type the type of the resources to return
-     */
-    public abstract ResourceItem[] getResources(ResourceType type);
-
-    /**
-     * Returns whether resources of the specified type are present.
-     * @param type the type of the resources to check.
-     */
-    public abstract boolean hasResources(ResourceType type);
-    
-    /**
-     * Returns whether the repository is a system repository.
-     */
-    public abstract boolean isSystemRepository();
-
-}
index 327bd89..568174c 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.ide.eclipse.adt.internal.resources;
 
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
 import com.android.resources.ResourceType;
 
 
@@ -29,11 +30,8 @@ public class ResourceHelper {
      */
     public static String getXmlString(ResourceType type, ResourceItem resourceItem,
             boolean system) {
-        if (type == ResourceType.ID && resourceItem instanceof IIdResourceItem) {
-            IIdResourceItem idResource = (IIdResourceItem)resourceItem;
-            if (idResource.isDeclaredInline()) {
-                return (system?"@android:":"@+") + type.getName() + "/" + resourceItem.getName(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-            }
+        if (type == ResourceType.ID && resourceItem.isDeclaredInline()) {
+            return (system?"@android:":"@+") + type.getName() + "/" + resourceItem.getName(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
         }
 
         return (system?"@android:":"@") + type.getName() + "/" + resourceItem.getName(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceItem.java
deleted file mode 100644 (file)
index c340ffe..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008 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.resources;
-
-/**
- * Base class representing a Resource Item, as returned by a {@link IResourceRepository}.
- */
-public class ResourceItem implements Comparable<ResourceItem> {
-    
-    private final String mName;
-    
-    /**
-     * Constructs a new ResourceItem
-     * @param name the name of the resource as it appears in the XML and R.java files.
-     */
-    public ResourceItem(String name) {
-        mName = name;
-    }
-
-    /**
-     * Returns the name of the resource item.
-     */
-    public final String getName() {
-        return mName;
-    }
-
-    /**
-     * Compares the {@link ResourceItem} to another.
-     * @param other the ResourceItem to be compared to.
-     */
-    public int compareTo(ResourceItem other) {
-        return mName.compareTo(other.mName);
-    }
-}
index 543719b..3ad9d50 100644 (file)
@@ -20,7 +20,7 @@ import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
 
 import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
 import com.android.resources.FolderTypeRelationship;
@@ -32,6 +32,7 @@ import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.core.JavaConventions;
 import org.eclipse.jface.dialogs.IInputValidator;
 
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -160,9 +161,9 @@ public class ResourceNameValidator implements IInputValidator {
         Set<String> existing = new HashSet<String>();
         ResourceManager manager = ResourceManager.getInstance();
         ProjectResources projectResources = manager.getProjectResources(project);
-        ProjectResourceItem[] resources = projectResources.getResources(type);
-        for (ProjectResourceItem resource : resources) {
-            existing.add(resource.getName());
+        Collection<ResourceItem> items = projectResources.getResourceItemsOfType(type);
+        for (ResourceItem item : items) {
+            existing.add(item.getName());
         }
 
         boolean isFileType = isFileBasedResourceType(type);
@@ -19,28 +19,11 @@ package com.android.ide.eclipse.adt.internal.resources.manager;
 import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
 
 /**
- * Base class for file system resource items (Folders, Files).
+ * An object that is associated with a {@link FolderConfiguration}.
  */
-public abstract class Resource {
-    private boolean mTouched = true;
-
+public interface Configurable {
     /**
      * Returns the {@link FolderConfiguration} for this object.
      */
-    public abstract FolderConfiguration getConfiguration();
-
-    /**
-     * Indicates that the underlying file was changed.
-     */
-    public final void touch() {
-       mTouched = true;
-    }
-
-    public final boolean isTouched() {
-        return mTouched;
-    }
-
-    public final void resetTouch() {
-        mTouched = false;
-    }
+    public FolderConfiguration getConfiguration();
 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ConfigurableResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ConfigurableResourceItem.java
deleted file mode 100644 (file)
index 2a998f8..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2007 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.resources.manager;
-
-/**
- * Represents a resource item that can exist in multiple "alternate" versions. 
- */
-public class ConfigurableResourceItem extends ProjectResourceItem {
-    
-    /**
-     * Constructs a new Resource Item.
-     * @param name the name of the resource as it appears in the XML and R.java files.
-     */
-    public ConfigurableResourceItem(String name) {
-        super(name);
-    }
-
-    /**
-     * Returns if the resource item has at least one non-default configuration.
-     */
-    public boolean hasAlternates() {
-        for (ResourceFile file : mFiles) {
-            if (file.getFolder().getConfiguration().isDefault() == false) {
-                return true;
-            }
-        }
-        
-        return false;
-    }
-
-    /**
-     * Returns whether the resource has a default version, with no qualifier.
-     */
-    public boolean hasDefault() {
-        for (ResourceFile file : mFiles) {
-            if (file.getFolder().getConfiguration().isDefault()) {
-                return true;
-            }
-        }
-        
-        // We only want to return false if there's no default and more than 0 items.
-        return (mFiles.size() == 0);
-    }
-
-    /**
-     * Returns the number of alternate versions of this resource.
-     */
-    public int getAlternateCount() {
-        int count = 0;
-        for (ResourceFile file : mFiles) {
-            if (file.getFolder().getConfiguration().isDefault() == false) {
-                count++;
-            }
-        }
-
-        return count;
-    }
-
-    /*
-     * (non-Javadoc)
-     * Returns whether the item can be edited directly (ie it does not have alternate versions).
-     */
-    @Override
-    public boolean isEditableDirectly() {
-        return hasAlternates() == false;
-    }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FrameworkResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FrameworkResourceItem.java
new file mode 100644 (file)
index 0000000..a0e79d9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.resources.manager;
+
+/**
+ * A custom {@link ResourceItem} for resources provided by the framework.
+ *
+ * The main change is that {@link #isEditableDirectly()} returns false.
+ */
+class FrameworkResourceItem extends ResourceItem {
+
+    FrameworkResourceItem(String name) {
+        super(name);
+    }
+
+    @Override
+    public boolean isEditableDirectly() {
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "FrameworkResourceItem [mName=" + getName() + ", mFiles=" //$NON-NLS-1$ //$NON-NLS-2$
+                + getSourceFileList() + "]"; //$NON-NLS-1$
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FrameworkResources.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FrameworkResources.java
new file mode 100644 (file)
index 0000000..fc29dd9
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * 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.resources.manager;
+
+import static com.android.AndroidConstants.FD_RES_VALUES;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.resources.ResourceType;
+
+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.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * Framework resources repository.
+ *
+ * This behaves the same as {@link ResourceRepository} except that it differentiates between
+ * resources that are public and non public.
+ * {@link #getResources(ResourceType)} and {@link #hasResourcesOfType(ResourceType)} only return
+ * public resources. This is typically used to display resource lists in the UI.
+ *
+ * {@link #getConfiguredResources(com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration)}
+ * returns all resources, even the non public ones so that this can be used for rendering.
+ */
+class FrameworkResources extends ResourceRepository {
+
+    /**
+     * Map of {@link ResourceType} to list of items. It is guaranteed to contain a list for all
+     * possible values of ResourceType.
+     */
+    protected final Map<ResourceType, List<ResourceItem>> mPublicResourceMap =
+        new EnumMap<ResourceType, List<ResourceItem>>(ResourceType.class);
+
+    FrameworkResources() {
+        super(true /*isFrameworkRepository*/);
+    }
+
+    /**
+     * Returns a {@link Collection} (always non null, but can be empty) of <b>public</b>
+     * {@link ResourceItem} matching a given {@link ResourceType}.
+     *
+     * @param type the type of the resources to return
+     * @return a collection of items, possible empty.
+     */
+    @Override
+    public List<ResourceItem> getResourceItemsOfType(ResourceType type) {
+        return mPublicResourceMap.get(type);
+    }
+
+    /**
+     * Returns whether the repository has <b>public</b> resources of a given {@link ResourceType}.
+     * @param type the type of resource to check.
+     * @return true if the repository contains resources of the given type, false otherwise.
+     */
+    @Override
+    public boolean hasResourcesOfType(ResourceType type) {
+        return mPublicResourceMap.get(type).size() > 0;
+    }
+
+    @Override
+    protected ResourceItem createResourceItem(String name) {
+        return new FrameworkResourceItem(name);
+    }
+
+    /**
+     * Reads the public.xml file in data/res/values/ for a given resource folder and builds up
+     * a map of public resources.
+     *
+     * This map is a subset of the full resource map that only contains framework resources
+     * that are public.
+     *
+     * @param osFrameworkResourcePath The root folder of the resources
+     */
+    void loadPublicResources(IAbstractFolder resFolder) {
+        IAbstractFolder valueFolder = resFolder.getFolder(FD_RES_VALUES);
+        if (valueFolder.exists() == false) {
+            return;
+        }
+
+        IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); //$NON-NLS-1$
+        if (publicXmlFile.exists()) {
+            Document document = null;
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            Reader reader = null;
+            try {
+                reader = new BufferedReader(new InputStreamReader(publicXmlFile.getContents()));
+                InputSource is = new InputSource(reader);
+                factory.setNamespaceAware(true);
+                factory.setValidating(false);
+                DocumentBuilder builder = factory.newDocumentBuilder();
+                document = builder.parse(is);
+
+                ResourceType lastType = null;
+                String lastTypeName = "";
+
+                NodeList children = document.getDocumentElement().getChildNodes();
+                for (int i = 0, n = children.getLength(); i < n; i++) {
+                    Node node = children.item(i);
+                    if (node.getNodeType() == Node.ELEMENT_NODE) {
+                        Element element = (Element) node;
+                        String name = element.getAttribute("name"); //$NON-NLS-1$
+                        if (name.length() > 0) {
+                            String typeName = element.getAttribute("type"); //$NON-NLS-1$
+                            ResourceType type = null;
+                            if (typeName.equals(lastTypeName)) {
+                                type = lastType;
+                            } else {
+                                type = ResourceType.getEnum(typeName);
+                                lastType = type;
+                                lastTypeName = typeName;
+                            }
+                            if (type != null) {
+                                List<ResourceItem> typeList = mResourceMap.get(type);
+
+                                ResourceItem match = null;
+                                if (typeList != null) {
+                                    for (ResourceItem item : typeList) {
+                                        if (name.equals(item.getName())) {
+                                            match = item;
+                                            break;
+                                        }
+                                    }
+                                }
+
+                                if (match != null) {
+                                    List<ResourceItem> publicList = mPublicResourceMap.get(type);
+                                    if (publicList == null) {
+                                        publicList = new ArrayList<ResourceItem>();
+                                        mPublicResourceMap.put(type, publicList);
+                                    }
+
+                                    publicList.add(match);
+                                } else {
+                                    // log that there's a public resource that doesn't actually
+                                    // exist?
+                                    if (ResourceManager.DEBUG) {
+                                        System.out.println(String.format(
+                                                "No res matching public value %s/%s",
+                                                typeName, name));
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                AdtPlugin.log(e, "Can't read and parse public attribute list");
+            } finally {
+                if (reader != null) {
+                    try {
+                        reader.close();
+                    } catch (IOException e) {
+                        // Nothing to be done here - we don't care if it closed or not.
+                    }
+                }
+            }
+        }
+
+        // put unmodifiable list for all res type in the public resource map
+        // this will simplify access
+        for (ResourceType type : ResourceType.values()) {
+            List<ResourceItem> list = mPublicResourceMap.get(type);
+            if (list == null) {
+                list = Collections.emptyList();
+            } else {
+                list = Collections.unmodifiableList(list);
+            }
+
+            // put the new list in the map
+            mPublicResourceMap.put(type, list);
+        }
+    }
+}
 
 package com.android.ide.eclipse.adt.internal.resources.manager;
 
-import com.android.ide.eclipse.adt.internal.resources.IIdResourceItem;
-import com.android.resources.ResourceType;
 
 /**
- * Represents a resource item of type {@link ResourceType#ID}
+ * Represents a resource item that has been declared inline in another resource file.
+ *
+ * This covers the typical ID declaration of "@+id/foo", but does not cover normal value
+ * resources declared in strings.xml or other similar value files.
+ *
+ * This resource will return {@code true} for {@link #isDeclaredInline()} and {@code false} for
+ * {@link #isEditableDirectly()}.
  */
-public class IdResourceItem extends ProjectResourceItem implements IIdResourceItem {
-
-    private final boolean mIsDeclaredInline;
+class InlineResourceItem extends ResourceItem {
 
     /**
-     * Constructs a new ResourceItem.
+     * Constructs a new inline ResourceItem.
      * @param name the name of the resource as it appears in the XML and R.java files.
-     * @param isDeclaredInline Whether this id was declared inline.
      */
-    IdResourceItem(String name, boolean isDeclaredInline) {
+    InlineResourceItem(String name) {
         super(name);
-        mIsDeclaredInline = isDeclaredInline;
     }
 
-    /*
-     * (non-Javadoc)
-     * Returns whether the ID resource has been declared inline inside another resource XML file. 
-     */
+    @Override
     public boolean isDeclaredInline() {
-        return mIsDeclaredInline;
+        return true;
     }
 
-    /* (non-Javadoc)
-     * Returns whether the item can be edited (ie, the id was not declared inline).
-     */
     @Override
     public boolean isEditableDirectly() {
-        return !mIsDeclaredInline;
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "InlineResourceItem [mName=" + getName() + ", mFiles=" //$NON-NLS-1$ //$NON-NLS-2$
+                + getSourceFileList() + "]"; //$NON-NLS-1$
     }
 }
index 8f8e0d3..b37e7a2 100644 (file)
@@ -26,14 +26,11 @@ import com.android.resources.ResourceType;
 import org.xml.sax.SAXException;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParser;
@@ -49,81 +46,79 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
 
     private final static SAXParserFactory sParserFactory = SAXParserFactory.newInstance();
 
-    private final Map<ResourceType, HashMap<String, ResourceValue>> mResourceItems =
-        new EnumMap<ResourceType, HashMap<String, ResourceValue>>(ResourceType.class);
+    private final Map<ResourceType, Map<String, ResourceValue>> mResourceItems =
+        new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class);
 
-    private List<ResourceType> mResourceTypeList = null;
+    private Collection<ResourceType> mResourceTypeList = null;
 
     public MultiResourceFile(IAbstractFile file, ResourceFolder folder) {
         super(file, folder);
     }
 
     @Override
-    public List<ResourceType> getResourceTypes() {
-        update();
-
-        if (mResourceTypeList == null) {
-            Set<ResourceType> keys = mResourceItems.keySet();
-            mResourceTypeList = new ArrayList<ResourceType>();
-            mResourceTypeList.addAll(keys);
-            mResourceTypeList = Collections.unmodifiableList(mResourceTypeList);
-        }
+    protected void load() {
+        // need to parse the file and find the content.
+        parseFile();
 
-        return mResourceTypeList;
+        // create new ResourceItems for the new content.
+        mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
+
+        // create/update the resource items.
+        updateResourceItems();
     }
 
     @Override
-    public boolean hasResources(ResourceType type) {
-        update();
+    protected void update() {
+        // remove this file from all existing ResourceItem.
+        getFolder().getRepository().removeFile(mResourceTypeList, this);
 
-        HashMap<String, ResourceValue> list = mResourceItems.get(type);
-        return (list != null && list.size() > 0);
-    }
+        // reset current content.
+        mResourceItems.clear();
 
-    @Override
-    public Collection<ProjectResourceItem> getResources(ResourceType type,
-            ProjectResources projectResources) {
-        update();
+        // need to parse the file and find the content.
+        parseFile();
 
-        HashMap<String, ResourceValue> list = mResourceItems.get(type);
+        // create new ResourceItems for the new content.
+        mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
 
-        ArrayList<ProjectResourceItem> items = new ArrayList<ProjectResourceItem>();
+        // create/update the resource items.
+        updateResourceItems();
+    }
 
-        if (list != null) {
-            Collection<ResourceValue> values = list.values();
-            for (ResourceValue res : values) {
-                ProjectResourceItem item = projectResources.findResourceItem(type, res.getName());
-
-                if (item == null) {
-                    if (type == ResourceType.ID) {
-                        item = new IdResourceItem(res.getName(), false /* isDeclaredInline */);
-                    } else {
-                        item = new ConfigurableResourceItem(res.getName());
-                    }
-                    items.add(item);
-                }
+    @Override
+    protected void dispose() {
+        // only remove this file from all existing ResourceItem.
+        getFolder().getRepository().removeFile(mResourceTypeList, this);
 
-                item.add(this);
-            }
-        }
+        // don't need to touch the content, it'll get reclaimed as this objects disappear.
+        // In the mean time other objects may need to access it.
+    }
 
-        return items;
+    @Override
+    public Collection<ResourceType> getResourceTypes() {
+        return mResourceTypeList;
     }
 
-    /**
-     * Updates the Resource items if necessary.
-     */
-    private void update() {
-        if (isTouched() == true) {
-            // reset current content.
-            mResourceItems.clear();
+    @Override
+    public boolean hasResources(ResourceType type) {
+        Map<String, ResourceValue> list = mResourceItems.get(type);
+        return (list != null && list.size() > 0);
+    }
 
-            // need to parse the file and find the content.
-            parseFile();
+    private void updateResourceItems() {
+        ResourceRepository repository = getRepository();
+        for (ResourceType type : mResourceTypeList) {
+            Map<String, ResourceValue> list = mResourceItems.get(type);
 
-            resetTouch();
+            if (list != null) {
+                Collection<ResourceValue> values = list.values();
+                for (ResourceValue res : values) {
+                    ResourceItem item = repository.getResourceItem(type, res.getName());
 
-            mResourceTypeList = null;
+                    // add this file to the list of files generating this resource item.
+                    item.add(this);
+                }
+            }
         }
     }
 
@@ -147,7 +142,7 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
      * @param value The value of the resource.
      */
     public void addResourceValue(ResourceType resType, ResourceValue value) {
-        HashMap<String, ResourceValue> list = mResourceItems.get(resType);
+        Map<String, ResourceValue> list = mResourceItems.get(resType);
 
         // if the list does not exist, create it.
         if (list == null) {
@@ -169,10 +164,8 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
 
     @Override
     public ResourceValue getValue(ResourceType type, String name) {
-        update();
-
         // get the list for the given type
-        HashMap<String, ResourceValue> list = mResourceItems.get(type);
+        Map<String, ResourceValue> list = mResourceItems.get(type);
 
         if (list != null) {
             return list.get(name);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResourceItem.java
deleted file mode 100644 (file)
index 845a974..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.android.ide.eclipse.adt.internal.resources.manager;
-
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.resources.ResourceType;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Base class for Resource Item coming from an Android Project.
- */
-public abstract class ProjectResourceItem extends ResourceItem {
-
-    private final static Comparator<ResourceFile> sComparator = new Comparator<ResourceFile>() {
-        public int compare(ResourceFile file1, ResourceFile file2) {
-            // get both FolderConfiguration and compare them
-            FolderConfiguration fc1 = file1.getFolder().getConfiguration();
-            FolderConfiguration fc2 = file2.getFolder().getConfiguration();
-            
-            return fc1.compareTo(fc2);
-        }
-    };
-
-    /**
-     * List of files generating this ResourceItem.
-     */
-    protected final ArrayList<ResourceFile> mFiles = new ArrayList<ResourceFile>();
-
-    /**
-     * Constructs a new ResourceItem.
-     * @param name the name of the resource as it appears in the XML and R.java files.
-     */
-    public ProjectResourceItem(String name) {
-        super(name);
-    }
-    
-    /**
-     * Returns whether the resource item is editable directly.
-     * <p/>
-     * This is typically the case for resources that don't have alternate versions, or resources
-     * of type {@link ResourceType#ID} that aren't declared inline.
-     */
-    public abstract boolean isEditableDirectly();
-
-    /**
-     * Adds a new version of this resource item, by adding its {@link ResourceFile}.
-     * @param file the {@link ResourceFile} object.
-     */
-    protected void add(ResourceFile file) {
-        mFiles.add(file);
-    }
-    
-    /**
-     * Reset the item by emptying its version list.
-     */
-    protected void reset() {
-        mFiles.clear();
-    }
-    
-    /**
-     * Returns the sorted list of {@link ResourceItem} objects for this resource item.
-     */
-    public ResourceFile[] getSourceFileArray() {
-        ArrayList<ResourceFile> list = new ArrayList<ResourceFile>();
-        list.addAll(mFiles);
-        
-        Collections.sort(list, sComparator);
-        
-        return list.toArray(new ResourceFile[list.size()]);
-    }
-    
-    /**
-     * Returns the list of {@link ResourceItem} objects for this resource item.
-     */
-    public List<ResourceFile> getSourceFileList() {
-        return Collections.unmodifiableList(mFiles);
-    }
-    
-
-    /**
-     * Replaces the content of the receiver with the ResourceItem received as parameter.
-     * @param item
-     */
-    protected void replaceWith(ProjectResourceItem item) {
-        mFiles.clear();
-        mFiles.addAll(item.mFiles);
-    }
-}
index b7f406d..a6ee95d 100644 (file)
 package com.android.ide.eclipse.adt.internal.resources.manager;
 
 import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
-import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.ide.eclipse.adt.io.IFolderWrapper;
-import com.android.io.IAbstractFolder;
-import com.android.resources.FolderTypeRelationship;
-import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
 import com.android.util.Pair;
 
-import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
 import java.util.Map.Entry;
 
 /**
- * Represents the resources of a project. This is a file view of the resources, with handling
- * for the alternate resource types. For a compiled view use CompiledResources.
+ * Represents the resources of a project.
+ * On top of the regular {@link ResourceRepository} features it provides:
+ *<ul>
+ *<li>configured resources contain the resources coming from the libraries.</li>
+ *<li>resolution to and from resource integer (compiled value in R.java).</li>
+ *<li>handles resource integer for non existing values of type ID. This is used when rendering.</li>
+ *<li>layouts that have no been saved yet. This is handled by generating dynamic IDs
+ *       on the fly.</li>
+ *</ul>
  */
-public class ProjectResources implements IResourceRepository {
+public class ProjectResources extends ResourceRepository {
     private final static int DYNAMIC_ID_SEED_START = 0; // this should not conflict with any
                                                         // project IDs that start at a much higher
                                                         // value
 
-    private final Map<ResourceFolderType, List<ResourceFolder>> mFolderMap =
-        new EnumMap<ResourceFolderType, List<ResourceFolder>>(ResourceFolderType.class);
-
-    private final Map<ResourceType, List<ProjectResourceItem>> mResourceMap =
-        new EnumMap<ResourceType, List<ProjectResourceItem>>(ResourceType.class);
-
     /** Map of (name, id) for resources of type {@link ResourceType#ID} coming from R.java */
     private Map<ResourceType, Map<String, Integer>> mResourceValueMap;
     /** Map of (id, [name, resType]) for all resources coming from R.java */
@@ -69,352 +55,38 @@ public class ProjectResources implements IResourceRepository {
     /** Map of (int[], name) for styleable resources coming from R.java */
     private Map<IntArrayWrapper, String> mStyleableValueToNameMap;
 
+    /**
+     * This list is used by {@link #getResourceValue(String, String)} when the resource
+     * query is an ID that doesn't exist (for example for ID automatically generated in
+     * layout files that are not saved yet).
+     */
     private final Map<String, Integer> mDynamicIds = new HashMap<String, Integer>();
     private int mDynamicSeed = DYNAMIC_ID_SEED_START;
 
-    /** Cached list of {@link IdResourceItem}. This is mix of IdResourceItem created by
-     * {@link MultiResourceFile} for ids coming from XML files under res/values and
-     * {@link IdResourceItem} created manually, from the list coming from R.java */
-    private final List<IdResourceItem> mIdResourceList = new ArrayList<IdResourceItem>();
-
-    private final boolean mIsFrameworkRepository;
     private final IProject mProject;
 
-    private final IntArrayWrapper mWrapper = new IntArrayWrapper(null);
-
 
     /**
      * Makes a ProjectResources for a given <var>project</var>.
      * @param project the project.
      */
     public ProjectResources(IProject project) {
-        mIsFrameworkRepository = false;
+        super(false /*isFrameworkRepository*/);
         mProject = project;
     }
 
     /**
-     * Makes a ProjectResource for a framework repository.
+     * Returns the resources values matching a given {@link FolderConfiguration}, this will
+     * include library dependency.
      *
-     * @see #isSystemRepository()
-     */
-    public ProjectResources() {
-        mIsFrameworkRepository = true;
-        mProject = null;
-    }
-
-    /**
-     * Returns whether this ProjectResources is for a project or for a framework.
-     */
-    public boolean isSystemRepository() {
-        return mIsFrameworkRepository;
-    }
-
-    /**
-     * Adds a Folder Configuration to the project.
-     * @param type The resource type.
-     * @param config The resource configuration.
-     * @param folder The workspace folder object.
-     * @return the {@link ResourceFolder} object associated to this folder.
-     */
-    protected ResourceFolder add(ResourceFolderType type, FolderConfiguration config,
-            IAbstractFolder folder) {
-        // get the list for the resource type
-        List<ResourceFolder> list = mFolderMap.get(type);
-
-        if (list == null) {
-            list = new ArrayList<ResourceFolder>();
-
-            ResourceFolder cf = new ResourceFolder(type, config, folder, mIsFrameworkRepository);
-            list.add(cf);
-
-            mFolderMap.put(type, list);
-
-            return cf;
-        }
-
-        // look for an already existing folder configuration.
-        for (ResourceFolder cFolder : list) {
-            if (cFolder.mConfiguration.equals(config)) {
-                // config already exist. Nothing to be done really, besides making sure
-                // the IFolder object is up to date.
-                cFolder.mFolder = folder;
-                return cFolder;
-            }
-        }
-
-        // If we arrive here, this means we didn't find a matching configuration.
-        // So we add one.
-        ResourceFolder cf = new ResourceFolder(type, config, folder, mIsFrameworkRepository);
-        list.add(cf);
-
-        return cf;
-    }
-
-    /**
-     * Removes a {@link ResourceFolder} associated with the specified {@link IAbstractFolder}.
-     * @param type The type of the folder
-     * @param folder the IFolder object.
-     * @return the {@link ResourceFolder} that was removed, or null if no matches were found.
-     */
-    protected ResourceFolder removeFolder(ResourceFolderType type, IFolder folder) {
-        // get the list of folders for the resource type.
-        List<ResourceFolder> list = mFolderMap.get(type);
-
-        if (list != null) {
-            int count = list.size();
-            for (int i = 0 ; i < count ; i++) {
-                ResourceFolder resFolder = list.get(i);
-                // this is only used for Eclipse stuff so we know it's an IFolderWrapper
-                IFolderWrapper wrapper = (IFolderWrapper) resFolder.getFolder();
-                if (wrapper.getIFolder().equals(folder)) {
-                    // we found the matching ResourceFolder. we need to remove it.
-                    list.remove(i);
-
-                    // we now need to invalidate this resource type.
-                    // The easiest way is to touch one of the other folders of the same type.
-                    if (list.size() > 0) {
-                        list.get(0).touch();
-                    } else {
-                        // if the list is now empty, and we have a single ResouceType out of this
-                        // ResourceFolderType, then we are done.
-                        // However, if another ResourceFolderType can generate similar ResourceType
-                        // than this, we need to update those ResourceTypes as well.
-                        // For instance, if the last "drawable-*" folder is deleted, we need to
-                        // refresh the ResourceItem associated with ResourceType.DRAWABLE.
-                        // Those can be found in ResourceFolderType.DRAWABLE but also in
-                        // ResourceFolderType.VALUES.
-                        // If we don't find a single folder to touch, then it's fine, as the top
-                        // level items (the list of generated resource types) is not cached
-                        // (for now)
-
-                        // get the lists of ResourceTypes generated by this ResourceFolderType
-                        List<ResourceType> resTypes =
-                            FolderTypeRelationship.getRelatedResourceTypes(type);
-
-                        // for each of those, make sure to find one folder to touch so that the
-                        // list of ResourceItem associated with the type is rebuilt.
-                        for (ResourceType resType : resTypes) {
-                            // get the list of folder that can generate this type
-                            List<ResourceFolderType> folderTypes =
-                                FolderTypeRelationship.getRelatedFolders(resType);
-
-                            // we only need to touch one folder in any of those (since it's one
-                            // folder per type, not per folder type).
-                            for (ResourceFolderType folderType : folderTypes) {
-                                List<ResourceFolder> resFolders = mFolderMap.get(folderType);
-
-                                if (resFolders != null && resFolders.size() > 0) {
-                                    resFolders.get(0).touch();
-                                    break;
-                                }
-                            }
-                        }
-                    }
-
-                    // we're done updating/touching, we can stop
-                    return resFolder;
-                }
-            }
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Returns a list of {@link ResourceFolder} for a specific {@link ResourceFolderType}.
-     * @param type The {@link ResourceFolderType}
-     */
-    public List<ResourceFolder> getFolders(ResourceFolderType type) {
-        return mFolderMap.get(type);
-    }
-
-    /* (non-Javadoc)
-     * @see com.android.ide.eclipse.editors.resources.IResourceRepository#getAvailableResourceTypes()
-     */
-    public ResourceType[] getAvailableResourceTypes() {
-        ArrayList<ResourceType> list = new ArrayList<ResourceType>();
-
-        // For each key, we check if there's a single ResourceType match.
-        // If not, we look for the actual content to give us the resource type.
-
-        for (ResourceFolderType folderType : mFolderMap.keySet()) {
-            List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(folderType);
-            if (types.size() == 1) {
-                // before we add it we check if it's not already present, since a ResourceType
-                // could be created from multiple folders, even for the folders that only create
-                // one type of resource (drawable for instance, can be created from drawable/ and
-                // values/)
-                if (list.indexOf(types.get(0)) == -1) {
-                    list.add(types.get(0));
-                }
-            } else {
-                // there isn't a single resource type out of this folder, so we look for all
-                // content.
-                List<ResourceFolder> folders = mFolderMap.get(folderType);
-                if (folders != null) {
-                    for (ResourceFolder folder : folders) {
-                        Collection<ResourceType> folderContent = folder.getResourceTypes();
-
-                        // then we add them, but only if they aren't already in the list.
-                        for (ResourceType folderResType : folderContent) {
-                            if (list.indexOf(folderResType) == -1) {
-                                list.add(folderResType);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // in case ResourceType.ID haven't been added yet because there's no id defined
-        // in XML, we check on the list of compiled id resources.
-        if (list.indexOf(ResourceType.ID) == -1 && mResourceValueMap != null) {
-            Map<String, Integer> map = mResourceValueMap.get(ResourceType.ID);
-            if (map != null && map.size() > 0) {
-                list.add(ResourceType.ID);
-            }
-        }
-
-        // at this point the list is full of ResourceType defined in the files.
-        // We need to sort it.
-        Collections.sort(list);
-
-        return list.toArray(new ResourceType[list.size()]);
-    }
-
-    /* (non-Javadoc)
-     * @see com.android.ide.eclipse.editors.resources.IResourceRepository#getResources(com.android.ide.eclipse.common.resources.ResourceType)
-     */
-    public ProjectResourceItem[] getResources(ResourceType type) {
-        checkAndUpdate(type);
-
-        if (type == ResourceType.ID) {
-            synchronized (mIdResourceList) {
-                return mIdResourceList.toArray(new ProjectResourceItem[mIdResourceList.size()]);
-            }
-        }
-
-        List<ProjectResourceItem> items = mResourceMap.get(type);
-
-        return items.toArray(new ProjectResourceItem[items.size()]);
-    }
-
-    /* (non-Javadoc)
-     * @see com.android.ide.eclipse.editors.resources.IResourceRepository#hasResources(com.android.ide.eclipse.common.resources.ResourceType)
-     */
-    public boolean hasResources(ResourceType type) {
-        checkAndUpdate(type);
-
-        if (type == ResourceType.ID) {
-            synchronized (mIdResourceList) {
-                return mIdResourceList.size() > 0;
-            }
-        }
-
-        List<ProjectResourceItem> items = mResourceMap.get(type);
-        return (items != null && items.size() > 0);
-    }
-
-    /**
-     * Returns the {@link ResourceFolder} associated with a {@link IFolder}.
-     * @param folder The {@link IFolder} object.
-     * @return the {@link ResourceFolder} or null if it was not found.
-     */
-    public ResourceFolder getResourceFolder(IFolder folder) {
-        for (List<ResourceFolder> list : mFolderMap.values()) {
-            for (ResourceFolder resFolder : list) {
-                // this is only used for Eclipse stuff so we know it's an IFolderWrapper
-                IFolderWrapper wrapper = (IFolderWrapper) resFolder.getFolder();
-                if (wrapper.getIFolder().equals(folder)) {
-                    return resFolder;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the {@link ResourceFile} matching the given name, {@link ResourceFolderType} and
-     * configuration.
-     * <p/>This only works with files generating one resource named after the file (for instance,
-     * layouts, bitmap based drawable, xml, anims).
-     * @return the matching file or <code>null</code> if no match was found.
-     */
-    public ResourceFile getMatchingFile(String name, ResourceFolderType type,
-            FolderConfiguration config) {
-        // get the folders for the given type
-        List<ResourceFolder> folders = mFolderMap.get(type);
-
-        // look for folders containing a file with the given name.
-        ArrayList<ResourceFolder> matchingFolders = new ArrayList<ResourceFolder>();
-
-        // remove the folders that do not have a file with the given name.
-        for (int i = 0 ; i < folders.size(); i++) {
-            ResourceFolder folder = folders.get(i);
-
-            if (folder.hasFile(name) == true) {
-                matchingFolders.add(folder);
-            }
-        }
-
-        // from those, get the folder with a config matching the given reference configuration.
-        Resource match = findMatchingConfiguredResource(matchingFolders, config);
-
-        // do we have a matching folder?
-        if (match instanceof ResourceFolder) {
-            // get the ResourceFile from the filename
-            return ((ResourceFolder)match).getFile(name);
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the list of source files for a given resource.
-     * Optionally, if a {@link FolderConfiguration} is given, then only the best
-     * match for this config is returned.
-     *
-     * @param type the type of the resource.
-     * @param name the name of the resource.
-     * @param referenceConfig an optional config for which only the best match will be returned.
-     *
-     * @return a list of files generating this resource or null if it was not found.
-     */
-    public List<ResourceFile> getSourceFiles(ResourceType type, String name,
-            FolderConfiguration referenceConfig) {
-
-        ProjectResourceItem[] resources = getResources(type);
-
-        for (ProjectResourceItem item : resources) {
-            if (name.equals(item.getName())) {
-                if (referenceConfig != null) {
-                    Resource match = findMatchingConfiguredResource(item.getSourceFileList(),
-                            referenceConfig);
-                    if (match instanceof ResourceFile) {
-                        ArrayList<ResourceFile> list = new ArrayList<ResourceFile>();
-                        list.add((ResourceFile) match);
-                        return list;
-                    }
-
-                    return null;
-                }
-                return item.getSourceFileList();
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the resources values matching a given {@link FolderConfiguration}.
      * @param referenceConfig the configuration that each value must match.
+     * @return a map with guaranteed to contain an entry for each {@link ResourceType}
      */
+    @Override
     public Map<ResourceType, Map<String, ResourceValue>> getConfiguredResources(
             FolderConfiguration referenceConfig) {
 
-        Map<ResourceType, Map<String, ResourceValue>> map =
+        Map<ResourceType, Map<String, ResourceValue>> resultMap =
             new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class);
 
         // if the project contains libraries, we need to add the libraries resources here
@@ -435,25 +107,27 @@ public class ProjectResources implements IResourceRepository {
 
                     ProjectResources libRes = resMgr.getProjectResources(library);
                     if (libRes != null) {
-                        // make sure they are loaded
-                        libRes.loadAll();
+                        // get the library resources, and only the library, not the dependencies
+                        // so call doGetConfiguredResources() directly.
+                        Map<ResourceType, Map<String, ResourceValue>> libMap =
+                                libRes.doGetConfiguredResources(referenceConfig);
 
                         // we don't want to simply replace the whole map, but instead merge the
                         // content of any sub-map
-                        Map<ResourceType, Map<String, ResourceValue>> libMap =
-                                libRes.getConfiguredResources(referenceConfig);
+                        for (Entry<ResourceType, Map<String, ResourceValue>> libEntry :
+                                libMap.entrySet()) {
 
-                        for (Entry<ResourceType, Map<String, ResourceValue>> entry : libMap.entrySet()) {
                             // get the map currently in the result map for this resource type
-                            Map<String, ResourceValue> tempMap = map.get(entry.getKey());
+                            Map<String, ResourceValue> tempMap = resultMap.get(libEntry.getKey());
                             if (tempMap == null) {
                                 // since there's no current map for this type, just add the map
                                 // directly coming from the library resources
-                                map.put(entry.getKey(), entry.getValue());
+                                resultMap.put(libEntry.getKey(), libEntry.getValue());
                             } else {
                                 // already a map for this type. add the resources from the
-                                // library.
-                                tempMap.putAll(entry.getValue());
+                                // library, this will override existing value, which is why
+                                // we loop in a specific library order.
+                                tempMap.putAll(libEntry.getValue());
                             }
                         }
                     }
@@ -462,62 +136,21 @@ public class ProjectResources implements IResourceRepository {
         }
 
         // now the project resources themselves.
-        // Don't blindly fill the map, instead check if there are sub-map already present
-        // due to library resources.
-
-        // special case for Id since there's a mix of compiled id (declared inline) and id declared
-        // in the XML files.
-        if (mIdResourceList.size() > 0) {
-            Map<String, ResourceValue> idMap = map.get(ResourceType.ID);
-
-            if (idMap == null) {
-                idMap = new HashMap<String, ResourceValue>();
-                map.put(ResourceType.ID, idMap);
-            }
-            for (IdResourceItem id : mIdResourceList) {
-                // FIXME: cache the ResourceValue!
-                idMap.put(id.getName(), new ResourceValue(ResourceType.ID, id.getName(),
-                        mIsFrameworkRepository));
-            }
-
-        }
-
-        Set<ResourceType> keys = mResourceMap.keySet();
-        for (ResourceType key : keys) {
-            // we don't process ID resources since we already did it above.
-            if (key != ResourceType.ID) {
-                // get the local results
-                Map<String, ResourceValue> localResMap = getConfiguredResource(key,
-                        referenceConfig);
-
-                // check if a map for this type already exists
-                Map<String, ResourceValue> resMap = map.get(key);
-                if (resMap == null) {
-                    // just use the local results.
-                    map.put(key, localResMap);
-                } else {
-                    // add to the library results.
-                    resMap.putAll(localResMap);
-                }
+        Map<ResourceType, Map<String, ResourceValue>> thisProjectMap =
+                doGetConfiguredResources(referenceConfig);
+
+        // now merge the maps.
+        for (Entry<ResourceType, Map<String, ResourceValue>> entry : thisProjectMap.entrySet()) {
+            ResourceType type = entry.getKey();
+            Map<String, ResourceValue> typeMap = resultMap.get(type);
+            if (typeMap == null) {
+                resultMap.put(type, entry.getValue());
+            } else {
+                typeMap.putAll(entry.getValue());
             }
         }
 
-        return map;
-    }
-
-    /**
-     * Loads all the resources. Essentially this forces to load the values from the
-     * {@link ResourceFile} objects to make sure they are up to date and loaded
-     * in {@link #mResourceMap}.
-     */
-    public void loadAll() {
-        // gets all the resource types available.
-        ResourceType[] types = getAvailableResourceTypes();
-
-        // loop on them and load them
-        for (ResourceType type: types) {
-            checkAndUpdate(type);
-        }
+        return resultMap;
     }
 
     /**
@@ -572,55 +205,9 @@ public class ProjectResources implements IResourceRepository {
     }
 
     /**
-     * Returns the sorted list of languages used in the resources.
-     */
-    public SortedSet<String> getLanguages() {
-        SortedSet<String> set = new TreeSet<String>();
-
-        Collection<List<ResourceFolder>> folderList = mFolderMap.values();
-        for (List<ResourceFolder> folderSubList : folderList) {
-            for (ResourceFolder folder : folderSubList) {
-                FolderConfiguration config = folder.getConfiguration();
-                LanguageQualifier lang = config.getLanguageQualifier();
-                if (lang != null) {
-                    set.add(lang.getShortDisplayValue());
-                }
-            }
-        }
-
-        return set;
-    }
-
-    /**
-     * Returns the sorted list of regions used in the resources with the given language.
-     * @param currentLanguage the current language the region must be associated with.
-     */
-    public SortedSet<String> getRegions(String currentLanguage) {
-        SortedSet<String> set = new TreeSet<String>();
-
-        Collection<List<ResourceFolder>> folderList = mFolderMap.values();
-        for (List<ResourceFolder> folderSubList : folderList) {
-            for (ResourceFolder folder : folderSubList) {
-                FolderConfiguration config = folder.getConfiguration();
-
-                // get the language
-                LanguageQualifier lang = config.getLanguageQualifier();
-                if (lang != null && lang.getShortDisplayValue().equals(currentLanguage)) {
-                    RegionQualifier region = config.getRegionQualifier();
-                    if (region != null) {
-                        set.add(region.getShortDisplayValue());
-                    }
-                }
-            }
-        }
-
-        return set;
-    }
-
-    /**
      * Resets the list of dynamic Ids. This list is used by
      * {@link #getResourceValue(String, String)} when the resource query is an ID that doesn't
-     * exist (for example for ID automatically generated in layout files that are not saved.
+     * exist (for example for ID automatically generated in layout files that are not saved yet.)
      * <p/>This method resets those dynamic ID and must be called whenever the actual list of IDs
      * change.
      */
@@ -631,262 +218,20 @@ public class ProjectResources implements IResourceRepository {
         }
     }
 
-    /**
-     * Returns a map of (resource name, resource value) for the given {@link ResourceType}.
-     * <p/>The values returned are taken from the resource files best matching a given
-     * {@link FolderConfiguration}.
-     * @param type the type of the resources.
-     * @param referenceConfig the configuration to best match.
-     */
-    private Map<String, ResourceValue> getConfiguredResource(ResourceType type,
-            FolderConfiguration referenceConfig) {
-        // get the resource item for the given type
-        List<ProjectResourceItem> items = mResourceMap.get(type);
-
-        // create the map
-        HashMap<String, ResourceValue> map = new HashMap<String, ResourceValue>();
-
-        for (ProjectResourceItem item : items) {
-            // get the source files generating this resource
-            List<ResourceFile> list = item.getSourceFileList();
-
-            // look for the best match for the given configuration
-            Resource match = findMatchingConfiguredResource(list, referenceConfig);
-
-            if (match instanceof ResourceFile) {
-                ResourceFile matchResFile = (ResourceFile)match;
-
-                // get the value of this configured resource.
-                ResourceValue value = matchResFile.getValue(type, item.getName());
-
-                if (value != null) {
-                    map.put(item.getName(), value);
-                }
-            }
-        }
-
-        return map;
-    }
-
-    /**
-     * Returns the best matching {@link Resource}.
-     * @param resources the list of {@link Resource} to choose from.
-     * @param referenceConfig the {@link FolderConfiguration} to match.
-     * @see http://d.android.com/guide/topics/resources/resources-i18n.html#best-match
-     */
-    private Resource findMatchingConfiguredResource(List<? extends Resource> resources,
-            FolderConfiguration referenceConfig) {
-        //
-        // 1: eliminate resources that contradict the reference configuration
-        // 2: pick next qualifier type
-        // 3: check if any resources use this qualifier, if no, back to 2, else move on to 4.
-        // 4: eliminate resources that don't use this qualifier.
-        // 5: if more than one resource left, go back to 2.
-        //
-        // The precedence of the qualifiers is more important than the number of qualifiers that
-        // exactly match the device.
-
-        // 1: eliminate resources that contradict
-        ArrayList<Resource> matchingResources = new ArrayList<Resource>();
-        for (int i = 0 ; i < resources.size(); i++) {
-            Resource res = resources.get(i);
-
-            if (res.getConfiguration().isMatchFor(referenceConfig)) {
-                matchingResources.add(res);
-            }
-        }
-
-        // if there is only one match, just take it
-        if (matchingResources.size() == 1) {
-            return matchingResources.get(0);
-        } else if (matchingResources.size() == 0) {
-            return null;
-        }
-
-        // 2. Loop on the qualifiers, and eliminate matches
-        final int count = FolderConfiguration.getQualifierCount();
-        for (int q = 0 ; q < count ; q++) {
-            // look to see if one resource has this qualifier.
-            // At the same time also record the best match value for the qualifier (if applicable).
-
-            // The reference value, to find the best match.
-            // Note that this qualifier could be null. In which case any qualifier found in the
-            // possible match, will all be considered best match.
-            ResourceQualifier referenceQualifier = referenceConfig.getQualifier(q);
-
-            boolean found = false;
-            ResourceQualifier bestMatch = null; // this is to store the best match.
-            for (Resource res : matchingResources) {
-                ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
-                if (qualifier != null) {
-                    // set the flag.
-                    found = true;
-
-                    // Now check for a best match. If the reference qualifier is null ,
-                    // any qualifier is a "best" match (we don't need to record all of them.
-                    // Instead the non compatible ones are removed below)
-                    if (referenceQualifier != null) {
-                        if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) {
-                            bestMatch = qualifier;
-                        }
-                    }
-                }
-            }
-
-            // 4. If a resources has a qualifier at the current index, remove all the resources that
-            // do not have one, or whose qualifier value does not equal the best match found above
-            // unless there's no reference qualifier, in which case they are all considered
-            // "best" match.
-            if (found) {
-                for (int i = 0 ; i < matchingResources.size(); ) {
-                    Resource res = matchingResources.get(i);
-                    ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
-
-                    if (qualifier == null) {
-                        // this resources has no qualifier of this type: rejected.
-                        matchingResources.remove(res);
-                    } else if (referenceQualifier != null && bestMatch != null &&
-                            bestMatch.equals(qualifier) == false) {
-                        // there's a reference qualifier and there is a better match for it than
-                        // this resource, so we reject it.
-                        matchingResources.remove(res);
-                    } else {
-                        // looks like we keep this resource, move on to the next one.
-                        i++;
-                    }
-                }
-
-                // at this point we may have run out of matching resources before going
-                // through all the qualifiers.
-                if (matchingResources.size() < 2) {
-                    break;
-                }
-            }
-        }
-
-        // Because we accept resources whose configuration have qualifiers where the reference
-        // configuration doesn't, we can end up with more than one match. In this case, we just
-        // take the first one.
-        if (matchingResources.size() == 0) {
-            return null;
-        }
-        return matchingResources.get(0);
-    }
-
-    /**
-     * Checks if the list of {@link ResourceItem}s for the specified {@link ResourceType} needs
-     * to be updated.
-     * @param type the Resource Type.
-     */
-    private void checkAndUpdate(ResourceType type) {
-        // get the list of folder that can output this type
-        List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
-
-        for (ResourceFolderType folderType : folderTypes) {
-            List<ResourceFolder> folders = mFolderMap.get(folderType);
-
-            if (folders != null) {
-                for (ResourceFolder folder : folders) {
-                    if (folder.isTouched()) {
-                        // if this folder is touched we need to update all the types that can
-                        // be generated from a file in this folder.
-                        // This will include 'type' obviously.
-                        List<ResourceType> resTypes =
-                            FolderTypeRelationship.getRelatedResourceTypes(folderType);
-                        for (ResourceType resType : resTypes) {
-                            update(resType);
-                        }
-                        return;
-                    }
-                }
-            }
-        }
+    @Override
+    protected ResourceItem createResourceItem(String name) {
+        return new ResourceItem(name);
     }
 
     /**
-     * Updates the list of {@link ResourceItem} objects associated with a {@link ResourceType}.
-     * This will reset the touch status of all the folders that can generate this resource type.
-     * @param type the Resource Type.
+     * Returns a dynamic integer for the given resource name, creating it if it doesn't
+     * already exist.
+     *
+     * @param name the name of the resource
+     * @return an integer.
+     *
+     * @see #resetDynamicIds()
      */
-    private void update(ResourceType type) {
-        // get the cache list, and lets make a backup
-        List<ProjectResourceItem> items = mResourceMap.get(type);
-        List<ProjectResourceItem> backup = new ArrayList<ProjectResourceItem>();
-
-        if (items == null) {
-            items = new ArrayList<ProjectResourceItem>();
-            mResourceMap.put(type, items);
-        } else {
-            // backup the list
-            backup.addAll(items);
-
-            // we reset the list itself.
-            items.clear();
-        }
-
-        // get the list of folder that can output this type
-        List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
-
-        for (ResourceFolderType folderType : folderTypes) {
-            List<ResourceFolder> folders = mFolderMap.get(folderType);
-
-            if (folders != null) {
-                for (ResourceFolder folder : folders) {
-                    items.addAll(folder.getResources(type, this));
-                    folder.resetTouch();
-                }
-            }
-        }
-
-        // now items contains the new list. We "merge" it with the backup list.
-        // Basically, we need to keep the old instances of ResourceItem (where applicable),
-        // but replace them by the content of the new items.
-        // This will let the resource explorer keep the expanded state of the nodes whose data
-        // is a ResourceItem object.
-        if (backup.size() > 0) {
-            // this is not going to change as we're only replacing instances.
-            int count = items.size();
-
-            for (int i = 0 ; i < count;) {
-                // get the "new" item
-                ProjectResourceItem item = items.get(i);
-
-                // look for a similar item in the old list.
-                ProjectResourceItem foundOldItem = null;
-                for (ProjectResourceItem oldItem : backup) {
-                    if (oldItem.getName().equals(item.getName())) {
-                        foundOldItem = oldItem;
-                        break;
-                    }
-                }
-
-                if (foundOldItem != null) {
-                    // erase the data of the old item with the data from the new one.
-                    foundOldItem.replaceWith(item);
-
-                    // remove the old and new item from their respective lists
-                    items.remove(i);
-                    backup.remove(foundOldItem);
-
-                    // add the old item to the new list
-                    items.add(foundOldItem);
-                } else {
-                    // this is a new item, we skip to the next object
-                    i++;
-                }
-            }
-        }
-
-        // if this is the ResourceType.ID, we create the actual list, from this list and
-        // the compiled resource list.
-        if (type == ResourceType.ID) {
-            mergeIdResources();
-        } else {
-            // else this is the list that will actually be displayed, so we sort it.
-            Collections.sort(items);
-        }
-    }
-
     private Integer getDynamicId(String name) {
         synchronized (mDynamicIds) {
             Integer value = mDynamicIds.get(name);
@@ -900,24 +245,6 @@ public class ProjectResources implements IResourceRepository {
     }
 
     /**
-     * Looks up an existing {@link ProjectResourceItem} by {@link ResourceType} and name.
-     * @param type the Resource Type.
-     * @param name the Resource name.
-     * @return the existing ResourceItem or null if no match was found.
-     */
-    protected ProjectResourceItem findResourceItem(ResourceType type, String name) {
-        List<ProjectResourceItem> list = mResourceMap.get(type);
-
-        for (ProjectResourceItem item : list) {
-            if (name.equals(item.getName())) {
-                return item;
-            }
-        }
-
-        return null;
-    }
-
-    /**
      * Sets compiled resource information.
      * @param resIdValueToNameMap a map of compiled resource id to resource name.
      *  The map is acquired by the {@link ProjectResources} object.
@@ -934,77 +261,67 @@ public class ProjectResources implements IResourceRepository {
         mergeIdResources();
     }
 
+    @Override
+    protected void postUpdate() {
+        super.postUpdate();
+        mergeIdResources();
+    }
+
     /**
      * Merges the list of ID resource coming from R.java and the list of ID resources
      * coming from XML declaration into the cached list {@link #mIdResourceList}.
      */
     void mergeIdResources() {
-        // get the list of IDs coming from XML declaration. Those ids are present in
-        // mCompiledIdResources already, so we'll need to use those instead of creating
-        // new IdResourceItem
-        List<ProjectResourceItem> xmlIdResources = mResourceMap.get(ResourceType.ID);
-
-        synchronized (mIdResourceList) {
-            // copy the currently cached items.
-            ArrayList<IdResourceItem> oldItems = new ArrayList<IdResourceItem>();
-            oldItems.addAll(mIdResourceList);
-
-            // empty the current list
-            mIdResourceList.clear();
-
-            // get the list of compile id resources.
-            Map<String, Integer> idMap = null;
-            if (mResourceValueMap != null) {
-                idMap = mResourceValueMap.get(ResourceType.ID);
-            }
+        // get the current ID values
+        List<ResourceItem> resources = mResourceMap.get(ResourceType.ID);
 
-            if (idMap == null) {
-                if (xmlIdResources != null) {
-                    for (ProjectResourceItem resourceItem : xmlIdResources) {
-                        // check the actual class just for safety.
-                        if (resourceItem instanceof IdResourceItem) {
-                            mIdResourceList.add((IdResourceItem)resourceItem);
-                        }
-                    }
-                }
-            } else {
-                // loop on the full list of id, and look for a match in the old list,
-                // in the list coming from XML (in case a new XML item was created.)
-
-                Set<String> idSet = idMap.keySet();
-
-                idLoop: for (String idResource : idSet) {
-                    // first look in the XML list in case an id went from inline to XML declared.
-                    if (xmlIdResources != null) {
-                        for (ProjectResourceItem resourceItem : xmlIdResources) {
-                            if (resourceItem instanceof IdResourceItem &&
-                                    resourceItem.getName().equals(idResource)) {
-                                mIdResourceList.add((IdResourceItem)resourceItem);
-                                continue idLoop;
-                            }
-                        }
-                    }
+        // get the ID values coming from the R class.
+        Map<String, Integer> rResources = mResourceValueMap.get(ResourceType.ID);
+
+        if (rResources != null) {
+            Map<String, Integer> copy;
 
-                    // if we haven't found it, look in the old items.
-                    int count = oldItems.size();
-                    for (int i = 0 ; i < count ; i++) {
-                        IdResourceItem resourceItem = oldItems.get(i);
-                        if (resourceItem.getName().equals(idResource)) {
-                            oldItems.remove(i);
-                            mIdResourceList.add(resourceItem);
-                            continue idLoop;
+            if (resources == null) {
+                resources = new ArrayList<ResourceItem>(rResources.entrySet().size());
+                mResourceMap.put(ResourceType.ID, resources);
+                copy = rResources;
+            } else {
+                // make a copy of the compiled Resources.
+                // As we loop on the full resources, we'll check with this copy map and remove
+                // from it all the resources we find in the full list.
+                // At the end, whatever is in the copy of the compile list is not in the full map,
+                // and should be added as inlined resource items.
+                copy = new HashMap<String, Integer>(rResources);
+
+                for (int i = 0 ; i < resources.size(); ) {
+                    ResourceItem item = resources.get(i);
+                    String name = item.getName();
+                    if (item.isDeclaredInline()) {
+                        // This ID is declared inline in the full resource map.
+                        // Check if it's also in the compiled version, in which case we can keep it.
+                        // Otherwise, if it doesn't exist in the compiled map, remove it from the
+                        // full map.
+                        // Since we're going to remove it from the copy map either way, we can use
+                        // remove to test if it's there
+                        if (copy.remove(name) != null) {
+                            // there is a match in the compiled list, do nothing, keep current one.
+                            i++;
+                        } else {
+                            // the ID is now gone, remove it from the list
+                            resources.remove(i);
                         }
+                    } else {
+                        // not an inline item, remove it from the copy.
+                        copy.remove(name);
+                        i++;
                     }
-
-                    // if we haven't found it, it looks like it's a new id that was
-                    // declared inline.
-                    mIdResourceList.add(new IdResourceItem(idResource,
-                            true /* isDeclaredInline */));
                 }
             }
 
-            // now we sort the list
-            Collections.sort(mIdResourceList);
+            // now add what's left in copy to the list
+            for (String name : copy.keySet()) {
+                resources.add(new InlineResourceItem(name));
+            }
         }
     }
 }
index a6e10ae..151830e 100644 (file)
@@ -22,12 +22,11 @@ import com.android.io.IAbstractFile;
 import com.android.resources.ResourceType;
 
 import java.util.Collection;
-import java.util.List;
 
 /**
  * Represents a Resource file (a file under $Project/res/)
  */
-public abstract class ResourceFile extends Resource {
+public abstract class ResourceFile implements Configurable {
 
     private final IAbstractFile mFile;
     private final ResourceFolder mFolder;
@@ -37,11 +36,10 @@ public abstract class ResourceFile extends Resource {
         mFolder = folder;
     }
 
-    /*
-     * (non-Javadoc)
-     * @see com.android.ide.eclipse.editors.resources.manager.Resource#getConfiguration()
-     */
-    @Override
+    protected abstract void load();
+    protected abstract void update();
+    protected abstract void dispose();
+
     public FolderConfiguration getConfiguration() {
         return mFolder.getConfiguration();
     }
@@ -60,17 +58,21 @@ public abstract class ResourceFile extends Resource {
         return mFolder;
     }
 
+    public final ResourceRepository getRepository() {
+        return mFolder.getRepository();
+    }
+
     /**
      * Returns whether the resource is a framework resource.
      */
     public final boolean isFramework() {
-        return mFolder.isFramework();
+        return mFolder.getRepository().isFrameworkRepository();
     }
 
     /**
      * Returns the list of {@link ResourceType} generated by the file. This is never null.
      */
-    public abstract List<ResourceType> getResourceTypes();
+    public abstract Collection<ResourceType> getResourceTypes();
 
     /**
      * Returns whether the file generated a resource of a specific type.
@@ -79,18 +81,6 @@ public abstract class ResourceFile extends Resource {
     public abstract boolean hasResources(ResourceType type);
 
     /**
-     * Get the list of {@link ProjectResourceItem} of a specific type generated by the file.
-     * This method must make sure not to create duplicate.
-     * @param type The type of {@link ProjectResourceItem} to return.
-     * @param projectResources The global Project Resource object, allowing the implementation to
-     * query for already existing {@link ProjectResourceItem}
-     * @return The list of <b>new</b> {@link ProjectResourceItem}
-     * @see ProjectResources#findResourceItem(ResourceType, String)
-     */
-    public abstract Collection<ProjectResourceItem> getResources(ResourceType type,
-            ProjectResources projectResources);
-
-    /**
      * Returns the value of a resource generated by this file by {@link ResourceType} and name.
      * <p/>If no resource match, <code>null</code> is returned.
      * @param type the type of the resource.
index 40f6fbb..3d3cf70 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.resources.manager;
 
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
+import com.android.annotations.VisibleForTesting;
+import com.android.annotations.VisibleForTesting.Visibility;
 import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.io.IFileWrapper;
 import com.android.io.IAbstractFile;
 import com.android.io.IAbstractFolder;
 import com.android.resources.FolderTypeRelationship;
 import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
 
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IFolder;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
 /**
  * Resource Folder class. Contains list of {@link ResourceFile}s,
- * the {@link FolderConfiguration}, and a link to the workspace {@link IFolder} object.
+ * the {@link FolderConfiguration}, and a link to the {@link IAbstractFolder} object.
  */
-public final class ResourceFolder extends Resource {
-    ResourceFolderType mType;
-    FolderConfiguration mConfiguration;
+public final class ResourceFolder implements Configurable {
+    final ResourceFolderType mType;
+    final FolderConfiguration mConfiguration;
     IAbstractFolder mFolder;
     ArrayList<ResourceFile> mFiles = null;
-    private final boolean mIsFramework;
+    private final ResourceRepository mRepository;
+
 
     /**
      * Creates a new {@link ResourceFolder}
@@ -50,18 +48,19 @@ public final class ResourceFolder extends Resource {
      * @param folder The associated {@link IAbstractFolder} object.
      * @param isFrameworkRepository
      */
-    public ResourceFolder(ResourceFolderType type, FolderConfiguration config,
-            IAbstractFolder folder, boolean isFrameworkRepository) {
+    protected ResourceFolder(ResourceFolderType type, FolderConfiguration config,
+            IAbstractFolder folder, ResourceRepository repository) {
         mType = type;
         mConfiguration = config;
         mFolder = folder;
-        mIsFramework = isFrameworkRepository;
+        mRepository = repository;
     }
 
     /**
      * Adds a {@link ResourceFile} to the folder.
      * @param file The {@link ResourceFile}.
      */
+    @VisibleForTesting(visibility=Visibility.PROTECTED)
     public void addFile(ResourceFile file) {
         if (mFiles == null) {
             mFiles = new ArrayList<ResourceFile>();
@@ -70,35 +69,21 @@ public final class ResourceFolder extends Resource {
         mFiles.add(file);
     }
 
-    /**
-     * Attempts to remove the {@link ResourceFile} associated with a specified {@link IFile}.
-     * @param file the IFile object.
-     * @return the {@link ResourceFile} that was removed.
-     */
-    public ResourceFile removeFile(IFile file) {
-        if (mFiles != null) {
-            int count = mFiles.size();
-            for (int i = 0 ; i < count ; i++) {
-                ResourceFile resFile = mFiles.get(i);
-                if (resFile != null) {
-                    IAbstractFile abstractFile = resFile.getFile();
-                    if (abstractFile instanceof IFileWrapper) {
-                        IFile iFile = ((IFileWrapper)resFile.getFile()).getIFile();
-                        if (iFile != null && iFile.equals(file)) {
-                            mFiles.remove(i);
-                            touch();
-                            return resFile;
-                        }
-                    }
-                }
-            }
+    protected void removeFile(ResourceFile file) {
+        file.dispose();
+        mFiles.remove(file);
+    }
+
+    protected void dispose() {
+        for (ResourceFile file : mFiles) {
+            file.dispose();
         }
 
-        return null;
+        mFiles.clear();
     }
 
     /**
-     * Returns the {@link IFolder} associated with this object.
+     * Returns the {@link IAbstractFolder} associated with this object.
      */
     public IAbstractFolder getFolder() {
         return mFolder;
@@ -111,11 +96,8 @@ public final class ResourceFolder extends Resource {
         return mType;
     }
 
-    /**
-     * Returns whether the folder is a framework resource folder.
-     */
-    public boolean isFramework() {
-        return mIsFramework;
+    public ResourceRepository getRepository() {
+        return mRepository;
     }
 
     /**
@@ -126,7 +108,7 @@ public final class ResourceFolder extends Resource {
 
         if (mFiles != null) {
             for (ResourceFile file : mFiles) {
-                List<ResourceType> types = file.getResourceTypes();
+                Collection<ResourceType> types = file.getResourceTypes();
 
                 // loop through those and add them to the main list,
                 // if they are not already present
@@ -141,11 +123,6 @@ public final class ResourceFolder extends Resource {
         return list;
     }
 
-    /*
-     * (non-Javadoc)
-     * @see com.android.ide.eclipse.editors.resources.manager.Resource#getConfiguration()
-     */
-    @Override
     public FolderConfiguration getConfiguration() {
         return mConfiguration;
     }
@@ -160,7 +137,7 @@ public final class ResourceFolder extends Resource {
 
     /**
      * Returns the {@link ResourceFile} matching a {@link IAbstractFile} object.
-     * @param file The {@link IFile} object.
+     * @param file The {@link IAbstractFile} object.
      * @return the {@link ResourceFile} or null if no match was found.
      */
     public ResourceFile getFile(IAbstractFile file) {
@@ -175,27 +152,6 @@ public final class ResourceFolder extends Resource {
     }
 
     /**
-     * Returns the {@link ResourceFile} matching a {@link IFile} object.
-     * @param file The {@link IFile} object.
-     * @return the {@link ResourceFile} or null if no match was found.
-     */
-    public ResourceFile getFile(IFile file) {
-        if (mFiles != null) {
-            for (ResourceFile f : mFiles) {
-                IAbstractFile abstractFile = f.getFile();
-                if (abstractFile instanceof IFileWrapper) {
-                    IFile iFile = ((IFileWrapper)f.getFile()).getIFile();
-                    if (iFile != null && iFile.equals(file)) {
-                        return f;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-
-    /**
      * Returns the {@link ResourceFile} matching a given name.
      * @param filename The name of the file to return.
      * @return the {@link ResourceFile} or <code>null</code> if no match was found.
@@ -240,27 +196,6 @@ public final class ResourceFolder extends Resource {
         return false;
     }
 
-    /**
-     * Get the list of {@link ResourceItem} of a specific type generated by all the files
-     * in the folder.
-     * This method must make sure not to create duplicates.
-     * @param type The type of {@link ResourceItem} to return.
-     * @param projectResources The global Project Resource object, allowing the implementation to
-     * query for already existing {@link ResourceItem}
-     * @return The list of <b>new</b> {@link ResourceItem}
-     * @see ProjectResources#findResourceItem(ResourceType, String)
-     */
-    public Collection<ProjectResourceItem> getResources(ResourceType type,
-            ProjectResources projectResources) {
-        Collection<ProjectResourceItem> list = new ArrayList<ProjectResourceItem>();
-        if (mFiles != null) {
-            for (ResourceFile f : mFiles) {
-                list.addAll(f.getResources(type, projectResources));
-            }
-        }
-        return list;
-    }
-
     @Override
     public String toString() {
         return mFolder.toString();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceItem.java
new file mode 100644 (file)
index 0000000..f826ed7
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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.resources.manager;
+
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.resources.ResourceType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An android resource.
+ *
+ * This is a representation of the resource, not of its value(s). It gives access to all
+ * the source files that generate this particular resource which then can be used to access
+ * the actual value(s).
+ *
+ * @see ResourceFile#getResources(ResourceType, ResourceRepository)
+ */
+public class ResourceItem implements Comparable<ResourceItem> {
+
+    private final static Comparator<ResourceFile> sComparator = new Comparator<ResourceFile>() {
+        public int compare(ResourceFile file1, ResourceFile file2) {
+            // get both FolderConfiguration and compare them
+            FolderConfiguration fc1 = file1.getFolder().getConfiguration();
+            FolderConfiguration fc2 = file2.getFolder().getConfiguration();
+
+            return fc1.compareTo(fc2);
+        }
+    };
+
+    private final String mName;
+
+    /**
+     * List of files generating this ResourceItem.
+     */
+    private final List<ResourceFile> mFiles = new ArrayList<ResourceFile>();
+
+    /**
+     * Constructs a new ResourceItem.
+     * @param name the name of the resource as it appears in the XML and R.java files.
+     */
+    public ResourceItem(String name) {
+        mName = name;
+    }
+
+    /**
+     * Returns the name of the resource.
+     */
+    public final String getName() {
+        return mName;
+    }
+
+    /**
+     * Compares the {@link ResourceItem} to another.
+     * @param other the ResourceItem to be compared to.
+     */
+    public int compareTo(ResourceItem other) {
+        return mName.compareTo(other.mName);
+    }
+
+    /**
+     * Returns whether the resource is editable directly.
+     * <p/>
+     * This is typically the case for resources that don't have alternate versions, or resources
+     * of type {@link ResourceType#ID} that aren't declared inline.
+     */
+    public boolean isEditableDirectly() {
+        return hasAlternates() == false;
+    }
+
+    /**
+     * Returns whether the ID resource has been declared inline inside another resource XML file.
+     * If the resource type is not {@link ResourceType#ID}, this will always return {@code false}.
+     */
+    public boolean isDeclaredInline() {
+        return false;
+    }
+
+    /**
+     * Adds a new source file.
+     * @param file the source file.
+     */
+    protected void add(ResourceFile file) {
+        mFiles.add(file);
+    }
+
+    /**
+     * Removes a file from the list of source files.
+     * @param file the file to remove
+     */
+    protected void removeFile(ResourceFile file) {
+        mFiles.remove(file);
+    }
+
+    /**
+     * Returns {@code true} if the item has no source file.
+     * @return
+     */
+    protected boolean hasNoSourceFile() {
+        return mFiles.size() == 0;
+    }
+
+    /**
+     * Reset the item by emptying its source file list.
+     */
+    protected void reset() {
+        mFiles.clear();
+    }
+
+    /**
+     * Returns the sorted list of {@link ResourceItem} objects for this resource item.
+     */
+    public ResourceFile[] getSourceFileArray() {
+        ArrayList<ResourceFile> list = new ArrayList<ResourceFile>();
+        list.addAll(mFiles);
+
+        Collections.sort(list, sComparator);
+
+        return list.toArray(new ResourceFile[list.size()]);
+    }
+
+    /**
+     * Returns the list of source file for this resource.
+     */
+    public List<ResourceFile> getSourceFileList() {
+        return Collections.unmodifiableList(mFiles);
+    }
+
+    /**
+     * Returns if the resource has at least one non-default version.
+     *
+     * @see ResourceFile#getConfiguration()
+     * @see FolderConfiguration#isDefault()
+     */
+    public boolean hasAlternates() {
+        for (ResourceFile file : mFiles) {
+            if (file.getFolder().getConfiguration().isDefault() == false) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns whether the resource has a default version, with no qualifier.
+     *
+     * @see ResourceFile#getConfiguration()
+     * @see FolderConfiguration#isDefault()
+     */
+    public boolean hasDefault() {
+        for (ResourceFile file : mFiles) {
+            if (file.getFolder().getConfiguration().isDefault()) {
+                return true;
+            }
+        }
+
+        // We only want to return false if there's no default and more than 0 items.
+        return (mFiles.size() == 0);
+    }
+
+    /**
+     * Returns the number of alternate versions for this resource.
+     *
+     * @see ResourceFile#getConfiguration()
+     * @see FolderConfiguration#isDefault()
+     */
+    public int getAlternateCount() {
+        int count = 0;
+        for (ResourceFile file : mFiles) {
+            if (file.getFolder().getConfiguration().isDefault() == false) {
+                count++;
+            }
+        }
+
+        return count;
+    }
+
+    @Override
+    public String toString() {
+        return "ResourceItem [mName=" + mName + ", mFiles=" + mFiles + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+}
index c039f6b..1d3c709 100644 (file)
@@ -17,6 +17,8 @@
 package com.android.ide.eclipse.adt.internal.resources.manager;
 
 import com.android.AndroidConstants;
+import com.android.annotations.VisibleForTesting;
+import com.android.annotations.VisibleForTesting.Visibility;
 import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
@@ -24,6 +26,7 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQua
 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener;
 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFolderListener;
 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IProjectListener;
+import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener;
 import com.android.ide.eclipse.adt.io.IFileWrapper;
 import com.android.ide.eclipse.adt.io.IFolderWrapper;
 import com.android.io.FolderWrapper;
@@ -70,6 +73,7 @@ import java.util.List;
  * @see ProjectResources
  */
 public final class ResourceManager {
+    public final static boolean DEBUG = false;
 
     private final static ResourceManager sThis = new ResourceManager();
 
@@ -114,7 +118,9 @@ public final class ResourceManager {
      * @param monitor The global project monitor
      */
     public static void setup(GlobalProjectMonitor monitor) {
+        monitor.addResourceEventListener(sThis.mResourceEventListener);
         monitor.addProjectListener(sThis.mProjectListener);
+
         int mask = IResourceDelta.ADDED | IResourceDelta.REMOVED | IResourceDelta.CHANGED;
         monitor.addFolderListener(sThis.mFolderListener, mask);
         monitor.addFileListener(sThis.mFileListener, mask);
@@ -160,6 +166,40 @@ public final class ResourceManager {
         }
     }
 
+    private class ResourceEventListener implements IResourceEventListener {
+        private final List<IProject> mChangedProjects = new ArrayList<IProject>();
+
+        public void resourceChangeEventEnd() {
+            for (IProject project : mChangedProjects) {
+                ProjectResources resources;
+                synchronized (mMap) {
+                    resources = mMap.get(project);
+                }
+
+                resources.postUpdate();
+            }
+
+            mChangedProjects.clear();
+        }
+
+        public void resourceChangeEventStart() {
+            // pass
+        }
+
+        void addProject(IProject project) {
+            if (mChangedProjects.contains(project) == false) {
+                mChangedProjects.add(project);
+            }
+        }
+    }
+
+    /**
+     * Delegate listener for resource changes. This is called before and after any calls to the
+     * project and file listeners (for a given resource change event).
+     */
+    private ResourceEventListener mResourceEventListener = new ResourceEventListener();
+
+
     /**
      * Implementation of the {@link IFolderListener} as an internal class so that the methods
      * do not appear in the public API of {@link ResourceManager}.
@@ -179,6 +219,8 @@ public final class ResourceManager {
                 return;
             }
 
+            mResourceEventListener.addProject(project);
+
             switch (kind) {
                 case IResourceDelta.ADDED:
                     // checks if the folder is under res.
@@ -207,13 +249,13 @@ public final class ResourceManager {
                     }
                     break;
                 case IResourceDelta.CHANGED:
+                    // only call the listeners.
                     synchronized (mMap) {
                         resources = mMap.get(folder.getProject());
                     }
                     if (resources != null) {
                         ResourceFolder resFolder = resources.getResourceFolder(folder);
                         if (resFolder != null) {
-                            resFolder.touch();
                             notifyListenerOnFolderChange(project, resFolder, kind);
                         }
                     }
@@ -227,7 +269,8 @@ public final class ResourceManager {
                         ResourceFolderType type = ResourceFolderType.getFolderType(
                                 folder.getName());
 
-                        ResourceFolder removedFolder = resources.removeFolder(type, folder);
+                        ResourceFolder removedFolder = resources.removeFolder(type,
+                                new IFolderWrapper(folder));
                         if (removedFolder != null) {
                             notifyListenerOnFolderChange(project, removedFolder, kind);
                         }
@@ -255,8 +298,6 @@ public final class ResourceManager {
          * @see IFileListener#fileChanged
          */
         public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
-            ProjectResources resources;
-
             final IProject project = file.getProject();
 
             try {
@@ -268,80 +309,39 @@ public final class ResourceManager {
                 return;
             }
 
-            switch (kind) {
-                case IResourceDelta.ADDED:
-                    // checks if the file is under res/something.
-                    IPath path = file.getFullPath();
-
-                    if (path.segmentCount() == 4) {
-                        if (isInResFolder(path)) {
-                            // get the project and its resources
-                            synchronized (mMap) {
-                                resources = mMap.get(project);
-                            }
-
-                            IContainer container = file.getParent();
-                            if (container instanceof IFolder && resources != null) {
-
-                                ResourceFolder folder = resources.getResourceFolder(
-                                        (IFolder)container);
-
-                                if (folder != null) {
-                                    ResourceFile resFile = processFile(
-                                            new IFileWrapper(file), folder);
-                                    notifyListenerOnFileChange(project, resFile, kind);
-                                }
-                            }
-                        }
-                    }
-                    break;
-                case IResourceDelta.CHANGED:
-                    // try to find a matching ResourceFile
-                    synchronized (mMap) {
-                        resources = mMap.get(project);
-                    }
-                    if (resources != null) {
-                        IContainer container = file.getParent();
-                        if (container instanceof IFolder) {
-                            ResourceFolder resFolder = resources.getResourceFolder(
-                                    (IFolder)container);
+            // get the project resources
+            ProjectResources resources;
+            synchronized (mMap) {
+                resources = mMap.get(project);
+            }
 
-                            // we get the delete on the folder before the file, so it is possible
-                            // the associated ResourceFolder doesn't exist anymore.
-                            if (resFolder != null) {
-                                // get the resourceFile, and touch it.
-                                ResourceFile resFile = resFolder.getFile(file);
-                                if (resFile != null) {
-                                    resFile.touch();
-                                    notifyListenerOnFileChange(project, resFile, kind);
-                                }
-                            }
-                        }
-                    }
-                    break;
-                case IResourceDelta.REMOVED:
-                    // try to find a matching ResourceFile
-                    synchronized (mMap) {
-                        resources = mMap.get(project);
-                    }
-                    if (resources != null) {
-                        IContainer container = file.getParent();
-                        if (container instanceof IFolder) {
-                            ResourceFolder resFolder = resources.getResourceFolder(
-                                    (IFolder)container);
+            if (resources == null) {
+                return;
+            }
 
-                            // we get the delete on the folder before the file, so it is possible
-                            // the associated ResourceFolder doesn't exist anymore.
-                            if (resFolder != null) {
-                                // remove the file
-                                ResourceFile resFile = resFolder.removeFile(file);
-                                if (resFile != null) {
-                                    notifyListenerOnFileChange(project, resFile, kind);
-                                }
-                            }
+            // checks if the file is under res/something.
+            IPath path = file.getFullPath();
+
+            if (path.segmentCount() == 4) {
+                if (isInResFolder(path)) {
+                    IContainer container = file.getParent();
+                    if (container instanceof IFolder) {
+
+                        ResourceFolder folder = resources.getResourceFolder(
+                                (IFolder)container);
+
+                        // folder can be null as when the whole folder is deleted, the
+                        // REMOVED event for the folder comes first. In this case, the
+                        // folder will have taken care of things.
+                        if (folder != null) {
+                            ResourceFile resFile = processFile(
+                                    new IFileWrapper(file),
+                                    folder,
+                                    kind);
+                            notifyListenerOnFileChange(project, resFile, kind);
                         }
                     }
-                    break;
+                }
             }
         }
     };
@@ -413,15 +413,16 @@ public final class ResourceManager {
      * Loads and returns the resources for a given {@link IAndroidTarget}
      * @param androidTarget the target from which to load the framework resources
      */
-    public ProjectResources loadFrameworkResources(IAndroidTarget androidTarget) {
+    public ResourceRepository loadFrameworkResources(IAndroidTarget androidTarget) {
         String osResourcesPath = androidTarget.getPath(IAndroidTarget.RESOURCES);
 
         FolderWrapper frameworkRes = new FolderWrapper(osResourcesPath);
         if (frameworkRes.exists()) {
-            ProjectResources resources = new ProjectResources();
+            FrameworkResources resources = new FrameworkResources();
 
             try {
                 loadResources(resources, frameworkRes);
+                resources.loadPublicResources(frameworkRes);
                 return resources;
             } catch (IOException e) {
                 // since we test that folders are folders, and files are files, this shouldn't
@@ -433,7 +434,7 @@ public final class ResourceManager {
     }
 
     /**
-     * Loads the resources from a folder, and fills the given {@link ProjectResources}.
+     * Loads the resources from a folder, and fills the given {@link ResourceRepository}.
      * <p/>
      * This is mostly a utility method that should not be used to process actual Eclipse projects
      * (Those are loaded with {@link #createProject(IProject)} for new project or
@@ -447,13 +448,14 @@ public final class ResourceManager {
      * setting rendering tests.
      *
      *
-     * @param resources The {@link ProjectResources} files to load. It is expected that the
-     * framework flag has been properly setup. This is filled up with the content of the folder.
+     * @param resources The {@link ResourceRepository} files to fill.
+     *       This is filled up with the content of the folder.
      * @param rootFolder The folder to read the resources from. This is the top level
      * resource folder (res/)
      * @throws IOException
      */
-    public void loadResources(ProjectResources resources, IAbstractFolder rootFolder)
+    @VisibleForTesting(visibility=Visibility.PRIVATE)
+    public void loadResources(ResourceRepository resources, IAbstractFolder rootFolder)
             throws IOException {
         IAbstractResource[] files = rootFolder.listMembers();
         for (IAbstractResource file : files) {
@@ -467,15 +469,12 @@ public final class ResourceManager {
 
                     for (IAbstractResource childRes : children) {
                         if (childRes instanceof IAbstractFile) {
-                            processFile((IAbstractFile) childRes, resFolder);
+                            processFile((IAbstractFile) childRes, resFolder, IResourceDelta.ADDED);
                         }
                     }
                 }
             }
         }
-
-        // now that we have loaded the files, we need to force load the resources from them
-        resources.loadAll();
     }
 
     /**
@@ -522,7 +521,8 @@ public final class ResourceManager {
                                     if (fileRes.getType() == IResource.FILE) {
                                         IFile file = (IFile)fileRes;
 
-                                        processFile(new IFileWrapper(file), resFolder);
+                                        processFile(new IFileWrapper(file), resFolder,
+                                                IResourceDelta.ADDED);
                                     }
                                 }
                             }
@@ -578,10 +578,10 @@ public final class ResourceManager {
     /**
      * Processes a folder and adds it to the list of the project resources.
      * @param folder the folder to process
-     * @param project the folder's project.
+     * @param resources the resource repository.
      * @return the ConfiguredFolder created from this folder, or null if the process failed.
      */
-    private ResourceFolder processFolder(IAbstractFolder folder, ProjectResources project) {
+    private ResourceFolder processFolder(IAbstractFolder folder, ResourceRepository resources) {
         // split the name of the folder in segments.
         String[] folderSegments = folder.getName().split(AndroidConstants.RES_QUALIFIER_SEP);
 
@@ -593,7 +593,7 @@ public final class ResourceManager {
             FolderConfiguration config = getConfig(folderSegments);
 
             if (config != null) {
-                ResourceFolder configuredFolder = project.add(type, config, folder);
+                ResourceFolder configuredFolder = resources.add(type, config, folder);
 
                 return configuredFolder;
             }
@@ -606,37 +606,45 @@ public final class ResourceManager {
      * Processes a file and adds it to its parent folder resource.
      * @param file the underlying resource file.
      * @param folder the parent of the resource file.
+     * @param kind the file change kind.
      * @return the {@link ResourceFile} that was created.
      */
-    private ResourceFile processFile(IAbstractFile file, ResourceFolder folder) {
+    private ResourceFile processFile(IAbstractFile file, ResourceFolder folder, int kind) {
         // get the type of the folder
         ResourceFolderType type = folder.getType();
 
         // look for this file if it's already been created
         ResourceFile resFile = folder.getFile(file);
 
-        if (resFile != null) {
-            // invalidate the file
-            resFile.touch();
+        if (resFile == null) {
+            if (kind != IResourceDelta.REMOVED) {
+                // create a ResourceFile for it.
+
+                // check if that's a single or multi resource type folder. For now we define this by
+                // the number of possible resource type output by files in the folder. This does
+                // not make the difference between several resource types from a single file or
+                // the ability to have 2 files in the same folder generating 2 different types of
+                // resource. The former is handled by MultiResourceFile properly while we don't
+                // handle the latter. If we were to add this behavior we'd have to change this call.
+                List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(type);
+
+                if (types.size() == 1) {
+                    resFile = new SingleResourceFile(file, folder);
+                } else {
+                    resFile = new MultiResourceFile(file, folder);
+                }
+
+                resFile.load();
+
+                // add it to the folder
+                folder.addFile(resFile);
+            }
         } else {
-            // create a ResourceFile for it.
-
-            // check if that's a single or multi resource type folder. For now we define this by
-            // the number of possible resource type output by files in the folder. This does
-            // not make the difference between several resource types from a single file or
-            // the ability to have 2 files in the same folder generating 2 different types of
-            // resource. The former is handled by MultiResourceFile properly while we don't
-            // handle the latter. If we were to add this behavior we'd have to change this call.
-            List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(type);
-
-            if (types.size() == 1) {
-                resFile = new SingleResourceFile(file, folder);
+            if (kind == IResourceDelta.REMOVED) {
+                folder.removeFile(resFile);
             } else {
-                resFile = new MultiResourceFile(file, folder);
+                resFile.update();
             }
-
-            // add it to the folder
-            folder.addFile(resFile);
         }
 
         return resFile;
@@ -687,4 +695,18 @@ public final class ResourceManager {
         defaultConfig.createDefault();
         mQualifiers = defaultConfig.getQualifiers();
     }
+
+    // debug only
+    @SuppressWarnings("unused")
+    private String getKindString(int kind) {
+        if (DEBUG) {
+            switch (kind) {
+                case IResourceDelta.ADDED: return "ADDED";
+                case IResourceDelta.REMOVED: return "REMOVED";
+                case IResourceDelta.CHANGED: return "CHANGED";
+            }
+        }
+
+        return Integer.toString(kind);
+    }
 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceRepository.java
new file mode 100644 (file)
index 0000000..39de45c
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2007 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.resources.manager;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
+import com.android.ide.eclipse.adt.io.IFolderWrapper;
+import com.android.io.IAbstractFolder;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+
+import org.eclipse.core.resources.IFolder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Base class for resource repository.
+ *
+ * A repository is both a file representation of a resource folder and a representation
+ * of the generated resources, organized by type.
+ *
+ * {@link #getResourceFolder(IFolder)} and {@link #getSourceFiles(ResourceType, String, FolderConfiguration)}
+ * give access to the folders and files of the resource folder.
+ *
+ * {@link #getResources(ResourceType)} gives access to the resources directly.
+ *
+ */
+public abstract class ResourceRepository {
+
+    protected final Map<ResourceFolderType, List<ResourceFolder>> mFolderMap =
+        new EnumMap<ResourceFolderType, List<ResourceFolder>>(ResourceFolderType.class);
+
+    protected final Map<ResourceType, List<ResourceItem>> mResourceMap =
+        new EnumMap<ResourceType, List<ResourceItem>>(ResourceType.class);
+
+    private final Map<List<ResourceItem>, List<ResourceItem>> mReadOnlyListMap =
+        new IdentityHashMap<List<ResourceItem>, List<ResourceItem>>();
+
+    private final boolean mFrameworkRepository;
+
+    protected final IntArrayWrapper mWrapper = new IntArrayWrapper(null);
+
+    /**
+     * Makes a resource repository
+     * @param isFrameworkRepository whether the repository is for framework resources.
+     */
+    protected ResourceRepository(boolean isFrameworkRepository) {
+        mFrameworkRepository = isFrameworkRepository;
+    }
+
+    public boolean isFrameworkRepository() {
+        return mFrameworkRepository;
+    }
+
+    /**
+     * Adds a Folder Configuration to the project.
+     * @param type The resource type.
+     * @param config The resource configuration.
+     * @param folder The workspace folder object.
+     * @return the {@link ResourceFolder} object associated to this folder.
+     */
+    protected ResourceFolder add(ResourceFolderType type, FolderConfiguration config,
+            IAbstractFolder folder) {
+        // get the list for the resource type
+        List<ResourceFolder> list = mFolderMap.get(type);
+
+        if (list == null) {
+            list = new ArrayList<ResourceFolder>();
+
+            ResourceFolder cf = new ResourceFolder(type, config, folder, this);
+            list.add(cf);
+
+            mFolderMap.put(type, list);
+
+            return cf;
+        }
+
+        // look for an already existing folder configuration.
+        for (ResourceFolder cFolder : list) {
+            if (cFolder.mConfiguration.equals(config)) {
+                // config already exist. Nothing to be done really, besides making sure
+                // the IAbstractFolder object is up to date.
+                cFolder.mFolder = folder;
+                return cFolder;
+            }
+        }
+
+        // If we arrive here, this means we didn't find a matching configuration.
+        // So we add one.
+        ResourceFolder cf = new ResourceFolder(type, config, folder, this);
+        list.add(cf);
+
+        return cf;
+    }
+
+    /**
+     * Removes a {@link ResourceFolder} associated with the specified {@link IAbstractFolder}.
+     * @param type The type of the folder
+     * @param removedFolder the IAbstractFolder object.
+     * @return the {@link ResourceFolder} that was removed, or null if no matches were found.
+     */
+    protected ResourceFolder removeFolder(ResourceFolderType type, IAbstractFolder removedFolder) {
+        // get the list of folders for the resource type.
+        List<ResourceFolder> list = mFolderMap.get(type);
+
+        if (list != null) {
+            int count = list.size();
+            for (int i = 0 ; i < count ; i++) {
+                ResourceFolder resFolder = list.get(i);
+                // this is only used for Eclipse stuff so we know it's an IFolderWrapper
+                IAbstractFolder folder = (IFolderWrapper) resFolder.getFolder();
+                if (removedFolder.equals(folder)) {
+                    // we found the matching ResourceFolder. we need to remove it.
+                    list.remove(i);
+
+                    // remove its content
+                    resFolder.dispose();
+
+                    return resFolder;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a {@link ResourceItem} matching the given {@link ResourceType} and name. If none
+     * exist, it creates one.
+     *
+     * @param type the resource type
+     * @param name the name of the resource.
+     * @return A resource item matching the type and name.
+     */
+    protected ResourceItem getResourceItem(ResourceType type, String name) {
+        // looking for an existing ResourceItem with this type and name
+        ResourceItem item = findDeclaredResourceItem(type, name);
+
+        // create one if there isn't one already, or if the existing one is inlined, since
+        // clearly we need a non inlined one (the inline one is removed too)
+        if (item == null || item.isDeclaredInline()) {
+            ResourceItem oldItem = item != null && item.isDeclaredInline() ? item : null;
+
+            item = createResourceItem(name);
+
+            List<ResourceItem> list = mResourceMap.get(type);
+            if (list == null) {
+                list = new ArrayList<ResourceItem>();
+                mResourceMap.put(type, list);
+            }
+
+            list.add(item);
+
+            if (oldItem != null) {
+                list.remove(oldItem);
+            }
+        }
+
+        return item;
+    }
+
+    /**
+     * Creates a resource item with the given name.
+     * @param name the name of the resource
+     * @return a new ResourceItem (or child class) instance.
+     */
+    protected abstract ResourceItem createResourceItem(String name);
+
+
+    /**
+     * Returns a list of {@link ResourceFolder} for a specific {@link ResourceFolderType}.
+     * @param type The {@link ResourceFolderType}
+     */
+    public List<ResourceFolder> getFolders(ResourceFolderType type) {
+        return mFolderMap.get(type);
+    }
+
+    public List<ResourceType> getAvailableResourceTypes() {
+        List<ResourceType> list = new ArrayList<ResourceType>();
+
+        // For each key, we check if there's a single ResourceType match.
+        // If not, we look for the actual content to give us the resource type.
+
+        for (ResourceFolderType folderType : mFolderMap.keySet()) {
+            List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(folderType);
+            if (types.size() == 1) {
+                // before we add it we check if it's not already present, since a ResourceType
+                // could be created from multiple folders, even for the folders that only create
+                // one type of resource (drawable for instance, can be created from drawable/ and
+                // values/)
+                if (list.contains(types.get(0)) == false) {
+                    list.add(types.get(0));
+                }
+            } else {
+                // there isn't a single resource type out of this folder, so we look for all
+                // content.
+                List<ResourceFolder> folders = mFolderMap.get(folderType);
+                if (folders != null) {
+                    for (ResourceFolder folder : folders) {
+                        Collection<ResourceType> folderContent = folder.getResourceTypes();
+
+                        // then we add them, but only if they aren't already in the list.
+                        for (ResourceType folderResType : folderContent) {
+                            if (list.contains(folderResType) == false) {
+                                list.add(folderResType);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * Returns a list of {@link ResourceItem} matching a given {@link ResourceType}.
+     * @param type the type of the resource items to return
+     * @return a non null collection of resource items
+     */
+    public Collection<ResourceItem> getResourceItemsOfType(ResourceType type) {
+        List<ResourceItem> list = mResourceMap.get(type);
+
+        if (list == null) {
+            return Collections.emptyList();
+        }
+
+        List<ResourceItem> roList = mReadOnlyListMap.get(list);
+        if (roList == null) {
+            roList = Collections.unmodifiableList(list);
+            mReadOnlyListMap.put(list, roList);
+        }
+
+        return roList;
+    }
+
+    /**
+     * Returns whether the repository has resources of a given {@link ResourceType}.
+     * @param type the type of resource to check.
+     * @return true if the repository contains resources of the given type, false otherwise.
+     */
+    public boolean hasResourcesOfType(ResourceType type) {
+        List<ResourceItem> items = mResourceMap.get(type);
+        return (items != null && items.size() > 0);
+    }
+
+    /**
+     * Returns the {@link ResourceFolder} associated with a {@link IFolder}.
+     * @param folder The {@link IFolder} object.
+     * @return the {@link ResourceFolder} or null if it was not found.
+     */
+    public ResourceFolder getResourceFolder(IFolder folder) {
+        for (List<ResourceFolder> list : mFolderMap.values()) {
+            for (ResourceFolder resFolder : list) {
+                // this is only used for Eclipse stuff so we know it's an IFolderWrapper
+                IFolderWrapper wrapper = (IFolderWrapper) resFolder.getFolder();
+                if (wrapper.getIFolder().equals(folder)) {
+                    return resFolder;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the {@link ResourceFile} matching the given name, {@link ResourceFolderType} and
+     * configuration.
+     * <p/>This only works with files generating one resource named after the file (for instance,
+     * layouts, bitmap based drawable, xml, anims).
+     * @return the matching file or <code>null</code> if no match was found.
+     */
+    public ResourceFile getMatchingFile(String name, ResourceFolderType type,
+            FolderConfiguration config) {
+        // get the folders for the given type
+        List<ResourceFolder> folders = mFolderMap.get(type);
+
+        // look for folders containing a file with the given name.
+        ArrayList<ResourceFolder> matchingFolders = new ArrayList<ResourceFolder>(folders.size());
+
+        // remove the folders that do not have a file with the given name.
+        for (int i = 0 ; i < folders.size(); i++) {
+            ResourceFolder folder = folders.get(i);
+
+            if (folder.hasFile(name) == true) {
+                matchingFolders.add(folder);
+            }
+        }
+
+        // from those, get the folder with a config matching the given reference configuration.
+        Configurable match = findMatchingConfigurable(matchingFolders, config);
+
+        // do we have a matching folder?
+        if (match instanceof ResourceFolder) {
+            // get the ResourceFile from the filename
+            return ((ResourceFolder)match).getFile(name);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the list of source files for a given resource.
+     * Optionally, if a {@link FolderConfiguration} is given, then only the best
+     * match for this config is returned.
+     *
+     * @param type the type of the resource.
+     * @param name the name of the resource.
+     * @param referenceConfig an optional config for which only the best match will be returned.
+     *
+     * @return a list of files generating this resource or null if it was not found.
+     */
+    public List<ResourceFile> getSourceFiles(ResourceType type, String name,
+            FolderConfiguration referenceConfig) {
+
+        Collection<ResourceItem> items = getResourceItemsOfType(type);
+
+        for (ResourceItem item : items) {
+            if (name.equals(item.getName())) {
+                if (referenceConfig != null) {
+                    Configurable match = findMatchingConfigurable(item.getSourceFileList(),
+                            referenceConfig);
+                    if (match instanceof ResourceFile) {
+                        return Collections.singletonList((ResourceFile) match);
+                    }
+
+                    return null;
+                }
+                return item.getSourceFileList();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the resources values matching a given {@link FolderConfiguration}.
+     *
+     * @param referenceConfig the configuration that each value must match.
+     * @return a map with guaranteed to contain an entry for each {@link ResourceType}
+     */
+    public Map<ResourceType, Map<String, ResourceValue>> getConfiguredResources(
+            FolderConfiguration referenceConfig) {
+        return doGetConfiguredResources(referenceConfig);
+    }
+
+    /**
+     * Returns the resources values matching a given {@link FolderConfiguration} for the current
+     * project.
+     *
+     * @param referenceConfig the configuration that each value must match.
+     * @return a map with guaranteed to contain an entry for each {@link ResourceType}
+     */
+    protected final Map<ResourceType, Map<String, ResourceValue>> doGetConfiguredResources(
+            FolderConfiguration referenceConfig) {
+
+        Map<ResourceType, Map<String, ResourceValue>> map =
+            new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class);
+
+        for (ResourceType key : ResourceType.values()) {
+            // get the local results and put them in the map
+            map.put(key, getConfiguredResource(key, referenceConfig));
+        }
+
+        return map;
+    }
+
+    /**
+     * Returns the sorted list of languages used in the resources.
+     */
+    public SortedSet<String> getLanguages() {
+        SortedSet<String> set = new TreeSet<String>();
+
+        Collection<List<ResourceFolder>> folderList = mFolderMap.values();
+        for (List<ResourceFolder> folderSubList : folderList) {
+            for (ResourceFolder folder : folderSubList) {
+                FolderConfiguration config = folder.getConfiguration();
+                LanguageQualifier lang = config.getLanguageQualifier();
+                if (lang != null) {
+                    set.add(lang.getShortDisplayValue());
+                }
+            }
+        }
+
+        return set;
+    }
+
+    /**
+     * Returns the sorted list of regions used in the resources with the given language.
+     * @param currentLanguage the current language the region must be associated with.
+     */
+    public SortedSet<String> getRegions(String currentLanguage) {
+        SortedSet<String> set = new TreeSet<String>();
+
+        Collection<List<ResourceFolder>> folderList = mFolderMap.values();
+        for (List<ResourceFolder> folderSubList : folderList) {
+            for (ResourceFolder folder : folderSubList) {
+                FolderConfiguration config = folder.getConfiguration();
+
+                // get the language
+                LanguageQualifier lang = config.getLanguageQualifier();
+                if (lang != null && lang.getShortDisplayValue().equals(currentLanguage)) {
+                    RegionQualifier region = config.getRegionQualifier();
+                    if (region != null) {
+                        set.add(region.getShortDisplayValue());
+                    }
+                }
+            }
+        }
+
+        return set;
+    }
+
+    protected void removeFile(Collection<ResourceType> types, ResourceFile file) {
+        for (ResourceType type : types) {
+            removeFile(type, file);
+        }
+    }
+
+    protected void removeFile(ResourceType type, ResourceFile file) {
+        List<ResourceItem> list = mResourceMap.get(type);
+        for (int i = 0 ; i < list.size(); i++) {
+            ResourceItem item = list.get(i);
+            item.removeFile(file);
+        }
+    }
+
+    /**
+     * Returns a map of (resource name, resource value) for the given {@link ResourceType}.
+     * <p/>The values returned are taken from the resource files best matching a given
+     * {@link FolderConfiguration}.
+     * @param type the type of the resources.
+     * @param referenceConfig the configuration to best match.
+     */
+    private Map<String, ResourceValue> getConfiguredResource(ResourceType type,
+            FolderConfiguration referenceConfig) {
+        // get the resource item for the given type
+        List<ResourceItem> items = mResourceMap.get(type);
+        if (items == null) {
+            return Collections.emptyMap();
+        }
+
+        // create the map
+        HashMap<String, ResourceValue> map = new HashMap<String, ResourceValue>(items.size());
+
+        for (ResourceItem item : items) {
+            // get the source files generating this resource
+            List<ResourceFile> list = item.getSourceFileList();
+
+            // look for the best match for the given configuration
+            Configurable match = findMatchingConfigurable(list, referenceConfig);
+
+            if (match instanceof ResourceFile) {
+                ResourceFile matchResFile = (ResourceFile)match;
+
+                // get the value of this configured resource.
+                ResourceValue value = matchResFile.getValue(type, item.getName());
+
+                if (value != null) {
+                    map.put(item.getName(), value);
+                }
+            }
+        }
+
+        return map;
+    }
+
+    /**
+     * Returns the best matching {@link Configurable}.
+     *
+     * @param configurables the list of {@link Configurable} to choose from.
+     * @param referenceConfig the {@link FolderConfiguration} to match.
+     *
+     * @return an item from the given list of {@link Configurable} or null.
+     *
+     * @see http://d.android.com/guide/topics/resources/resources-i18n.html#best-match
+     */
+    private Configurable findMatchingConfigurable(List<? extends Configurable> configurables,
+            FolderConfiguration referenceConfig) {
+        //
+        // 1: eliminate resources that contradict the reference configuration
+        // 2: pick next qualifier type
+        // 3: check if any resources use this qualifier, if no, back to 2, else move on to 4.
+        // 4: eliminate resources that don't use this qualifier.
+        // 5: if more than one resource left, go back to 2.
+        //
+        // The precedence of the qualifiers is more important than the number of qualifiers that
+        // exactly match the device.
+
+        // 1: eliminate resources that contradict
+        ArrayList<Configurable> matchingConfigurables = new ArrayList<Configurable>();
+        for (int i = 0 ; i < configurables.size(); i++) {
+            Configurable res = configurables.get(i);
+
+            if (res.getConfiguration().isMatchFor(referenceConfig)) {
+                matchingConfigurables.add(res);
+            }
+        }
+
+        // if there is only one match, just take it
+        if (matchingConfigurables.size() == 1) {
+            return matchingConfigurables.get(0);
+        } else if (matchingConfigurables.size() == 0) {
+            return null;
+        }
+
+        // 2. Loop on the qualifiers, and eliminate matches
+        final int count = FolderConfiguration.getQualifierCount();
+        for (int q = 0 ; q < count ; q++) {
+            // look to see if one configurable has this qualifier.
+            // At the same time also record the best match value for the qualifier (if applicable).
+
+            // The reference value, to find the best match.
+            // Note that this qualifier could be null. In which case any qualifier found in the
+            // possible match, will all be considered best match.
+            ResourceQualifier referenceQualifier = referenceConfig.getQualifier(q);
+
+            boolean found = false;
+            ResourceQualifier bestMatch = null; // this is to store the best match.
+            for (Configurable configurable : matchingConfigurables) {
+                ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q);
+                if (qualifier != null) {
+                    // set the flag.
+                    found = true;
+
+                    // Now check for a best match. If the reference qualifier is null ,
+                    // any qualifier is a "best" match (we don't need to record all of them.
+                    // Instead the non compatible ones are removed below)
+                    if (referenceQualifier != null) {
+                        if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) {
+                            bestMatch = qualifier;
+                        }
+                    }
+                }
+            }
+
+            // 4. If a configurable has a qualifier at the current index, remove all the ones that
+            // do not have one, or whose qualifier value does not equal the best match found above
+            // unless there's no reference qualifier, in which case they are all considered
+            // "best" match.
+            if (found) {
+                for (int i = 0 ; i < matchingConfigurables.size(); ) {
+                    Configurable configurable = matchingConfigurables.get(i);
+                    ResourceQualifier qualifier = configurable.getConfiguration().getQualifier(q);
+
+                    if (qualifier == null) {
+                        // this resources has no qualifier of this type: rejected.
+                        matchingConfigurables.remove(configurable);
+                    } else if (referenceQualifier != null && bestMatch != null &&
+                            bestMatch.equals(qualifier) == false) {
+                        // there's a reference qualifier and there is a better match for it than
+                        // this resource, so we reject it.
+                        matchingConfigurables.remove(configurable);
+                    } else {
+                        // looks like we keep this resource, move on to the next one.
+                        i++;
+                    }
+                }
+
+                // at this point we may have run out of matching resources before going
+                // through all the qualifiers.
+                if (matchingConfigurables.size() < 2) {
+                    break;
+                }
+            }
+        }
+
+        // Because we accept resources whose configuration have qualifiers where the reference
+        // configuration doesn't, we can end up with more than one match. In this case, we just
+        // take the first one.
+        if (matchingConfigurables.size() == 0) {
+            return null;
+        }
+        return matchingConfigurables.get(0);
+    }
+
+    /**
+     * Called after a resource change event, when the resource delta has been processed.
+     */
+    protected void postUpdate() {
+        // Since removed files/folders remove source files from existing ResourceItem, loop through
+        // all resource items and remove the ones that have no source files.
+
+        Collection<List<ResourceItem>> lists = mResourceMap.values();
+        for (List<ResourceItem> list : lists) {
+            for (int i = 0 ; i < list.size() ;) {
+                if (list.get(i).hasNoSourceFile()) {
+                    list.remove(i);
+                } else {
+                    i++;
+                }
+            }
+        }
+    }
+
+    /**
+     * Looks up an existing {@link ResourceItem} by {@link ResourceType} and name. This
+     * ignores inline resources.
+     * @param type the Resource Type.
+     * @param name the Resource name.
+     * @return the existing ResourceItem or null if no match was found.
+     */
+    private ResourceItem findDeclaredResourceItem(ResourceType type, String name) {
+        List<ResourceItem> list = mResourceMap.get(type);
+
+        if (list != null) {
+            for (ResourceItem item : list) {
+                // ignore inline
+                if (name.equals(item.getName()) && item.isDeclaredInline() == false) {
+                    return item;
+                }
+            }
+        }
+
+        return null;
+    }
+}
index 8677e5d..bb8feb0 100644 (file)
@@ -23,7 +23,6 @@ import com.android.io.IAbstractFile;
 import com.android.resources.FolderTypeRelationship;
 import com.android.resources.ResourceType;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -74,33 +73,37 @@ public class SingleResourceFile extends ResourceFile {
     }
 
     @Override
-    public List<ResourceType> getResourceTypes() {
-        return FolderTypeRelationship.getRelatedResourceTypes(getFolder().getType());
+    protected void load() {
+        // get a resource item matching the given type and name
+        ResourceItem item = getRepository().getResourceItem(mType, mResourceName);
+
+        // add this file to the list of files generating this resource item.
+        item.add(this);
     }
 
     @Override
-    public boolean hasResources(ResourceType type) {
-        return FolderTypeRelationship.match(type, getFolder().getType());
+    protected void update() {
+        // when this happens, nothing needs to be done since the file only generates
+        // a single resources that doesn't actually change (its content is the file path)
     }
 
     @Override
-    public Collection<ProjectResourceItem> getResources(ResourceType type,
-            ProjectResources projectResources) {
-
-        // looking for an existing ResourceItem with this name and type
-        ProjectResourceItem item = projectResources.findResourceItem(type, mResourceName);
+    protected void dispose() {
+        // only remove this file from the existing ResourceItem.
+        getFolder().getRepository().removeFile(mType, this);
 
-        ArrayList<ProjectResourceItem> items = new ArrayList<ProjectResourceItem>();
-
-        if (item == null) {
-            item = new ConfigurableResourceItem(mResourceName);
-            items.add(item);
-        }
+        // don't need to touch the content, it'll get reclaimed as this objects disappear.
+        // In the mean time other objects may need to access it.
+    }
 
-        // add this ResourceFile to the ResourceItem
-        item.add(this);
+    @Override
+    public Collection<ResourceType> getResourceTypes() {
+        return FolderTypeRelationship.getRelatedResourceTypes(getFolder().getType());
+    }
 
-        return items;
+    @Override
+    public boolean hasResources(ResourceType type) {
+        return FolderTypeRelationship.match(type, getFolder().getType());
     }
 
     /*
index ccf4301..62aa180 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.sdk;
 
-import static com.android.AndroidConstants.FD_RES_VALUES;
-import static com.android.sdklib.SdkConstants.FD_DATA;
-import static com.android.sdklib.SdkConstants.FD_RES;
-
-import static java.io.File.separator;
-
 import com.android.ide.common.rendering.LayoutLibrary;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.sdk.LoadStatus;
@@ -32,35 +26,18 @@ import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.Android
 import com.android.ide.eclipse.adt.internal.editors.menu.descriptors.MenuDescriptors;
 import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
 import com.android.ide.eclipse.adt.internal.editors.xml.descriptors.XmlDescriptors;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.resources.ResourceType;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
 
 import org.eclipse.core.runtime.IStatus;
-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.BufferedReader;
 import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.Reader;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Map;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-
 /**
  * This class contains the data of an Android Target as loaded from the SDK.
  */
@@ -89,17 +66,14 @@ public class AndroidTargetData {
      */
     private Hashtable<String, String[]> mAttributeValues = new Hashtable<String, String[]>();
 
-    private IResourceRepository mSystemResourceRepository;
-
     private AndroidManifestDescriptors mManifestDescriptors;
     private LayoutDescriptors mLayoutDescriptors;
     private MenuDescriptors mMenuDescriptors;
     private XmlDescriptors mXmlDescriptors;
 
     private Map<String, Map<String, Integer>> mEnumValueMap;
-    private Map<ResourceType, Collection<String>> mPublicAttributeNames;
 
-    private ProjectResources mFrameworkResources;
+    private ResourceRepository mFrameworkResources;
     private LayoutLibrary mLayoutLibrary;
 
     private boolean mLayoutBridgeInit = false;
@@ -113,7 +87,7 @@ public class AndroidTargetData {
      * @param platformLibraries
      * @param optionalLibraries
      */
-    void setExtraData(IResourceRepository systemResourceRepository,
+    void setExtraData(
             AndroidManifestDescriptors manifestDescriptors,
             LayoutDescriptors layoutDescriptors,
             MenuDescriptors menuDescriptors,
@@ -126,16 +100,15 @@ public class AndroidTargetData {
             String[] intentCategoryValues,
             String[] platformLibraries,
             IOptionalLibrary[] optionalLibraries,
-            ProjectResources resources,
+            ResourceRepository frameworkResources,
             LayoutLibrary layoutLibrary) {
 
-        mSystemResourceRepository = systemResourceRepository;
         mManifestDescriptors = manifestDescriptors;
         mLayoutDescriptors = layoutDescriptors;
         mMenuDescriptors = menuDescriptors;
         mXmlDescriptors = xmlDescriptors;
         mEnumValueMap = enumValueMap;
-        mFrameworkResources = resources;
+        mFrameworkResources = frameworkResources;
         mLayoutLibrary = layoutLibrary;
 
         setPermissions(permissionValues);
@@ -144,10 +117,6 @@ public class AndroidTargetData {
         setOptionalLibraries(platformLibraries, optionalLibraries);
     }
 
-    public IResourceRepository getSystemResources() {
-        return mSystemResourceRepository;
-    }
-
     /**
      * Returns an {@link IDescriptorProvider} from a given Id.
      * The Id can be one of {@link #DESCRIPTOR_MANIFEST}, {@link #DESCRIPTOR_LAYOUT},
@@ -263,7 +232,7 @@ public class AndroidTargetData {
     /**
      * Returns the {@link ProjectResources} containing the Framework Resources.
      */
-    public ProjectResources getFrameworkResources() {
+    public ResourceRepository getFrameworkResources() {
         return mFrameworkResources;
     }
 
@@ -360,124 +329,4 @@ public class AndroidTargetData {
         mAttributeValues.remove(name);
         mAttributeValues.put(name, values);
     }
-
-    /**
-     * Returns true if the given name represents a public attribute of the given type.
-     *
-     * @param type the type of resource
-     * @param name the name of the resource
-     * @return true if the given property is public
-     */
-    public boolean isPublicResource(ResourceType type, String name) {
-        Collection<String> names = getNameMap(type);
-        if (names != null) {
-            return names.contains(name);
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns all public properties (in no particular order) of a given resource type.
-     *
-     * @param type the type of resource
-     * @return an unmodifiable collection of public resource names
-     */
-    public Collection<String> getPublicResourceNames(ResourceType type) {
-        Collection<String> names = getNameMap(type);
-        if (names != null) {
-            return Collections.<String>unmodifiableCollection(names);
-        }
-
-        return Collections.emptyList();
-    }
-
-    /** Returns a (possibly cached) list of names for the given resource type, or null */
-    private Collection<String> getNameMap(ResourceType type) {
-        if (mPublicAttributeNames == null) {
-            mPublicAttributeNames = readPublicAttributeLists();
-        }
-
-        return mPublicAttributeNames.get(type);
-    }
-
-    /**
-     * Reads the public.xml file in data/res/values/ for this SDK and
-     * returns the result as a map from resource type to a list of names
-     */
-    private Map<ResourceType, Collection<String>> readPublicAttributeLists() {
-        String relative = FD_DATA + separator + FD_RES + separator + FD_RES_VALUES + separator +
-            "public.xml"; //$NON-NLS-1$
-        File file = new File(mTarget.getLocation(), relative);
-        if (file.isFile()) {
-            Map<ResourceType, Collection<String>> map =
-                new HashMap<ResourceType, Collection<String>>();
-            Document document = null;
-            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-            Reader reader = null;
-            try {
-                reader = new BufferedReader(new FileReader(file));
-                InputSource is = new InputSource(reader);
-                factory.setNamespaceAware(true);
-                factory.setValidating(false);
-                DocumentBuilder builder = factory.newDocumentBuilder();
-                document = builder.parse(is);
-
-                ResourceType lastType = null;
-                String lastTypeName = "";
-
-                NodeList children = document.getDocumentElement().getChildNodes();
-                for (int i = 0, n = children.getLength(); i < n; i++) {
-                    Node node = children.item(i);
-                    if (node.getNodeType() == Node.ELEMENT_NODE) {
-                        Element element = (Element) node;
-                        String name = element.getAttribute("name"); //$NON-NLS-1$
-                        if (name.length() > 0) {
-                            String typeName = element.getAttribute("type"); //$NON-NLS-1$
-                            ResourceType type = null;
-                            if (typeName.equals(lastTypeName)) {
-                                type = lastType;
-                            } else {
-                                type = ResourceType.getEnum(typeName);
-                                lastType = type;
-                                lastTypeName = typeName;
-                            }
-                            if (type != null) {
-                                Collection<String> list = map.get(type);
-                                if (list == null) {
-                                    // Use sets for some of the larger maps to make
-                                    // searching for isPublicResource faster.
-                                    if (type == ResourceType.ATTR) {
-                                        list = new HashSet<String>(900);
-                                    } else if (type == ResourceType.STYLE) {
-                                        list = new HashSet<String>(300);
-                                    } else if (type == ResourceType.DRAWABLE) {
-                                        list = new HashSet<String>(200);
-                                    } else {
-                                        list = new ArrayList<String>(30);
-                                    }
-                                    map.put(type, list);
-                                }
-                                list.add(name);
-                            }
-                        }
-                    }
-                }
-            } catch (Exception e) {
-                AdtPlugin.log(e, "Can't read and parse public attribute list");
-            } finally {
-                if (reader != null) {
-                    try {
-                        reader.close();
-                    } catch (IOException e) {
-                        // Nothing to be done here - we don't care if it closed or not.
-                    }
-                }
-            }
-
-            return map;
-
-        }
-        return Collections.emptyMap();
-    }
 }
index 6426fdb..977c759 100644 (file)
@@ -25,11 +25,8 @@ import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDes
 import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
 import com.android.ide.eclipse.adt.internal.editors.menu.descriptors.MenuDescriptors;
 import com.android.ide.eclipse.adt.internal.editors.xml.descriptors.XmlDescriptors;
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.resources.ResourceType;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.SdkConstants;
 
@@ -86,7 +83,7 @@ public final class AndroidTargetParser {
         try {
             SubMonitor progress = SubMonitor.convert(monitor,
                     String.format("Parsing SDK %1$s", mAndroidTarget.getName()),
-                    13);
+                    12);
 
             AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget);
 
@@ -101,15 +98,6 @@ public final class AndroidTargetParser {
                 return Status.CANCEL_STATUS;
             }
 
-            // get the resource Ids.
-            progress.subTask("Resource IDs");
-            IResourceRepository frameworkRepository = collectResourceIds(classLoader);
-            progress.worked(1);
-
-            if (progress.isCanceled()) {
-                return Status.CANCEL_STATUS;
-            }
-
             // get the permissions
             progress.subTask("Permissions");
             String[] permissionValues = collectPermissions(classLoader);
@@ -231,8 +219,8 @@ public final class AndroidTargetParser {
             progress.worked(1);
 
             // load the framework resources.
-            ProjectResources resources = ResourceManager.getInstance().loadFrameworkResources(
-                    mAndroidTarget);
+            ResourceRepository frameworkResources =
+                    ResourceManager.getInstance().loadFrameworkResources(mAndroidTarget);
             progress.worked(1);
 
             // now load the layout lib bridge
@@ -243,7 +231,7 @@ public final class AndroidTargetParser {
             progress.worked(1);
 
             // and finally create the PlatformData with all that we loaded.
-            targetData.setExtraData(frameworkRepository,
+            targetData.setExtraData(
                     manifestDescriptors,
                     layoutDescriptors,
                     menuDescriptors,
@@ -256,7 +244,7 @@ public final class AndroidTargetParser {
                     categories.toArray(new String[categories.size()]),
                     mAndroidTarget.getPlatformLibraries(),
                     mAndroidTarget.getOptionalLibraries(),
-                    resources,
+                    frameworkResources,
                     layoutBridge);
 
             Sdk.getCurrent().setTargetData(mAndroidTarget, targetData);
@@ -290,72 +278,6 @@ public final class AndroidTargetParser {
     }
 
     /**
-     * Creates an IResourceRepository for the framework resources.
-     *
-     * @param classLoader The framework SDK jar classloader
-     * @return a map of the resources, or null if it failed.
-     */
-    private IResourceRepository collectResourceIds(
-            AndroidJarLoader classLoader) {
-        try {
-            Class<?> r = classLoader.loadClass(SdkConstants.CLASS_R);
-
-            if (r != null) {
-                Map<ResourceType, List<ResourceItem>> map = parseRClass(r);
-                if (map != null) {
-                    return new FrameworkResourceRepository(map);
-                }
-            }
-        } catch (ClassNotFoundException e) {
-            AdtPlugin.logAndPrintError(e, TAG,
-                    "Collect resource IDs failed, class %1$s not found in %2$s", //$NON-NLS-1$
-                    SdkConstants.CLASS_R,
-                    mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
-        }
-
-        return null;
-    }
-
-    /**
-     * Parse the R class and build the resource map.
-     *
-     * @param rClass the Class object representing the Resources.
-     * @return a map of the resource or null
-     */
-    private Map<ResourceType, List<ResourceItem>> parseRClass(Class<?> rClass) {
-        // get the sub classes.
-        Class<?>[] classes = rClass.getClasses();
-
-        if (classes.length > 0) {
-            HashMap<ResourceType, List<ResourceItem>> map =
-                new HashMap<ResourceType, List<ResourceItem>>();
-
-            // get the fields of each class.
-            for (int c = 0 ; c < classes.length ; c++) {
-                Class<?> subClass = classes[c];
-                String name = subClass.getSimpleName();
-
-                // get the matching ResourceType
-                ResourceType type = ResourceType.getEnum(name);
-                if (type != null) {
-                    List<ResourceItem> list = new ArrayList<ResourceItem>();
-                    map.put(type, list);
-
-                    Field[] fields = subClass.getFields();
-
-                    for (Field f : fields) {
-                        list.add(new ResourceItem(f.getName()));
-                    }
-                }
-            }
-
-            return map;
-        }
-
-        return null;
-    }
-
-    /**
      * Loads, collects and returns the list of default permissions from the framework.
      *
      * @param classLoader The framework SDK jar classloader
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/FrameworkResourceRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/FrameworkResourceRepository.java
deleted file mode 100644 (file)
index 247a888..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2008 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.sdk;
-
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.resources.ResourceType;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Implementation of the {@link IResourceRepository} interface to hold the system resource Ids
- * parsed by {@link AndroidTargetParser}. 
- */
-final class FrameworkResourceRepository implements IResourceRepository {
-    
-    private Map<ResourceType, List<ResourceItem>> mResourcesMap; 
-    
-    public FrameworkResourceRepository(Map<ResourceType, List<ResourceItem>> systemResourcesMap) {
-        mResourcesMap = systemResourcesMap;
-    }
-
-    public ResourceType[] getAvailableResourceTypes() {
-        if (mResourcesMap != null) {
-            Set<ResourceType> types = mResourcesMap.keySet();
-
-            if (types != null) {
-                return types.toArray(new ResourceType[types.size()]);
-            }
-        }
-
-        return null;
-    }
-
-    public ResourceItem[] getResources(ResourceType type) {
-        if (mResourcesMap != null) {
-            List<ResourceItem> items = mResourcesMap.get(type);
-
-            if (items != null) {
-                return items.toArray(new ResourceItem[items.size()]);
-            }
-        }
-
-        return null;
-    }
-
-    public boolean hasResources(ResourceType type) {
-        if (mResourcesMap != null) {
-            List<ResourceItem> items = mResourcesMap.get(type);
-
-            return (items != null && items.size() > 0);
-        }
-
-        return false;
-    }
-
-    public boolean isSystemRepository() {
-        return true;
-    }
-}
index 1a9a78f..3c291f3 100644 (file)
@@ -15,8 +15,9 @@
  */
 package com.android.ide.eclipse.adt.internal.ui;
 
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
+import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.resources.ResourceType;
 
@@ -196,11 +197,11 @@ public class MarginChooser extends SelectionStatusDialog implements Listener {
                 Button button = (Button) event.widget;
 
                 // Open a resource chooser dialog for specified resource type.
-                IResourceRepository projectRepository = ResourceManager.getInstance()
+                ProjectResources projectRepository = ResourceManager.getInstance()
                         .getProjectResources(mProject);
-                IResourceRepository systemRepository = mTargetData.getSystemResources();
+                ResourceRepository frameworkRepository = mTargetData.getFrameworkResources();
                 ResourceChooser dlg = new ResourceChooser(mProject, ResourceType.DIMEN,
-                        projectRepository, systemRepository, getShell());
+                        projectRepository, frameworkRepository, getShell());
                 Text text = (Text) button.getData(PROP_TEXTFIELD);
                 dlg.setCurrentResource(text.getText().trim());
                 if (dlg.open() == Window.OK) {
index 2a170a4..c6bbd0d 100644 (file)
@@ -19,9 +19,9 @@ package com.android.ide.eclipse.adt.internal.ui;
 import com.android.ide.eclipse.adt.AdtPlugin;
 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.IResourceRepository;
 import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.resources.ResourceType;
 
 import org.eclipse.core.resources.IProject;
@@ -52,6 +52,7 @@ import org.eclipse.ui.dialogs.FilteredTree;
 import org.eclipse.ui.dialogs.PatternFilter;
 import org.eclipse.ui.dialogs.SelectionStatusDialog;
 
+import java.util.Collection;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -66,7 +67,7 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
 
     private static IDialogSettings sDialogSettings = new DialogSettings("");
 
-    private IResourceRepository mResources;
+    private ResourceRepository mProjectResources;
     private String mCurrentResource;
     private FilteredTree mFilteredTree;
     private Button mNewResButton;
@@ -77,10 +78,11 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
      * @param project
      * @param parent
      */
-    public ReferenceChooserDialog(IProject project, IResourceRepository resources, Shell parent) {
+    public ReferenceChooserDialog(IProject project, ResourceRepository projectResources,
+            Shell parent) {
         super(parent);
         mProject = project;
-        mResources = resources;
+        mProjectResources = projectResources;
 
         int shellStyle = getShellStyle();
         setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
@@ -177,7 +179,7 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
 
         mTreeViewer.setLabelProvider(new ResourceLabelProvider());
         mTreeViewer.setContentProvider(new ResourceContentProvider(false /* fullLevels */));
-        mTreeViewer.setInput(mResources);
+        mTreeViewer.setInput(mProjectResources);
     }
 
     protected void handleSelection() {
@@ -339,7 +341,8 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
      */
     private void setupInitialSelection(ResourceType resourceType, String resourceName) {
         // get all the resources of this type
-        ResourceItem[] resourceItems = mResources.getResources(resourceType);
+        Collection<ResourceItem> resourceItems =
+                mProjectResources.getResourceItemsOfType(resourceType);
 
         for (ResourceItem resourceItem : resourceItems) {
             if (resourceName.equals(resourceItem.getName())) {
index 87d1b75..b57de72 100644 (file)
@@ -28,12 +28,10 @@ import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.Resour
 import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
 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.IResourceRepository;
 import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.resources.ResourceType;
 
 import org.eclipse.core.resources.IFile;
@@ -77,10 +75,8 @@ import org.w3c.dom.Text;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -92,7 +88,8 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
 
     private Pattern mProjectResourcePattern;
     private ResourceType mResourceType;
-    private IResourceRepository mProjectResources;
+    private final ResourceRepository mProjectResources;
+    private final ResourceRepository mFrameworkResources;
     private Pattern mSystemResourcePattern;
     private Button mProjectButton;
     private Button mSystemButton;
@@ -105,18 +102,19 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
      * @param project Project being worked on
      * @param type The type of the resource to choose
      * @param projectResources The repository for the project
-     * @param systemResources The System resource repository
+     * @param frameworkResources The Framework resource repository
      * @param parent the parent shell
      */
     public ResourceChooser(IProject project, ResourceType type,
-            IResourceRepository projectResources,
-            IResourceRepository systemResources,
+            ResourceRepository projectResources,
+            ResourceRepository frameworkResources,
             Shell parent) {
         super(parent, new ResourceLabelProvider());
         mProject = project;
 
         mResourceType = type;
         mProjectResources = projectResources;
+        mFrameworkResources = frameworkResources;
 
         mProjectResourcePattern = Pattern.compile(
                 "@" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
@@ -371,25 +369,21 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
      * Setups the current list.
      */
     private ResourceItem[] setupResourceList() {
-        ResourceItem[] items = null;
+        Collection<ResourceItem> items = null;
         if (mProjectButton.getSelection()) {
-            items = mProjectResources.getResources(mResourceType);
-            setListElements(items);
+            items = mProjectResources.getResourceItemsOfType(mResourceType);
         } else if (mSystemButton.getSelection()) {
-            AndroidTargetData targetData = Sdk.getCurrent().getTargetData(mProject);
-            if (targetData != null) {
-                Collection<String> names = targetData.getPublicResourceNames(mResourceType);
-                List<ResourceItem> list = new ArrayList<ResourceItem>();
-                for (String name : names) {
-                    list.add(new ResourceItem(name));
-                }
-                Collections.sort(list);
-                items = list.toArray(new ResourceItem[list.size()]);
-                setListElements(items);
-            }
+            items = mFrameworkResources.getResourceItemsOfType(mResourceType);
         }
 
-        return items;
+        ResourceItem[] arrayItems = items.toArray(new ResourceItem[items.size()]);
+
+        // sort the array
+        Arrays.sort(arrayItems);
+
+        setListElements(arrayItems);
+
+        return arrayItems;
     }
 
     /**
index f57b74e..ee13416 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.ui;
 
-import com.android.ide.eclipse.adt.internal.resources.IResourceRepository;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.ConfigurableResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.resources.ResourceType;
 
 import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.Viewer;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Content provider for the Resource Explorer TreeView.
  * Each level of the tree is represented by a different class.
@@ -40,10 +43,10 @@ import org.eclipse.jface.viewers.Viewer;
  * <li>{@link ResourceFile}. (optional) This represents a particular version of the
  * {@link ResourceItem}. It is displayed as a list of resource qualifier.
  * </li>
- * </ul> 
- * </ul> 
- * </ul> 
- * 
+ * </ul>
+ * </ul>
+ * </ul>
+ *
  * @see ResourceLabelProvider
  */
 public class ResourceContentProvider implements ITreeContentProvider {
@@ -51,10 +54,10 @@ public class ResourceContentProvider implements ITreeContentProvider {
     /**
      * The current ProjectResources being displayed.
      */
-    private IResourceRepository mResources;
-    
+    private ResourceRepository mResources;
+
     private boolean mFullLevels;
-    
+
    /**
      * Constructs a new content providers for resource display.
      * @param fullLevels if <code>true</code> the content provider will suppport all 3 levels. If
@@ -66,9 +69,12 @@ public class ResourceContentProvider implements ITreeContentProvider {
 
     public Object[] getChildren(Object parentElement) {
         if (parentElement instanceof ResourceType) {
-            return mResources.getResources((ResourceType)parentElement);
-        } else if (mFullLevels && parentElement instanceof ConfigurableResourceItem) {
-            return ((ConfigurableResourceItem)parentElement).getSourceFileArray();
+            Object[] array = mResources.getResourceItemsOfType(
+                    (ResourceType)parentElement).toArray();
+            Arrays.sort(array);
+            return array;
+        } else if (mFullLevels && parentElement instanceof ResourceItem) {
+            return ((ResourceItem)parentElement).getSourceFileArray();
         }
         return null;
     }
@@ -80,18 +86,20 @@ public class ResourceContentProvider implements ITreeContentProvider {
 
     public boolean hasChildren(Object element) {
         if (element instanceof ResourceType) {
-            return mResources.hasResources((ResourceType)element);
-        } else if (mFullLevels && element instanceof ConfigurableResourceItem) {
-            return ((ConfigurableResourceItem)element).hasAlternates();
+            return mResources.hasResourcesOfType((ResourceType)element);
+        } else if (mFullLevels && element instanceof ResourceItem) {
+            return ((ResourceItem)element).hasAlternates();
         }
         return false;
     }
 
     public Object[] getElements(Object inputElement) {
-        if (inputElement instanceof IResourceRepository) {
-            if ((IResourceRepository)inputElement == mResources) {
+        if (inputElement instanceof ResourceRepository) {
+            if ((ResourceRepository)inputElement == mResources) {
                 // get the top level resources.
-                return mResources.getAvailableResourceTypes();
+                List<ResourceType> types = mResources.getAvailableResourceTypes();
+                Collections.sort(types);
+                return types.toArray();
             }
         }
 
@@ -103,8 +111,8 @@ public class ResourceContentProvider implements ITreeContentProvider {
     }
 
     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-        if (newInput instanceof IResourceRepository) {
-             mResources = (IResourceRepository)newInput;
+        if (newInput instanceof ResourceRepository) {
+             mResources = (ResourceRepository)newInput;
         }
     }
 }
index 081b6b6..6115215 100644 (file)
@@ -18,7 +18,7 @@ package com.android.ide.eclipse.adt.internal.ui;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AdtConstants;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
@@ -138,10 +138,10 @@ public class ResourceExplorerView extends ViewPart implements ISelectionListener
                                 }
                             } catch (PartInitException e) {
                             }
-                        } else if (element instanceof ProjectResourceItem) {
+                        } else if (element instanceof ResourceItem) {
                             // if it's a ResourceItem, we open the first file, but only if
                             // there's no alternate files.
-                            ProjectResourceItem item = (ProjectResourceItem)element;
+                            ResourceItem item = (ResourceItem)element;
 
                             if (item.isEditableDirectly()) {
                                 ResourceFile[] files = item.getSourceFileArray();
index 50e1d07..f08389f 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.ui;
 
-import com.android.ide.eclipse.adt.internal.resources.IIdResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.ResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.ConfigurableResourceItem;
-import com.android.ide.eclipse.adt.internal.resources.manager.IdResourceItem;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
 import com.android.resources.ResourceType;
 
@@ -47,15 +44,15 @@ import org.eclipse.ui.PlatformUI;
  * <li>{@link ResourceFile}. This represents a particular version of the {@link ResourceItem}.
  * It is displayed as a list of resource qualifier.
  * </li>
- * </ul> 
- * </ul> 
- * </ul> 
- * 
+ * </ul>
+ * </ul>
+ * </ul>
+ *
  * @see ResourceContentProvider
  */
 public class ResourceLabelProvider implements ILabelProvider, ITableLabelProvider {
     private Image mWarningImage;
-    
+
     public ResourceLabelProvider() {
         mWarningImage = PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(
                 ISharedImages.IMG_OBJS_WARN_TSK).createImage();
@@ -94,8 +91,8 @@ public class ResourceLabelProvider implements ILabelProvider, ITableLabelProvide
 
     public Image getColumnImage(Object element, int columnIndex) {
         if (columnIndex == 1) {
-            if (element instanceof ConfigurableResourceItem) {
-                ConfigurableResourceItem item = (ConfigurableResourceItem)element;
+            if (element instanceof ResourceItem) {
+                ResourceItem item = (ResourceItem)element;
                 if (item.hasDefault() == false) {
                     return mWarningImage;
                 }
@@ -116,19 +113,18 @@ public class ResourceLabelProvider implements ILabelProvider, ITableLabelProvide
                 }
                 break;
             case 1:
-                if (element instanceof ConfigurableResourceItem) {
-                    ConfigurableResourceItem item = (ConfigurableResourceItem)element;
-                    int count = item.getAlternateCount();
-                    if (count > 0) {
-                        if (item.hasDefault()) {
-                            count++;
-                        }
-                        return String.format("%1$d version(s)", count);
-                    }
-                } else if (element instanceof IIdResourceItem) {
-                    IIdResourceItem idResource = (IIdResourceItem)element;
-                    if (idResource.isDeclaredInline()) {
+                if (element instanceof ResourceItem) {
+                    ResourceItem item = (ResourceItem)element;
+                    if (item.isDeclaredInline()) {
                         return "Declared inline";
+                    } else {
+                        int count = item.getAlternateCount();
+                        if (count > 0) {
+                            if (item.hasDefault()) {
+                                count++;
+                            }
+                            return String.format("%1$d version(s)", count);
+                        }
                     }
                 }
                 return null;
index 5131126..7fcb333 100644 (file)
@@ -38,6 +38,7 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMe
 import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceRepository;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.tests.SdkTestCase;
 import com.android.io.FolderWrapper;
@@ -175,7 +176,7 @@ public class ApiDemosRenderingTest extends SdkTestCase {
         }
 
         // first load the project's target framework resource
-        ProjectResources framework = ResourceManager.getInstance().loadFrameworkResources(target);
+        ResourceRepository framework = ResourceManager.getInstance().loadFrameworkResources(target);
 
         // now load the project resources
         ProjectResources project = new ProjectResources(null /*project*/);
index 2a06373..9493c35 100644 (file)
@@ -121,14 +121,10 @@ public class ResourceResolver extends RenderResources {
         if (frameworkTheme) {
             Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get(
                     ResourceType.STYLE);
-            if (frameworkStyleMap != null) {
-                theme = frameworkStyleMap.get(name);
-            }
+            theme = frameworkStyleMap.get(name);
         } else {
             Map<String, ResourceValue> projectStyleMap = mProjectResources.get(ResourceType.STYLE);
-            if (projectStyleMap != null) {
-                theme = projectStyleMap.get(name);
-            }
+            theme = projectStyleMap.get(name);
         }
 
         if (theme instanceof StyleResourceValue) {
@@ -334,29 +330,25 @@ public class ResourceResolver extends RenderResources {
         // if allowed, search in the project resources first.
         if (frameworkOnly == false) {
             typeMap = mProjectResources.get(resType);
-            if (typeMap != null) {
-                ResourceValue item = typeMap.get(resName);
-                if (item != null) {
-                    return item;
-                }
+            ResourceValue item = typeMap.get(resName);
+            if (item != null) {
+                return item;
             }
         }
 
         // now search in the framework resources.
         typeMap = mFrameworkResources.get(resType);
-        if (typeMap != null) {
-            ResourceValue item = typeMap.get(resName);
-            if (item != null) {
-                return item;
-            }
+        ResourceValue item = typeMap.get(resName);
+        if (item != null) {
+            return item;
+        }
 
-            // if it was not found and the type is an id, it is possible that the ID was
-            // generated dynamically when compiling the framework resources.
-            // Look for it in the R map.
-            if (mFrameworkProvider != null && resType == ResourceType.ID) {
-                if (mFrameworkProvider.getId(resType, resName) != null) {
-                    return new ResourceValue(resType, resName, true);
-                }
+        // if it was not found and the type is an id, it is possible that the ID was
+        // generated dynamically when compiling the framework resources.
+        // Look for it in the R map.
+        if (mFrameworkProvider != null && resType == ResourceType.ID) {
+            if (mFrameworkProvider.getId(resType, resName) != null) {
+                return new ResourceValue(resType, resName, true);
             }
         }
 
@@ -397,32 +389,30 @@ public class ResourceResolver extends RenderResources {
         Map<String, ResourceValue> projectStyleMap = mProjectResources.get(ResourceType.STYLE);
         Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get(ResourceType.STYLE);
 
-        if (projectStyleMap != null && frameworkStyleMap != null) {
-            // first, get the theme
-            ResourceValue theme = null;
-
-            // project theme names have been prepended with a *
-            if (isProjectTheme) {
-                theme = projectStyleMap.get(themeName);
-            } else {
-                theme = frameworkStyleMap.get(themeName);
-            }
-
-            if (theme instanceof StyleResourceValue) {
-                // compute the inheritance map for both the project and framework styles
-                computeStyleInheritance(projectStyleMap.values(), projectStyleMap,
-                        frameworkStyleMap);
+        // first, get the theme
+        ResourceValue theme = null;
 
-                // Compute the style inheritance for the framework styles/themes.
-                // Since, for those, the style parent values do not contain 'android:'
-                // we want to force looking in the framework style only to avoid using
-                // similarly named styles from the project.
-                // To do this, we pass null in lieu of the project style map.
-                computeStyleInheritance(frameworkStyleMap.values(), null /*inProjectStyleMap */,
-                        frameworkStyleMap);
+        // project theme names have been prepended with a *
+        if (isProjectTheme) {
+            theme = projectStyleMap.get(themeName);
+        } else {
+            theme = frameworkStyleMap.get(themeName);
+        }
 
-                mTheme = (StyleResourceValue) theme;
-            }
+        if (theme instanceof StyleResourceValue) {
+            // compute the inheritance map for both the project and framework styles
+            computeStyleInheritance(projectStyleMap.values(), projectStyleMap,
+                    frameworkStyleMap);
+
+            // Compute the style inheritance for the framework styles/themes.
+            // Since, for those, the style parent values do not contain 'android:'
+            // we want to force looking in the framework style only to avoid using
+            // similarly named styles from the project.
+            // To do this, we pass null in lieu of the project style map.
+            computeStyleInheritance(frameworkStyleMap.values(), null /*inProjectStyleMap */,
+                    frameworkStyleMap);
+
+            mTheme = (StyleResourceValue) theme;
         }
     }