/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.gradle.plugins.patcher;
import com.liferay.gradle.util.FileUtil;
import com.liferay.gradle.util.GradleUtil;
import com.liferay.gradle.util.Validator;
import com.liferay.gradle.util.copy.ReplaceLeadingPathAction;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import org.apache.tools.ant.filters.FixCrLfFilter;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ResolvableDependencies;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedConfiguration;
import org.gradle.api.artifacts.ResolvedModuleVersion;
import org.gradle.api.file.ConfigurableFileTree;
import org.gradle.api.file.CopySpec;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileTree;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.OutputFiles;
import org.gradle.api.tasks.SkipWhenEmpty;
import org.gradle.api.tasks.TaskAction;
import org.gradle.process.ExecResult;
import org.gradle.process.ExecSpec;
import org.gradle.util.GUtil;
/**
* @author Andrea Di Giorgi
*/
public class PatchTask extends DefaultTask {
public static final String PATCHED_SRC_DIR_MAPPING_DEFAULT_EXTENSION = "*";
public PatchTask() {
_originalLibFile = new Callable<File>() {
@Override
public File call() throws Exception {
return getOriginalLibModuleFile();
}
};
_originalLibSrcFile = new Callable<File>() {
@Override
public File call() throws Exception {
return FileUtil.get(getProject(), getOriginalLibSrcUrl());
}
};
args("--no-backup-if-mismatch", "--strip=1");
}
public PatchTask args(Iterable<Object> args) {
GUtil.addToCollection(_args, args);
return this;
}
public PatchTask args(Object... args) {
return args(Arrays.asList(args));
}
public PatchTask fileNames(Iterable<Object> fileNames) {
GUtil.addToCollection(_fileNames, fileNames);
return this;
}
public PatchTask fileNames(Object... fileNames) {
return fileNames(Arrays.asList(fileNames));
}
public List<String> getArgs() {
return GradleUtil.toStringList(_args);
}
@Input
public List<String> getFileNames() {
return GradleUtil.toStringList(_fileNames);
}
public String getOriginalLibConfigurationName() {
return GradleUtil.toString(_originalLibConfigurationName);
}
@InputFile
public File getOriginalLibFile() {
return GradleUtil.toFile(getProject(), _originalLibFile);
}
public String getOriginalLibModuleGroup() {
Dependency dependency = getOriginalLibDependency();
return dependency.getGroup();
}
public String getOriginalLibModuleName() {
return GradleUtil.toString(_originalLibModuleName);
}
public String getOriginalLibModuleVersion() {
Dependency dependency = getOriginalLibDependency();
return dependency.getVersion();
}
public String getOriginalLibSrcBaseUrl() {
return GradleUtil.toString(_originalLibSrcBaseUrl);
}
@Input
public String getOriginalLibSrcDirName() {
return GradleUtil.toString(_originalLibSrcDirName);
}
@InputFile
public File getOriginalLibSrcFile() {
return GradleUtil.toFile(getProject(), _originalLibSrcFile);
}
public Map<String, File> getPatchedSrcDirMappings() {
Map<String, File> patchedSrcDirMappings = new HashMap<>();
for (Map.Entry<String, Object> entry :
_patchedSrcDirMappings.entrySet()) {
String extension = entry.getKey();
File dir = GradleUtil.toFile(getProject(), entry.getValue());
patchedSrcDirMappings.put(extension, dir);
}
return patchedSrcDirMappings;
}
@OutputFiles
public FileCollection getPatchedSrcFiles() {
Project project = getProject();
Map<File, ConfigurableFileTree> patchedSrcFileTreeMap = new HashMap<>();
for (String fileName : getFileNames()) {
File patchedDir = getPatchedSrcDir(fileName);
ConfigurableFileTree configurableFileTree =
patchedSrcFileTreeMap.get(patchedDir);
if (configurableFileTree == null) {
configurableFileTree = project.fileTree(patchedDir);
patchedSrcFileTreeMap.put(patchedDir, configurableFileTree);
}
configurableFileTree.include(fileName);
}
Collection<ConfigurableFileTree> patchedSrcFileTrees =
patchedSrcFileTreeMap.values();
return project.files(patchedSrcFileTrees.toArray());
}
public File getPatchesDir() {
return GradleUtil.toFile(getProject(), _patchesDir);
}
@InputFiles
@SkipWhenEmpty
public FileCollection getPatchFiles() {
Project project = getProject();
if (!_patchFiles.isEmpty()) {
return project.files(_patchFiles);
}
else {
return project.fileTree(_patchesDir);
}
}
public boolean isCopyOriginalLibClasses() {
return _copyOriginalLibClasses;
}
@TaskAction
public void patch() throws Exception {
final Project project = getProject();
File patchesTemporaryDir = fixPatchFiles();
final File srcTemporaryDir = fixSrcFiles();
for (final File patchFile : getSortedFiles(patchesTemporaryDir)) {
final ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
ExecResult execResult = project.exec(
new Action<ExecSpec>() {
@Override
public void execute(ExecSpec execSpec) {
execSpec.args(getArgs());
execSpec.args(
"--input=" +
FileUtil.relativize(
patchFile, srcTemporaryDir));
execSpec.setExecutable("patch");
execSpec.setIgnoreExitValue(true);
execSpec.setStandardOutput(byteArrayOutputStream);
execSpec.setWorkingDir(srcTemporaryDir);
}
});
System.out.println(byteArrayOutputStream.toString());
execResult.rethrowFailure();
execResult.assertNormalExitValue();
}
FileTree fileTree = project.fileTree(srcTemporaryDir);
for (File file : fileTree) {
File patchedSrcDir = getPatchedSrcDir(file.getName());
if (patchedSrcDir == null) {
continue;
}
Path patchedSrcDirPath = patchedSrcDir.toPath();
String relativePath = FileUtil.relativize(file, srcTemporaryDir);
patchedSrcDirPath = patchedSrcDirPath.resolve(relativePath);
Files.createDirectories(patchedSrcDirPath.getParent());
Files.move(
file.toPath(), patchedSrcDirPath,
StandardCopyOption.REPLACE_EXISTING);
}
}
public PatchTask patchedSrcDirMapping(String extension, Object dir) {
_patchedSrcDirMappings.put(extension, dir);
return this;
}
public PatchTask patchFiles(Iterable<Object> patchFiles) {
GUtil.addToCollection(_patchFiles, patchFiles);
return this;
}
public PatchTask patchFiles(Object... patchFiles) {
return patchFiles(Arrays.asList(patchFiles));
}
public void setArgs(Iterable<Object> args) {
_args.clear();
args(args);
}
public void setArgs(Object... args) {
setArgs(Arrays.asList(args));
}
public void setCopyOriginalLibClasses(boolean copyOriginalLibClasses) {
_copyOriginalLibClasses = copyOriginalLibClasses;
}
public void setFileNames(Iterable<Object> fileNames) {
_fileNames.clear();
fileNames(fileNames);
}
public void setOriginalLibConfigurationName(
Object originalLibConfigurationName) {
_originalLibConfigurationName = originalLibConfigurationName;
}
public void setOriginalLibFile(Object originalLibFile) {
_originalLibFile = originalLibFile;
}
public void setOriginalLibModuleName(Object originalLibModuleName) {
_originalLibModuleName = originalLibModuleName;
}
public void setOriginalLibSrcBaseUrl(Object originalLibSrcBaseUrl) {
_originalLibSrcBaseUrl = originalLibSrcBaseUrl;
}
public void setOriginalLibSrcDirName(Object originalLibSrcDirName) {
_originalLibSrcDirName = originalLibSrcDirName;
}
public void setOriginalLibSrcFile(Object originalLibSrcFile) {
_originalLibSrcFile = originalLibSrcFile;
}
public void setPatchedSrcDirMappings(
Map<String, Object> patchedSrcDirMappings) {
_patchedSrcDirMappings.clear();
_patchedSrcDirMappings.putAll(patchedSrcDirMappings);
}
public void setPatchesDir(Object patchesDir) {
_patchesDir = patchesDir;
}
public void setPatchFiles(Iterable<Object> patchFiles) {
_patchFiles.clear();
patchFiles(patchFiles);
}
protected File fixPatchFiles() {
final Project project = getProject();
final File temporaryDir = new File(getTemporaryDir(), "patches");
project.delete(temporaryDir);
project.copy(
new Action<CopySpec>() {
@Override
public void execute(CopySpec copySpec) {
copySpec.filter(_fixCrLfArgs, FixCrLfFilter.class);
copySpec.from(getPatchFiles());
copySpec.into(temporaryDir);
copySpec.setIncludeEmptyDirs(false);
}
});
return temporaryDir;
}
protected File fixSrcFiles() {
final Project project = getProject();
final File temporaryDir = new File(getTemporaryDir(), "src");
project.delete(temporaryDir);
project.copy(
new Action<CopySpec>() {
@Override
public void execute(CopySpec copySpec) {
String originalLibSrcDirName = getOriginalLibSrcDirName();
if (!originalLibSrcDirName.equals(".")) {
Map<Object, Object> leadingPathReplacementsMap =
new HashMap<>();
leadingPathReplacementsMap.put(
originalLibSrcDirName, "");
copySpec.eachFile(
new ReplaceLeadingPathAction(
leadingPathReplacementsMap));
}
copySpec.filter(_fixCrLfArgs, FixCrLfFilter.class);
copySpec.from(project.zipTree(getOriginalLibSrcFile()));
copySpec.include(getFileNames());
copySpec.into(temporaryDir);
copySpec.setIncludeEmptyDirs(false);
}
});
return temporaryDir;
}
protected Dependency getOriginalLibDependency() {
Configuration configuration = GradleUtil.getConfiguration(
getProject(), getOriginalLibConfigurationName());
ResolvableDependencies resolvableDependencies =
configuration.getIncoming();
String moduleName = getOriginalLibModuleName();
for (Dependency dependency : resolvableDependencies.getDependencies()) {
if (moduleName.equals(dependency.getName())) {
return dependency;
}
}
throw new GradleException("Unable to find original lib " + moduleName);
}
protected File getOriginalLibModuleFile() {
String configurationName = getOriginalLibConfigurationName();
String moduleGroup = getOriginalLibModuleGroup();
String moduleName = getOriginalLibModuleName();
String moduleVersion = getOriginalLibModuleVersion();
if (Validator.isNull(configurationName) ||
Validator.isNull(moduleGroup) || Validator.isNull(moduleName) ||
Validator.isNull(moduleVersion)) {
return null;
}
Configuration configuration = GradleUtil.getConfiguration(
getProject(), configurationName);
ResolvedConfiguration resolvedConfiguration =
configuration.getResolvedConfiguration();
for (ResolvedArtifact resolvedArtifact :
resolvedConfiguration.getResolvedArtifacts()) {
ResolvedModuleVersion resolvedModuleVersion =
resolvedArtifact.getModuleVersion();
ModuleVersionIdentifier moduleVersionIdentifier =
resolvedModuleVersion.getId();
if (moduleGroup.equals(moduleVersionIdentifier.getGroup()) &&
moduleName.equals(moduleVersionIdentifier.getName()) &&
moduleVersion.equals(moduleVersionIdentifier.getVersion())) {
return resolvedArtifact.getFile();
}
}
return null;
}
protected String getOriginalLibSrcUrl() {
StringBuilder sb = new StringBuilder();
String baseUrl = getOriginalLibSrcBaseUrl();
if (Validator.isNotNull(baseUrl)) {
sb.append(baseUrl);
if (baseUrl.charAt(baseUrl.length() - 1) != '/') {
sb.append('/');
}
}
else {
sb.append(_BASE_URL);
String moduleGroup = getOriginalLibModuleGroup();
sb.append(moduleGroup.replace('.', '/'));
sb.append('/');
}
sb.append(getOriginalLibModuleName());
sb.append('/');
sb.append(getOriginalLibModuleVersion());
sb.append('/');
sb.append(getOriginalLibModuleName());
sb.append('-');
sb.append(getOriginalLibModuleVersion());
sb.append("-sources.jar");
return sb.toString();
}
protected File getPatchedSrcDir(String fileName) {
String extension = PATCHED_SRC_DIR_MAPPING_DEFAULT_EXTENSION;
int pos = fileName.indexOf('.');
if (pos != -1) {
extension = fileName.substring(pos + 1);
}
Object patchedSrcDir = _patchedSrcDirMappings.get(extension);
if (patchedSrcDir == null) {
patchedSrcDir = _patchedSrcDirMappings.get(
PATCHED_SRC_DIR_MAPPING_DEFAULT_EXTENSION);
}
return GradleUtil.toFile(getProject(), patchedSrcDir);
}
protected List<File> getSortedFiles(File dir) {
List<File> sortedFiles = new ArrayList<>();
Project project = getProject();
FileTree fileTree = project.fileTree(dir);
GUtil.addToCollection(sortedFiles, fileTree);
Collections.sort(sortedFiles);
return sortedFiles;
}
private static final String _BASE_URL =
"http://repo.maven.apache.org/maven2/";
private static final Map<String, Object> _fixCrLfArgs = new HashMap<>();
static {
_fixCrLfArgs.put(
"eof", FixCrLfFilter.AddAsisRemove.newInstance("remove"));
_fixCrLfArgs.put("eol", FixCrLfFilter.CrLf.newInstance("lf"));
_fixCrLfArgs.put("fixlast", false);
}
private final List<Object> _args = new ArrayList<>();
private boolean _copyOriginalLibClasses = true;
private final List<Object> _fileNames = new ArrayList<>();
private Object _originalLibConfigurationName =
JavaPlugin.COMPILE_CONFIGURATION_NAME;
private Object _originalLibFile;
private Object _originalLibModuleName;
private Object _originalLibSrcBaseUrl;
private Object _originalLibSrcDirName = ".";
private Object _originalLibSrcFile;
private final Map<String, Object> _patchedSrcDirMappings = new HashMap<>();
private Object _patchesDir = "patches";
private final List<Object> _patchFiles = new ArrayList<>();
}