You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
8.7 KiB
271 lines
8.7 KiB
package util;
|
|
|
|
import java.io.File;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.URLDecoder;
|
|
import java.text.MessageFormat;
|
|
import java.util.*;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
import java.util.stream.Stream;
|
|
|
|
import data.MultipleHMKeys;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
import data.Settings;
|
|
import gui.GUIController;
|
|
import gui.ValidationUtil;
|
|
|
|
public class Util {
|
|
public final static Logger logger = LogManager.getLogger(Util.class);
|
|
|
|
|
|
public static String toReadableTime(long time) {
|
|
long hours = time(TimeUnit.HOURS, time);
|
|
long minutes = time(TimeUnit.MINUTES, time) - TimeUnit.HOURS.toMinutes(hours);
|
|
long seconds = time(TimeUnit.SECONDS, time) - TimeUnit.HOURS.toSeconds(hours) - TimeUnit.MINUTES.toSeconds(minutes);
|
|
long milliseconds = time(TimeUnit.MILLISECONDS, time) - TimeUnit.HOURS.toMillis(hours) - TimeUnit.MINUTES.toMillis(minutes) - TimeUnit.SECONDS.toMillis(seconds);
|
|
long microseconds = time(TimeUnit.MICROSECONDS, time) - TimeUnit.HOURS.toMicros(hours) - TimeUnit.MINUTES.toMicros(minutes) - TimeUnit.SECONDS.toMicros(seconds) - TimeUnit.MILLISECONDS.toMicros(milliseconds);
|
|
long nanoseconds = time(TimeUnit.NANOSECONDS, time) - TimeUnit.HOURS.toNanos(hours) - TimeUnit.MINUTES.toNanos(minutes) - TimeUnit.SECONDS.toNanos(seconds) - TimeUnit.MILLISECONDS.toNanos(milliseconds) - TimeUnit.MICROSECONDS.toNanos(microseconds);
|
|
|
|
return String.format("%d h, %d min, %d s, %d ms, %d µs, %d ns", hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
|
|
}
|
|
|
|
private static long time(TimeUnit unit, long t) {
|
|
return unit.convert(t, TimeUnit.NANOSECONDS);
|
|
}
|
|
|
|
/**
|
|
* Converts a number to a more readable format.
|
|
* 12345 -> 12.345
|
|
* 12345,678 -> 12.345,67
|
|
*
|
|
* @param o byte, double, float, int,long, short
|
|
*
|
|
* @return number formatted with thousands separator and 2 decimal places (floats)
|
|
*/
|
|
private static String formatNumberReadable(Object o) {
|
|
if (isInstanceOfInteger(o))
|
|
return String.format("%,d", o);
|
|
else if (isInstanceOfFloat(o))
|
|
return String.format("%,.2f", o);
|
|
else
|
|
return "- invalid input format -";
|
|
}
|
|
|
|
public static String formatNumberAsPercent(Object o, String punctuation) {
|
|
if(punctuation.equals("punctuation.COMMA")) {
|
|
return MessageFormat.format("{0,number,#.### %}", o).replace('.', ',');
|
|
} else {
|
|
return MessageFormat.format("{0,number,#.### %}", o);
|
|
}
|
|
}
|
|
|
|
public static String formatNumberForExport(Object o, String punctuation) {
|
|
if(punctuation.equals("punctuation.COMMA")) {
|
|
return MessageFormat.format("{0,number,#.##}", o).replace('.', ',');
|
|
} else {
|
|
return MessageFormat.format("{0,number,#.##}", o);
|
|
}
|
|
|
|
}
|
|
|
|
public static String formatNumberForLongExport(Object o, String punctuation) {
|
|
if(punctuation.equals("punctuation.COMMA")) {
|
|
return MessageFormat.format("{0,number,#.########}", o).replace('.', ',');
|
|
} else {
|
|
return MessageFormat.format("{0,number,#.########}", o);
|
|
}
|
|
}
|
|
|
|
private static boolean isInstanceOfInteger(Object o) {
|
|
Set<Class<?>> types = new HashSet<>();
|
|
types.add(Byte.class);
|
|
types.add(Short.class);
|
|
types.add(Integer.class);
|
|
types.add(Long.class);
|
|
|
|
return types.contains(o.getClass());
|
|
}
|
|
|
|
private static boolean isInstanceOfFloat(Object o) {
|
|
Set<Class<?>> types = new HashSet<>();
|
|
types.add(Float.class);
|
|
types.add(Double.class);
|
|
|
|
return types.contains(o.getClass());
|
|
}
|
|
|
|
public static <K, V> void printMap(Map<K, V> map) {
|
|
System.out.println("\nkey: value");
|
|
map.forEach((k, v) -> System.out.print(String.format("%s:\t %,8d%n", k, v)));
|
|
System.out.println();
|
|
}
|
|
|
|
/**
|
|
* Generic map converter -> since AtomicLongs aren't as comparable.
|
|
* Converts ConcurrentHashMap<K, AtomicLong> to HashMap<K, Long>
|
|
*/
|
|
public static <K, V> Map<MultipleHMKeys, Long> atomicInt2StringAndInt(Map<K, V> map) {
|
|
Map m = new HashMap<MultipleHMKeys, Long>();
|
|
|
|
for (Map.Entry<K, V> e : map.entrySet()) {
|
|
m.put(e.getKey(), ((AtomicLong) e.getValue()).longValue());
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
public class ValueThenKeyComparator<K extends Comparable<? super K>,
|
|
V extends Comparable<? super V>>
|
|
implements Comparator<Map.Entry<K, V>> {
|
|
|
|
public int compare(Map.Entry<K, V> a, Map.Entry<K, V> b) {
|
|
int cmp1 = a.getValue().compareTo(b.getValue());
|
|
if (cmp1 != 0) {
|
|
return cmp1;
|
|
} else {
|
|
return a.getKey().compareTo(b.getKey());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Sorts a map in a descending order by value.
|
|
*/
|
|
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map, int limit) {
|
|
/*
|
|
sorted() in itself is O(1), since it's an intermediate operation that
|
|
doesn't consume the stream, but simply adds an operation to the pipeline.
|
|
Once the stream is consumed by a terminal operation, the sort happens and
|
|
either
|
|
- it doesn't do anything (O(1)) because the stream knows that the
|
|
elements are already sorted (because they come from a SortedSet, for example)
|
|
- or the stream is not parallel, and it delegates to Arrays.sort() (O(n log n))
|
|
- or the stream is parallel, and it delegates to Arrays.parallelSort() (O(n log n))
|
|
|
|
As of JDK 8, the main sorting algorithm which is also used in standard
|
|
stream API implementation for sequential sorting is TimSort. Its worst
|
|
case is O(n log n), but it works incredibly fast (with O(n) and quite
|
|
small constant) if data is presorted (in forward or reverse direction)
|
|
or partially presorted (for example, if you concatenate two sorted lists
|
|
and sort them again).
|
|
*/
|
|
// if limit is set to 0 or less, we take that to mean no limit at all
|
|
if (limit <= 0) {
|
|
limit = map.size();
|
|
}
|
|
|
|
TimeWatch watch = TimeWatch.start();
|
|
|
|
// sort by alphabet
|
|
Map<K, V> alphaSorted = new LinkedHashMap<>();
|
|
map.entrySet().stream().sorted((a, b) -> ((MultipleHMKeys)a.getKey()).compareTo((MultipleHMKeys)b.getKey())).limit(limit)
|
|
.forEachOrdered(e -> alphaSorted.put(e.getKey(), e.getValue()));
|
|
|
|
|
|
|
|
Map<K, V> result = new LinkedHashMap<>();
|
|
alphaSorted.entrySet().stream().sorted((a, b) -> (int) ((Long) b.getValue() - (Long) a.getValue())).limit(limit)
|
|
.forEachOrdered(e -> result.put(e.getKey(), e.getValue()));
|
|
|
|
if (Settings.PRINT_LOG) {
|
|
System.out.println(String.format("Elapsed time for sorting %s items: %s",
|
|
formatNumberReadable(result.size()),
|
|
watch.toFullTime()));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static <K, V> void printMap(Map<K, Integer> map, String title, int number_of_words) {
|
|
System.out.println(String.format("\n%s\n------------\nkey: value\tpercent", title));
|
|
map.forEach((k, v) ->
|
|
System.out.println(String.format("%s:\t %s\t %s%%",
|
|
k,
|
|
Util.formatNumberReadable(v),
|
|
Util.formatNumberReadable((double) v / number_of_words * 100))));
|
|
System.out.println();
|
|
}
|
|
|
|
static long mapSumFrequencies(Map<MultipleHMKeys, Long> map) {
|
|
long sum = 0;
|
|
|
|
for (long value : map.values()) {
|
|
sum += value;
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
/**
|
|
* Used for passing optional integer values for sorting.
|
|
*/
|
|
public static int getValidInt(int... i) {
|
|
if (i == null || i.length < 1 || i[0] <= 0) {
|
|
return 0;
|
|
} else {
|
|
return i[0];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check whether a map is empty. It also considers an edge case where map's keys are lists to check if those lists are empty.
|
|
*/
|
|
public static <K, V> boolean isMapEmpty(Map<K, V> map) {
|
|
if (map.isEmpty()) {
|
|
// default
|
|
return true;
|
|
}
|
|
|
|
// otherwise check if keys map to values that are empty
|
|
for (V v : map.values()) {
|
|
// todo: generalize to all collections if/when needed
|
|
ArrayList<String> vl = new ArrayList((List<String>) v);
|
|
if (!vl.isEmpty()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the location of the main class if possible, otherwise null
|
|
*/
|
|
public static File getWorkingDirectory() {
|
|
// get location of the currently executing class
|
|
String path = GUIController.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
|
|
|
logger.info("working dir path: ", path);
|
|
|
|
String decodedPath = null;
|
|
try {
|
|
decodedPath = URLDecoder.decode(path, "UTF-8");
|
|
} catch (UnsupportedEncodingException e) {
|
|
logger.error("decoding: ", e);
|
|
// e.printStackTrace();
|
|
}
|
|
|
|
if (decodedPath != null) {
|
|
File workingDirectory = new File(decodedPath);
|
|
|
|
// in case it's a file (class is packaged inside a jar), select its parent folder
|
|
workingDirectory = workingDirectory.isFile() ? workingDirectory.getParentFile() : workingDirectory;
|
|
|
|
if (ValidationUtil.isReadableDirectory(workingDirectory)) {
|
|
logger.info("working dir is ok: ", workingDirectory.getAbsolutePath());
|
|
return workingDirectory;
|
|
}
|
|
}
|
|
|
|
logger.info("working dir returing null");
|
|
return null;
|
|
}
|
|
}
|