blob: 4ec3f9dd7e067a9b48e51b9d6367e5591211ceeb [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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 org.jetbrains.plugins.gradle.util;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.externalSystem.model.ExternalSystemException;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileChooser.FileTypeDescriptor;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.containers.Stack;
import org.gradle.tooling.model.GradleProject;
import org.gradle.tooling.model.gradle.GradleScript;
import org.gradle.wrapper.WrapperConfiguration;
import org.gradle.wrapper.WrapperExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.URI;
import java.util.Arrays;
import java.util.Properties;
/**
* Holds miscellaneous utility methods.
*
* @author Denis Zhdanov
* @since 8/25/11 1:19 PM
*/
public class GradleUtil {
private static final String LAST_USED_GRADLE_HOME_KEY = "last.used.gradle.home";
private GradleUtil() { }
/**
* Allows to retrieve file chooser descriptor that filters gradle scripts.
* <p/>
* <b>Note:</b> we want to fall back to the standard {@link FileTypeDescriptor} when dedicated gradle file type
* is introduced (it's processed as groovy file at the moment). We use open project descriptor here in order to show
* custom gradle icon at the file chooser ({@link icons.GradleIcons#Gradle}, is used at the file chooser dialog via
* the dedicated gradle project open processor).
*/
@NotNull
public static FileChooserDescriptor getGradleProjectFileChooserDescriptor() {
return FileChooserDescriptorFactory.createSingleFileDescriptor(GradleConstants.EXTENSION);
}
@NotNull
public static FileChooserDescriptor getGradleHomeFileChooserDescriptor() {
return FileChooserDescriptorFactory.createSingleFolderDescriptor();
}
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
public static boolean isGradleDefaultWrapperFilesExist(@Nullable String gradleProjectPath) {
return getWrapperConfiguration(gradleProjectPath) != null;
}
/**
* Tries to retrieve what settings should be used with gradle wrapper for the gradle project located at the given path.
*
* @param gradleProjectPath target gradle project config (*.gradle) path or config file's directory path.
* @return gradle wrapper settings should be used with gradle wrapper for the gradle project located at the given path
* if any; <code>null</code> otherwise
*/
@Nullable
public static WrapperConfiguration getWrapperConfiguration(@Nullable String gradleProjectPath) {
final File wrapperPropertiesFile = findDefaultWrapperPropertiesFile(gradleProjectPath);
if (wrapperPropertiesFile == null) return null;
final WrapperConfiguration wrapperConfiguration = new WrapperConfiguration();
final Properties props = new Properties();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(wrapperPropertiesFile));
props.load(reader);
String distributionUrl = props.getProperty(WrapperExecutor.DISTRIBUTION_URL_PROPERTY);
if(StringUtil.isEmpty(distributionUrl)) {
throw new ExternalSystemException("Wrapper 'distributionUrl' property does not exist!");
} else {
wrapperConfiguration.setDistribution(new URI(distributionUrl));
}
String distributionPath = props.getProperty(WrapperExecutor.DISTRIBUTION_PATH_PROPERTY);
if(!StringUtil.isEmpty(distributionPath)) {
wrapperConfiguration.setDistributionPath(distributionPath);
}
String distPathBase = props.getProperty(WrapperExecutor.DISTRIBUTION_BASE_PROPERTY);
if(!StringUtil.isEmpty(distPathBase)) {
wrapperConfiguration.setDistributionBase(distPathBase);
}
return wrapperConfiguration;
}
catch (Exception e) {
GradleLog.LOG.warn(
String.format("I/O exception on reading gradle wrapper properties file at '%s'", wrapperPropertiesFile.getAbsolutePath()), e);
}
finally {
if (reader != null) {
try {
reader.close();
}
catch (IOException e) {
// Ignore
}
}
}
return null;
}
/**
* Allows to build file system path to the target gradle sub-project given the root project path.
*
* @param subProject target sub-project which config path we're interested in
* @param rootProjectPath path to root project's directory which contains 'build.gradle'
* @return path to the given sub-project's directory which contains 'build.gradle'
*/
@NotNull
public static String getConfigPath(@NotNull GradleProject subProject, @NotNull String rootProjectPath) {
try {
GradleScript script = subProject.getBuildScript();
if (script != null) {
File file = script.getSourceFile();
if (file != null) {
if (file.isFile()) {
// The file points to 'build.gradle' at the moment but we keep it's parent dir path instead.
file = file.getParentFile();
}
return ExternalSystemApiUtil.toCanonicalPath(file.getCanonicalPath());
}
}
}
catch (Exception e) {
// As said by gradle team: 'One thing I'm interested in is whether you have any thoughts about how the tooling API should
// deal with missing details from the model - for example, when asking for details about the build scripts when using
// a version of Gradle that does not supply that information. Currently, you'll get a `UnsupportedOperationException`
// when you call the `getBuildScript()` method'.
//
// So, just ignore it and assume that the user didn't define any custom build file name.
}
File rootProjectParent = new File(rootProjectPath);
StringBuilder buffer = new StringBuilder(FileUtil.toCanonicalPath(rootProjectParent.getAbsolutePath()));
Stack<String> stack = ContainerUtilRt.newStack();
for (GradleProject p = subProject; p != null; p = p.getParent()) {
stack.push(p.getName());
}
// pop root project
stack.pop();
while (!stack.isEmpty()) {
buffer.append(ExternalSystemConstants.PATH_SEPARATOR).append(stack.pop());
}
return buffer.toString();
}
@NotNull
public static String getLastUsedGradleHome() {
return PropertiesComponent.getInstance().getValue(LAST_USED_GRADLE_HOME_KEY, "");
}
public static void storeLastUsedGradleHome(@Nullable String gradleHomePath) {
if (gradleHomePath != null) {
PropertiesComponent.getInstance().setValue(LAST_USED_GRADLE_HOME_KEY, gradleHomePath);
}
}
@Nullable
public static File findDefaultWrapperPropertiesFile(@Nullable String gradleProjectPath) {
if (gradleProjectPath == null) {
return null;
}
File file = new File(gradleProjectPath);
// There is a possible case that given path points to a gradle script (*.gradle) but it's also possible that
// it references script's directory. We want to provide flexibility here.
File gradleDir;
if (file.isFile()) {
gradleDir = new File(file.getParentFile(), "gradle");
}
else {
gradleDir = new File(file, "gradle");
}
if (!gradleDir.isDirectory()) {
return null;
}
File wrapperDir = new File(gradleDir, "wrapper");
if (!wrapperDir.isDirectory()) {
return null;
}
File[] candidates = wrapperDir.listFiles(new FileFilter() {
@Override
public boolean accept(File candidate) {
return candidate.isFile() && candidate.getName().endsWith(".properties");
}
});
if (candidates == null) {
GradleLog.LOG.warn("No *.properties file is found at the gradle wrapper directory " + wrapperDir.getAbsolutePath());
return null;
}
else if (candidates.length != 1) {
GradleLog.LOG.warn(String.format(
"%d *.properties files instead of one have been found at the wrapper directory (%s): %s",
candidates.length, wrapperDir.getAbsolutePath(), Arrays.toString(candidates)
));
return null;
}
return candidates[0];
}
}