]>
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
.Dictionary
;
17 import java
.util
.Enumeration
;
18 import java
.util
.HashMap
;
19 import java
.util
.Hashtable
;
20 import java
.util
.Iterator
;
21 import java
.util
.LinkedHashMap
;
22 import java
.util
.List
;
24 import java
.util
.Map
.Entry
;
25 import java
.util
.Properties
;
27 import javax
.naming
.InvalidNameException
;
28 import javax
.naming
.ldap
.LdapName
;
30 /** Utilities around Java basic features. */
31 public class LangUtils
{
36 * Whether this {@link String} is <code>null</null>, empty, or only white
39 public static boolean isEmpty(String str
) {
40 return str
== null || "".equals(str
.strip());
48 * Creates a new {@link Dictionary} with one key-value pair. Key should not be
49 * null, but if the value is null, it returns an empty {@link Dictionary}.
51 public static Dictionary
<String
, Object
> dict(String key
, Object value
) {
53 Hashtable
<String
, Object
> props
= new Hashtable
<>();
55 props
.put(key
, value
);
59 /** @deprecated Use {@link #dict(String, Object)} instead. */
61 public static Dictionary
<String
, Object
> dico(String key
, Object value
) {
62 return dict(key
, value
);
65 /** Converts a {@link Dictionary} to a {@link Map} of strings. */
66 public static Map
<String
, String
> dictToStringMap(Dictionary
<String
, ?
> properties
) {
67 if (properties
== null) {
70 Map
<String
, String
> res
= new HashMap
<>(properties
.size());
71 Enumeration
<String
> keys
= properties
.keys();
72 while (keys
.hasMoreElements()) {
73 String key
= keys
.nextElement();
74 res
.put(key
, properties
.get(key
).toString());
79 /** Converts a {@link Dictionary} to a {@link Map}. */
80 public static Map
<String
, Object
> dictToMap(Dictionary
<String
, ?
> properties
) {
81 if (properties
== null) {
84 Map
<String
, Object
> res
= new HashMap
<>(properties
.size());
85 Enumeration
<String
> keys
= properties
.keys();
86 while (keys
.hasMoreElements()) {
87 String key
= keys
.nextElement();
88 res
.put(key
, properties
.get(key
));
94 * Get a string property from this map, expecting to find it, or
95 * <code>null</code> if not found.
97 public static String
get(Map
<String
, ?
> map
, String key
) {
98 Object res
= map
.get(key
);
101 return res
.toString();
105 * Get a string property from this map, expecting to find it.
107 * @throws IllegalArgumentException if the key was not found
109 public static String
getNotNull(Map
<String
, ?
> map
, String key
) {
110 Object res
= map
.get(key
);
112 throw new IllegalArgumentException("Map " + map
+ " should contain key " + key
);
113 return res
.toString();
117 * Wraps the keys of the provided {@link Dictionary} as an {@link Iterable}.
119 public static Iterable
<String
> keys(Dictionary
<String
, ?
> props
) {
120 assert props
!= null;
121 return new DictionaryKeys(props
);
124 static String
toJson(Dictionary
<String
, ?
> props
) {
125 return toJson(props
, false);
128 static String
toJson(Dictionary
<String
, ?
> props
, boolean pretty
) {
129 StringBuilder sb
= new StringBuilder();
133 Enumeration
<String
> keys
= props
.keys();
134 while (keys
.hasMoreElements()) {
135 String key
= keys
.nextElement();
138 sb
.append('\"').append(key
).append('\"');
143 sb
.append('\"').append(props
.get(key
)).append('\"');
144 if (keys
.hasMoreElements())
150 return sb
.toString();
153 static void storeAsProperties(Dictionary
<String
, Object
> props
, Path path
) throws IOException
{
155 throw new IllegalArgumentException("Props cannot be null");
156 Properties toStore
= new Properties();
157 for (Enumeration
<String
> keys
= props
.keys(); keys
.hasMoreElements();) {
158 String key
= keys
.nextElement();
159 toStore
.setProperty(key
, props
.get(key
).toString());
161 try (OutputStream out
= Files
.newOutputStream(path
)) {
162 toStore
.store(out
, null);
166 static void appendAsLdif(String dnBase
, String dnKey
, Dictionary
<String
, Object
> props
, Path path
)
169 throw new IllegalArgumentException("Props cannot be null");
170 Object dnValue
= props
.get(dnKey
);
171 String dnStr
= dnKey
+ '=' + dnValue
+ ',' + dnBase
;
174 dn
= new LdapName(dnStr
);
175 } catch (InvalidNameException e
) {
176 throw new IllegalArgumentException("Cannot interpret DN " + dnStr
, e
);
179 throw new IllegalArgumentException("DN key " + dnKey
+ " must have a value");
180 try (Writer writer
= Files
.newBufferedWriter(path
, StandardOpenOption
.APPEND
, StandardOpenOption
.CREATE
)) {
181 writer
.append("\ndn: ");
182 writer
.append(dn
.toString());
184 for (Enumeration
<String
> keys
= props
.keys(); keys
.hasMoreElements();) {
185 String key
= keys
.nextElement();
186 Object value
= props
.get(key
);
189 // FIXME deal with binary and multiple values
190 writer
.append(value
.toString());
196 static Dictionary
<String
, Object
> loadFromProperties(Path path
) throws IOException
{
197 Properties toLoad
= new Properties();
198 try (InputStream in
= Files
.newInputStream(path
)) {
201 Dictionary
<String
, Object
> res
= new Hashtable
<String
, Object
>();
202 for (Object key
: toLoad
.keySet())
203 res
.put(key
.toString(), toLoad
.get(key
));
211 * Convert a comma-separated separated {@link String} or a {@link String} array
212 * to a {@link List} of {@link String}, trimming them. Useful to quickly
213 * interpret OSGi services properties.
215 * @return a {@link List} containing the trimmed {@link String}s, or an empty
216 * {@link List} if the argument was <code>null</code>.
218 public static List
<String
> toStringList(Object value
) {
219 List
<String
> values
= new ArrayList
<>();
223 if (value
instanceof String
) {
224 arr
= ((String
) value
).split(",");
225 } else if (value
instanceof String
[]) {
226 arr
= (String
[]) value
;
228 throw new IllegalArgumentException("Unsupported value type " + value
.getClass());
230 for (String str
: arr
) {
231 values
.add(str
.trim());
236 /** Size of an {@link Iterable}, optimised if it is a {@link Collection}. */
237 public static int size(Iterable
<?
> iterable
) {
238 if (iterable
instanceof Collection
)
239 return ((Collection
<?
>) iterable
).size();
242 for (Iterator
<?
> it
= iterable
.iterator(); it
.hasNext(); size
++)
247 public static <T
> T
getAt(Iterable
<T
> iterable
, int index
) {
248 if (iterable
instanceof List
) {
249 List
<T
> lst
= ((List
<T
>) iterable
);
250 if (index
>= lst
.size())
251 throw new IllegalArgumentException("Index " + index
+ " is not available (size is " + lst
.size() + ")");
252 return lst
.get(index
);
255 for (Iterator
<T
> it
= iterable
.iterator(); it
.hasNext(); i
++) {
261 throw new IllegalArgumentException("Index " + index
+ " is not available (size is " + i
+ ")");
264 public static <K
, V
extends Comparable
<?
super V
>> Map
<K
, V
> sortByValue(Map
<K
, V
> map
) {
265 return sortByValue(map
, false);
268 public static <K
, V
extends Comparable
<?
super V
>> Map
<K
, V
> sortByValue(Map
<K
, V
> map
, boolean descending
) {
269 List
<Entry
<K
, V
>> list
= new ArrayList
<>(map
.entrySet());
270 list
.sort(Entry
.comparingByValue());
272 Collections
.reverse(list
);
274 Map
<K
, V
> result
= new LinkedHashMap
<>();
275 for (Entry
<K
, V
> entry
: list
) {
276 result
.put(entry
.getKey(), entry
.getValue());
286 * Chain the messages of all causes (one per line, <b>starts with a line
287 * return</b>) without all the stack
289 public static String
chainCausesMessages(Throwable t
) {
290 StringBuffer buf
= new StringBuffer();
291 chainCauseMessage(buf
, t
);
292 return buf
.toString();
295 /** Recursive chaining of messages */
296 private static void chainCauseMessage(StringBuffer buf
, Throwable t
) {
297 buf
.append('\n').append(' ').append(t
.getClass().getCanonicalName()).append(": ").append(t
.getMessage());
298 if (t
.getCause() != null)
299 chainCauseMessage(buf
, t
.getCause());
305 /** Formats time elapsed since start. */
306 public static String
since(ZonedDateTime start
) {
307 ZonedDateTime now
= ZonedDateTime
.now();
308 return duration(start
, now
);
311 /** Formats a duration. */
312 public static String
duration(Temporal start
, Temporal end
) {
313 long count
= ChronoUnit
.DAYS
.between(start
, end
);
315 return count
> 1 ? count
+ " days" : count
+ " day";
316 count
= ChronoUnit
.HOURS
.between(start
, end
);
318 return count
> 1 ? count
+ " hours" : count
+ " hours";
319 count
= ChronoUnit
.MINUTES
.between(start
, end
);
321 return count
> 1 ? count
+ " minutes" : count
+ " minute";
322 count
= ChronoUnit
.SECONDS
.between(start
, end
);
323 return count
> 1 ? count
+ " seconds" : count
+ " second";
330 * Returns an array with the names of the provided classes. Useful when
331 * registering services with multiple interfaces in OSGi.
333 public static String
[] names(Class
<?
>... clzz
) {
334 String
[] res
= new String
[clzz
.length
];
335 for (int i
= 0; i
< clzz
.length
; i
++)
336 res
[i
] = clzz
[i
].getName();
340 /** Singleton constructor. */
341 private LangUtils() {