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