/**
* 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.util;
import groovy.lang.Closure;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import org.gradle.api.AntBuilder;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.file.FileCollection;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
/**
* @author Andrea Di Giorgi
*/
public class FileUtil {
public static void concatenate(
File destinationFile, Iterable<File> sourceFiles)
throws IOException {
try (FileOutputStream fileOutputStream = new FileOutputStream(
destinationFile);
FileChannel destinationChannel = fileOutputStream.getChannel()) {
for (File sourceFile : sourceFiles) {
try (FileInputStream fileInputStream = new FileInputStream(
sourceFile);
FileChannel sourceChannel = fileInputStream.getChannel()) {
sourceChannel.transferTo(
0, sourceChannel.size(), destinationChannel);
}
}
}
}
public static String createTempFileName(String prefix, String extension) {
StringBuilder sb = new StringBuilder();
if (Validator.isNotNull(prefix)) {
sb.append(prefix);
}
sb.append(System.currentTimeMillis());
sb.append(UUID.randomUUID());
if (Validator.isNotNull(extension)) {
sb.append('.');
sb.append(extension);
}
return sb.toString();
}
public static boolean exists(Project project, String fileName) {
File file = project.file(fileName);
return file.exists();
}
public static File get(Project project, String url) throws IOException {
return get(project, url, null);
}
public static File get(Project project, String url, File destinationFile)
throws IOException {
return get(project, url, destinationFile, false, true);
}
public static synchronized File get(
Project project, String url, File destinationFile,
boolean ignoreErrors, boolean tryLocalNetwork)
throws IOException {
String mirrorsCacheArtifactSubdir = url.replaceFirst(
"https?:\\/\\/(.+\\/).+", "$1");
File mirrorsCacheArtifactDir = new File(
_getMirrorsCacheDir(), mirrorsCacheArtifactSubdir);
String fileName = url.replaceFirst(".+\\/(.+)", "$1");
File mirrorsCacheArtifactFile = new File(
mirrorsCacheArtifactDir, fileName);
if (!mirrorsCacheArtifactFile.exists()) {
mirrorsCacheArtifactDir.mkdirs();
String mirrorsUrl = url.replaceFirst(
"http:\\/\\/", "http://mirrors.lax.liferay.com/");
if (tryLocalNetwork) {
try {
_get(
project, mirrorsUrl, mirrorsCacheArtifactFile,
ignoreErrors);
}
catch (Exception e) {
_get(project, url, mirrorsCacheArtifactFile, ignoreErrors);
}
}
else {
_get(project, url, mirrorsCacheArtifactFile, ignoreErrors);
}
}
if (destinationFile == null) {
return mirrorsCacheArtifactFile;
}
Path destinationPath = destinationFile.toPath();
if (destinationFile.isDirectory()) {
destinationPath = destinationPath.resolve(fileName);
}
Files.createDirectories(destinationPath.getParent());
Files.copy(
mirrorsCacheArtifactFile.toPath(), destinationPath,
StandardCopyOption.REPLACE_EXISTING);
return destinationPath.toFile();
}
public static String getAbsolutePath(File file) {
String absolutePath = file.getAbsolutePath();
return absolutePath.replace('\\', '/');
}
public static char getDriveLetter(File file) {
if (!OSDetector.isWindows()) {
throw new UnsupportedOperationException();
}
String absolutePath = getAbsolutePath(file);
char driveLetter = absolutePath.charAt(0);
return Character.toLowerCase(driveLetter);
}
public static boolean isChild(File file, File parentFile) {
Path path = file.toPath();
path = path.toAbsolutePath();
Path parentPath = parentFile.toPath();
parentPath = parentPath.toAbsolutePath();
if (path.startsWith(parentPath)) {
return true;
}
return false;
}
public static boolean isUpToDate(
Project project, Object source, Object target) {
File sourceFile = project.file(source);
File targetFile = project.file(target);
if (!sourceFile.exists() || !targetFile.exists()) {
return false;
}
boolean upToDate = false;
try {
long sourceLastModified = _getLastModified(sourceFile);
long targetLastModified = _getLastModified(targetFile);
if (targetLastModified >= sourceLastModified) {
upToDate = true;
}
}
catch (IOException ioe) {
throw new GradleException(ioe.getMessage(), ioe);
}
return upToDate;
}
public static void jar(
Project project, final File destinationFile, final String duplicate,
final boolean update, final String[][] filesets) {
Closure<Void> closure = new Closure<Void>(project) {
@SuppressWarnings("unused")
public void doCall(AntBuilder antBuilder) {
_invokeAntMethodJar(
antBuilder, destinationFile, duplicate, update, filesets,
null);
}
};
project.ant(closure);
}
public static String merge(Iterable<File> files, String separator) {
StringBuilder sb = new StringBuilder();
for (File file : files) {
sb.append(getAbsolutePath(file));
sb.append(separator);
}
sb.setLength(sb.length() - separator.length());
return sb.toString();
}
public static String read(String resourceName) throws IOException {
StringBuilder sb = new StringBuilder();
ClassLoader classLoader = FileUtil.class.getClassLoader();
try (BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(
classLoader.getResourceAsStream(resourceName)))) {
String line = null;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
sb.append('\n');
}
}
return sb.toString();
}
public static Properties readProperties(File file) throws IOException {
Properties properties = new Properties();
if (file.exists()) {
try (FileInputStream fileInputStream = new FileInputStream(file)) {
properties.load(fileInputStream);
}
}
return properties;
}
public static Properties readProperties(Project project, String fileName)
throws IOException {
File file = project.file(fileName);
return readProperties(file);
}
public static String relativize(File file, File startFile) {
Path path = file.toPath();
Path startPath = startFile.toPath();
Path relativePath = startPath.relativize(path);
return relativePath.toString();
}
public static File replaceExtension(File file, String extension) {
String fileName = file.getPath();
int pos = fileName.lastIndexOf('.');
if (pos != -1) {
if (Validator.isNotNull(extension) && !extension.startsWith(".")) {
extension = "." + extension;
}
fileName = fileName.substring(0, pos) + extension;
}
return new File(fileName);
}
public static FileCollection shrinkClasspath(
Project project, FileCollection fileCollection) {
if (!OSDetector.isWindows()) {
return fileCollection;
}
List<File> shrunkClasspath = new ArrayList<>();
Map<Character, File> driveJarDirs = new HashMap<>();
driveJarDirs.put(getDriveLetter(_TMP_DIR), _TMP_DIR);
driveJarDirs.put(
getDriveLetter(project.getBuildDir()), project.getBuildDir());
char curDriveLetter = 0;
List<File> curDriveFiles = new ArrayList<>();
for (File file : fileCollection) {
char driveLetter = getDriveLetter(file);
if (curDriveLetter != driveLetter) {
File jarFile = _createClasspathJarFile(
project, curDriveFiles, driveJarDirs.get(curDriveLetter));
if (jarFile != null) {
shrunkClasspath.add(jarFile);
}
curDriveLetter = driveLetter;
curDriveFiles.clear();
}
if (driveJarDirs.containsKey(driveLetter)) {
curDriveFiles.add(file);
}
else {
shrunkClasspath.add(file);
}
}
File jarFile = _createClasspathJarFile(
project, curDriveFiles, driveJarDirs.get(curDriveLetter));
if (jarFile != null) {
shrunkClasspath.add(jarFile);
}
return project.files(shrunkClasspath);
}
public static String stripExtension(String fileName) {
int index = fileName.lastIndexOf('.');
if (index != -1) {
fileName = fileName.substring(0, index);
}
return fileName;
}
public static void write(File file, List<String> lines) throws IOException {
try (PrintWriter printWriter = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream(file), StandardCharsets.UTF_8))) {
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if ((i + 1) < lines.size()) {
printWriter.println(line);
}
else {
printWriter.print(line);
}
}
}
}
private static File _createClasspathJarFile(
Project project, List<File> files, File jarDir) {
if (files.isEmpty()) {
return null;
}
if (files.size() == 1) {
return files.get(0);
}
AntBuilder antBuilder = project.createAntBuilder();
jarDir.mkdirs();
File jarFile = new File(jarDir, createTempFileName("classpath", "jar"));
jarFile.deleteOnExit();
String manifestClasspathProperty =
"manifest.classpath." + jarFile.getName();
_invokeAntMethodManifestClasspath(
antBuilder, files, jarFile, manifestClasspathProperty);
String manifestClasspath = String.valueOf(
antBuilder.getProperty(manifestClasspathProperty));
File manifestFile = new File(
jarFile.getParentFile(), jarFile.getName() + ".manifest");
try {
_invokeAntMethodManifest(
antBuilder, manifestFile,
Collections.singletonMap("Class-Path", manifestClasspath));
_invokeAntMethodJar(
antBuilder, jarFile, null, false, new String[0][0],
manifestFile);
}
finally {
project.delete(manifestFile);
}
return jarFile;
}
private static void _get(
Project project, final String url, File destinationFile,
final boolean ignoreErrors) {
final File tmpFile = new File(
destinationFile.getParentFile(),
destinationFile.getName() + ".tmp");
project.delete(destinationFile, tmpFile);
Closure<Void> closure = new Closure<Void>(project) {
@SuppressWarnings("unused")
public void doCall(AntBuilder antBuilder) {
Map<String, Object> args = new HashMap<>();
args.put("dest", tmpFile);
args.put("ignoreerrors", ignoreErrors);
args.put("src", url);
if (_logger.isLifecycleEnabled()) {
_logger.lifecycle(
"Trying to download " + url + " to " + tmpFile);
}
antBuilder.invokeMethod("get", args);
}
};
project.ant(closure);
if (!tmpFile.renameTo(destinationFile)) {
throw new GradleException(
"Unable to rename " + tmpFile + " to " + destinationFile);
}
}
private static long _getLastModified(File file) throws IOException {
if (file.isFile()) {
return file.lastModified();
}
final AtomicLong lastModified = new AtomicLong();
Files.walkFileTree(
file.toPath(),
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(
Path path, BasicFileAttributes basicFileAttributes)
throws IOException {
FileTime fileTime = basicFileAttributes.lastModifiedTime();
long fileTimeMillis = fileTime.toMillis();
if (fileTimeMillis > lastModified.longValue()) {
lastModified.set(fileTimeMillis);
}
return FileVisitResult.CONTINUE;
}
});
return lastModified.get();
}
private static File _getMirrorsCacheDir() {
String userHome = System.getProperty("user.home");
return new File(userHome, ".liferay/mirrors");
}
private static void _invokeAntMethod(
AntBuilder antBuilder, String name, String argKey, Object argValue) {
antBuilder.invokeMethod(
name, Collections.singletonMap(argKey, argValue));
}
private static void _invokeAntMethodClasspath(
final AntBuilder antBuilder, final String path) {
Closure<Void> closure = new Closure<Void>(antBuilder) {
@SuppressWarnings("unused")
public void doCall() {
_invokeAntMethod(antBuilder, "pathelement", "path", path);
}
};
antBuilder.invokeMethod("classpath", closure);
}
private static void _invokeAntMethodFileset(
AntBuilder antBuilder, String[] fileset) {
Map<String, String> args = new HashMap<>();
args.put("dir", fileset[0]);
args.put("includes", fileset[1]);
antBuilder.invokeMethod("fileset", args);
}
private static void _invokeAntMethodJar(
final AntBuilder antBuilder, File destinationFile, String duplicate,
boolean update, final String[][] filesets, File manifestFile) {
Map<String, Object> args = new HashMap<>();
args.put("destfile", destinationFile);
if (Validator.isNotNull(duplicate)) {
args.put("duplicate", duplicate);
}
if (manifestFile != null) {
args.put("manifest", manifestFile);
}
args.put("update", update);
Closure<Void> closure = new Closure<Void>(antBuilder) {
@SuppressWarnings("unused")
public void doCall() {
for (String[] fileset : filesets) {
_invokeAntMethodFileset(antBuilder, fileset);
}
}
};
antBuilder.invokeMethod("jar", new Object[] {args, closure});
}
private static void _invokeAntMethodManifest(
final AntBuilder antBuilder, File file,
final Map<String, String> attributes) {
Map<String, File> args = Collections.singletonMap("file", file);
Closure<Void> closure = new Closure<Void>(antBuilder) {
@SuppressWarnings("unused")
public void doCall() {
Map<String, String> args = new HashMap<>();
for (Map.Entry<String, String> entry : attributes.entrySet()) {
args.put("name", entry.getKey());
args.put("value", entry.getValue());
antBuilder.invokeMethod("attribute", args);
}
}
};
antBuilder.invokeMethod("manifest", new Object[] {args, closure});
}
private static void _invokeAntMethodManifestClasspath(
final AntBuilder antBuilder, final Iterable<File> files, File jarFile,
String property) {
Map<String, Object> args = new HashMap<>();
args.put("jarfile", jarFile);
args.put("maxParentLevels", 99);
args.put("property", property);
Closure<Void> closure = new Closure<Void>(antBuilder) {
@SuppressWarnings("unused")
public void doCall() {
_invokeAntMethodClasspath(
antBuilder, merge(files, File.pathSeparator));
}
};
antBuilder.invokeMethod(
"manifestclasspath", new Object[] {args, closure});
}
private static final File _TMP_DIR = new File(
System.getProperty("java.io.tmpdir"));
private static final Logger _logger = Logging.getLogger(FileUtil.class);
}