X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.util%2Fsrc%2Forg%2Fargeo%2Futil%2FLangUtils.java;h=7824d12de41945c37a6ea9eeb1962db4f390428d;hb=986233dff943b24e545caecaa4658639c36172eb;hp=3b29e868c13291b05751869174ce39e67ffc26a3;hpb=82dbb6068c494047bce1ab170f2f6b8b608b2a42;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 3b29e868c..7824d12de 100644 --- a/org.argeo.util/src/org/argeo/util/LangUtils.java +++ b/org.argeo.util/src/org/argeo/util/LangUtils.java @@ -3,14 +3,24 @@ package org.argeo.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +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.Dictionary; import java.util.Enumeration; +import java.util.HashMap; import java.util.Hashtable; 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 @@ -26,22 +36,80 @@ public class LangUtils { return res; } + /* + * MAP + */ + /** + * 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 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; + } + + /** + * 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}. */ @@ -50,11 +118,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) @@ -79,7 +147,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(); @@ -92,7 +160,37 @@ public class LangUtils { } } - public static Dictionary loadFromProperties(Path path) throws IOException { + static void appendAsLdif(String dnBase, String dnKey, Dictionary props, Path path) + throws IOException { + if (props == null) + throw new IllegalArgumentException("Props cannot be null"); + Object dnValue = props.get(dnKey); + String dnStr = dnKey + '=' + dnValue + ',' + dnBase; + LdapName dn; + try { + dn = new LdapName(dnStr); + } catch (InvalidNameException e) { + throw new IllegalArgumentException("Cannot interpret DN " + dnStr, e); + } + if (dnValue == null) + throw new IllegalArgumentException("DN key " + dnKey + " must have a value"); + try (Writer writer = Files.newBufferedWriter(path, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { + writer.append("\ndn: "); + writer.append(dn.toString()); + writer.append('\n'); + for (Enumeration keys = props.keys(); keys.hasMoreElements();) { + String key = keys.nextElement(); + Object value = props.get(key); + writer.append(key); + writer.append(": "); + // FIXME deal with binary and multiple values + writer.append(value.toString()); + writer.append('\n'); + } + } + } + + static Dictionary loadFromProperties(Path path) throws IOException { Properties toLoad = new Properties(); try (InputStream in = Files.newInputStream(path)) { toLoad.load(in); @@ -103,6 +201,50 @@ public class LangUtils { return res; } + /* + * 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() {