Home | History | Annotate | Download | only in manager
      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