Home | History | Annotate | Download | only in project
      1 /*
      2  * Copyright (C) 2008 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.sdklib.internal.project;
     18 
     19 import com.android.sdklib.SdkConstants;
     20 import com.android.sdklib.SdkManager;
     21 
     22 import java.io.File;
     23 import java.io.FileOutputStream;
     24 import java.io.IOException;
     25 import java.io.OutputStreamWriter;
     26 import java.util.HashMap;
     27 import java.util.HashSet;
     28 import java.util.Map;
     29 import java.util.Set;
     30 import java.util.Map.Entry;
     31 
     32 /**
     33  * Class to load and save project properties for both ADT and Ant-based build.
     34  *
     35  */
     36 public final class ProjectProperties {
     37     /** The property name for the project target */
     38     public final static String PROPERTY_TARGET = "target";
     39 
     40     public final static String PROPERTY_LIBRARY = "android.library";
     41     public final static String PROPERTY_LIB_REF = "android.library.reference.";
     42 
     43     public final static String PROPERTY_SDK = "sdk.dir";
     44     // LEGACY - compatibility with 1.6 and before
     45     public final static String PROPERTY_SDK_LEGACY = "sdk-location";
     46 
     47     public final static String PROPERTY_APP_PACKAGE = "application.package";
     48     // LEGACY - compatibility with 1.6 and before
     49     public final static String PROPERTY_APP_PACKAGE_LEGACY = "application-package";
     50 
     51     public final static String PROPERTY_SPLIT_BY_DENSITY = "split.density";
     52 
     53     public final static String PROPERTY_TESTED_PROJECT = "tested.project.dir";
     54 
     55     public final static String PROPERTY_BUILD_SOURCE_DIR = "source.dir";
     56 
     57     public static enum PropertyType {
     58         BUILD("build.properties", BUILD_HEADER),
     59         DEFAULT(SdkConstants.FN_DEFAULT_PROPERTIES, DEFAULT_HEADER),
     60         LOCAL("local.properties", LOCAL_HEADER);
     61 
     62         private final String mFilename;
     63         private final String mHeader;
     64 
     65         PropertyType(String filename, String header) {
     66             mFilename = filename;
     67             mHeader = header;
     68         }
     69 
     70         public String getFilename() {
     71             return mFilename;
     72         }
     73     }
     74 
     75     private final static String LOCAL_HEADER =
     76 //           1-------10--------20--------30--------40--------50--------60--------70--------80
     77             "# This file is automatically generated by Android Tools.\n" +
     78             "# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
     79             "# \n" +
     80             "# This file must *NOT* be checked in Version Control Systems,\n" +
     81             "# as it contains information specific to your local configuration.\n" +
     82             "\n";
     83 
     84     private final static String DEFAULT_HEADER =
     85 //          1-------10--------20--------30--------40--------50--------60--------70--------80
     86            "# This file is automatically generated by Android Tools.\n" +
     87            "# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
     88            "# \n" +
     89            "# This file must be checked in Version Control Systems.\n" +
     90            "# \n" +
     91            "# To customize properties used by the Ant build system use,\n" +
     92            "# \"build.properties\", and override values to adapt the script to your\n" +
     93            "# project structure.\n" +
     94            "\n";
     95 
     96     private final static String BUILD_HEADER =
     97 //          1-------10--------20--------30--------40--------50--------60--------70--------80
     98            "# This file is used to override default values used by the Ant build system.\n" +
     99            "# \n" +
    100            "# This file must be checked in Version Control Systems, as it is\n" +
    101            "# integral to the build system of your project.\n" +
    102            "\n" +
    103            "# This file is only used by the Ant script.\n" +
    104            "\n" +
    105            "# You can use this to override default values such as\n" +
    106            "#  'source.dir' for the location of your java source folder and\n" +
    107            "#  'out.dir' for the location of your output folder.\n" +
    108            "\n" +
    109            "# You can also use it define how the release builds are signed by declaring\n" +
    110            "# the following properties:\n" +
    111            "#  'key.store' for the location of your keystore and\n" +
    112            "#  'key.alias' for the name of the key to use.\n" +
    113            "# The password will be asked during the build when you use the 'release' target.\n" +
    114            "\n";
    115 
    116     private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>();
    117     static {
    118 //               1-------10--------20--------30--------40--------50--------60--------70--------80
    119         COMMENT_MAP.put(PROPERTY_TARGET,
    120                 "# Project target.\n");
    121         COMMENT_MAP.put(PROPERTY_SPLIT_BY_DENSITY,
    122                 "# Indicates whether an apk should be generated for each density.\n");
    123         COMMENT_MAP.put(PROPERTY_SDK,
    124                 "# location of the SDK. This is only used by Ant\n" +
    125                 "# For customization when using a Version Control System, please read the\n" +
    126                 "# header note.\n");
    127         COMMENT_MAP.put(PROPERTY_APP_PACKAGE,
    128                 "# The name of your application package as defined in the manifest.\n" +
    129                 "# Used by the 'uninstall' rule.\n");
    130     }
    131 
    132     private final String mProjectFolderOsPath;
    133     private final Map<String, String> mProperties;
    134     private final PropertyType mType;
    135 
    136     /**
    137      * Loads a project properties file and return a {@link ProjectProperties} object
    138      * containing the properties
    139      *
    140      * @param projectFolderOsPath the project folder.
    141      * @param type One the possible {@link PropertyType}s.
    142      */
    143     public static ProjectProperties load(String projectFolderOsPath, PropertyType type) {
    144         File projectFolder = new File(projectFolderOsPath);
    145         if (projectFolder.isDirectory()) {
    146             File defaultFile = new File(projectFolder, type.mFilename);
    147             if (defaultFile.isFile()) {
    148                 Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
    149                 if (map != null) {
    150                     return new ProjectProperties(projectFolderOsPath, map, type);
    151                 }
    152             }
    153         }
    154         return null;
    155     }
    156 
    157     /**
    158      * Merges all properties from the given file into the current properties.
    159      * <p/>
    160      * This emulates the Ant behavior: existing properties are <em>not</em> overriden.
    161      * Only new undefined properties become defined.
    162      * <p/>
    163      * Typical usage:
    164      * <ul>
    165      * <li>Create a ProjectProperties with {@link PropertyType#BUILD}
    166      * <li>Merge in values using {@link PropertyType#DEFAULT}
    167      * <li>The result is that this contains all the properties from default plus those
    168      *     overridden by the build.properties file.
    169      * </ul>
    170      *
    171      * @param type One the possible {@link PropertyType}s.
    172      * @return this object, for chaining.
    173      */
    174     public synchronized ProjectProperties merge(PropertyType type) {
    175         File projectFolder = new File(mProjectFolderOsPath);
    176         if (projectFolder.isDirectory()) {
    177             File defaultFile = new File(projectFolder, type.mFilename);
    178             if (defaultFile.isFile()) {
    179                 Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
    180                 if (map != null) {
    181                     for(Entry<String, String> entry : map.entrySet()) {
    182                         String key = entry.getKey();
    183                         String value = entry.getValue();
    184                         if (!mProperties.containsKey(key) && value != null) {
    185                             mProperties.put(key, value);
    186                         }
    187                     }
    188                 }
    189             }
    190         }
    191         return this;
    192     }
    193 
    194     /**
    195      * Creates a new project properties object, with no properties.
    196      * <p/>The file is not created until {@link #save()} is called.
    197      * @param projectFolderOsPath the project folder.
    198      * @param type
    199      */
    200     public static ProjectProperties create(String projectFolderOsPath, PropertyType type) {
    201         // create and return a ProjectProperties with an empty map.
    202         return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>(), type);
    203     }
    204 
    205     /**
    206      * Returns the type of the property file.
    207      *
    208      * @see PropertyType
    209      */
    210     public PropertyType getType() {
    211         return mType;
    212     }
    213 
    214     /**
    215      * Sets a new properties. If a property with the same name already exists, it is replaced.
    216      * @param name the name of the property.
    217      * @param value the value of the property.
    218      */
    219     public synchronized void setProperty(String name, String value) {
    220         mProperties.put(name, value);
    221     }
    222 
    223     /**
    224      * Returns the value of a property.
    225      * @param name the name of the property.
    226      * @return the property value or null if the property is not set.
    227      */
    228     public synchronized String getProperty(String name) {
    229         return mProperties.get(name);
    230     }
    231 
    232     /**
    233      * Removes a property and returns its previous value (or null if the property did not exist).
    234      * @param name the name of the property to remove.
    235      */
    236     public synchronized String removeProperty(String name) {
    237         return mProperties.remove(name);
    238     }
    239 
    240     /**
    241      * Returns a set of the property keys. Unlike {@link Map#keySet()} this is not a view of the
    242      * map keys. Modifying the returned {@link Set} will not impact the underlying {@link Map}.
    243      */
    244     public synchronized Set<String> keySet() {
    245         return new HashSet<String>(mProperties.keySet());
    246     }
    247 
    248     /**
    249      * Reloads the properties from the underlying file.
    250      */
    251     public synchronized void reload() {
    252         File projectFolder = new File(mProjectFolderOsPath);
    253         if (projectFolder.isDirectory()) {
    254             File defaultFile = new File(projectFolder, mType.mFilename);
    255             if (defaultFile.isFile()) {
    256                 Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
    257                 if (map != null) {
    258                     mProperties.clear();
    259                     mProperties.putAll(map);
    260                 }
    261             }
    262         }
    263     }
    264 
    265     /**
    266      * Saves the property file, using UTF-8 encoding.
    267      * @throws IOException
    268      */
    269     public synchronized void save() throws IOException {
    270         File toSave = new File(mProjectFolderOsPath, mType.mFilename);
    271 
    272         OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(toSave),
    273                 SdkConstants.INI_CHARSET);
    274 
    275         // write the header
    276         writer.write(mType.mHeader);
    277 
    278         // write the properties.
    279         for (Entry<String, String> entry : mProperties.entrySet()) {
    280             String comment = COMMENT_MAP.get(entry.getKey());
    281             if (comment != null) {
    282                 writer.write(comment);
    283             }
    284             String value = entry.getValue();
    285             if (value != null) {
    286                 value = value.replaceAll("\\\\", "\\\\\\\\");
    287                 writer.write(String.format("%s=%s\n", entry.getKey(), value));
    288             }
    289         }
    290 
    291         // close the file to flush
    292         writer.close();
    293     }
    294 
    295     /**
    296      * Private constructor.
    297      * <p/>
    298      * Use {@link #load(String, PropertyType)} or {@link #create(String, PropertyType)}
    299      * to instantiate.
    300      */
    301     private ProjectProperties(String projectFolderOsPath, Map<String, String> map,
    302             PropertyType type) {
    303         mProjectFolderOsPath = projectFolderOsPath;
    304         mProperties = map;
    305         mType = type;
    306     }
    307 }
    308