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