• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.common.resources;
18 
19 
20 import com.android.SdkConstants;
21 import com.android.annotations.NonNull;
22 import com.android.annotations.Nullable;
23 import com.android.io.IAbstractFile;
24 import com.android.io.IAbstractFolder;
25 import com.android.resources.ResourceType;
26 import com.android.utils.ILogger;
27 
28 import org.kxml2.io.KXmlParser;
29 import org.xmlpull.v1.XmlPullParser;
30 
31 import java.io.BufferedReader;
32 import java.io.IOException;
33 import java.io.InputStreamReader;
34 import java.io.Reader;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.EnumMap;
39 import java.util.List;
40 import java.util.Map;
41 
42 /**
43  * Framework resources repository.
44  *
45  * This behaves the same as {@link ResourceRepository} except that it differentiates between
46  * resources that are public and non public.
47  * {@link #getResources(ResourceType)} and {@link #hasResourcesOfType(ResourceType)} only return
48  * public resources. This is typically used to display resource lists in the UI.
49  *
50  * {@link #getConfiguredResources(com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration)}
51  * returns all resources, even the non public ones so that this can be used for rendering.
52  */
53 public class FrameworkResources extends ResourceRepository {
54 
55     /**
56      * Map of {@link ResourceType} to list of items. It is guaranteed to contain a list for all
57      * possible values of ResourceType.
58      */
59     protected final Map<ResourceType, List<ResourceItem>> mPublicResourceMap =
60         new EnumMap<ResourceType, List<ResourceItem>>(ResourceType.class);
61 
FrameworkResources()62     public FrameworkResources() {
63         super(true /*isFrameworkRepository*/);
64     }
65 
66     /**
67      * Returns a {@link Collection} (always non null, but can be empty) of <b>public</b>
68      * {@link ResourceItem} matching a given {@link ResourceType}.
69      *
70      * @param type the type of the resources to return
71      * @return a collection of items, possible empty.
72      */
73     @Override
74     @NonNull
getResourceItemsOfType(@onNull ResourceType type)75     public List<ResourceItem> getResourceItemsOfType(@NonNull ResourceType type) {
76         return mPublicResourceMap.get(type);
77     }
78 
79     /**
80      * Returns whether the repository has <b>public</b> resources of a given {@link ResourceType}.
81      * @param type the type of resource to check.
82      * @return true if the repository contains resources of the given type, false otherwise.
83      */
84     @Override
hasResourcesOfType(@onNull ResourceType type)85     public boolean hasResourcesOfType(@NonNull ResourceType type) {
86         return mPublicResourceMap.get(type).size() > 0;
87     }
88 
89     @Override
90     @NonNull
createResourceItem(@onNull String name)91     protected ResourceItem createResourceItem(@NonNull String name) {
92         return new FrameworkResourceItem(name);
93     }
94 
95     /**
96      * Reads the public.xml file in data/res/values/ for a given resource folder and builds up
97      * a map of public resources.
98      *
99      * This map is a subset of the full resource map that only contains framework resources
100      * that are public.
101      *
102      * @param resFolder The root folder of the resources
103      * @param logger a logger to report issues to
104      */
loadPublicResources(@onNull IAbstractFolder resFolder, @Nullable ILogger logger)105     public void loadPublicResources(@NonNull IAbstractFolder resFolder, @Nullable ILogger logger) {
106         IAbstractFolder valueFolder = resFolder.getFolder(SdkConstants.FD_RES_VALUES);
107         if (valueFolder.exists() == false) {
108             return;
109         }
110 
111         IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); //$NON-NLS-1$
112         if (publicXmlFile.exists()) {
113             Reader reader = null;
114             try {
115                 reader = new BufferedReader(new InputStreamReader(publicXmlFile.getContents(),
116                         "UTF-8")); //$NON-NLS-1$
117                 KXmlParser parser = new KXmlParser();
118                 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
119                 parser.setInput(reader);
120 
121                 ResourceType lastType = null;
122                 String lastTypeName = "";
123                 while (true) {
124                     int event = parser.next();
125                     if (event == XmlPullParser.START_TAG) {
126                         // As of API 15 there are a number of "java-symbol" entries here
127                         if (!parser.getName().equals("public")) { //$NON-NLS-1$
128                             continue;
129                         }
130 
131                         String name = null;
132                         String typeName = null;
133                         for (int i = 0, n = parser.getAttributeCount(); i < n; i++) {
134                             String attribute = parser.getAttributeName(i);
135 
136                             if (attribute.equals("name")) { //$NON-NLS-1$
137                                 name = parser.getAttributeValue(i);
138                                 if (typeName != null) {
139                                     // Skip id attribute processing
140                                     break;
141                                 }
142                             } else if (attribute.equals("type")) { //$NON-NLS-1$
143                                 typeName = parser.getAttributeValue(i);
144                             }
145                         }
146 
147                         if (name != null && typeName != null) {
148                             ResourceType type = null;
149                             if (typeName.equals(lastTypeName)) {
150                                 type = lastType;
151                             } else {
152                                 type = ResourceType.getEnum(typeName);
153                                 lastType = type;
154                                 lastTypeName = typeName;
155                             }
156                             if (type != null) {
157                                 ResourceItem match = null;
158                                 Map<String, ResourceItem> map = mResourceMap.get(type);
159                                 if (map != null) {
160                                     match = map.get(name);
161                                 }
162 
163                                 if (match != null) {
164                                     List<ResourceItem> publicList = mPublicResourceMap.get(type);
165                                     if (publicList == null) {
166                                         // Pick initial size for the list to hold the public
167                                         // resources. We could just use map.size() here,
168                                         // but they're usually much bigger; for example,
169                                         // in one platform version, there are 1500 drawables
170                                         // and 1200 strings but only 175 and 25 public ones
171                                         // respectively.
172                                         int size;
173                                         switch (type) {
174                                             case STYLE: size = 500; break;
175                                             case ATTR: size = 1000; break;
176                                             case DRAWABLE: size = 200; break;
177                                             case ID: size = 50; break;
178                                             case LAYOUT:
179                                             case COLOR:
180                                             case STRING:
181                                             case ANIM:
182                                             case INTERPOLATOR:
183                                                 size = 30;
184                                                 break;
185                                             default:
186                                                 size = 10;
187                                                 break;
188                                         }
189                                         publicList = new ArrayList<ResourceItem>(size);
190                                         mPublicResourceMap.put(type, publicList);
191                                     }
192 
193                                     publicList.add(match);
194                                 } else {
195                                     // log that there's a public resource that doesn't actually
196                                     // exist?
197                                 }
198                             } else {
199                                 // log that there was a reference to a typo that doesn't actually
200                                 // exist?
201                             }
202                         }
203                     } else if (event == XmlPullParser.END_DOCUMENT) {
204                         break;
205                     }
206                 }
207             } catch (Exception e) {
208                 if (logger != null) {
209                     logger.error(e, "Can't read and parse public attribute list");
210                 }
211             } finally {
212                 if (reader != null) {
213                     try {
214                         reader.close();
215                     } catch (IOException e) {
216                         // Nothing to be done here - we don't care if it closed or not.
217                     }
218                 }
219             }
220         }
221 
222         // put unmodifiable list for all res type in the public resource map
223         // this will simplify access
224         for (ResourceType type : ResourceType.values()) {
225             List<ResourceItem> list = mPublicResourceMap.get(type);
226             if (list == null) {
227                 list = Collections.emptyList();
228             } else {
229                 list = Collections.unmodifiableList(list);
230             }
231 
232             // put the new list in the map
233             mPublicResourceMap.put(type, list);
234         }
235     }
236 }
237 
238