1 /* 2 * Copyright (C) 2007 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.annotations.VisibleForTesting; 20 import com.android.annotations.VisibleForTesting.Visibility; 21 import com.android.ide.common.resources.configuration.Configurable; 22 import com.android.ide.common.resources.configuration.FolderConfiguration; 23 import com.android.io.IAbstractFile; 24 import com.android.io.IAbstractFolder; 25 import com.android.resources.FolderTypeRelationship; 26 import com.android.resources.ResourceFolderType; 27 import com.android.resources.ResourceType; 28 29 import java.util.ArrayList; 30 import java.util.Collection; 31 import java.util.List; 32 33 /** 34 * Resource Folder class. Contains list of {@link ResourceFile}s, 35 * the {@link FolderConfiguration}, and a link to the {@link IAbstractFolder} object. 36 */ 37 public final class ResourceFolder implements Configurable { 38 final ResourceFolderType mType; 39 final FolderConfiguration mConfiguration; 40 IAbstractFolder mFolder; 41 ArrayList<ResourceFile> mFiles = null; 42 private final ResourceRepository mRepository; 43 44 45 /** 46 * Creates a new {@link ResourceFolder} 47 * @param type The type of the folder 48 * @param config The configuration of the folder 49 * @param folder The associated {@link IAbstractFolder} object. 50 * @param repository The associated {@link ResourceRepository} 51 */ 52 protected ResourceFolder(ResourceFolderType type, FolderConfiguration config, 53 IAbstractFolder folder, ResourceRepository repository) { 54 mType = type; 55 mConfiguration = config; 56 mFolder = folder; 57 mRepository = repository; 58 } 59 60 /** 61 * Processes a file and adds it to its parent folder resource. 62 * 63 * @param file the underlying resource file. 64 * @param kind the file change kind. 65 * @param context a context object with state for the current update, such 66 * as a place to stash errors encountered 67 * @return the {@link ResourceFile} that was created. 68 */ 69 public ResourceFile processFile(IAbstractFile file, ResourceDeltaKind kind, 70 ScanningContext context) { 71 // look for this file if it's already been created 72 ResourceFile resFile = getFile(file); 73 74 if (resFile == null) { 75 if (kind != ResourceDeltaKind.REMOVED) { 76 // create a ResourceFile for it. 77 78 // check if that's a single or multi resource type folder. For now we define this by 79 // the number of possible resource type output by files in the folder. 80 // We have a special case for layout/menu folders which can also generate IDs. 81 // This does 82 // not make the difference between several resource types from a single file or 83 // the ability to have 2 files in the same folder generating 2 different types of 84 // resource. The former is handled by MultiResourceFile properly while we don't 85 // handle the latter. If we were to add this behavior we'd have to change this call. 86 List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(mType); 87 88 if (types.size() == 1) { 89 resFile = new SingleResourceFile(file, this); 90 } else if (types.contains(ResourceType.LAYOUT)) { 91 resFile = new IdGeneratingResourceFile(file, this, ResourceType.LAYOUT); 92 } else if (types.contains(ResourceType.MENU)) { 93 resFile = new IdGeneratingResourceFile(file, this, ResourceType.MENU); 94 } else { 95 resFile = new MultiResourceFile(file, this); 96 } 97 98 resFile.load(context); 99 100 // add it to the folder 101 addFile(resFile); 102 } 103 } else { 104 if (kind == ResourceDeltaKind.REMOVED) { 105 removeFile(resFile, context); 106 } else { 107 resFile.update(context); 108 } 109 } 110 111 return resFile; 112 } 113 114 115 /** 116 * Adds a {@link ResourceFile} to the folder. 117 * @param file The {@link ResourceFile}. 118 */ 119 @VisibleForTesting(visibility=Visibility.PROTECTED) 120 public void addFile(ResourceFile file) { 121 if (mFiles == null) { 122 mFiles = new ArrayList<ResourceFile>(); 123 } 124 125 mFiles.add(file); 126 } 127 128 protected void removeFile(ResourceFile file, ScanningContext context) { 129 file.dispose(context); 130 mFiles.remove(file); 131 } 132 133 protected void dispose(ScanningContext context) { 134 if (mFiles != null) { 135 for (ResourceFile file : mFiles) { 136 file.dispose(context); 137 } 138 139 mFiles.clear(); 140 } 141 } 142 143 /** 144 * Returns the {@link IAbstractFolder} associated with this object. 145 */ 146 public IAbstractFolder getFolder() { 147 return mFolder; 148 } 149 150 /** 151 * Returns the {@link ResourceFolderType} of this object. 152 */ 153 public ResourceFolderType getType() { 154 return mType; 155 } 156 157 public ResourceRepository getRepository() { 158 return mRepository; 159 } 160 161 /** 162 * Returns the list of {@link ResourceType}s generated by the files inside this folder. 163 */ 164 public Collection<ResourceType> getResourceTypes() { 165 ArrayList<ResourceType> list = new ArrayList<ResourceType>(); 166 167 if (mFiles != null) { 168 for (ResourceFile file : mFiles) { 169 Collection<ResourceType> types = file.getResourceTypes(); 170 171 // loop through those and add them to the main list, 172 // if they are not already present 173 for (ResourceType resType : types) { 174 if (list.indexOf(resType) == -1) { 175 list.add(resType); 176 } 177 } 178 } 179 } 180 181 return list; 182 } 183 184 public FolderConfiguration getConfiguration() { 185 return mConfiguration; 186 } 187 188 /** 189 * Returns whether the folder contains a file with the given name. 190 * @param name the name of the file. 191 */ 192 public boolean hasFile(String name) { 193 return mFolder.hasFile(name); 194 } 195 196 /** 197 * Returns the {@link ResourceFile} matching a {@link IAbstractFile} object. 198 * @param file The {@link IAbstractFile} object. 199 * @return the {@link ResourceFile} or null if no match was found. 200 */ 201 private ResourceFile getFile(IAbstractFile file) { 202 if (mFiles != null) { 203 for (ResourceFile f : mFiles) { 204 if (f.getFile().equals(file)) { 205 return f; 206 } 207 } 208 } 209 return null; 210 } 211 212 /** 213 * Returns the {@link ResourceFile} matching a given name. 214 * @param filename The name of the file to return. 215 * @return the {@link ResourceFile} or <code>null</code> if no match was found. 216 */ 217 public ResourceFile getFile(String filename) { 218 if (mFiles != null) { 219 for (ResourceFile f : mFiles) { 220 if (f.getFile().getName().equals(filename)) { 221 return f; 222 } 223 } 224 } 225 return null; 226 } 227 228 /** 229 * Returns whether a file in the folder is generating a resource of a specified type. 230 * @param type The {@link ResourceType} being looked up. 231 */ 232 public boolean hasResources(ResourceType type) { 233 // Check if the folder type is able to generate resource of the type that was asked. 234 // this is a first check to avoid going through the files. 235 List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type); 236 237 boolean valid = false; 238 for (ResourceFolderType rft : folderTypes) { 239 if (rft == mType) { 240 valid = true; 241 break; 242 } 243 } 244 245 if (valid) { 246 if (mFiles != null) { 247 for (ResourceFile f : mFiles) { 248 if (f.hasResources(type)) { 249 return true; 250 } 251 } 252 } 253 } 254 return false; 255 } 256 257 @Override 258 public String toString() { 259 return mFolder.toString(); 260 } 261 } 262