1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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 package com.android.ide.eclipse.adt.internal.wizards.templates; 17 18 import static com.android.SdkConstants.FD_EXTRAS; 19 import static com.android.SdkConstants.FD_TEMPLATES; 20 import static com.android.SdkConstants.FD_TOOLS; 21 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TEMPLATE_XML; 22 23 import com.android.annotations.NonNull; 24 import com.android.annotations.Nullable; 25 import com.android.ide.eclipse.adt.AdtPlugin; 26 import com.android.ide.eclipse.adt.AdtUtils; 27 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; 28 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; 29 import com.google.common.base.Charsets; 30 import com.google.common.collect.Maps; 31 import com.google.common.collect.Sets; 32 import com.google.common.io.Files; 33 34 import org.w3c.dom.Document; 35 36 import java.io.File; 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.Comparator; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Set; 44 45 /** Handles locating templates and providing template metadata */ 46 public class TemplateManager { 47 private static final Set<String> EXCLUDED_CATEGORIES = Sets.newHashSet("Folder", "Google"); 48 private static final Set<String> EXCLUDED_FORMFACTORS = Sets.newHashSet("Wear", "TV"); 49 TemplateManager()50 TemplateManager() { 51 } 52 53 /** @return the root folder containing templates */ 54 @Nullable getTemplateRootFolder()55 public static File getTemplateRootFolder() { 56 String location = AdtPrefs.getPrefs().getOsSdkFolder(); 57 if (location != null) { 58 File folder = new File(location, FD_TOOLS + File.separator + FD_TEMPLATES); 59 if (folder.isDirectory()) { 60 return folder; 61 } 62 } 63 64 return null; 65 } 66 67 /** @return the root folder containing extra templates */ 68 @NonNull getExtraTemplateRootFolders()69 public static List<File> getExtraTemplateRootFolders() { 70 List<File> folders = new ArrayList<File>(); 71 String location = AdtPrefs.getPrefs().getOsSdkFolder(); 72 if (location != null) { 73 File extras = new File(location, FD_EXTRAS); 74 if (extras.isDirectory()) { 75 for (File vendor : AdtUtils.listFiles(extras)) { 76 if (!vendor.isDirectory()) { 77 continue; 78 } 79 for (File pkg : AdtUtils.listFiles(vendor)) { 80 if (pkg.isDirectory()) { 81 File folder = new File(pkg, FD_TEMPLATES); 82 if (folder.isDirectory()) { 83 folders.add(folder); 84 } 85 } 86 } 87 } 88 89 // Legacy 90 File folder = new File(extras, FD_TEMPLATES); 91 if (folder.isDirectory()) { 92 folders.add(folder); 93 } 94 } 95 } 96 97 return folders; 98 } 99 100 /** 101 * Returns a template file under the given root, if it exists 102 * 103 * @param root the root folder 104 * @param relativePath the relative path 105 * @return a template file under the given root, if it exists 106 */ 107 @Nullable getTemplateLocation(@onNull File root, @NonNull String relativePath)108 public static File getTemplateLocation(@NonNull File root, @NonNull String relativePath) { 109 File templateRoot = getTemplateRootFolder(); 110 if (templateRoot != null) { 111 String rootPath = root.getPath(); 112 File templateFile = new File(templateRoot, 113 rootPath.replace('/', File.separatorChar) + File.separator 114 + relativePath.replace('/', File.separatorChar)); 115 if (templateFile.exists()) { 116 return templateFile; 117 } 118 } 119 120 return null; 121 } 122 123 /** 124 * Returns a template file under one of the available roots, if it exists 125 * 126 * @param relativePath the relative path 127 * @return a template file under one of the available roots, if it exists 128 */ 129 @Nullable getTemplateLocation(@onNull String relativePath)130 public static File getTemplateLocation(@NonNull String relativePath) { 131 File templateRoot = getTemplateRootFolder(); 132 if (templateRoot != null) { 133 File templateFile = new File(templateRoot, 134 relativePath.replace('/', File.separatorChar)); 135 if (templateFile.exists()) { 136 return templateFile; 137 } 138 } 139 140 return null; 141 142 } 143 144 /** 145 * Returns all the templates with the given prefix 146 * 147 * @param folder the folder prefix 148 * @return the available templates 149 */ 150 @NonNull getTemplates(@onNull String folder)151 List<File> getTemplates(@NonNull String folder) { 152 List<File> templates = new ArrayList<File>(); 153 Map<String, File> templateNames = Maps.newHashMap(); 154 File root = getTemplateRootFolder(); 155 if (root != null) { 156 File[] files = new File(root, folder).listFiles(); 157 if (files != null) { 158 for (File file : files) { 159 if (file.isDirectory()) { // Avoid .DS_Store etc 160 templates.add(file); 161 templateNames.put(file.getName(), file); 162 } 163 } 164 } 165 } 166 167 // Add in templates from extras/ as well. 168 for (File extra : getExtraTemplateRootFolders()) { 169 File[] files = new File(extra, folder).listFiles(); 170 if (files != null) { 171 for (File file : files) { 172 if (file.isDirectory()) { 173 File replaces = templateNames.get(file.getName()); 174 if (replaces != null) { 175 int compare = compareTemplates(replaces, file); 176 if (compare > 0) { 177 int index = templates.indexOf(replaces); 178 if (index != -1) { 179 templates.set(index, file); 180 } else { 181 templates.add(file); 182 } 183 } 184 } else { 185 templates.add(file); 186 } 187 } 188 } 189 } 190 } 191 192 // Sort by file name (not path as is File's default) 193 if (templates.size() > 1) { 194 Collections.sort(templates, new Comparator<File>() { 195 @Override 196 public int compare(File file1, File file2) { 197 return file1.getName().compareTo(file2.getName()); 198 } 199 }); 200 } 201 202 return templates; 203 } 204 205 /** 206 * Compare two files, and return the one with the HIGHEST revision, and if 207 * the same, most recently modified 208 */ compareTemplates(File file1, File file2)209 private int compareTemplates(File file1, File file2) { 210 TemplateMetadata template1 = getTemplate(file1); 211 TemplateMetadata template2 = getTemplate(file2); 212 213 if (template1 == null) { 214 return 1; 215 } else if (template2 == null) { 216 return -1; 217 } else { 218 int delta = template2.getRevision() - template1.getRevision(); 219 if (delta == 0) { 220 delta = (int) (file2.lastModified() - file1.lastModified()); 221 } 222 return delta; 223 } 224 } 225 226 /** Cache for {@link #getTemplate()} */ 227 private Map<File, TemplateMetadata> mTemplateMap; 228 229 @Nullable getTemplate(File templateDir)230 TemplateMetadata getTemplate(File templateDir) { 231 if (mTemplateMap != null) { 232 TemplateMetadata metadata = mTemplateMap.get(templateDir); 233 if (metadata != null) { 234 return metadata; 235 } 236 } else { 237 mTemplateMap = Maps.newHashMap(); 238 } 239 240 try { 241 File templateFile = new File(templateDir, TEMPLATE_XML); 242 if (templateFile.isFile()) { 243 String xml = Files.toString(templateFile, Charsets.UTF_8); 244 Document doc = DomUtilities.parseDocument(xml, true); 245 if (doc != null && doc.getDocumentElement() != null) { 246 TemplateMetadata metadata = new TemplateMetadata(doc); 247 if (EXCLUDED_CATEGORIES.contains(metadata.getCategory()) || 248 EXCLUDED_FORMFACTORS.contains(metadata.getFormFactor())) { 249 return null; 250 } 251 mTemplateMap.put(templateDir, metadata); 252 return metadata; 253 } 254 } 255 } catch (IOException e) { 256 AdtPlugin.log(e, null); 257 } 258 259 return null; 260 } 261 } 262