View Javadoc
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.Hashtable;
16  import java.util.Properties;
17  
18  import javax.naming.InvalidNameException;
19  import javax.naming.ldap.LdapName;
20  
21  public class LangUtils {
22  	/*
23  	 * NON-API OSGi
24  	 */
25  	/**
26  	 * Returns an array with the names of the provided classes. Useful when
27  	 * registering services with multiple interfaces in OSGi.
28  	 */
29  	public static String[] names(Class<?>... clzz) {
30  		String[] res = new String[clzz.length];
31  		for (int i = 0; i < clzz.length; i++)
32  			res[i] = clzz[i].getName();
33  		return res;
34  	}
35  
36  	/*
37  	 * DICTIONARY
38  	 */
39  
40  	/**
41  	 * Creates a new {@link Dictionary} with one key-value pair (neither key not
42  	 * value should be null)
43  	 */
44  	public static Dictionary<String, Object> dico(String key, Object value) {
45  		assert key != null;
46  		assert value != null;
47  		Hashtable<String, Object> props = new Hashtable<>();
48  		props.put(key, value);
49  		return props;
50  	}
51  
52  	/**
53  	 * Wraps the keys of the provided {@link Dictionary} as an {@link Iterable}.
54  	 */
55  	public static Iterable<String> keys(Dictionary<String, ?> props) {
56  		assert props != null;
57  		return new DictionaryKeys(props);
58  	}
59  
60  	static String toJson(Dictionary<String, ?> props) {
61  		return toJson(props, false);
62  	}
63  
64  	static String toJson(Dictionary<String, ?> props, boolean pretty) {
65  		StringBuilder sb = new StringBuilder();
66  		sb.append('{');
67  		if (pretty)
68  			sb.append('\n');
69  		Enumeration<String> keys = props.keys();
70  		while (keys.hasMoreElements()) {
71  			String key = keys.nextElement();
72  			if (pretty)
73  				sb.append(' ');
74  			sb.append('\"').append(key).append('\"');
75  			if (pretty)
76  				sb.append(" : ");
77  			else
78  				sb.append(':');
79  			sb.append('\"').append(props.get(key)).append('\"');
80  			if (keys.hasMoreElements())
81  				sb.append(", ");
82  			if (pretty)
83  				sb.append('\n');
84  		}
85  		sb.append('}');
86  		return sb.toString();
87  	}
88  
89  	static void storeAsProperties(Dictionary<String, Object> props, Path path) throws IOException {
90  		if (props == null)
91  			throw new IllegalArgumentException("Props cannot be null");
92  		Properties toStore = new Properties();
93  		for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
94  			String key = keys.nextElement();
95  			toStore.setProperty(key, props.get(key).toString());
96  		}
97  		try (OutputStream out = Files.newOutputStream(path)) {
98  			toStore.store(out, null);
99  		}
100 	}
101 
102 	static void appendAsLdif(String dnBase, String dnKey, Dictionary<String, Object> props, Path path)
103 			throws IOException {
104 		if (props == null)
105 			throw new IllegalArgumentException("Props cannot be null");
106 		Object dnValue = props.get(dnKey);
107 		String dnStr = dnKey + '=' + dnValue + ',' + dnBase;
108 		LdapName dn;
109 		try {
110 			dn = new LdapName(dnStr);
111 		} catch (InvalidNameException e) {
112 			throw new IllegalArgumentException("Cannot interpret DN " + dnStr, e);
113 		}
114 		if (dnValue == null)
115 			throw new IllegalArgumentException("DN key " + dnKey + " must have a value");
116 		try (Writer writer = Files.newBufferedWriter(path, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) {
117 			writer.append("\ndn: ");
118 			writer.append(dn.toString());
119 			writer.append('\n');
120 			for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
121 				String key = keys.nextElement();
122 				Object value = props.get(key);
123 				writer.append(key);
124 				writer.append(": ");
125 				// FIXME deal with binary and multiple values
126 				writer.append(value.toString());
127 				writer.append('\n');
128 			}
129 		}
130 	}
131 
132 	static Dictionary<String, Object> loadFromProperties(Path path) throws IOException {
133 		Properties toLoad = new Properties();
134 		try (InputStream in = Files.newInputStream(path)) {
135 			toLoad.load(in);
136 		}
137 		Dictionary<String, Object> res = new Hashtable<String, Object>();
138 		for (Object key : toLoad.keySet())
139 			res.put(key.toString(), toLoad.get(key));
140 		return res;
141 	}
142 
143 	/*
144 	 * EXCEPTIONS
145 	 */
146 	/**
147 	 * Chain the messages of all causes (one per line, <b>starts with a line
148 	 * return</b>) without all the stack
149 	 */
150 	public static String chainCausesMessages(Throwable t) {
151 		StringBuffer buf = new StringBuffer();
152 		chainCauseMessage(buf, t);
153 		return buf.toString();
154 	}
155 
156 	/** Recursive chaining of messages */
157 	private static void chainCauseMessage(StringBuffer buf, Throwable t) {
158 		buf.append('\n').append(' ').append(t.getClass().getCanonicalName()).append(": ").append(t.getMessage());
159 		if (t.getCause() != null)
160 			chainCauseMessage(buf, t.getCause());
161 	}
162 
163 	/*
164 	 * TIME
165 	 */
166 	/** Formats time elapsed since start. */
167 	public static String since(ZonedDateTime start) {
168 		ZonedDateTime now = ZonedDateTime.now();
169 		return duration(start, now);
170 	}
171 
172 	/** Formats a duration. */
173 	public static String duration(Temporal start, Temporal end) {
174 		long count = ChronoUnit.DAYS.between(start, end);
175 		if (count != 0)
176 			return count > 1 ? count + " days" : count + " day";
177 		count = ChronoUnit.HOURS.between(start, end);
178 		if (count != 0)
179 			return count > 1 ? count + " hours" : count + " hours";
180 		count = ChronoUnit.MINUTES.between(start, end);
181 		if (count != 0)
182 			return count > 1 ? count + " minutes" : count + " minute";
183 		count = ChronoUnit.SECONDS.between(start, end);
184 		return count > 1 ? count + " seconds" : count + " second";
185 	}
186 
187 	/** Singleton constructor. */
188 	private LangUtils() {
189 
190 	}
191 
192 }