1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ide.eclipse.adt.internal.resources.manager; 18 19 import com.android.SdkConstants; 20 import com.android.annotations.NonNull; 21 import com.android.ide.common.rendering.api.ResourceValue; 22 import com.android.ide.common.resources.IntArrayWrapper; 23 import com.android.ide.common.resources.ResourceFolder; 24 import com.android.ide.common.resources.ResourceItem; 25 import com.android.ide.common.resources.ResourceRepository; 26 import com.android.ide.common.resources.configuration.FolderConfiguration; 27 import com.android.ide.eclipse.adt.internal.sdk.ProjectState; 28 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 29 import com.android.ide.eclipse.adt.io.IFolderWrapper; 30 import com.android.io.IAbstractFolder; 31 import com.android.resources.ResourceType; 32 import com.android.util.Pair; 33 34 import org.eclipse.core.resources.IFolder; 35 import org.eclipse.core.resources.IProject; 36 37 import java.util.EnumMap; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Map.Entry; 41 42 /** 43 * Represents the resources of a project. 44 * On top of the regular {@link ResourceRepository} features it provides: 45 *<ul> 46 *<li>configured resources contain the resources coming from the libraries.</li> 47 *<li>resolution to and from resource integer (compiled value in R.java).</li> 48 *<li>handles resource integer for non existing values of type ID. This is used when rendering.</li> 49 *<li>layouts that have no been saved yet. This is handled by generating dynamic IDs 50 * on the fly.</li> 51 *</ul> 52 */ 53 @SuppressWarnings("deprecation") 54 public class ProjectResources extends ResourceRepository { 55 // project resources are defined as 0x7FXX#### where XX is the resource type (layout, drawable, 56 // etc...). Using FF as the type allows for 255 resource types before we get a collision 57 // which should be fine. 58 private final static int DYNAMIC_ID_SEED_START = 0x7fff0000; 59 60 /** Map of (name, id) for resources of type {@link ResourceType#ID} coming from R.java */ 61 private Map<ResourceType, Map<String, Integer>> mResourceValueMap; 62 /** Map of (id, [name, resType]) for all resources coming from R.java */ 63 private Map<Integer, Pair<ResourceType, String>> mResIdValueToNameMap; 64 /** Map of (int[], name) for styleable resources coming from R.java */ 65 private Map<IntArrayWrapper, String> mStyleableValueToNameMap; 66 67 private final DynamicIdMap mDynamicIdMap = new DynamicIdMap(DYNAMIC_ID_SEED_START); 68 private final IntArrayWrapper mWrapper = new IntArrayWrapper(null); 69 private final IProject mProject; 70 create(IProject project)71 public static ProjectResources create(IProject project) { 72 IFolder resFolder = project.getFolder(SdkConstants.FD_RESOURCES); 73 74 return new ProjectResources(project, new IFolderWrapper(resFolder)); 75 } 76 77 /** 78 * Makes a ProjectResources for a given <var>project</var>. 79 * @param project the project. 80 */ ProjectResources(IProject project, IAbstractFolder resFolder)81 private ProjectResources(IProject project, IAbstractFolder resFolder) { 82 super(resFolder, false /*isFrameworkRepository*/); 83 mProject = project; 84 } 85 86 /** 87 * Returns the resources values matching a given {@link FolderConfiguration}, this will 88 * include library dependency. 89 * 90 * @param referenceConfig the configuration that each value must match. 91 * @return a map with guaranteed to contain an entry for each {@link ResourceType} 92 */ 93 @Override 94 @NonNull getConfiguredResources( @onNull FolderConfiguration referenceConfig)95 public Map<ResourceType, Map<String, ResourceValue>> getConfiguredResources( 96 @NonNull FolderConfiguration referenceConfig) { 97 ensureInitialized(); 98 99 Map<ResourceType, Map<String, ResourceValue>> resultMap = 100 new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class); 101 102 // if the project contains libraries, we need to add the libraries resources here 103 // so that they are accessible to the layout rendering. 104 if (mProject != null) { 105 ProjectState state = Sdk.getProjectState(mProject); 106 if (state != null) { 107 List<IProject> libraries = state.getFullLibraryProjects(); 108 109 ResourceManager resMgr = ResourceManager.getInstance(); 110 111 // because aapt put all the library in their order in this array, the first 112 // one will have priority over the 2nd one. So it's better to loop in the inverse 113 // order and fill the map with resources that will be overwritten by higher 114 // priority resources 115 for (int i = libraries.size() - 1 ; i >= 0 ; i--) { 116 IProject library = libraries.get(i); 117 118 ProjectResources libRes = resMgr.getProjectResources(library); 119 if (libRes != null) { 120 // get the library resources, and only the library, not the dependencies 121 // so call doGetConfiguredResources() directly. 122 Map<ResourceType, Map<String, ResourceValue>> libMap = 123 libRes.doGetConfiguredResources(referenceConfig); 124 125 // we don't want to simply replace the whole map, but instead merge the 126 // content of any sub-map 127 for (Entry<ResourceType, Map<String, ResourceValue>> libEntry : 128 libMap.entrySet()) { 129 130 // get the map currently in the result map for this resource type 131 Map<String, ResourceValue> tempMap = resultMap.get(libEntry.getKey()); 132 if (tempMap == null) { 133 // since there's no current map for this type, just add the map 134 // directly coming from the library resources 135 resultMap.put(libEntry.getKey(), libEntry.getValue()); 136 } else { 137 // already a map for this type. add the resources from the 138 // library, this will override existing value, which is why 139 // we loop in a specific library order. 140 tempMap.putAll(libEntry.getValue()); 141 } 142 } 143 } 144 } 145 } 146 } 147 148 // now the project resources themselves. 149 Map<ResourceType, Map<String, ResourceValue>> thisProjectMap = 150 doGetConfiguredResources(referenceConfig); 151 152 // now merge the maps. 153 for (Entry<ResourceType, Map<String, ResourceValue>> entry : thisProjectMap.entrySet()) { 154 ResourceType type = entry.getKey(); 155 Map<String, ResourceValue> typeMap = resultMap.get(type); 156 if (typeMap == null) { 157 resultMap.put(type, entry.getValue()); 158 } else { 159 typeMap.putAll(entry.getValue()); 160 } 161 } 162 163 return resultMap; 164 } 165 166 /** 167 * Returns the {@link ResourceFolder} associated with a {@link IFolder}. 168 * @param folder The {@link IFolder} object. 169 * @return the {@link ResourceFolder} or null if it was not found. 170 * 171 * @see ResourceRepository#getResourceFolder(com.android.io.IAbstractFolder) 172 */ getResourceFolder(IFolder folder)173 public ResourceFolder getResourceFolder(IFolder folder) { 174 return getResourceFolder(new IFolderWrapper(folder)); 175 } 176 177 /** 178 * Resolves a compiled resource id into the resource name and type 179 * @param id the resource integer id. 180 * @return a {@link Pair} of 2 strings { name, type } or null if the id could not be resolved 181 */ resolveResourceId(int id)182 public Pair<ResourceType, String> resolveResourceId(int id) { 183 Pair<ResourceType, String> result = null; 184 if (mResIdValueToNameMap != null) { 185 result = mResIdValueToNameMap.get(id); 186 } 187 188 if (result == null) { 189 synchronized (mDynamicIdMap) { 190 result = mDynamicIdMap.resolveId(id); 191 } 192 } 193 194 return result; 195 } 196 197 /** 198 * Resolves a compiled styleable id of type int[] into the styleable name. 199 */ resolveStyleable(int[] id)200 public String resolveStyleable(int[] id) { 201 if (mStyleableValueToNameMap != null) { 202 mWrapper.set(id); 203 return mStyleableValueToNameMap.get(mWrapper); 204 } 205 206 return null; 207 } 208 209 /** 210 * Returns the integer id of a resource given its type and name. 211 * <p/>If the resource is of type {@link ResourceType#ID} and does not exist in the 212 * internal map, then new id values are dynamically generated (and stored so that queries 213 * with the same names will return the same value). 214 */ getResourceId(ResourceType type, String name)215 public Integer getResourceId(ResourceType type, String name) { 216 Integer result = null; 217 if (mResourceValueMap != null) { 218 Map<String, Integer> map = mResourceValueMap.get(type); 219 if (map != null) { 220 result = map.get(name); 221 } 222 } 223 224 if (result == null) { 225 synchronized (mDynamicIdMap) { 226 result = mDynamicIdMap.getId(type, name); 227 } 228 } 229 230 return result; 231 } 232 233 /** 234 * Resets the list of dynamic Ids. This list is used by 235 * {@link #getResourceId(String, String)} when the resource query is an ID that doesn't 236 * exist (for example for ID automatically generated in layout files that are not saved yet.) 237 * <p/>This method resets those dynamic ID and must be called whenever the actual list of IDs 238 * change. 239 */ resetDynamicIds()240 public void resetDynamicIds() { 241 synchronized (mDynamicIdMap) { 242 mDynamicIdMap.reset(DYNAMIC_ID_SEED_START); 243 } 244 } 245 246 @Override 247 @NonNull createResourceItem(@onNull String name)248 protected ResourceItem createResourceItem(@NonNull String name) { 249 return new ResourceItem(name); 250 } 251 252 /** 253 * Sets compiled resource information. 254 * 255 * @param resIdValueToNameMap a map of compiled resource id to resource name. 256 * The map is acquired by the {@link ProjectResources} object. 257 * @param styleableValueMap a map of (int[], name) for the styleable information. The map is 258 * acquired by the {@link ProjectResources} object. 259 * @param resourceValueMap a map of (name, id) for resources of type {@link ResourceType#ID}. 260 * The list is acquired by the {@link ProjectResources} object. 261 */ setCompiledResources(Map<Integer, Pair<ResourceType, String>> resIdValueToNameMap, Map<IntArrayWrapper, String> styleableValueMap, Map<ResourceType, Map<String, Integer>> resourceValueMap)262 void setCompiledResources(Map<Integer, Pair<ResourceType, String>> resIdValueToNameMap, 263 Map<IntArrayWrapper, String> styleableValueMap, 264 Map<ResourceType, Map<String, Integer>> resourceValueMap) { 265 mResourceValueMap = resourceValueMap; 266 mResIdValueToNameMap = resIdValueToNameMap; 267 mStyleableValueToNameMap = styleableValueMap; 268 269 resetDynamicIds(); 270 } 271 } 272