]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.util/src/org/argeo/util/LangUtils.java
Mini desktop graalvm packaging.
[lgpl/argeo-commons.git] / org.argeo.util / src / org / argeo / util / LangUtils.java
1 package org.argeo.util;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.io.Writer;
7 import java.nio.file.Files;
8 import java.nio.file.Path;
9 import java.nio.file.StandardOpenOption;
10 import java.time.ZonedDateTime;
11 import java.time.temporal.ChronoUnit;
12 import java.time.temporal.Temporal;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.Dictionary;
16 import java.util.Enumeration;
17 import java.util.HashMap;
18 import java.util.Hashtable;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Properties;
22
23 import javax.naming.InvalidNameException;
24 import javax.naming.ldap.LdapName;
25
26 /** Utilities around Java basic features. */
27 public class LangUtils {
28 /*
29 * NON-API OSGi
30 */
31 /**
32 * Returns an array with the names of the provided classes. Useful when
33 * registering services with multiple interfaces in OSGi.
34 */
35 public static String[] names(Class<?>... clzz) {
36 String[] res = new String[clzz.length];
37 for (int i = 0; i < clzz.length; i++)
38 res[i] = clzz[i].getName();
39 return res;
40 }
41
42 // /*
43 // * MAP
44 // */
45 // /**
46 // * Creates a new {@link Map} with one key-value pair. Key should not be null,
47 // * but if the value is null, it returns an empty {@link Map}.
48 // *
49 // * @deprecated Use {@link Collections#singletonMap(Object, Object)} instead.
50 // */
51 // @Deprecated
52 // public static Map<String, Object> map(String key, Object value) {
53 // assert key != null;
54 // HashMap<String, Object> props = new HashMap<>();
55 // if (value != null)
56 // props.put(key, value);
57 // return props;
58 // }
59
60 /*
61 * DICTIONARY
62 */
63
64 /**
65 * Creates a new {@link Dictionary} with one key-value pair. Key should not be
66 * null, but if the value is null, it returns an empty {@link Dictionary}.
67 */
68 public static Dictionary<String, Object> dict(String key, Object value) {
69 assert key != null;
70 Hashtable<String, Object> props = new Hashtable<>();
71 if (value != null)
72 props.put(key, value);
73 return props;
74 }
75
76 /** @deprecated Use {@link #dict(String, Object)} instead. */
77 @Deprecated
78 public static Dictionary<String, Object> dico(String key, Object value) {
79 return dict(key, value);
80 }
81
82 /** Converts a {@link Dictionary} to a {@link Map} of strings. */
83 public static Map<String, String> dictToStringMap(Dictionary<String, ?> properties) {
84 if (properties == null) {
85 return null;
86 }
87 Map<String, String> res = new HashMap<>(properties.size());
88 Enumeration<String> keys = properties.keys();
89 while (keys.hasMoreElements()) {
90 String key = keys.nextElement();
91 res.put(key, properties.get(key).toString());
92 }
93 return res;
94 }
95
96 /**
97 * Get a string property from this map, expecting to find it, or
98 * <code>null</code> if not found.
99 */
100 public static String get(Map<String, ?> map, String key) {
101 Object res = map.get(key);
102 if (res == null)
103 return null;
104 return res.toString();
105 }
106
107 /**
108 * Get a string property from this map, expecting to find it.
109 *
110 * @throws IllegalArgumentException if the key was not found
111 */
112 public static String getNotNull(Map<String, ?> map, String key) {
113 Object res = map.get(key);
114 if (res == null)
115 throw new IllegalArgumentException("Map " + map + " should contain key " + key);
116 return res.toString();
117 }
118
119 /**
120 * Wraps the keys of the provided {@link Dictionary} as an {@link Iterable}.
121 */
122 public static Iterable<String> keys(Dictionary<String, ?> props) {
123 assert props != null;
124 return new DictionaryKeys(props);
125 }
126
127 static String toJson(Dictionary<String, ?> props) {
128 return toJson(props, false);
129 }
130
131 static String toJson(Dictionary<String, ?> props, boolean pretty) {
132 StringBuilder sb = new StringBuilder();
133 sb.append('{');
134 if (pretty)
135 sb.append('\n');
136 Enumeration<String> keys = props.keys();
137 while (keys.hasMoreElements()) {
138 String key = keys.nextElement();
139 if (pretty)
140 sb.append(' ');
141 sb.append('\"').append(key).append('\"');
142 if (pretty)
143 sb.append(" : ");
144 else
145 sb.append(':');
146 sb.append('\"').append(props.get(key)).append('\"');
147 if (keys.hasMoreElements())
148 sb.append(", ");
149 if (pretty)
150 sb.append('\n');
151 }
152 sb.append('}');
153 return sb.toString();
154 }
155
156 static void storeAsProperties(Dictionary<String, Object> props, Path path) throws IOException {
157 if (props == null)
158 throw new IllegalArgumentException("Props cannot be null");
159 Properties toStore = new Properties();
160 for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
161 String key = keys.nextElement();
162 toStore.setProperty(key, props.get(key).toString());
163 }
164 try (OutputStream out = Files.newOutputStream(path)) {
165 toStore.store(out, null);
166 }
167 }
168
169 static void appendAsLdif(String dnBase, String dnKey, Dictionary<String, Object> props, Path path)
170 throws IOException {
171 if (props == null)
172 throw new IllegalArgumentException("Props cannot be null");
173 Object dnValue = props.get(dnKey);
174 String dnStr = dnKey + '=' + dnValue + ',' + dnBase;
175 LdapName dn;
176 try {
177 dn = new LdapName(dnStr);
178 } catch (InvalidNameException e) {
179 throw new IllegalArgumentException("Cannot interpret DN " + dnStr, e);
180 }
181 if (dnValue == null)
182 throw new IllegalArgumentException("DN key " + dnKey + " must have a value");
183 try (Writer writer = Files.newBufferedWriter(path, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) {
184 writer.append("\ndn: ");
185 writer.append(dn.toString());
186 writer.append('\n');
187 for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
188 String key = keys.nextElement();
189 Object value = props.get(key);
190 writer.append(key);
191 writer.append(": ");
192 // FIXME deal with binary and multiple values
193 writer.append(value.toString());
194 writer.append('\n');
195 }
196 }
197 }
198
199 static Dictionary<String, Object> loadFromProperties(Path path) throws IOException {
200 Properties toLoad = new Properties();
201 try (InputStream in = Files.newInputStream(path)) {
202 toLoad.load(in);
203 }
204 Dictionary<String, Object> res = new Hashtable<String, Object>();
205 for (Object key : toLoad.keySet())
206 res.put(key.toString(), toLoad.get(key));
207 return res;
208 }
209
210 /*
211 * COLLECTIONS
212 */
213 /**
214 * Convert a comma-separated separated {@link String} or a {@link String} array
215 * to a {@link List} of {@link String}, trimming them. Useful to quickly
216 * interpret OSGi services properties.
217 *
218 * @return a {@link List} containing the trimmed {@link String}s, or an empty
219 * {@link List} if the argument was <code>null</code>.
220 */
221 public static List<String> toStringList(Object value) {
222 List<String> values = new ArrayList<>();
223 if (value == null)
224 return values;
225 String[] arr;
226 if (value instanceof String) {
227 arr = ((String) value).split(",");
228 } else if (value instanceof String[]) {
229 arr = (String[]) value;
230 } else {
231 throw new IllegalArgumentException("Unsupported value type " + value.getClass());
232 }
233 for (String str : arr) {
234 values.add(str.trim());
235 }
236 return values;
237 }
238
239 /*
240 * EXCEPTIONS
241 */
242 /**
243 * Chain the messages of all causes (one per line, <b>starts with a line
244 * return</b>) without all the stack
245 */
246 public static String chainCausesMessages(Throwable t) {
247 StringBuffer buf = new StringBuffer();
248 chainCauseMessage(buf, t);
249 return buf.toString();
250 }
251
252 /** Recursive chaining of messages */
253 private static void chainCauseMessage(StringBuffer buf, Throwable t) {
254 buf.append('\n').append(' ').append(t.getClass().getCanonicalName()).append(": ").append(t.getMessage());
255 if (t.getCause() != null)
256 chainCauseMessage(buf, t.getCause());
257 }
258
259 /*
260 * TIME
261 */
262 /** Formats time elapsed since start. */
263 public static String since(ZonedDateTime start) {
264 ZonedDateTime now = ZonedDateTime.now();
265 return duration(start, now);
266 }
267
268 /** Formats a duration. */
269 public static String duration(Temporal start, Temporal end) {
270 long count = ChronoUnit.DAYS.between(start, end);
271 if (count != 0)
272 return count > 1 ? count + " days" : count + " day";
273 count = ChronoUnit.HOURS.between(start, end);
274 if (count != 0)
275 return count > 1 ? count + " hours" : count + " hours";
276 count = ChronoUnit.MINUTES.between(start, end);
277 if (count != 0)
278 return count > 1 ? count + " minutes" : count + " minute";
279 count = ChronoUnit.SECONDS.between(start, end);
280 return count > 1 ? count + " seconds" : count + " second";
281 }
282
283 /** Singleton constructor. */
284 private LangUtils() {
285
286 }
287
288 }