com.android.ide.eclipse.adt.internal.wizards.exportgradle.BuildFileCreator.java Source code

Java tutorial

Introduction

Here is the source code for com.android.ide.eclipse.adt.internal.wizards.exportgradle.BuildFileCreator.java

Source

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
 *
 * 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.ide.eclipse.adt.internal.wizards.exportgradle;

import static com.android.SdkConstants.GRADLE_LATEST_VERSION;
import static com.android.SdkConstants.GRADLE_PLUGIN_LATEST_VERSION;
import static com.android.SdkConstants.GRADLE_PLUGIN_NAME;
import static com.android.tools.lint.checks.GradleDetector.APP_PLUGIN_ID;
import static com.android.tools.lint.checks.GradleDetector.LIB_PLUGIN_ID;

import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
import com.android.io.IAbstractFile;
import com.android.sdklib.io.FileOp;
import com.android.xml.AndroidManifest;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.io.Closeables;
import com.google.common.io.Files;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;

/**
 * Creates build.gradle and settings.gradle files for a set of projects.
 * <p>
 * Based on {@link org.eclipse.ant.internal.ui.datatransfer.BuildFileCreator}
 */
public class BuildFileCreator {
    static final String BUILD_FILE = "build.gradle"; //$NON-NLS-1$
    static final String SETTINGS_FILE = "settings.gradle"; //$NON-NLS-1$
    private static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
    private static final String GRADLE_WRAPPER_LOCATION = "tools/templates/gradle/wrapper"; //$NON-NLS-1$
    static final String PLUGIN_CLASSPATH = "classpath '" + GRADLE_PLUGIN_NAME + GRADLE_PLUGIN_LATEST_VERSION + "'"; //$NON-NLS-1$
    static final String MAVEN_REPOSITORY = "jcenter()"; //$NON-NLS-1$

    private static final String[] GRADLE_WRAPPER_FILES = new String[] { "gradlew", //$NON-NLS-1$
            "gradlew.bat", //$NON-NLS-1$
            "gradle/wrapper/gradle-wrapper.jar", //$NON-NLS-1$
            "gradle/wrapper/gradle-wrapper.properties" //$NON-NLS-1$
    };

    private static final Comparator<IFile> FILE_COMPARATOR = new Comparator<IFile>() {
        @Override
        public int compare(IFile o1, IFile o2) {
            return o1.toString().compareTo(o2.toString());
        }
    };

    private final GradleModule mModule;
    private final StringBuilder mBuildFile = new StringBuilder();

    /**
     * Create buildfile for the projects.
     *
     * @param shell parent instance for dialogs
     * @return project names for which buildfiles were created
     * @throws InterruptedException thrown when user cancels task
     */
    public static void createBuildFiles(@NonNull ProjectSetupBuilder builder, @NonNull Shell shell,
            @NonNull IProgressMonitor pm) {

        File gradleLocation = new File(Sdk.getCurrent().getSdkOsLocation(), GRADLE_WRAPPER_LOCATION);
        SubMonitor localmonitor = null;

        try {
            // See if we have a Gradle wrapper in the SDK templates directory. If so, we can copy
            // it over.
            boolean hasGradleWrapper = true;
            for (File wrapperFile : getGradleWrapperFiles(gradleLocation)) {
                if (!wrapperFile.exists()) {
                    hasGradleWrapper = false;
                }
            }

            Collection<GradleModule> modules = builder.getModules();
            boolean multiModules = modules.size() > 1;

            // determine files to create/change
            List<IFile> files = new ArrayList<IFile>();

            // add the build.gradle file for all modules.
            for (GradleModule module : modules) {
                // build.gradle file
                IFile file = module.getProject().getFile(BuildFileCreator.BUILD_FILE);
                files.add(file);
            }

            // get the commonRoot for all modules. If only one module, this returns the path
            // of the project.
            IPath commonRoot = builder.getCommonRoot();

            IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
            IPath workspaceLocation = workspaceRoot.getLocation();

            IPath relativePath = commonRoot.makeRelativeTo(workspaceLocation);
            // if makeRelativePath to returns the same path, then commonRoot is not in the
            // workspace.
            boolean rootInWorkspace = !relativePath.equals(commonRoot);
            // we only care if the root is a workspace project. if it's the workspace folder itself,
            // then the files won't be handled by the workspace.
            rootInWorkspace = rootInWorkspace && relativePath.segmentCount() > 0;

            File settingsFile = new File(commonRoot.toFile(), SETTINGS_FILE);

            // more than one modules -> generate settings.gradle
            if (multiModules && rootInWorkspace) {

                // Locate the settings.gradle file and add it to the changed files list
                IPath settingsGradle = Path.fromOSString(settingsFile.getAbsolutePath());

                // different path, means commonRoot is inside the workspace, which means we have
                // to add settings.gradle and wrapper files to the list of files to add.
                IFile iFile = workspaceRoot.getFile(settingsGradle);
                if (iFile != null) {
                    files.add(iFile);
                }
            }

            // Gradle wrapper files
            if (hasGradleWrapper && rootInWorkspace) {
                // See if there already wrapper files there and only mark nonexistent ones for
                // creation.
                for (File wrapperFile : getGradleWrapperFiles(commonRoot.toFile())) {
                    if (!wrapperFile.exists()) {
                        IPath path = Path.fromOSString(wrapperFile.getAbsolutePath());
                        IFile file = workspaceRoot.getFile(path);
                        files.add(file);
                    }
                }
            }

            ExportStatus status = new ExportStatus();
            builder.setStatus(status);

            // Trigger checkout of changed files
            Set<IFile> confirmedFiles = validateEdit(files, status, shell);

            if (status.hasError()) {
                return;
            }

            // Now iterate over all the modules and generate the build files.
            localmonitor = SubMonitor.convert(pm, ExportMessages.PageTitle, confirmedFiles.size());
            List<String> projectSettingsPath = Lists.newArrayList();
            for (GradleModule currentModule : modules) {
                IProject moduleProject = currentModule.getProject();

                IFile file = moduleProject.getFile(BuildFileCreator.BUILD_FILE);
                if (!confirmedFiles.contains(file)) {
                    continue;
                }

                localmonitor.setTaskName(NLS.bind(ExportMessages.FileStatusMessage, moduleProject.getName()));

                ProjectState projectState = Sdk.getProjectState(moduleProject);
                BuildFileCreator instance = new BuildFileCreator(currentModule, shell);
                if (projectState != null) {
                    // This is an Android project
                    if (!multiModules) {
                        instance.appendBuildScript();
                    }
                    instance.appendHeader(projectState.isLibrary());
                    instance.appendDependencies();
                    instance.startAndroidTask(projectState);
                    //instance.appendDefaultConfig();
                    instance.createAndroidSourceSets();
                    instance.finishAndroidTask();
                } else {
                    // This is a plain Java project
                    instance.appendJavaHeader();
                    instance.createJavaSourceSets();
                }

                try {
                    // Write the build file
                    String buildfile = instance.mBuildFile.toString();
                    InputStream is = new ByteArrayInputStream(buildfile.getBytes("UTF-8")); //$NON-NLS-1$
                    if (file.exists()) {
                        file.setContents(is, true, true, null);
                    } else {
                        file.create(is, true, null);
                    }
                } catch (Exception e) {
                    status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, file.getLocation().toFile());
                    status.setErrorMessage(e.getMessage());
                    return;
                }

                if (localmonitor.isCanceled()) {
                    return;
                }
                localmonitor.worked(1);

                // get the project path to add it to the settings.gradle.
                projectSettingsPath.add(currentModule.getPath());
            }

            // write the settings file.
            if (multiModules) {
                try {
                    writeGradleSettingsFile(settingsFile, projectSettingsPath);
                } catch (IOException e) {
                    status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, settingsFile);
                    status.setErrorMessage(e.getMessage());
                    return;
                }
                File mainBuildFile = new File(commonRoot.toFile(), BUILD_FILE);
                try {
                    writeRootBuildGradle(mainBuildFile);
                } catch (IOException e) {
                    status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, mainBuildFile);
                    status.setErrorMessage(e.getMessage());
                    return;
                }
            }

            // finally write the wrapper
            // TODO check we can based on where it is
            if (hasGradleWrapper) {
                copyGradleWrapper(gradleLocation, commonRoot.toFile(), status);
                if (status.hasError()) {
                    return;
                }
            }

        } finally {
            if (localmonitor != null && !localmonitor.isCanceled()) {
                localmonitor.done();
            }
            if (pm != null) {
                pm.done();
            }
        }
    }

    /**
     * @param GradleModule create buildfile for this project
     * @param shell parent instance for dialogs
     */
    private BuildFileCreator(GradleModule module, Shell shell) {
        mModule = module;
    }

    /**
     * Return the files that comprise the Gradle wrapper as a collection of {@link File} instances.
     * @param root
     * @return
     */
    private static List<File> getGradleWrapperFiles(File root) {
        List<File> files = new ArrayList<File>(GRADLE_WRAPPER_FILES.length);
        for (String file : GRADLE_WRAPPER_FILES) {
            files.add(new File(root, file));
        }
        return files;
    }

    /**
     * Copy the Gradle wrapper files from one directory to another.
     */
    private static void copyGradleWrapper(File from, File to, ExportStatus status) {
        for (String file : GRADLE_WRAPPER_FILES) {
            File dest = new File(to, file);
            try {
                File src = new File(from, file);
                dest.getParentFile().mkdirs();
                new FileOp().copyFile(src, dest);

                if (src.getName().equals(GRADLE_PROPERTIES)) {
                    updateGradleDistributionUrl(GRADLE_LATEST_VERSION, dest);
                }
                dest.setExecutable(src.canExecute());
                status.addFileStatus(ExportStatus.FileStatus.OK, dest);
            } catch (IOException e) {
                status.addFileStatus(ExportStatus.FileStatus.IO_FAILURE, dest);
                return;
            }
        }
    }

    /**
     * Outputs boilerplate buildscript information common to all Gradle build files.
     */
    private void appendBuildScript() {
        appendBuildScript(mBuildFile);
    }

    /**
     * Outputs boilerplate header information common to all Gradle build files.
     */
    private static void appendBuildScript(StringBuilder builder) {
        builder.append("buildscript {\n"); //$NON-NLS-1$
        builder.append("    repositories {\n"); //$NON-NLS-1$
        builder.append("        " + MAVEN_REPOSITORY + "\n"); //$NON-NLS-1$
        builder.append("    }\n"); //$NON-NLS-1$
        builder.append("    dependencies {\n"); //$NON-NLS-1$
        builder.append("        " + PLUGIN_CLASSPATH + "\n"); //$NON-NLS-1$
        builder.append("    }\n"); //$NON-NLS-1$
        builder.append("}\n"); //$NON-NLS-1$
    }

    /**
     * Outputs boilerplate header information common to all Gradle build files.
     */
    private void appendHeader(boolean isLibrary) {
        if (isLibrary) {
            mBuildFile.append("apply plugin: '").append(LIB_PLUGIN_ID).append("'\n"); //$NON-NLS-1$  //$NON-NLS-2$
        } else {
            mBuildFile.append("apply plugin: '").append(APP_PLUGIN_ID).append("'\n"); //$NON-NLS-1$  //$NON-NLS-2$
        }
        mBuildFile.append("\n"); //$NON-NLS-1$
    }

    /**
     * Outputs a block which sets up library and project dependencies.
     */
    private void appendDependencies() {
        mBuildFile.append("dependencies {\n"); //$NON-NLS-1$

        // first the local jars.
        // TODO: Fix
        mBuildFile.append("    compile fileTree(dir: 'libs', include: '*.jar')\n"); //$NON-NLS-1$

        for (GradleModule dep : mModule.getDependencies()) {
            mBuildFile.append("    compile project('" + dep.getPath() + "')\n"); //$NON-NLS-1$ //$NON-NLS-2$
        }

        mBuildFile.append("}\n"); //$NON-NLS-1$
        mBuildFile.append("\n"); //$NON-NLS-1$
    }

    /**
     * Outputs the beginning of an Android task in the build file.
     */
    private void startAndroidTask(ProjectState projectState) {
        int buildApi = projectState.getTarget().getVersion().getApiLevel();
        String toolsVersion = projectState.getTarget().getBuildToolInfo().getRevision().toString();
        mBuildFile.append("android {\n"); //$NON-NLS-1$
        mBuildFile.append("    compileSdkVersion " + buildApi + "\n"); //$NON-NLS-1$
        mBuildFile.append("    buildToolsVersion \"" + toolsVersion + "\"\n"); //$NON-NLS-1$
        mBuildFile.append("\n"); //$NON-NLS-1$

        try {
            IJavaProject javaProject = BaseProjectHelper.getJavaProject(projectState.getProject());
            // otherwise we check source compatibility
            String source = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
            if (JavaCore.VERSION_1_7.equals(source)) {
                mBuildFile.append("    compileOptions {\n" + //$NON-NLS-1$
                        "        sourceCompatibility JavaVersion.VERSION_1_7\n" + //$NON-NLS-1$
                        "        targetCompatibility JavaVersion.VERSION_1_7\n" + //$NON-NLS-1$
                        "    }\n" + //$NON-NLS-1$
                        "\n"); //$NON-NLS-1$
            }
        } catch (CoreException e) {
            // Ignore compliance level, go with default
        }
    }

    /**
     * Outputs a sourceSets block to the Android task that locates all of the various source
     * subdirectories in the project.
     */
    private void createAndroidSourceSets() {
        IFolderWrapper projectFolder = new IFolderWrapper(mModule.getProject());
        IAbstractFile mManifestFile = AndroidManifest.getManifest(projectFolder);
        if (mManifestFile == null) {
            return;
        }
        List<String> srcDirs = new ArrayList<String>();
        for (IClasspathEntry entry : mModule.getJavaProject().readRawClasspath()) {
            if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE
                    || SdkConstants.FD_GEN_SOURCES.equals(entry.getPath().lastSegment())) {
                continue;
            }
            IPath path = entry.getPath().removeFirstSegments(1);
            srcDirs.add("'" + path.toOSString() + "'"); //$NON-NLS-1$
        }

        String srcPaths = Joiner.on(",").join(srcDirs);

        mBuildFile.append("    sourceSets {\n"); //$NON-NLS-1$
        mBuildFile.append("        main {\n"); //$NON-NLS-1$
        mBuildFile.append("            manifest.srcFile '" + SdkConstants.FN_ANDROID_MANIFEST_XML + "'\n"); //$NON-NLS-1$
        mBuildFile.append("            java.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
        mBuildFile.append("            resources.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
        mBuildFile.append("            aidl.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
        mBuildFile.append("            renderscript.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
        mBuildFile.append("            res.srcDirs = ['res']\n"); //$NON-NLS-1$
        mBuildFile.append("            assets.srcDirs = ['assets']\n"); //$NON-NLS-1$
        mBuildFile.append("        }\n"); //$NON-NLS-1$
        mBuildFile.append("\n"); //$NON-NLS-1$
        mBuildFile.append("        // Move the tests to tests/java, tests/res, etc...\n"); //$NON-NLS-1$
        mBuildFile.append("        instrumentTest.setRoot('tests')\n"); //$NON-NLS-1$
        if (srcDirs.contains("'src'")) {
            mBuildFile.append("\n"); //$NON-NLS-1$
            mBuildFile.append("        // Move the build types to build-types/<type>\n"); //$NON-NLS-1$
            mBuildFile.append(
                    "        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...\n"); //$NON-NLS-1$
            mBuildFile.append(
                    "        // This moves them out of them default location under src/<type>/... which would\n"); //$NON-NLS-1$
            mBuildFile.append("        // conflict with src/ being used by the main source set.\n"); //$NON-NLS-1$
            mBuildFile.append("        // Adding new build types or product flavors should be accompanied\n"); //$NON-NLS-1$
            mBuildFile.append("        // by a similar customization.\n"); //$NON-NLS-1$
            mBuildFile.append("        debug.setRoot('build-types/debug')\n"); //$NON-NLS-1$
            mBuildFile.append("        release.setRoot('build-types/release')\n"); //$NON-NLS-1$
        }
        mBuildFile.append("    }\n"); //$NON-NLS-1$
    }

    /**
     * Outputs the completion of the Android task in the build file.
     */
    private void finishAndroidTask() {
        mBuildFile.append("}\n"); //$NON-NLS-1$
    }

    /**
     * Outputs a boilerplate header for non-Android projects
     */
    private void appendJavaHeader() {
        mBuildFile.append("apply plugin: 'java'\n"); //$NON-NLS-1$
    }

    /**
     * Outputs a sourceSets block for non-Android projects to locate the source directories.
     */
    private void createJavaSourceSets() {
        List<String> dirs = new ArrayList<String>();
        for (IClasspathEntry entry : mModule.getJavaProject().readRawClasspath()) {
            if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
                continue;
            }
            IPath path = entry.getPath().removeFirstSegments(1);
            dirs.add("'" + path.toOSString() + "'"); //$NON-NLS-1$
        }

        String srcPaths = Joiner.on(",").join(dirs);

        mBuildFile.append("sourceSets {\n"); //$NON-NLS-1$
        mBuildFile.append("    main.java.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
        mBuildFile.append("    main.resources.srcDirs = [" + srcPaths + "]\n"); //$NON-NLS-1$
        mBuildFile.append("    test.java.srcDirs = ['tests/java']\n"); //$NON-NLS-1$
        mBuildFile.append("    test.resources.srcDirs = ['tests/resources']\n"); //$NON-NLS-1$
        mBuildFile.append("}\n"); //$NON-NLS-1$
    }

    /**
     * Merges the new subproject dependencies into the settings.gradle file if it already exists,
     * and creates one if it does not.
     * @throws IOException
     */
    private static void writeGradleSettingsFile(File settingsFile, List<String> projectPaths) throws IOException {
        StringBuilder contents = new StringBuilder();
        for (String path : projectPaths) {
            contents.append("include '").append(path).append("'\n"); //$NON-NLS-1$ //$NON-NLS-2$
        }

        Files.write(contents.toString(), settingsFile, Charsets.UTF_8);
    }

    private static void writeRootBuildGradle(File buildFile) throws IOException {
        StringBuilder sb = new StringBuilder(
                "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n");

        appendBuildScript(sb);

        Files.write(sb.toString(), buildFile, Charsets.UTF_8);
    }

    /**
     * Request write access to given files. Depending on the version control
     * plug-in opens a confirm checkout dialog.
     *
     * @param shell
     *            parent instance for dialogs
     * @return <code>IFile</code> objects for which user confirmed checkout
     * @throws CoreException
     *             thrown if project is under version control, but not connected
     */
    static Set<IFile> validateEdit(@NonNull List<IFile> files, @NonNull ExportStatus exportStatus,
            @NonNull Shell shell) {
        Set<IFile> confirmedFiles = new TreeSet<IFile>(FILE_COMPARATOR);
        if (files.size() == 0) {
            return confirmedFiles;
        }
        IStatus status = (files.get(0)).getWorkspace().validateEdit(files.toArray(new IFile[files.size()]), shell);
        if (status.isMultiStatus() && status.getChildren().length > 0) {
            for (int i = 0; i < status.getChildren().length; i++) {
                IStatus statusChild = status.getChildren()[i];
                if (statusChild.isOK()) {
                    confirmedFiles.add(files.get(i));
                } else {
                    exportStatus.addFileStatus(ExportStatus.FileStatus.VCS_FAILURE,
                            files.get(i).getLocation().toFile());
                }
            }
        } else if (status.isOK()) {
            confirmedFiles.addAll(files);
        }
        if (status.getSeverity() == IStatus.ERROR) {
            // not possible to checkout files: not connected to version
            // control plugin or hijacked files and made read-only, so
            // collect error messages provided by validator and re-throw
            StringBuffer message = new StringBuffer(status.getPlugin() + ": " //$NON-NLS-1$
                    + status.getMessage() + NEWLINE);
            if (status.isMultiStatus()) {
                for (int i = 0; i < status.getChildren().length; i++) {
                    IStatus statusChild = status.getChildren()[i];
                    message.append(statusChild.getMessage() + NEWLINE);
                }
            }
            String s = message.toString();
            exportStatus.setErrorMessage(s);
        }

        return confirmedFiles;
    }

    // -------------------------------------------------------------------------------
    // Fix gradle wrapper version. This code is from GradleUtil in the Studio plugin:
    // -------------------------------------------------------------------------------

    private static final String GRADLE_PROPERTIES = "gradle-wrapper.properties";
    private static final String GRADLEW_PROPERTIES_PATH = "gradle" + File.separator + "wrapper" + File.separator
            + GRADLE_PROPERTIES;
    private static final String GRADLEW_DISTRIBUTION_URL_PROPERTY_NAME = "distributionUrl";

    @NonNull
    private static File getGradleWrapperPropertiesFilePath(@NonNull File projectRootDir) {
        return new File(projectRootDir, GRADLEW_PROPERTIES_PATH);
    }

    @Nullable
    public static File findWrapperPropertiesFile(@NonNull File projectRootDir) {
        File wrapperPropertiesFile = getGradleWrapperPropertiesFilePath(projectRootDir);
        return wrapperPropertiesFile.isFile() ? wrapperPropertiesFile : null;
    }

    private static boolean updateGradleDistributionUrl(@NonNull String gradleVersion, @NonNull File propertiesFile)
            throws IOException {
        Properties properties = loadGradleWrapperProperties(propertiesFile);
        String gradleDistributionUrl = getGradleDistributionUrl(gradleVersion, false);
        String property = properties.getProperty(GRADLEW_DISTRIBUTION_URL_PROPERTY_NAME);
        if (property != null && (property.equals(gradleDistributionUrl)
                || property.equals(getGradleDistributionUrl(gradleVersion, true)))) {
            return false;
        }
        properties.setProperty(GRADLEW_DISTRIBUTION_URL_PROPERTY_NAME, gradleDistributionUrl);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(propertiesFile);
            properties.store(out, null);
            return true;
        } finally {
            Closeables.close(out, true);
        }
    }

    @NonNull
    private static Properties loadGradleWrapperProperties(@NonNull File propertiesFile) throws IOException {
        Properties properties = new Properties();
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(propertiesFile);
            properties.load(fileInputStream);
            return properties;
        } finally {
            Closeables.close(fileInputStream, true);
        }
    }

    @NonNull
    private static String getGradleDistributionUrl(@NonNull String gradleVersion, boolean binOnly) {
        String suffix = binOnly ? "bin" : "all";
        return String.format("https://services.gradle.org/distributions/gradle-%1$s-" + suffix + ".zip",
                gradleVersion);
    }
}