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