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