Resources system rewrite + new "--save-config DIR" option
[jvcard.git] / src / be / nikiroo / jvcard / resources / Bundles.java
1 package be.nikiroo.jvcard.resources;
2
3 import java.io.BufferedWriter;
4 import java.io.File;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.OutputStreamWriter;
8 import java.io.Writer;
9 import java.lang.reflect.Field;
10 import java.util.Locale;
11 import java.util.ResourceBundle;
12
13 /**
14 * This class help you get UTF-8 bundles for this application.
15 *
16 * @author niki
17 *
18 */
19 public class Bundles {
20 static private String confDir = getConfDir();
21
22 /**
23 * The type of configuration information the associated {@link Bundle} will
24 * convey.
25 *
26 * @author niki
27 *
28 */
29 public enum Target {
30 colors, display, jvcard, remote, resources
31 }
32
33 /**
34 * Return the configuration directory where to try to find the
35 * <tt>.properties</tt> files in priority.
36 *
37 * @return the configuration directory
38 */
39 static private String getConfDir() {
40 // Do not override user-supplied config directory (see --help)
41 if (Bundles.confDir != null)
42 return Bundles.confDir;
43
44 try {
45 ResourceBundle bundle = ResourceBundle.getBundle(Bundles.class
46 .getPackage().getName() + "." + "jvcard",
47 Locale.getDefault(), new FixedResourceBundleControl(null));
48
49 String configDir = bundle.getString("CONFIG_DIR");
50 if (configDir != null && configDir.trim().length() > 0)
51 return configDir;
52 } catch (Exception e) {
53 }
54
55 return null;
56 }
57
58 /**
59 * Set the primary configuration directory to look for <tt>.properties</tt>
60 * files in.
61 *
62 * All {@link ResourceBundle}s returned by this class after that point will
63 * respect this new directory.
64 *
65 * @param confDir
66 * the new directory
67 */
68 static public void setDirectory(String confDir) {
69 Bundles.confDir = confDir;
70 }
71
72 /**
73 * This class encapsulate a {@link ResourceBundle} in UTF-8. It only allows
74 * to retrieve values associated to an enumeration, and allows some
75 * additional methods.
76 *
77 * @author niki
78 *
79 * @param <E>
80 * the enum to use to get values out of this class
81 */
82 public class Bundle<E extends Enum<E>> {
83 private Class<E> type;
84 protected Target name;
85 protected ResourceBundle map;
86
87 /**
88 * Create a new {@link Bundles} of the given name.
89 *
90 * @param type
91 * a runtime instance of the class of E
92 *
93 * @param name
94 * the name of the {@link Bundles}
95 */
96 protected Bundle(Class<E> type, Target name) {
97 this.type = type;
98 this.name = name;
99 this.map = getBundle(name);
100 }
101
102 /**
103 * Return the value associated to the given id as a {@link String}.
104 *
105 * @param mame
106 * the id of the value to get
107 *
108 * @return the associated value
109 */
110 public String getString(E id) {
111 if (map.containsKey(id.name())) {
112 return map.getString(id.name()).trim();
113 }
114
115 return "";
116 }
117
118 /**
119 * Return the value associated to the given id as a {@link Boolean}.
120 *
121 * @param mame
122 * the id of the value to get
123 *
124 * @return the associated value
125 */
126 public Boolean getBoolean(E id) {
127 String str = getString(id);
128 if (str != null && str.length() > 0) {
129 if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("on")
130 || str.equalsIgnoreCase("yes"))
131 return true;
132 if (str.equalsIgnoreCase("false")
133 || str.equalsIgnoreCase("off")
134 || str.equalsIgnoreCase("no"))
135 return false;
136
137 }
138
139 return null;
140 }
141
142 /**
143 * Return the value associated to the given id as a {@link boolean}.
144 *
145 * @param mame
146 * the id of the value to get
147 * @param def
148 * the default value when it is not present in the config
149 * file or if it is not a boolean value
150 *
151 * @return the associated value
152 */
153 public boolean getBoolean(E id, boolean def) {
154 Boolean b = getBoolean(id);
155 if (b != null)
156 return b;
157
158 return def;
159 }
160
161 /**
162 * Return the value associated to the given id as an {@link Integer}.
163 *
164 * @param mame
165 * the id of the value to get
166 *
167 * @return the associated value
168 */
169 public Integer getInteger(E id) {
170 try {
171 return Integer.parseInt(getString(id));
172 } catch (Exception e) {
173 }
174
175 return null;
176 }
177
178 /**
179 * Return the value associated to the given id as a {@link int}.
180 *
181 * @param mame
182 * the id of the value to get
183 * @param def
184 * the default value when it is not present in the config
185 * file or if it is not a int value
186 *
187 * @return the associated value
188 */
189 public int getInteger(E id, int def) {
190 Integer i = getInteger(id);
191 if (i != null)
192 return i;
193
194 return def;
195 }
196
197 /**
198 * Create/update the .properties file. Will use the most likely
199 * candidate as base if the file does not already exists and this
200 * resource is translatable (for instance, "en_US" will use "en" as a
201 * base if the resource is a translation file).
202 *
203 * @param path
204 * the path where the .properties files are
205 *
206 * @throws IOException
207 * in case of IO errors
208 */
209 public void updateFile(String path) throws IOException {
210 File file = getUpdateFile(path);
211
212 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
213 new FileOutputStream(file), "UTF-8"));
214
215 writeHeader(writer);
216 writer.write("\n");
217 writer.write("\n");
218
219 for (Field field : type.getDeclaredFields()) {
220 Meta meta = field.getAnnotation(Meta.class);
221 if (meta != null) {
222 E id = E.valueOf(type, field.getName());
223 String info = getMetaInfo(meta);
224
225 if (info != null) {
226 writer.write(info);
227 writer.write("\n");
228 }
229
230 writeValue(writer, id);
231 }
232 }
233
234 writer.close();
235 }
236
237 /**
238 * Return formated, display-able information from the {@link Meta} field
239 * given. Each line will always starts with a "#" character.
240 *
241 * @param meta
242 * the {@link Meta} field
243 *
244 * @return the information to display or NULL if none
245 */
246 protected String getMetaInfo(Meta meta) {
247 String what = meta.what();
248 String where = meta.where();
249 String format = meta.format();
250 String info = meta.info();
251
252 int opt = what.length() + where.length() + format.length();
253 if (opt + info.length() == 0)
254 return null;
255
256 StringBuilder builder = new StringBuilder();
257 builder.append("# ");
258
259 if (opt > 0) {
260 builder.append("(");
261 if (what.length() > 0) {
262 builder.append("WHAT: " + what);
263 if (where.length() + format.length() > 0)
264 builder.append(", ");
265 }
266
267 if (where.length() > 0) {
268 builder.append("WHERE: " + where);
269 if (format.length() > 0)
270 builder.append(", ");
271 }
272
273 if (format.length() > 0) {
274 builder.append("FORMAT: " + format);
275 }
276
277 builder.append(")");
278 if (info.length() > 0) {
279 builder.append("\n# ");
280 }
281 }
282
283 builder.append(info);
284
285 return builder.toString();
286 }
287
288 /**
289 * Write the header found in the configuration <tt>.properties</tt> file
290 * of this {@link Bundles}.
291 *
292 * @param writer
293 * the {@link Writer} to write the header in
294 *
295 * @throws IOException
296 * in case of IO error
297 */
298 protected void writeHeader(Writer writer) throws IOException {
299 writer.write("# " + name + "\n");
300 writer.write("#\n");
301 }
302
303 /**
304 * Write the given id to the config file, i.e.,
305 * "MY_ID = my_curent_value" followed by a new line
306 *
307 * @param writer
308 * the {@link Writer} to write into
309 * @param id
310 * the id to write
311 *
312 * @throws IOException
313 * in case of IO error
314 */
315 protected void writeValue(Writer writer, E id) throws IOException {
316 writer.write(id.name());
317 writer.write(" = ");
318 writer.write(getString(id));
319 writer.write("\n");
320 }
321
322 /**
323 * Return the non-localised bundle of the given name.
324 *
325 * @param name
326 * the name of the bundle to load
327 *
328 * @return the bundle
329 */
330 protected ResourceBundle getBundle(Target name) {
331 return ResourceBundle.getBundle(Bundles.class.getPackage()
332 .getName() + "." + name.name(),
333 new FixedResourceBundleControl(confDir));
334 }
335
336 /**
337 * Return the localised bundle of the given name and {@link Locale}.
338 *
339 * @param name
340 * the name of the bundle to load
341 * @param locale
342 * the {@link Locale} to use
343 *
344 * @return the localised bundle
345 */
346 protected ResourceBundle getBundle(Target name, Locale locale) {
347 return ResourceBundle.getBundle(Bundles.class.getPackage()
348 .getName() + "." + name.name(), locale,
349 new FixedResourceBundleControl(confDir));
350 }
351
352 /**
353 * Return the source file for this {@link Bundles} from the given path.
354 *
355 * @param path
356 * the path where the .properties files are
357 *
358 * @return the source {@link File}
359 *
360 * @throws IOException
361 * in case of IO errors
362 */
363 protected File getUpdateFile(String path) {
364 return new File(path, name.name() + ".properties");
365 }
366 }
367 }