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