diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java | 271 |
1 files changed, 271 insertions, 0 deletions
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 new file mode 100644 index 000000000..7c3fd4c13 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java @@ -0,0 +1,271 @@ +/* + * 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.SdkConstants; +import com.android.annotations.NonNull; +import com.android.ide.common.rendering.api.ResourceValue; +import com.android.ide.common.resources.IntArrayWrapper; +import com.android.ide.common.resources.ResourceFolder; +import com.android.ide.common.resources.ResourceItem; +import com.android.ide.common.resources.ResourceRepository; +import com.android.ide.common.resources.configuration.FolderConfiguration; +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.ResourceType; +import com.android.util.Pair; + +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * 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> + */ +@SuppressWarnings("deprecation") +public class ProjectResources extends ResourceRepository { + // project resources are defined as 0x7FXX#### where XX is the resource type (layout, drawable, + // etc...). Using FF as the type allows for 255 resource types before we get a collision + // which should be fine. + private final static int DYNAMIC_ID_SEED_START = 0x7fff0000; + + /** 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 */ + private Map<Integer, Pair<ResourceType, String>> mResIdValueToNameMap; + /** Map of (int[], name) for styleable resources coming from R.java */ + private Map<IntArrayWrapper, String> mStyleableValueToNameMap; + + private final DynamicIdMap mDynamicIdMap = new DynamicIdMap(DYNAMIC_ID_SEED_START); + private final IntArrayWrapper mWrapper = new IntArrayWrapper(null); + private final IProject mProject; + + public static ProjectResources create(IProject project) { + IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES); + + return new ProjectResources(project, new IFolderWrapper(resFolder)); + } + + /** + * Makes a ProjectResources for a given <var>project</var>. + * @param project the project. + */ + private ProjectResources(IProject project, IAbstractFolder resFolder) { + super(resFolder, false /*isFrameworkRepository*/); + mProject = project; + } + + /** + * Returns the resources values matching a given {@link FolderConfiguration}, this will + * include library dependency. + * + * @param referenceConfig the configuration that each value must match. + * @return a map with guaranteed to contain an entry for each {@link ResourceType} + */ + @Override + @NonNull + public Map<ResourceType, Map<String, ResourceValue>> getConfiguredResources( + @NonNull FolderConfiguration referenceConfig) { + ensureInitialized(); + + 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 + // so that they are accessible to the layout rendering. + if (mProject != null) { + ProjectState state = Sdk.getProjectState(mProject); + if (state != null) { + List<IProject> libraries = state.getFullLibraryProjects(); + + ResourceManager resMgr = ResourceManager.getInstance(); + + // because aapt put all the library in their order in this array, the first + // one will have priority over the 2nd one. So it's better to loop in the inverse + // order and fill the map with resources that will be overwritten by higher + // priority resources + for (int i = libraries.size() - 1 ; i >= 0 ; i--) { + IProject library = libraries.get(i); + + ProjectResources libRes = resMgr.getProjectResources(library); + if (libRes != null) { + // 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 + for (Entry<ResourceType, Map<String, ResourceValue>> libEntry : + libMap.entrySet()) { + + // get the map currently in the result map for this resource type + 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 + resultMap.put(libEntry.getKey(), libEntry.getValue()); + } else { + // already a map for this type. add the resources from the + // library, this will override existing value, which is why + // we loop in a specific library order. + tempMap.putAll(libEntry.getValue()); + } + } + } + } + } + } + + // now the project resources themselves. + 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 resultMap; + } + + /** + * 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. + * + * @see ResourceRepository#getResourceFolder(com.android.io.IAbstractFolder) + */ + public ResourceFolder getResourceFolder(IFolder folder) { + return getResourceFolder(new IFolderWrapper(folder)); + } + + /** + * Resolves a compiled resource id into the resource name and type + * @param id the resource integer id. + * @return a {@link Pair} of 2 strings { name, type } or null if the id could not be resolved + */ + public Pair<ResourceType, String> resolveResourceId(int id) { + Pair<ResourceType, String> result = null; + if (mResIdValueToNameMap != null) { + result = mResIdValueToNameMap.get(id); + } + + if (result == null) { + synchronized (mDynamicIdMap) { + result = mDynamicIdMap.resolveId(id); + } + } + + return result; + } + + /** + * Resolves a compiled styleable id of type int[] into the styleable name. + */ + public String resolveStyleable(int[] id) { + if (mStyleableValueToNameMap != null) { + mWrapper.set(id); + return mStyleableValueToNameMap.get(mWrapper); + } + + return null; + } + + /** + * Returns the integer id of a resource given its type and name. + * <p/>If the resource is of type {@link ResourceType#ID} and does not exist in the + * internal map, then new id values are dynamically generated (and stored so that queries + * with the same names will return the same value). + */ + public Integer getResourceId(ResourceType type, String name) { + Integer result = null; + if (mResourceValueMap != null) { + Map<String, Integer> map = mResourceValueMap.get(type); + if (map != null) { + result = map.get(name); + } + } + + if (result == null) { + synchronized (mDynamicIdMap) { + result = mDynamicIdMap.getId(type, name); + } + } + + return result; + } + + /** + * Resets the list of dynamic Ids. This list is used by + * {@link #getResourceId(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.) + * <p/>This method resets those dynamic ID and must be called whenever the actual list of IDs + * change. + */ + public void resetDynamicIds() { + synchronized (mDynamicIdMap) { + mDynamicIdMap.reset(DYNAMIC_ID_SEED_START); + } + } + + @Override + @NonNull + protected ResourceItem createResourceItem(@NonNull String name) { + return new ResourceItem(name); + } + + /** + * Sets compiled resource information. + * + * @param resIdValueToNameMap a map of compiled resource id to resource name. + * The map is acquired by the {@link ProjectResources} object. + * @param styleableValueMap a map of (int[], name) for the styleable information. The map is + * acquired by the {@link ProjectResources} object. + * @param resourceValueMap a map of (name, id) for resources of type {@link ResourceType#ID}. + * The list is acquired by the {@link ProjectResources} object. + */ + void setCompiledResources(Map<Integer, Pair<ResourceType, String>> resIdValueToNameMap, + Map<IntArrayWrapper, String> styleableValueMap, + Map<ResourceType, Map<String, Integer>> resourceValueMap) { + mResourceValueMap = resourceValueMap; + mResIdValueToNameMap = resIdValueToNameMap; + mStyleableValueToNameMap = styleableValueMap; + + resetDynamicIds(); + } +} |