/*
* Copyright 2010 the original author or authors.
*
* 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.gradle.util;
import com.google.common.base.Charsets;
import org.apache.commons.lang.StringUtils;
import org.gradle.api.UncheckedIOException;
import org.gradle.internal.UncheckedException;
import org.gradle.internal.io.LineBufferingOutputStream;
import org.gradle.internal.io.SkipFirstTextStream;
import org.gradle.internal.io.StreamByteBuffer;
import org.gradle.internal.io.WriterTextStream;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
public class GUtil {
private static final Pattern WORD_SEPARATOR = Pattern.compile("\\W+");
private static final Pattern UPPER_LOWER = Pattern.compile("(?m)([A-Z]*)([a-z0-9]*)");
public static <T extends Collection> T flatten(Object[] elements, T addTo, boolean flattenMaps) {
return flatten(asList(elements), addTo, flattenMaps);
}
public static <T extends Collection> T flatten(Object[] elements, T addTo) {
return flatten(asList(elements), addTo);
}
public static <T extends Collection> T flatten(Collection elements, T addTo) {
return flatten(elements, addTo, true);
}
public static <T extends Collection> T flattenElements(Object... elements) {
Collection<T> out = new LinkedList<T>();
flatten(elements, out, true);
return (T) out;
}
public static <T extends Collection> T flatten(Collection elements, T addTo, boolean flattenMapsAndArrays) {
return flatten(elements, addTo, flattenMapsAndArrays, flattenMapsAndArrays);
}
public static <T extends Collection> T flatten(Collection elements, T addTo, boolean flattenMaps, boolean flattenArrays) {
Iterator iter = elements.iterator();
while (iter.hasNext()) {
Object element = iter.next();
if (element instanceof Collection) {
flatten((Collection) element, addTo, flattenMaps, flattenArrays);
} else if ((element instanceof Map) && flattenMaps) {
flatten(((Map) element).values(), addTo, flattenMaps, flattenArrays);
} else if ((element.getClass().isArray()) && flattenArrays) {
flatten(asList((Object[]) element), addTo, flattenMaps, flattenArrays);
} else {
addTo.add(element);
}
}
return addTo;
}
/**
* Flattens input collections (including arrays *but* not maps). If input is not a collection wraps it in a collection and returns it.
*
* @param input any object
* @return collection of flattened input or single input wrapped in a collection.
*/
public static Collection collectionize(Object input) {
if (input == null) {
return emptyList();
} else if (input instanceof Collection) {
Collection out = new LinkedList();
flatten((Collection) input, out, false, true);
return out;
} else if (input.getClass().isArray()) {
Collection out = new LinkedList();
flatten(asList((Object[]) input), out, false, true);
return out;
} else {
return asList(input);
}
}
public static List flatten(Collection elements, boolean flattenMapsAndArrays) {
return flatten(elements, new ArrayList(), flattenMapsAndArrays);
}
public static List flatten(Collection elements) {
return flatten(elements, new ArrayList());
}
public static String asPath(Iterable<?> collection) {
return CollectionUtils.join(File.pathSeparator, collection);
}
public static List<String> prefix(String prefix, Collection<String> strings) {
List<String> prefixed = new ArrayList<String>();
for (String string : strings) {
prefixed.add(prefix + string);
}
return prefixed;
}
public static boolean isTrue(Object object) {
if (object == null) {
return false;
}
if (object instanceof Collection) {
return ((Collection) object).size() > 0;
} else if (object instanceof String) {
return ((String) object).length() > 0;
}
return true;
}
public static <T> T elvis(T object, T defaultValue) {
return isTrue(object) ? object : defaultValue;
}
public static <V, T extends Collection<? super V>> T addToCollection(T dest, boolean failOnNull, Iterable<? extends V>... srcs) {
for (Iterable<? extends V> src : srcs) {
for (V v : src) {
if (failOnNull && v == null) {
throw new IllegalArgumentException("Illegal null value provided in this collection: " + src);
}
dest.add(v);
}
}
return dest;
}
public static <V, T extends Collection<? super V>> T addToCollection(T dest, Iterable<? extends V>... srcs) {
return addToCollection(dest, false, srcs);
}
public static Comparator<String> caseInsensitive() {
return new Comparator<String>() {
public int compare(String o1, String o2) {
int diff = o1.compareToIgnoreCase(o2);
if (diff != 0) {
return diff;
}
return o1.compareTo(o2);
}
};
}
public static Map addMaps(Map map1, Map map2) {
HashMap map = new HashMap();
map.putAll(map1);
map.putAll(map2);
return map;
}
public static void addToMap(Map<String, String> dest, Map<?, ?> src) {
for (Map.Entry<?, ?> entry : src.entrySet()) {
dest.put(entry.getKey().toString(), entry.getValue().toString());
}
}
public static Properties loadProperties(File propertyFile) {
try {
FileInputStream inputStream = new FileInputStream(propertyFile);
try {
return loadProperties(inputStream);
} finally {
inputStream.close();
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public static Properties loadProperties(URL url) {
try {
URLConnection uc = url.openConnection();
uc.setUseCaches(false);
return loadProperties(uc.getInputStream());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public static Properties loadProperties(InputStream inputStream) {
Properties properties = new Properties();
try {
properties.load(inputStream);
inputStream.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return properties;
}
public static void saveProperties(Properties properties, File propertyFile) {
try {
FileOutputStream propertiesFileOutputStream = new FileOutputStream(propertyFile);
try {
properties.store(propertiesFileOutputStream, null);
} finally {
propertiesFileOutputStream.close();
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public static void saveProperties(Properties properties, OutputStream outputStream) {
try {
try {
properties.store(outputStream, null);
} finally {
outputStream.close();
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public static void savePropertiesNoDateComment(Properties properties, OutputStream outputStream) {
saveProperties(properties,
new LineBufferingOutputStream(
new SkipFirstTextStream(
new WriterTextStream(
new OutputStreamWriter(outputStream, Charsets.ISO_8859_1)
)
)
)
);
}
public static Map map(Object... objects) {
Map map = new HashMap();
assert objects.length % 2 == 0;
for (int i = 0; i < objects.length; i += 2) {
map.put(objects[i], objects[i + 1]);
}
return map;
}
public static String toString(Iterable<?> names) {
Formatter formatter = new Formatter();
boolean first = true;
for (Object name : names) {
if (first) {
formatter.format("'%s'", name);
first = false;
} else {
formatter.format(", '%s'", name);
}
}
return formatter.toString();
}
/**
* Converts an arbitrary string to a camel-case string which can be used in a Java identifier. Eg, with_underscores -> withUnderscores
*/
public static String toCamelCase(CharSequence string) {
if (string == null) {
return null;
}
StringBuilder builder = new StringBuilder();
Matcher matcher = WORD_SEPARATOR.matcher(string);
int pos = 0;
while (matcher.find()) {
builder.append(StringUtils.capitalize(string.subSequence(pos, matcher.start()).toString()));
pos = matcher.end();
}
builder.append(StringUtils.capitalize(string.subSequence(pos, string.length()).toString()));
return builder.toString();
}
public static String toLowerCamelCase(CharSequence string) {
String camelCase = toCamelCase(string);
if (camelCase == null) {
return null;
}
if (camelCase.length() == 0) {
return "";
}
return ((Character) camelCase.charAt(0)).toString().toLowerCase() + camelCase.subSequence(1, camelCase.length());
}
/**
* Converts an arbitrary string to upper case identifier with words separated by _. Eg, camelCase -> CAMEL_CASE
*/
public static String toConstant(CharSequence string) {
if (string == null) {
return null;
}
return toWords(string, '_').toUpperCase();
}
/**
* Converts an arbitrary string to space-separated words. Eg, camelCase -> camel case, with_underscores -> with underscores
*/
public static String toWords(CharSequence string) {
return toWords(string, ' ');
}
public static String toWords(CharSequence string, char separator) {
if (string == null) {
return null;
}
StringBuilder builder = new StringBuilder();
int pos = 0;
Matcher matcher = UPPER_LOWER.matcher(string);
while (pos < string.length()) {
matcher.find(pos);
if (matcher.end() == pos) {
// Not looking at a match
pos++;
continue;
}
if (builder.length() > 0) {
builder.append(separator);
}
String group1 = matcher.group(1).toLowerCase();
String group2 = matcher.group(2);
if (group2.length() == 0) {
builder.append(group1);
} else {
if (group1.length() > 1) {
builder.append(group1.substring(0, group1.length() - 1));
builder.append(separator);
builder.append(group1.substring(group1.length() - 1));
} else {
builder.append(group1);
}
builder.append(group2);
}
pos = matcher.end();
}
return builder.toString();
}
public static byte[] serialize(Object object) {
StreamByteBuffer buffer = new StreamByteBuffer();
serialize(object, buffer.getOutputStream());
return buffer.readAsByteArray();
}
public static void serialize(Object object, OutputStream outputStream) {
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(object);
objectOutputStream.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public static <T> Comparator<T> last(final Comparator<? super T> comparator, final T lastValue) {
return new Comparator<T>() {
public int compare(T o1, T o2) {
boolean o1Last = comparator.compare(o1, lastValue) == 0;
boolean o2Last = comparator.compare(o2, lastValue) == 0;
if (o1Last && o2Last) {
return 0;
}
if (o1Last && !o2Last) {
return 1;
}
if (!o1Last && o2Last) {
return -1;
}
return comparator.compare(o1, o2);
}
};
}
/**
* Calls the given callable converting any thrown exception to an unchecked exception via {@link UncheckedException#throwAsUncheckedException(Throwable)}
*
* @param callable The callable to call
* @param <T> Callable's return type
* @return The value returned by {@link Callable#call()}
*/
public static <T> T uncheckedCall(Callable<T> callable) {
try {
return callable.call();
} catch (Exception e) {
throw UncheckedException.throwAsUncheckedException(e);
}
}
/**
* Note that setting the January 1st 1980 (or even worse, "0", as time) won't work due
* to Java 8 doing some interesting time processing: It checks if this date is before January 1st 1980
* and if it is it starts setting some extra fields in the zip. Java 7 does not do that - but in the
* zip not the milliseconds are saved but values for each of the date fields - but no time zone. And
* 1980 is the first year which can be saved.
* If you use January 1st 1980 then it is treated as a special flag in Java 8.
* Moreover, only even seconds can be stored in the zip file. Java 8 uses the upper half of
* some other long to store the remaining millis while Java 7 doesn't do that. So make sure
* that your seconds are even.
* Moreover, parsing happens via `new Date(millis)` in {@link java.util.zip.ZipUtils}#javaToDosTime() so we
* must use default timezone and locale.
*
* The date is 1980 February 1st.
*/
public static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = new GregorianCalendar(1980, Calendar.FEBRUARY, 1, 0, 0, 0).getTimeInMillis();
/**
* Tar files are simpler. We can just use "0" as a timestamp.
*/
public static final long CONSTANT_TIME_FOR_TAR_ENTRIES = 0;
}