package org.netbeans.gradle.model.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
public final class BasicFileUtils {
private static final Logger LOGGER = Logger.getLogger(BasicFileUtils.class.getName());
private static final char[] HEX_TABLE_LOWER = "0123456789abcdef".toCharArray();
private static final char[] HEX_TABLE_UPPER = "0123456789ABCDEF".toCharArray();
private static final AtomicReference<File> NB_GRADLE_TEMP_DIR_REF = new AtomicReference<File>(null);
private static final int MAX_TMP_FILE_WITH_SAME_NAME = 5;
private static int sumLengths(String... strings) {
int length = 0;
for (String string: strings) {
length += string.length();
}
return length;
}
private static void appendAsEscaped(char ch, StringBuilder result) {
result.append('\\');
result.append('u');
int value = ch & 0xFFFF;
result.append(HEX_TABLE_UPPER[(value >>> 12) & 0xF]);
result.append(HEX_TABLE_UPPER[(value >>> 8) & 0xF]);
result.append(HEX_TABLE_UPPER[(value >>> 4) & 0xF]);
result.append(HEX_TABLE_UPPER[value & 0xF]);
}
public static String toSafelyPastableToJavaCode(String... strings) {
StringBuilder result = new StringBuilder(sumLengths(strings));
char lastCh = '?'; // Anything but '\\'
for (String string: strings) {
for (int i = 0; i < string.length(); i++) {
char ch = string.charAt(i);
if ((lastCh == '\\' && ch == 'u') || ch > 127) {
appendAsEscaped(ch, result);
}
else {
result.append(ch);
}
lastCh = ch;
}
}
return result.toString();
}
private static File findAvailableTempFileDir() {
File tempDir = new File(System.getProperty("java.io.tmpdir"));
String userName = makeSafeForFileNames(System.getProperty("user.name"));
String defaultDirName = "nb-gradle-plugin-" + userName;
for (int i = 0; i < MAX_TMP_FILE_WITH_SAME_NAME; i++) {
String dirName = i > 0
? defaultDirName + "-" + i
: defaultDirName;
File result = getSubPath(tempDir, dirName);
result.mkdirs();
if (isWriteable(result)) {
return result;
}
}
SecureRandom rand = new SecureRandom();
String part1 = Long.toHexString(rand.nextLong());
String part2 = Long.toHexString(rand.nextLong());
File result = getSubPath(tempDir, defaultDirName + "-" + part1 + "-" + part2);
result.mkdirs();
if (isWriteable(result)) {
LOGGER.log(Level.WARNING,
"Using a directory with random name for temporary storage: {0}",
result);
return result;
}
LOGGER.log(Level.WARNING,
"Couldn't create any directory for test files. Using the temp directory: {0}",
tempDir);
return tempDir;
}
private static boolean isWriteable(File dir) {
if (!dir.isDirectory()) {
return false;
}
File testTempFile = null;
try {
testTempFile = File.createTempFile("write-test", ".tmp", dir);
if (!testTempFile.isFile()) {
return false;
}
int testByte = 0xAC;
FileOutputStream output = new FileOutputStream(testTempFile);
try {
output.write(testByte);
output.flush();
} finally {
output.close();
}
FileInputStream input = new FileInputStream(testTempFile);
try {
if (input.read() != testByte) {
return false;
}
} finally {
input.close();
}
return true;
} catch (IOException ex) {
return false;
} finally {
if (testTempFile != null) {
testTempFile.delete();
}
}
}
private static boolean isCharOkForFileName(char ch) {
if (ch >= 'A' && ch <= 'Z') return true;
if (ch >= 'a' && ch <= 'z') return true;
if (ch >= '0' && ch <= '9') return true;
return ch == '-' || ch == '_';
}
private static String makeSafeForFileNames(String unsafeName) {
if (unsafeName == null) {
return "";
}
StringBuilder result = new StringBuilder(unsafeName.length());
for (int i = 0; i < unsafeName.length(); i++) {
char ch = unsafeName.charAt(i);
if (isCharOkForFileName(ch)) {
result.append(ch);
}
else {
result.append('_');
}
}
return result.toString();
}
public static File getPluginTmpDir() {
File result = NB_GRADLE_TEMP_DIR_REF.get();
if (result == null) {
result = toCanonicalFile(findAvailableTempFileDir());
NB_GRADLE_TEMP_DIR_REF.compareAndSet(null, result);
result = NB_GRADLE_TEMP_DIR_REF.get();
}
return result;
}
public static File createTmpFile(String preferredName, String suffix) throws IOException {
File tmpDir = getPluginTmpDir();
if (!tmpDir.isDirectory()) {
if (!tmpDir.mkdirs()) {
LOGGER.log(Level.WARNING, "Could not create directory: {0}", tmpDir);
}
}
File result = getSubPath(tmpDir, preferredName + suffix);
if (result.createNewFile()) {
return result;
}
for (int i = 1; i <= MAX_TMP_FILE_WITH_SAME_NAME; i++) {
result = getSubPath(tmpDir, preferredName + i + suffix);
if (result.createNewFile()) {
return result;
}
}
return File.createTempFile(preferredName, suffix, tmpDir);
}
public static File getSubPath(File root, String... subPaths) {
File result = root;
for (String subprojectName: subPaths) {
result = new File(result, subprojectName);
}
return result;
}
public static File toCanonicalFile(File file) {
if (file == null) {
return null;
}
try {
return file.getCanonicalFile();
} catch (IOException ex) {
return file;
}
}
public static String getMD5(byte[] input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] md5Hash = md.digest(input);
return byteArrayToHex(md5Hash);
} catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException("Missing MD5 MessageDigest");
}
}
private static String byteArrayToHex(byte[] array) {
StringBuilder result = new StringBuilder(array.length * 2);
for (byte value: array) {
result.append(HEX_TABLE_LOWER[((int)value & 0xF0) >>> 4]);
result.append(HEX_TABLE_LOWER[(int)value & 0x0F]);
}
return result.toString();
}
private BasicFileUtils() {
throw new AssertionError();
}
}