1 /* 2 * Copyright (C) 2012 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.idegen; 18 19 import com.google.common.base.Preconditions; 20 import com.google.common.base.Predicate; 21 import com.google.common.collect.Collections2; 22 import com.google.common.collect.ImmutableList; 23 import com.google.common.collect.Sets; 24 25 import java.io.File; 26 import java.io.FileNotFoundException; 27 import java.io.FilenameFilter; 28 import java.io.IOException; 29 import java.net.URISyntaxException; 30 import java.util.Collection; 31 import java.util.HashSet; 32 import java.util.logging.Level; 33 import java.util.logging.Logger; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 36 37 /** 38 * Find directories utility. 39 */ 40 public class DirectorySearch { 41 42 private static final Logger logger = Logger.getLogger(DirectorySearch.class.getName()); 43 44 public static final HashSet<String> SOURCE_DIRS = Sets.newHashSet(); 45 46 static { 47 SOURCE_DIRS.add("src"); 48 SOURCE_DIRS.add("java"); 49 } 50 51 private static final Pattern EXCLUDE_PATTERN = Pattern.compile("values-..(-.*)*"); 52 53 private static File repoRoot = null; 54 public static final String REL_TEMPLATE_DIR = "templates"; 55 public static final String REL_TEMPLATE_PATH_FROM_ROOT = "development/tools/idegen/" 56 + REL_TEMPLATE_DIR; 57 58 /** 59 * Returns the previously initialized repo root. 60 */ 61 public static File getRepoRoot() { 62 Preconditions.checkNotNull(repoRoot, "repoRoot has not been initialized yet. Call " 63 + "findAndInitRepoRoot() first."); 64 return repoRoot; 65 } 66 67 /** 68 * Find the repo root. This is the root branch directory of a full repo checkout. 69 * 70 * @param file any file inside the root. 71 * @return the root directory. 72 */ 73 public static void findAndInitRepoRoot(File file) { 74 Preconditions.checkNotNull(file); 75 if (repoRoot != null) { 76 return; 77 } 78 79 if (file.isDirectory()) { 80 File[] files = file.listFiles(new FilenameFilter() { 81 @Override 82 public boolean accept(File dir, String name) { 83 return ".repo".equals(name); 84 } 85 }); 86 if (files.length > 0) { 87 repoRoot = file; 88 } 89 } 90 File parent = file.getParentFile(); 91 if (parent == null) { 92 throw new IllegalStateException("Repo root not found from starting point " + 93 file.getPath()); 94 } 95 findAndInitRepoRoot(parent); 96 } 97 98 /** 99 * Searches up the parent chain to find the closes module root directory. A module root is one 100 * with an Android.mk file in it. <p> For example, the module root for directory 101 * <code>package/apps/Contacts/src</code> is <code>packages/apps/Contacts</code> 102 * 103 * @return the module root. 104 * @throws IOException when module root is not found. 105 */ 106 public static File findModuleRoot(File path) throws IOException { 107 Preconditions.checkNotNull(path); 108 File dir; 109 if (path.isFile()) { 110 dir = path.getParentFile(); 111 } else { 112 dir = path; 113 } 114 while (dir != null) { 115 File makeFile = new File(dir, "Android.mk"); 116 if (makeFile.exists()) { 117 return dir; 118 } else { 119 dir = dir.getParentFile(); 120 } 121 } 122 // At this point, there are no parents and we have not found a module. Error. 123 throw new IOException("Module root not found for path " + path.getCanonicalPath()); 124 } 125 126 /** 127 * Find all source directories from a given root file. 128 * 129 * If the root file is a file, the directory of that file will be used as the starting 130 * location. 131 * 132 * @param file The starting location. Can be a file or directory. 133 * @return List of 134 */ 135 public static ImmutableList<File> findSourceDirs(File file) { 136 Preconditions.checkNotNull(file); 137 if (!file.exists()) { 138 return ImmutableList.of(); 139 } 140 if (!file.isDirectory()) { 141 file = file.getParentFile(); 142 } 143 ImmutableList.Builder<File> builder = ImmutableList.builder(); 144 File[] children = file.listFiles(); 145 for (File child : children) { 146 if (child.isDirectory()) { 147 // Recurse further down the tree first to cover case of: 148 // 149 // src/java 150 // or 151 // java/src 152 // or 153 // src/main/java/java 154 // 155 // In first two cases, we don't want the parent. 156 // In third case we want the parent of the last "java", the last "java" is actually 157 // part of namespace. 158 ImmutableList<File> dirs = findSourceDirs(child); 159 // filter out the third case. 160 Collection<File> filteredDirs = Collections2.filter(dirs, new Predicate<File>() { 161 @Override 162 public boolean apply(File input) { 163 return !input.getAbsolutePath().endsWith("java/java"); 164 } 165 }); 166 if (filteredDirs.isEmpty()) { 167 if (SOURCE_DIRS.contains(child.getName())) { 168 builder.add(child); 169 } 170 } else { 171 builder.addAll(filteredDirs); 172 } 173 } 174 } 175 176 return builder.build(); 177 } 178 179 public static ImmutableList<File> findExcludeDirs(File file) { 180 Preconditions.checkNotNull(file); 181 if (!file.exists()) { 182 return ImmutableList.of(); 183 } 184 if (!file.isDirectory()) { 185 file = file.getParentFile(); 186 } 187 ImmutableList.Builder<File> builder = ImmutableList.builder(); 188 // Go into the res folder 189 File resFile = new File(file, "res"); 190 if (resFile.exists()) { 191 192 File[] children = resFile.listFiles(); 193 for (File child : children) { 194 if (child.isDirectory()) { 195 Matcher matcher = EXCLUDE_PATTERN.matcher(child.getName()); 196 if (matcher.matches()) { 197 // Exclude internationalization language folders. 198 // ex: values-zh 199 // But don't exclude values-land. Assume all language folders are two 200 // letters. 201 builder.add(child); 202 } 203 } 204 } 205 } 206 207 return builder.build(); 208 } 209 210 private static File templateDirCurrent = null; 211 private static File templateDirRoot = null; 212 213 public static File findTemplateDir() throws IOException { 214 // Cache optimization. 215 if (templateDirCurrent != null && templateDirCurrent.exists()) { 216 return templateDirCurrent; 217 } 218 if (templateDirRoot != null && templateDirRoot.exists()) { 219 return templateDirRoot; 220 } 221 222 File currentDir = null; 223 try { 224 currentDir = new File( 225 IntellijProject.class.getProtectionDomain().getCodeSource().getLocation() 226 .toURI().getPath()).getParentFile(); 227 } catch (URISyntaxException e) { 228 logger.log(Level.SEVERE, "Could not get jar location.", e); 229 return null; 230 } 231 // Support for program execution in intellij. 232 if (currentDir.getPath().endsWith("out/production")) { 233 return new File(currentDir.getParentFile().getParentFile(), REL_TEMPLATE_DIR); 234 } 235 // First check relative to current run directory. 236 templateDirCurrent = new File(currentDir, REL_TEMPLATE_DIR); 237 if (templateDirCurrent.exists()) { 238 return templateDirCurrent; 239 } else { 240 // Then check relative to root directory. 241 templateDirRoot = new File(repoRoot, REL_TEMPLATE_PATH_FROM_ROOT); 242 if (templateDirRoot.exists()) { 243 return templateDirRoot; 244 } 245 } 246 throw new FileNotFoundException( 247 "Unable to find template dir. Tried the following locations:\n" + 248 templateDirCurrent.getCanonicalPath() + "\n" + 249 templateDirRoot.getCanonicalPath()); 250 } 251 } 252