diff options
author | Xavier Ducrohet <xav@android.com> | 2011-03-09 13:08:53 -0800 |
---|---|---|
committer | Android Code Review <code-review@android.com> | 2011-03-09 13:08:53 -0800 |
commit | 57eb998efcd6031d01fc441ed0bd8b09c30b78c0 (patch) | |
tree | c2b5870a081f8fc00f01656d168b406a185d2c19 | |
parent | 3a661854fa8921ee27de0ac7461f9c2b8fc744f5 (diff) | |
parent | da02c18ad5b54d97a1fcfd5f6633062b0c873c22 (diff) | |
download | sdk-57eb998efcd6031d01fc441ed0bd8b09c30b78c0.zip sdk-57eb998efcd6031d01fc441ed0bd8b09c30b78c0.tar.gz sdk-57eb998efcd6031d01fc441ed0bd8b09c30b78c0.tar.bz2 |
Merge "Resource management refactoring and clean-up."
37 files changed, 1648 insertions, 1934 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java index 10ccd77..c28a561 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java @@ -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) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java index 3c5cea4..5e03b54 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java @@ -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(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java index 9e0525e..858bfb8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java @@ -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); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java index f9e103a..3461d18 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java @@ -16,10 +16,11 @@ 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(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java index ce299e4..7a1e06e 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java @@ -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, diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java index b10d69b..1af04a8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiResourceAttributeNode.java @@ -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()); } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java index efd95e2..5a2f39f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java @@ -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 index acc4cf2..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IIdResourceItem.java +++ /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 index 1abd9eb..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/IResourceRepository.java +++ /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(); - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java index 327bd89..568174c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java @@ -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 index c340ffe..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceItem.java +++ /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); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java index 543719b..3ad9d50 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java @@ -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); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Resource.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Configurable.java index 0ad5c39..64dce73 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Resource.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/Configurable.java @@ -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 index 2a998f8..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ConfigurableResourceItem.java +++ /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 index 0000000..a0e79d9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FrameworkResourceItem.java @@ -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 index 0000000..fc29dd9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/FrameworkResources.java @@ -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); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/InlineResourceItem.java index 8b142fb..6d1bd23 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdResourceItem.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/InlineResourceItem.java @@ -16,39 +16,39 @@ 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$ } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java index 8f8e0d3..b37e7a2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/MultiResourceFile.java @@ -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 index 845a974..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResourceItem.java +++ /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); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java index b7f406d..a6ee95d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java @@ -17,51 +17,37 @@ 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)); + } } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java index a6e10ae..151830e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFile.java @@ -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. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java index 40f6fbb..3d3cf70 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceFolder.java @@ -16,32 +16,30 @@ 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 index 0000000..f826ed7 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceItem.java @@ -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$ + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java index c039f6b..1d3c709 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java @@ -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 index 0000000..39de45c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceRepository.java @@ -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; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java index 8677e5d..bb8feb0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/SingleResourceFile.java @@ -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()); } /* diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java index ccf4301..62aa180 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java @@ -16,12 +16,6 @@ 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(); - } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java index 6426fdb..977c759 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java @@ -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 index 247a888..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/FrameworkResourceRepository.java +++ /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; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java index 1a9a78f..3c291f3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java @@ -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) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java index 2a170a4..c6bbd0d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java @@ -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())) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java index 87d1b75..b57de72 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java @@ -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; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java index f57b74e..ee13416 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java @@ -16,15 +16,18 @@ 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; } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java index 081b6b6..6115215 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java @@ -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(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java index 50e1d07..f08389f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java @@ -16,10 +16,7 @@ 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; diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java index 5131126..7fcb333 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java @@ -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*/); diff --git a/ide_common/src/com/android/ide/common/resources/ResourceResolver.java b/ide_common/src/com/android/ide/common/resources/ResourceResolver.java index 2a06373..9493c35 100644 --- a/ide_common/src/com/android/ide/common/resources/ResourceResolver.java +++ b/ide_common/src/com/android/ide/common/resources/ResourceResolver.java @@ -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; } } |