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 com.android.ide.common.rendering.api.DensityBasedResourceValue; 20 import com.android.ide.common.rendering.api.ResourceValue; 21 import com.android.ide.common.resources.ValueResourceParser.IValueResourceRepository; 22 import com.android.ide.common.resources.configuration.DensityQualifier; 23 import com.android.io.IAbstractFile; 24 import com.android.io.StreamException; 25 import com.android.resources.ResourceType; 26 27 import java.io.IOException; 28 import java.util.Collection; 29 import java.util.HashMap; 30 import java.util.HashSet; 31 import java.util.Map; 32 import java.util.Set; 33 34 /** 35 * Represents a resource file that also generates ID resources. 36 * <p/> 37 * This is typically an XML file in res/layout or res/menu 38 */ 39 public final class IdGeneratingResourceFile extends ResourceFile 40 implements IValueResourceRepository { 41 42 private final Map<String, ResourceValue> mIdResources = 43 new HashMap<String, ResourceValue>(); 44 45 private final Collection<ResourceType> mResourceTypeList; 46 47 private final String mFileName; 48 49 private final ResourceType mFileType; 50 51 private final ResourceValue mFileValue; 52 IdGeneratingResourceFile(IAbstractFile file, ResourceFolder folder, ResourceType type)53 public IdGeneratingResourceFile(IAbstractFile file, ResourceFolder folder, ResourceType type) { 54 super(file, folder); 55 56 mFileType = type; 57 58 // Set up our resource types 59 mResourceTypeList = new HashSet<ResourceType>(); 60 mResourceTypeList.add(mFileType); 61 mResourceTypeList.add(ResourceType.ID); 62 63 // compute the resource name 64 mFileName = getFileName(type); 65 66 // Get the resource value of this file as a whole layout 67 mFileValue = getFileValue(file, folder); 68 } 69 70 @Override load(ScanningContext context)71 protected void load(ScanningContext context) { 72 // Parse the file and look for @+id/ entries 73 parseFileForIds(context); 74 75 // create the resource items in the repository 76 updateResourceItems(context); 77 } 78 79 @Override update(ScanningContext context)80 protected void update(ScanningContext context) { 81 // Copy the previous list of ID names 82 Set<String> oldIdNames = new HashSet<String>(mIdResources.keySet()); 83 84 // reset current content. 85 mIdResources.clear(); 86 87 // need to parse the file and find the IDs. 88 if (!parseFileForIds(context)) { 89 context.requestFullAapt(); 90 return; 91 } 92 93 // We only need to update the repository if our IDs have changed 94 Set<String> keySet = mIdResources.keySet(); 95 assert keySet != oldIdNames; 96 if (oldIdNames.equals(keySet) == false) { 97 updateResourceItems(context); 98 } 99 } 100 101 @Override dispose(ScanningContext context)102 protected void dispose(ScanningContext context) { 103 ResourceRepository repository = getRepository(); 104 105 // Remove declarations from this file from the repository 106 repository.removeFile(mResourceTypeList, this); 107 108 // Ask for an ID refresh since we'll be taking away ID generating items 109 context.requestFullAapt(); 110 } 111 112 @Override getResourceTypes()113 public Collection<ResourceType> getResourceTypes() { 114 return mResourceTypeList; 115 } 116 117 @Override hasResources(ResourceType type)118 public boolean hasResources(ResourceType type) { 119 return (type == mFileType) || (type == ResourceType.ID && !mIdResources.isEmpty()); 120 } 121 122 @Override getValue(ResourceType type, String name)123 public ResourceValue getValue(ResourceType type, String name) { 124 // Check to see if they're asking for one of the right types: 125 if (type != mFileType && type != ResourceType.ID) { 126 return null; 127 } 128 129 // If they're looking for a resource of this type with this name give them the whole file 130 if (type == mFileType && name.equals(mFileName)) { 131 return mFileValue; 132 } else { 133 // Otherwise try to return them an ID 134 // the map will return null if it's not found 135 return mIdResources.get(name); 136 } 137 } 138 139 /** 140 * Looks through the file represented for Ids and adds them to 141 * our id repository 142 * 143 * @return true if parsing succeeds and false if it fails 144 */ parseFileForIds(ScanningContext context)145 private boolean parseFileForIds(ScanningContext context) { 146 IdResourceParser parser = new IdResourceParser(this, context, isFramework()); 147 try { 148 IAbstractFile file = getFile(); 149 return parser.parse(mFileType, file.getOsLocation(), file.getContents()); 150 } catch (IOException e) { 151 // Pass 152 } catch (StreamException e) { 153 // Pass 154 } 155 156 return false; 157 } 158 159 /** 160 * Add the resources represented by this file to the repository 161 */ updateResourceItems(ScanningContext context)162 private void updateResourceItems(ScanningContext context) { 163 ResourceRepository repository = getRepository(); 164 165 // remove this file from all existing ResourceItem. 166 repository.removeFile(mResourceTypeList, this); 167 168 // First add this as a layout file 169 ResourceItem item = repository.getResourceItem(mFileType, mFileName); 170 item.add(this); 171 172 // Now iterate through our IDs and add 173 for (String idName : mIdResources.keySet()) { 174 item = repository.getResourceItem(ResourceType.ID, idName); 175 // add this file to the list of files generating ID resources. 176 item.add(this); 177 } 178 179 // Ask the repository for an ID refresh 180 context.requestFullAapt(); 181 } 182 183 /** 184 * Returns the resource value associated with this whole file as a layout resource 185 * @param file the file handler that represents this file 186 * @param folder the folder this file is under 187 * @return a resource value associated with this layout 188 */ getFileValue(IAbstractFile file, ResourceFolder folder)189 private ResourceValue getFileValue(IAbstractFile file, ResourceFolder folder) { 190 // test if there's a density qualifier associated with the resource 191 DensityQualifier qualifier = folder.getConfiguration().getDensityQualifier(); 192 193 ResourceValue value; 194 if (qualifier == null) { 195 value = new ResourceValue(mFileType, mFileName, 196 file.getOsLocation(), isFramework()); 197 } else { 198 value = new DensityBasedResourceValue( 199 mFileType, mFileName, 200 file.getOsLocation(), 201 qualifier.getValue(), 202 isFramework()); 203 } 204 return value; 205 } 206 207 208 /** 209 * Returns the name of this resource. 210 */ getFileName(ResourceType type)211 private String getFileName(ResourceType type) { 212 // get the name from the filename. 213 String name = getFile().getName(); 214 215 int pos = name.indexOf('.'); 216 if (pos != -1) { 217 name = name.substring(0, pos); 218 } 219 220 return name; 221 } 222 addResourceValue(ResourceValue value)223 public void addResourceValue(ResourceValue value) { 224 // Just overwrite collisions. We're only interested in the unique 225 // IDs declared 226 mIdResources.put(value.getName(), value); 227 } 228 } 229