1 /*
2  * Copyright (C) 2022 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.deprecated;
18 
19 
20 import com.android.SdkConstants;
21 import com.android.io.IAbstractFile;
22 import com.android.io.IAbstractFolder;
23 import com.android.resources.ResourceType;
24 import com.android.tools.layoutlib.annotations.NotNull;
25 import com.android.tools.layoutlib.annotations.Nullable;
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.InputStreamReader;
33 import java.io.Reader;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.EnumMap;
37 import java.util.List;
38 import java.util.Map;
39 
40 import com.google.common.base.Charsets;
41 
42 /**
43  * @deprecated This class is part of an obsolete resource repository system that is no longer used
44  *     in production code. The class is preserved temporarily for LayoutLib tests.
45  */
46 @Deprecated
47 public class FrameworkResources extends ResourceRepository {
48 
49     /**
50      * Map of {@link ResourceType} to list of items. It is guaranteed to contain a list for all
51      * possible values of ResourceType.
52      */
53     private final Map<ResourceType, List<ResourceItem>> mPublicResourceMap =
54         new EnumMap<>(ResourceType.class);
55 
FrameworkResources(@otNull TestFolderWrapper resFolder)56     public FrameworkResources(@NotNull TestFolderWrapper resFolder) {
57         super(resFolder, true /*isFrameworkRepository*/);
58     }
59 
60     @Override
61     @NotNull
createResourceItem(@otNull String name)62     protected ResourceItem createResourceItem(@NotNull String name) {
63         return new ResourceItem(name);
64     }
65 
66     /**
67      * Reads the public.xml file in data/res/values/ for a given resource folder and builds up
68      * a map of public resources.
69      *
70      * This map is a subset of the full resource map that only contains framework resources
71      * that are public.
72      *
73      * @param logger a logger to report issues to
74      */
loadPublicResources(@ullable ILogger logger)75     public void loadPublicResources(@Nullable ILogger logger) {
76         IAbstractFolder valueFolder = getResFolder().getFolder(SdkConstants.FD_RES_VALUES);
77         if (!valueFolder.exists()) {
78             return;
79         }
80 
81         IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); //$NON-NLS-1$
82         if (publicXmlFile.exists()) {
83             try (Reader reader = new BufferedReader(
84                     new InputStreamReader(publicXmlFile.getContents(), Charsets.UTF_8))) {
85                 KXmlParser parser = new KXmlParser();
86                 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
87                 parser.setInput(reader);
88 
89                 ResourceType lastType = null;
90                 String lastTypeName = "";
91                 while (true) {
92                     int event = parser.next();
93                     if (event == XmlPullParser.START_TAG) {
94                         // As of API 15 there are a number of "java-symbol" entries here
95                         if (!parser.getName().equals("public")) { //$NON-NLS-1$
96                             continue;
97                         }
98 
99                         String name = null;
100                         String typeName = null;
101                         for (int i = 0, n = parser.getAttributeCount(); i < n; i++) {
102                             String attribute = parser.getAttributeName(i);
103 
104                             if (attribute.equals("name")) { //$NON-NLS-1$
105                                 name = parser.getAttributeValue(i);
106                                 if (typeName != null) {
107                                     // Skip id attribute processing
108                                     break;
109                                 }
110                             } else if (attribute.equals("type")) { //$NON-NLS-1$
111                                 typeName = parser.getAttributeValue(i);
112                             }
113                         }
114 
115                         if (name != null && typeName != null) {
116                             ResourceType type;
117                             if (typeName.equals(lastTypeName)) {
118                                 type = lastType;
119                             } else {
120                                 type = ResourceType.fromXmlValue(typeName);
121                                 lastType = type;
122                                 lastTypeName = typeName;
123                             }
124                             if (type != null) {
125                                 ResourceItem match = null;
126                                 Map<String, ResourceItem> map = mResourceMap.get(type);
127                                 if (map != null) {
128                                     match = map.get(name);
129                                 }
130 
131                                 if (match != null) {
132                                     List<ResourceItem> publicList = mPublicResourceMap.get(type);
133                                     if (publicList == null) {
134                                         // Pick initial size for the list to hold the public
135                                         // resources. We could just use map.size() here,
136                                         // but they're usually much bigger; for example,
137                                         // in one platform version, there are 1500 drawables
138                                         // and 1200 strings but only 175 and 25 public ones
139                                         // respectively.
140                                         int size;
141                                         switch (type) {
142                                             case STYLE:
143                                                 size = 500;
144                                                 break;
145                                             case ATTR:
146                                                 size = 1050;
147                                                 break;
148                                             case DRAWABLE:
149                                                 size = 200;
150                                                 break;
151                                             case ID:
152                                                 size = 50;
153                                                 break;
154                                             case LAYOUT:
155                                             case COLOR:
156                                             case STRING:
157                                             case ANIM:
158                                             case INTERPOLATOR:
159                                                 size = 30;
160                                                 break;
161                                             default:
162                                                 size = 10;
163                                                 break;
164                                         }
165                                         publicList = new ArrayList<>(size);
166                                         mPublicResourceMap.put(type, publicList);
167                                     }
168 
169                                     publicList.add(match);
170                                 }
171                             }
172                         }
173                     } else if (event == XmlPullParser.END_DOCUMENT) {
174                         break;
175                     }
176                 }
177             } catch (Exception e) {
178                 if (logger != null) {
179                     logger.error(e, "Can't read and parse public attribute list");
180                 }
181             }
182         }
183 
184         // put unmodifiable list for all res type in the public resource map
185         // this will simplify access
186         for (ResourceType type : ResourceType.values()) {
187             List<ResourceItem> list = mPublicResourceMap.get(type);
188             if (list == null) {
189                 list = Collections.emptyList();
190             } else {
191                 list = Collections.unmodifiableList(list);
192             }
193 
194             // put the new list in the map
195             mPublicResourceMap.put(type, list);
196         }
197     }
198 }
199 
200