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 import static com.android.AndroidConstants.FD_RES_VALUES; 20 21 import com.android.ide.common.log.ILogger; 22 import com.android.io.IAbstractFile; 23 import com.android.io.IAbstractFolder; 24 import com.android.resources.ResourceType; 25 26 import org.w3c.dom.Document; 27 import org.w3c.dom.Element; 28 import org.w3c.dom.Node; 29 import org.w3c.dom.NodeList; 30 import org.xml.sax.InputSource; 31 32 import java.io.BufferedReader; 33 import java.io.IOException; 34 import java.io.InputStreamReader; 35 import java.io.Reader; 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.Collections; 39 import java.util.EnumMap; 40 import java.util.List; 41 import java.util.Map; 42 43 import javax.xml.parsers.DocumentBuilder; 44 import javax.xml.parsers.DocumentBuilderFactory; 45 46 /** 47 * Framework resources repository. 48 * 49 * This behaves the same as {@link ResourceRepository} except that it differentiates between 50 * resources that are public and non public. 51 * {@link #getResources(ResourceType)} and {@link #hasResourcesOfType(ResourceType)} only return 52 * public resources. This is typically used to display resource lists in the UI. 53 * 54 * {@link #getConfiguredResources(com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration)} 55 * returns all resources, even the non public ones so that this can be used for rendering. 56 */ 57 public class FrameworkResources extends ResourceRepository { 58 59 /** 60 * Map of {@link ResourceType} to list of items. It is guaranteed to contain a list for all 61 * possible values of ResourceType. 62 */ 63 protected final Map<ResourceType, List<ResourceItem>> mPublicResourceMap = 64 new EnumMap<ResourceType, List<ResourceItem>>(ResourceType.class); 65 66 public FrameworkResources() { 67 super(true /*isFrameworkRepository*/); 68 } 69 70 /** 71 * Returns a {@link Collection} (always non null, but can be empty) of <b>public</b> 72 * {@link ResourceItem} matching a given {@link ResourceType}. 73 * 74 * @param type the type of the resources to return 75 * @return a collection of items, possible empty. 76 */ 77 @Override 78 public List<ResourceItem> getResourceItemsOfType(ResourceType type) { 79 return mPublicResourceMap.get(type); 80 } 81 82 /** 83 * Returns whether the repository has <b>public</b> resources of a given {@link ResourceType}. 84 * @param type the type of resource to check. 85 * @return true if the repository contains resources of the given type, false otherwise. 86 */ 87 @Override 88 public boolean hasResourcesOfType(ResourceType type) { 89 return mPublicResourceMap.get(type).size() > 0; 90 } 91 92 @Override 93 protected ResourceItem createResourceItem(String name) { 94 return new FrameworkResourceItem(name); 95 } 96 97 /** 98 * Reads the public.xml file in data/res/values/ for a given resource folder and builds up 99 * a map of public resources. 100 * 101 * This map is a subset of the full resource map that only contains framework resources 102 * that are public. 103 * 104 * @param osFrameworkResourcePath The root folder of the resources 105 */ 106 public void loadPublicResources(IAbstractFolder resFolder, ILogger logger) { 107 IAbstractFolder valueFolder = resFolder.getFolder(FD_RES_VALUES); 108 if (valueFolder.exists() == false) { 109 return; 110 } 111 112 IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); //$NON-NLS-1$ 113 if (publicXmlFile.exists()) { 114 Document document = null; 115 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 116 Reader reader = null; 117 try { 118 reader = new BufferedReader(new InputStreamReader(publicXmlFile.getContents())); 119 InputSource is = new InputSource(reader); 120 factory.setNamespaceAware(true); 121 factory.setValidating(false); 122 DocumentBuilder builder = factory.newDocumentBuilder(); 123 document = builder.parse(is); 124 125 ResourceType lastType = null; 126 String lastTypeName = ""; 127 128 NodeList children = document.getDocumentElement().getChildNodes(); 129 for (int i = 0, n = children.getLength(); i < n; i++) { 130 Node node = children.item(i); 131 if (node.getNodeType() == Node.ELEMENT_NODE) { 132 Element element = (Element) node; 133 String name = element.getAttribute("name"); //$NON-NLS-1$ 134 if (name.length() > 0) { 135 String typeName = element.getAttribute("type"); //$NON-NLS-1$ 136 ResourceType type = null; 137 if (typeName.equals(lastTypeName)) { 138 type = lastType; 139 } else { 140 type = ResourceType.getEnum(typeName); 141 lastType = type; 142 lastTypeName = typeName; 143 } 144 if (type != null) { 145 List<ResourceItem> typeList = mResourceMap.get(type); 146 147 ResourceItem match = null; 148 if (typeList != null) { 149 for (ResourceItem item : typeList) { 150 if (name.equals(item.getName())) { 151 match = item; 152 break; 153 } 154 } 155 } 156 157 if (match != null) { 158 List<ResourceItem> publicList = mPublicResourceMap.get(type); 159 if (publicList == null) { 160 publicList = new ArrayList<ResourceItem>(); 161 mPublicResourceMap.put(type, publicList); 162 } 163 164 publicList.add(match); 165 } else { 166 // log that there's a public resource that doesn't actually 167 // exist? 168 } 169 } 170 } 171 } 172 } 173 } catch (Exception e) { 174 if (logger != null) { 175 logger.error(e, "Can't read and parse public attribute list"); 176 } 177 } finally { 178 if (reader != null) { 179 try { 180 reader.close(); 181 } catch (IOException e) { 182 // Nothing to be done here - we don't care if it closed or not. 183 } 184 } 185 } 186 } 187 188 // put unmodifiable list for all res type in the public resource map 189 // this will simplify access 190 for (ResourceType type : ResourceType.values()) { 191 List<ResourceItem> list = mPublicResourceMap.get(type); 192 if (list == null) { 193 list = Collections.emptyList(); 194 } else { 195 list = Collections.unmodifiableList(list); 196 } 197 198 // put the new list in the map 199 mPublicResourceMap.put(type, list); 200 } 201 } 202 } 203