Home | History | Annotate | Download | only in idegen
      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