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