package dtool.util;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import melnorme.utilbox.collections.SimpleChainedIterator;
import melnorme.utilbox.core.Assert;
import melnorme.utilbox.misc.IteratorUtil;
import melnorme.utilbox.misc.Pair;
import melnorme.utilbox.misc.StreamUtil;
public class NewUtils {
/** Asserts that given object is null or an instance of given klass. Returns casted object. */
@SuppressWarnings("unchecked")
public static <T> T assertCast(Object object, Class<T> klass) {
assertTrue(object == null || klass.isInstance(object));
return (T) object;
}
/** Asserts that given object an instance of given klass. Returns casted object. */
@SuppressWarnings("unchecked")
public static <T> T assertInstance(Object object, Class<T> klass) {
assertTrue(klass.isInstance(object));
return (T) object;
}
/* ----------------- collection utils ----------------- */
public static <T> T getSingleElementOrNull(Iterable<T> iterable) {
if(iterable == null) {
return null;
}
Iterator<T> iterator = iterable.iterator();
if(iterator.hasNext() == false) {
return null;
}
T next = iterator.next();
assertTrue(iterator.hasNext() == false);
return next;
}
/* ----------------- ----------------- */
public static final String[] EMPTY_STRING_ARRAY = new String[0];
/** Shortcut for creating a new {@link ArrayList} */
public static <T> ArrayList<T> createArrayList(Collection<T> coll) {
return new ArrayList<T>(coll);
}
public static <T> Iterator<? extends T> getChainedIterator(
Iterable<? extends T> iter1, Iterable<? extends T> iter2)
{
if(iter1 == null && iter2 == null)
return IteratorUtil.emptyIterator();
if(iter1 == null)
return iter2.iterator();
if(iter2 == null)
return iter1.iterator();
return new SimpleChainedIterator<T>(iter1.iterator(), iter2.iterator());
}
@SafeVarargs
public static <T> T[] assertNotContainsNull_(T... arr) {
for (T elem : arr) {
Assert.AssertNamespace.assertNotNull(elem);
}
return arr;
}
public static <T> T lastElement(List<T> list) {
return list.get(list.size()-1);
}
public static <T> T[] emptyToNull(T[] array) {
if(array == null || array.length == 0) {
return null;
}
return array;
}
public static String emptyToNull(String string) {
if(string.isEmpty()) {
return null;
}
return string;
}
/** Returns true if one, and only one of the given objects is null. */
public static boolean exactlyOneIsNull(Object objA, Object objB) {
return (objA == null) != (objB == null);
}
public static int updateIfNull(int currentValue, int newValue) {
return currentValue == -1 ? newValue : currentValue;
}
public static String replaceRegexFirstOccurrence(String str, String regex, int regexGroup, String replacement) {
Matcher matcher = Pattern.compile(regex).matcher(str);
if(matcher.find()) {
int matchIx = matcher.start();
int matchEndIx = matchIx + matcher.group(regexGroup).length();
return str.substring(0, matchIx) + replacement + str.substring(matchEndIx, str.length());
} else {
return str;
}
}
public static String substringRemoveEnd(String string, int lengthFromEnd) {
return string.substring(0, string.length() - lengthFromEnd);
}
public static boolean isValidStringRange(String string, int startIndex, int length) {
return startIndex >= 0 && length >= 0 && startIndex + length <= string.length();
}
public static String removeRange(String string, int startIndex, int length) {
assertTrue(isValidStringRange(string, startIndex, length));
int endIndex = startIndex + length;
return string.substring(0, startIndex) + string.substring(endIndex, string.length());
}
public static String replaceRange(String string, int startIndex, int length, String repl) {
assertTrue(isValidStringRange(string, startIndex, length));
int endIndex = startIndex + length;
return string.substring(0, startIndex) + repl + string.substring(endIndex, string.length());
}
public static <T, U> void addNew(Map<T, U> map, Map<? extends T, ? extends U> newGlobalExpansions) {
for (Entry<? extends T, ? extends U> entry : newGlobalExpansions.entrySet()) {
assertTrue(map.containsKey(entry.getKey()) == false);
map.put(entry.getKey(), entry.getValue());
}
}
public static <T> ArrayList<T> lazyInitArrayList(ArrayList<T> arrayList) {
if(arrayList == null) {
return new ArrayList<T>();
}
return arrayList;
}
/**
* Compares two strings according to the contract of {@link Comparator#compare(Object, Object)}
* Allows null values.
*/
public static int compareStrings(String str1, String str2) {
if(str1 == str2) {
return 0;
}
if(str1 == null && str2 != null) {
return -1;
}
if(str2 == null && str1 != null) {
return 1;
}
return str1.compareTo(str2);
}
@SafeVarargs
public static <K, V> HashMap<K, V> newHashMap(Pair<K, V>... entries) {
return addEntries(new HashMap<K, V>(), entries);
}
@SafeVarargs
public static <K, V> HashMap<K, V> addEntries(HashMap<K, V> hashMap, Pair<K, V>... entries) {
for (Pair<K,V> pair : entries) {
hashMap.put(pair.getFirst(), pair.getSecond());
}
return hashMap;
}
public static final int EOF = -1;
public static final Charset UTF8 = assertNotNull(Charset.forName("UTF-8"));
public static final Charset UTF16BE = assertNotNull(Charset.forName("UTF-16BE"));
public static final Charset UTF16LE = assertNotNull(Charset.forName("UTF-16LE"));
public static final Charset UTF32BE = assertNotNull(Charset.forName("UTF-32BE"));
public static final Charset UTF32LE = assertNotNull(Charset.forName("UTF-32LE"));
public static Charset detectEncoding(InputStream is, boolean resetIS) throws IOException {
byte[] bom = new byte[4];
bom[3] = 0x42; // To disambiguate UTF16LE edge case and UTF32LE
int read = is.read(bom);
assertTrue(read == bom.length || is.read() == EOF);
if(resetIS) {
is.reset();
}
if(bom[0] == (byte)0xEF && bom[1] == (byte)0xBB && bom[2] == (byte)0xBF) {
return UTF8;
}
if(bom[0] == (byte)0xFE && bom[1] == (byte)0xFF) {
return UTF16BE;
}
if(bom[0] == (byte)0xFF && bom[1] == (byte)0xFE) {
if(bom[2] == 0x00 && bom[3] == 0x00) {
return UTF32LE;
}
return UTF16LE;
}
if(bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == (byte)0xFE && bom[3] == (byte)0xFF) {
return UTF32BE;
}
return null;
}
public static String readStringFromFile_PreserveBOM(File file, Charset defaultCharset) throws IOException {
FileInputStream inputStream = new FileInputStream(file);
return readStringFromStream_preserveBOM(inputStream, defaultCharset);
}
// XXX: This method could be optimized a bit
public static String readStringFromStream_preserveBOM(InputStream is, Charset defaultCharset)
throws IOException {
byte[] fileBytes = StreamUtil.readAllBytesFromStream(is).toByteArray();
final Charset encoding = detectEncoding(new ByteArrayInputStream(fileBytes), false);
String string = new String(fileBytes, encoding == null ? defaultCharset : encoding);
if(encoding != null) {
if(encoding == UTF32BE || encoding == UTF32LE) {
string = "\uFEFF" + string; // Make things consistent across encodings
}
assertTrue(string.charAt(0) == 0xFEFF);
}
return string;
}
public static <T> List<T> arrayListFromElement(T element) {
ArrayList<T> list = new ArrayList<>();
list.add(element);
return list;
}
}