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.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 
     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      */
     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
     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      */
    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      */
    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      */
    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      */
    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      */
    240     public void resetDynamicIds() {
    241         synchronized (mDynamicIdMap) {
    242             mDynamicIdMap.reset(DYNAMIC_ID_SEED_START);
    243         }
    244     }
    245 
    246     @Override
    247     @NonNull
    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      */
    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