blob: 35fc548ef1c3bd279864d4487147b84b32d580a6 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.gradle.util;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.util.SystemProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import static com.android.SdkConstants.*;
import static com.android.tools.idea.gradle.util.Projects.getBaseDirPath;
import static com.android.tools.idea.gradle.util.PropertiesUtil.getProperties;
import static com.android.tools.idea.gradle.util.PropertiesUtil.savePropertiesToFile;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.intellij.openapi.util.io.FileUtil.*;
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
/**
* Utility methods related to a Gradle project's local.properties file.
*/
public final class LocalProperties {
@NotNull private final File myFilePath;
@NotNull private final File myProjectDirPath;
@NotNull private final Properties myProperties;
/**
* Creates a new {@link LocalProperties}. If a local.properties file does not exist, a new one will be created when the method
* {@link #save()} is invoked.
*
* @param project the Android project.
* @throws IOException if an I/O error occurs while reading the file.
* @throws IllegalArgumentException if there is already a directory called "local.properties" in the given project.
*/
public LocalProperties(@NotNull Project project) throws IOException {
this(getBaseDirPath(project));
}
/**
* Creates a new {@link LocalProperties}. If a local.properties file does not exist, a new one will be created when the method
* {@link #save()} is invoked.
*
* @param projectDirPath the path of the Android project's root directory.
* @throws IOException if an I/O error occurs while reading the file.
* @throws IllegalArgumentException if there is already a directory called "local.properties" at the given path.
*/
public LocalProperties(@NotNull File projectDirPath) throws IOException {
myProjectDirPath = projectDirPath;
myFilePath = new File(projectDirPath, FN_LOCAL_PROPERTIES);
myProperties = getProperties(myFilePath);
}
/**
* @return the path of the Android SDK specified in this local.properties file; or {@code null} if such property is not specified.
*/
@Nullable
public File getAndroidSdkPath() {
return getPath(SDK_DIR_PROPERTY);
}
/**
* @return the path of the Android NDK specified in this local.properties file; or {@code null} if such property is not specified.
*/
@Nullable
public File getAndroidNdkPath() {
return getPath(NDK_DIR_PROPERTY);
}
/**
* @return the path for the given propery name specified in this local.properties file; or {@code null} if such property is not specified.
*/
@Nullable
private File getPath(String property) {
String path = getProperty(property);
if (isNotEmpty(path)) {
if (!isAbsolute(path)) {
String canonicalPath = toCanonicalPath(new File(myProjectDirPath, toSystemDependentName(path)).getPath());
File file = new File(canonicalPath);
if (!file.isDirectory()) {
// Only accept resolved relative paths if they exist, otherwise just use the path as it was declared in local.properties.
// When getting a path from another platform (e.g. a Windows path when opening a project on Mac), java.io.File will think that the
// path is relative and it will prepend the path of the project to it.
// See https://code.google.com/p/android/issues/detail?id=82184
return new File(path);
}
}
return new File(toSystemDependentName(path));
}
return null;
}
public void setAndroidSdkPath(@NotNull Sdk androidSdk) {
String androidSdkPath = androidSdk.getHomePath();
assert androidSdkPath != null;
setAndroidSdkPath(androidSdkPath);
}
public void setAndroidSdkPath(@NotNull String androidSdkPath) {
doSetAndroidSdkPath(toSystemDependentName(androidSdkPath));
}
public void setAndroidSdkPath(@NotNull File androidSdkPath) {
doSetAndroidSdkPath(androidSdkPath.getPath());
}
// Sets the path as it is given. When invoked from production code, the path is assumed to be "system dependent".
@VisibleForTesting
void doSetAndroidSdkPath(@NotNull String path) {
myProperties.setProperty(SDK_DIR_PROPERTY, path);
}
public void setAndroidNdkPath(@NotNull String androidNdkPath) {
doSetAndroidNdkPath(toSystemDependentName(androidNdkPath));
}
public void setAndroidNdkPath(@Nullable File androidNdkPath) {
String path = androidNdkPath != null ? androidNdkPath.getPath() : null;
doSetAndroidNdkPath(path);
}
// Sets the path as it is given. When invoked from production code, the path is assumed to be "system dependent".
@VisibleForTesting
void doSetAndroidNdkPath(@Nullable String path) {
if (isNotEmpty(path)) {
myProperties.setProperty(NDK_DIR_PROPERTY, path);
}
else {
myProperties.remove(NDK_DIR_PROPERTY);
}
}
public boolean hasAndroidDirProperty() {
String property = getProperty("android.dir");
return !isNullOrEmpty(property);
}
@Nullable
public String getProperty(@NotNull String key) {
return myProperties.getProperty(key);
}
/**
* Saves any changes to the underlying local.properties file.
*/
public void save() throws IOException {
savePropertiesToFile(myProperties, myFilePath, getHeaderComment());
}
@NotNull
private static String getHeaderComment() {
String[] lines = {
"# This file is automatically generated by Android Studio.",
"# Do not modify this file -- YOUR CHANGES WILL BE ERASED!",
"#",
"# This file must *NOT* be checked into Version Control Systems,",
"# as it contains information specific to your local configuration.",
"",
"# Location of the SDK. This is only used by Gradle.",
"# For customization when using a Version Control System, please read the",
"# header note."
};
return Joiner.on(SystemProperties.getLineSeparator()).join(lines);
}
@NotNull
public File getFilePath() {
return myFilePath;
}
}