X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.util%2Fsrc%2Forg%2Fargeo%2Futil%2FLangUtils.java;h=1aee28c03c776d7438cc266e4842dee7e21af671;hb=3c1cdc594d954520b14646102b366290bdad58c7;hp=3c0baed1c8047ad0c9cccb10a77c12a84def8912;hpb=2d6b7c0c3badea29451c4d8e41ebb5aca2258806;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.util/src/org/argeo/util/LangUtils.java b/org.argeo.util/src/org/argeo/util/LangUtils.java index 3c0baed1c..1aee28c03 100644 --- a/org.argeo.util/src/org/argeo/util/LangUtils.java +++ b/org.argeo.util/src/org/argeo/util/LangUtils.java @@ -7,14 +7,24 @@ import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.util.ArrayList; +import java.util.Collection; import java.util.Dictionary; import java.util.Enumeration; +import java.util.HashMap; import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Properties; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; +/** Utilities around Java basic features. */ public class LangUtils { /* * NON-API OSGi @@ -30,22 +40,97 @@ public class LangUtils { return res; } +// /* +// * MAP +// */ +// /** +// * Creates a new {@link Map} with one key-value pair. Key should not be null, +// * but if the value is null, it returns an empty {@link Map}. +// * +// * @deprecated Use {@link Collections#singletonMap(Object, Object)} instead. +// */ +// @Deprecated +// public static Map map(String key, Object value) { +// assert key != null; +// HashMap props = new HashMap<>(); +// if (value != null) +// props.put(key, value); +// return props; +// } + /* * DICTIONARY */ /** - * Creates a new {@link Dictionary} with one key-value pair (neither key not - * value should be null) + * Creates a new {@link Dictionary} with one key-value pair. Key should not be + * null, but if the value is null, it returns an empty {@link Dictionary}. */ - public static Dictionary init(String key, Object value) { + public static Dictionary dict(String key, Object value) { assert key != null; - assert value != null; Hashtable props = new Hashtable<>(); - props.put(key, value); + if (value != null) + props.put(key, value); return props; } + /** @deprecated Use {@link #dict(String, Object)} instead. */ + @Deprecated + public static Dictionary dico(String key, Object value) { + return dict(key, value); + } + + /** Converts a {@link Dictionary} to a {@link Map} of strings. */ + public static Map dictToStringMap(Dictionary properties) { + if (properties == null) { + return null; + } + Map res = new HashMap<>(properties.size()); + Enumeration keys = properties.keys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + res.put(key, properties.get(key).toString()); + } + return res; + } + + /** Converts a {@link Dictionary} to a {@link Map}. */ + public static Map dictToMap(Dictionary properties) { + if (properties == null) { + return null; + } + Map res = new HashMap<>(properties.size()); + Enumeration keys = properties.keys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + res.put(key, properties.get(key)); + } + return res; + } + + /** + * Get a string property from this map, expecting to find it, or + * null if not found. + */ + public static String get(Map map, String key) { + Object res = map.get(key); + if (res == null) + return null; + return res.toString(); + } + + /** + * Get a string property from this map, expecting to find it. + * + * @throws IllegalArgumentException if the key was not found + */ + public static String getNotNull(Map map, String key) { + Object res = map.get(key); + if (res == null) + throw new IllegalArgumentException("Map " + map + " should contain key " + key); + return res.toString(); + } + /** * Wraps the keys of the provided {@link Dictionary} as an {@link Iterable}. */ @@ -54,11 +139,11 @@ public class LangUtils { return new DictionaryKeys(props); } - public static String toJson(Dictionary props) { + static String toJson(Dictionary props) { return toJson(props, false); } - public static String toJson(Dictionary props, boolean pretty) { + static String toJson(Dictionary props, boolean pretty) { StringBuilder sb = new StringBuilder(); sb.append('{'); if (pretty) @@ -83,7 +168,7 @@ public class LangUtils { return sb.toString(); } - public static void storeAsProperties(Dictionary props, Path path) throws IOException { + static void storeAsProperties(Dictionary props, Path path) throws IOException { if (props == null) throw new IllegalArgumentException("Props cannot be null"); Properties toStore = new Properties(); @@ -96,7 +181,7 @@ public class LangUtils { } } - public static void appendAsLdif(String dnBase, String dnKey, Dictionary props, Path path) + static void appendAsLdif(String dnBase, String dnKey, Dictionary props, Path path) throws IOException { if (props == null) throw new IllegalArgumentException("Props cannot be null"); @@ -126,7 +211,7 @@ public class LangUtils { } } - public static Dictionary loadFromProperties(Path path) throws IOException { + static Dictionary loadFromProperties(Path path) throws IOException { Properties toLoad = new Properties(); try (InputStream in = Files.newInputStream(path)) { toLoad.load(in); @@ -137,6 +222,107 @@ public class LangUtils { return res; } + /* + * COLLECTIONS + */ + /** + * Convert a comma-separated separated {@link String} or a {@link String} array + * to a {@link List} of {@link String}, trimming them. Useful to quickly + * interpret OSGi services properties. + * + * @return a {@link List} containing the trimmed {@link String}s, or an empty + * {@link List} if the argument was null. + */ + public static List toStringList(Object value) { + List values = new ArrayList<>(); + if (value == null) + return values; + String[] arr; + if (value instanceof String) { + arr = ((String) value).split(","); + } else if (value instanceof String[]) { + arr = (String[]) value; + } else { + throw new IllegalArgumentException("Unsupported value type " + value.getClass()); + } + for (String str : arr) { + values.add(str.trim()); + } + return values; + } + + /** Size of an {@link Iterable}, optimised if it is a {@link Collection}. */ + public static int size(Iterable iterable) { + if (iterable instanceof Collection) + return ((Collection) iterable).size(); + + int size = 0; + for (Iterator it = iterable.iterator(); it.hasNext(); size++) + it.next(); + return size; + } + + public static T getAt(Iterable iterable, int index) { + if (iterable instanceof List) { + List lst = ((List) iterable); + if (index >= lst.size()) + throw new IllegalArgumentException("Index " + index + " is not available (size is " + lst.size() + ")"); + return lst.get(index); + } + int i = 0; + for (Iterator it = iterable.iterator(); it.hasNext(); i++) { + if (i == index) + return it.next(); + else + it.next(); + } + throw new IllegalArgumentException("Index " + index + " is not available (size is " + i + ")"); + } + + /* + * EXCEPTIONS + */ + /** + * Chain the messages of all causes (one per line, starts with a line + * return) without all the stack + */ + public static String chainCausesMessages(Throwable t) { + StringBuffer buf = new StringBuffer(); + chainCauseMessage(buf, t); + return buf.toString(); + } + + /** Recursive chaining of messages */ + private static void chainCauseMessage(StringBuffer buf, Throwable t) { + buf.append('\n').append(' ').append(t.getClass().getCanonicalName()).append(": ").append(t.getMessage()); + if (t.getCause() != null) + chainCauseMessage(buf, t.getCause()); + } + + /* + * TIME + */ + /** Formats time elapsed since start. */ + public static String since(ZonedDateTime start) { + ZonedDateTime now = ZonedDateTime.now(); + return duration(start, now); + } + + /** Formats a duration. */ + public static String duration(Temporal start, Temporal end) { + long count = ChronoUnit.DAYS.between(start, end); + if (count != 0) + return count > 1 ? count + " days" : count + " day"; + count = ChronoUnit.HOURS.between(start, end); + if (count != 0) + return count > 1 ? count + " hours" : count + " hours"; + count = ChronoUnit.MINUTES.between(start, end); + if (count != 0) + return count > 1 ? count + " minutes" : count + " minute"; + count = ChronoUnit.SECONDS.between(start, end); + return count > 1 ? count + " seconds" : count + " second"; + } + /** Singleton constructor. */ private LangUtils() {