]>
git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/util/LangUtils.java
1 package org
.argeo
.cms
.util
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.io
.OutputStream
;
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
.Collection
;
15 import java
.util
.Collections
;
16 import java
.util
.Comparator
;
17 import java
.util
.Dictionary
;
18 import java
.util
.Enumeration
;
19 import java
.util
.HashMap
;
20 import java
.util
.Hashtable
;
21 import java
.util
.Iterator
;
22 import java
.util
.LinkedHashMap
;
23 import java
.util
.List
;
25 import java
.util
.Map
.Entry
;
26 import java
.util
.Properties
;
28 import javax
.naming
.InvalidNameException
;
29 import javax
.naming
.ldap
.LdapName
;
31 /** Utilities around Java basic features. */
32 public class LangUtils
{
37 * Returns an array with the names of the provided classes. Useful when
38 * registering services with multiple interfaces in OSGi.
40 public static String
[] names(Class
<?
>... clzz
) {
41 String
[] res
= new String
[clzz
.length
];
42 for (int i
= 0; i
< clzz
.length
; i
++)
43 res
[i
] = clzz
[i
].getName();
51 // * Creates a new {@link Map} with one key-value pair. Key should not be null,
52 // * but if the value is null, it returns an empty {@link Map}.
54 // * @deprecated Use {@link Collections#singletonMap(Object, Object)} instead.
57 // public static Map<String, Object> map(String key, Object value) {
58 // assert key != null;
59 // HashMap<String, Object> props = new HashMap<>();
61 // props.put(key, value);
70 * Creates a new {@link Dictionary} with one key-value pair. Key should not be
71 * null, but if the value is null, it returns an empty {@link Dictionary}.
73 public static Dictionary
<String
, Object
> dict(String key
, Object value
) {
75 Hashtable
<String
, Object
> props
= new Hashtable
<>();
77 props
.put(key
, value
);
81 /** @deprecated Use {@link #dict(String, Object)} instead. */
83 public static Dictionary
<String
, Object
> dico(String key
, Object value
) {
84 return dict(key
, value
);
87 /** Converts a {@link Dictionary} to a {@link Map} of strings. */
88 public static Map
<String
, String
> dictToStringMap(Dictionary
<String
, ?
> properties
) {
89 if (properties
== null) {
92 Map
<String
, String
> res
= new HashMap
<>(properties
.size());
93 Enumeration
<String
> keys
= properties
.keys();
94 while (keys
.hasMoreElements()) {
95 String key
= keys
.nextElement();
96 res
.put(key
, properties
.get(key
).toString());
101 /** Converts a {@link Dictionary} to a {@link Map}. */
102 public static Map
<String
, Object
> dictToMap(Dictionary
<String
, ?
> properties
) {
103 if (properties
== null) {
106 Map
<String
, Object
> res
= new HashMap
<>(properties
.size());
107 Enumeration
<String
> keys
= properties
.keys();
108 while (keys
.hasMoreElements()) {
109 String key
= keys
.nextElement();
110 res
.put(key
, properties
.get(key
));
116 * Get a string property from this map, expecting to find it, or
117 * <code>null</code> if not found.
119 public static String
get(Map
<String
, ?
> map
, String key
) {
120 Object res
= map
.get(key
);
123 return res
.toString();
127 * Get a string property from this map, expecting to find it.
129 * @throws IllegalArgumentException if the key was not found
131 public static String
getNotNull(Map
<String
, ?
> map
, String key
) {
132 Object res
= map
.get(key
);
134 throw new IllegalArgumentException("Map " + map
+ " should contain key " + key
);
135 return res
.toString();
139 * Wraps the keys of the provided {@link Dictionary} as an {@link Iterable}.
141 public static Iterable
<String
> keys(Dictionary
<String
, ?
> props
) {
142 assert props
!= null;
143 return new DictionaryKeys(props
);
146 static String
toJson(Dictionary
<String
, ?
> props
) {
147 return toJson(props
, false);
150 static String
toJson(Dictionary
<String
, ?
> props
, boolean pretty
) {
151 StringBuilder sb
= new StringBuilder();
155 Enumeration
<String
> keys
= props
.keys();
156 while (keys
.hasMoreElements()) {
157 String key
= keys
.nextElement();
160 sb
.append('\"').append(key
).append('\"');
165 sb
.append('\"').append(props
.get(key
)).append('\"');
166 if (keys
.hasMoreElements())
172 return sb
.toString();
175 static void storeAsProperties(Dictionary
<String
, Object
> props
, Path path
) throws IOException
{
177 throw new IllegalArgumentException("Props cannot be null");
178 Properties toStore
= new Properties();
179 for (Enumeration
<String
> keys
= props
.keys(); keys
.hasMoreElements();) {
180 String key
= keys
.nextElement();
181 toStore
.setProperty(key
, props
.get(key
).toString());
183 try (OutputStream out
= Files
.newOutputStream(path
)) {
184 toStore
.store(out
, null);
188 static void appendAsLdif(String dnBase
, String dnKey
, Dictionary
<String
, Object
> props
, Path path
)
191 throw new IllegalArgumentException("Props cannot be null");
192 Object dnValue
= props
.get(dnKey
);
193 String dnStr
= dnKey
+ '=' + dnValue
+ ',' + dnBase
;
196 dn
= new LdapName(dnStr
);
197 } catch (InvalidNameException e
) {
198 throw new IllegalArgumentException("Cannot interpret DN " + dnStr
, e
);
201 throw new IllegalArgumentException("DN key " + dnKey
+ " must have a value");
202 try (Writer writer
= Files
.newBufferedWriter(path
, StandardOpenOption
.APPEND
, StandardOpenOption
.CREATE
)) {
203 writer
.append("\ndn: ");
204 writer
.append(dn
.toString());
206 for (Enumeration
<String
> keys
= props
.keys(); keys
.hasMoreElements();) {
207 String key
= keys
.nextElement();
208 Object value
= props
.get(key
);
211 // FIXME deal with binary and multiple values
212 writer
.append(value
.toString());
218 static Dictionary
<String
, Object
> loadFromProperties(Path path
) throws IOException
{
219 Properties toLoad
= new Properties();
220 try (InputStream in
= Files
.newInputStream(path
)) {
223 Dictionary
<String
, Object
> res
= new Hashtable
<String
, Object
>();
224 for (Object key
: toLoad
.keySet())
225 res
.put(key
.toString(), toLoad
.get(key
));
233 * Convert a comma-separated separated {@link String} or a {@link String} array
234 * to a {@link List} of {@link String}, trimming them. Useful to quickly
235 * interpret OSGi services properties.
237 * @return a {@link List} containing the trimmed {@link String}s, or an empty
238 * {@link List} if the argument was <code>null</code>.
240 public static List
<String
> toStringList(Object value
) {
241 List
<String
> values
= new ArrayList
<>();
245 if (value
instanceof String
) {
246 arr
= ((String
) value
).split(",");
247 } else if (value
instanceof String
[]) {
248 arr
= (String
[]) value
;
250 throw new IllegalArgumentException("Unsupported value type " + value
.getClass());
252 for (String str
: arr
) {
253 values
.add(str
.trim());
258 /** Size of an {@link Iterable}, optimised if it is a {@link Collection}. */
259 public static int size(Iterable
<?
> iterable
) {
260 if (iterable
instanceof Collection
)
261 return ((Collection
<?
>) iterable
).size();
264 for (Iterator
<?
> it
= iterable
.iterator(); it
.hasNext(); size
++)
269 public static <T
> T
getAt(Iterable
<T
> iterable
, int index
) {
270 if (iterable
instanceof List
) {
271 List
<T
> lst
= ((List
<T
>) iterable
);
272 if (index
>= lst
.size())
273 throw new IllegalArgumentException("Index " + index
+ " is not available (size is " + lst
.size() + ")");
274 return lst
.get(index
);
277 for (Iterator
<T
> it
= iterable
.iterator(); it
.hasNext(); i
++) {
283 throw new IllegalArgumentException("Index " + index
+ " is not available (size is " + i
+ ")");
286 public static <K
, V
extends Comparable
<?
super V
>> Map
<K
, V
> sortByValue(Map
<K
, V
> map
) {
287 return sortByValue(map
, false);
290 public static <K
, V
extends Comparable
<?
super V
>> Map
<K
, V
> sortByValue(Map
<K
, V
> map
, boolean descending
) {
291 List
<Entry
<K
, V
>> list
= new ArrayList
<>(map
.entrySet());
292 list
.sort(Entry
.comparingByValue());
294 Collections
.reverse(list
);
296 Map
<K
, V
> result
= new LinkedHashMap
<>();
297 for (Entry
<K
, V
> entry
: list
) {
298 result
.put(entry
.getKey(), entry
.getValue());
308 * Chain the messages of all causes (one per line, <b>starts with a line
309 * return</b>) without all the stack
311 public static String
chainCausesMessages(Throwable t
) {
312 StringBuffer buf
= new StringBuffer();
313 chainCauseMessage(buf
, t
);
314 return buf
.toString();
317 /** Recursive chaining of messages */
318 private static void chainCauseMessage(StringBuffer buf
, Throwable t
) {
319 buf
.append('\n').append(' ').append(t
.getClass().getCanonicalName()).append(": ").append(t
.getMessage());
320 if (t
.getCause() != null)
321 chainCauseMessage(buf
, t
.getCause());
327 /** Formats time elapsed since start. */
328 public static String
since(ZonedDateTime start
) {
329 ZonedDateTime now
= ZonedDateTime
.now();
330 return duration(start
, now
);
333 /** Formats a duration. */
334 public static String
duration(Temporal start
, Temporal end
) {
335 long count
= ChronoUnit
.DAYS
.between(start
, end
);
337 return count
> 1 ? count
+ " days" : count
+ " day";
338 count
= ChronoUnit
.HOURS
.between(start
, end
);
340 return count
> 1 ? count
+ " hours" : count
+ " hours";
341 count
= ChronoUnit
.MINUTES
.between(start
, end
);
343 return count
> 1 ? count
+ " minutes" : count
+ " minute";
344 count
= ChronoUnit
.SECONDS
.between(start
, end
);
345 return count
> 1 ? count
+ " seconds" : count
+ " second";
348 /** Singleton constructor. */
349 private LangUtils() {