• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
ProjectResources(IProject project)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
getConfiguredResources( FolderConfiguration referenceConfig)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      */
getResourceFolder(IFolder folder)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      */
resolveResourceId(int id)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      */
resolveStyleable(int[] id)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      */
getResourceId(ResourceType type, String name)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      */
resetDynamicIds()239     public void resetDynamicIds() {
240         synchronized (mDynamicIds) {
241             mDynamicIds.clear();
242             mRevDynamicIds.clear();
243             mDynamicSeed = DYNAMIC_ID_SEED_START;
244         }
245     }
246 
247     @Override
createResourceItem(String name)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      */
getDynamicId(String name)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      */
setCompiledResources(Map<Integer, Pair<ResourceType, String>> resIdValueToNameMap, Map<IntArrayWrapper, String> styleableValueMap, Map<ResourceType, Map<String, Integer>> resourceValueMap)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