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