Translation: update system to support arguments, add some translations
[jvcard.git] / src / be / nikiroo / jvcard / launcher / Main.java
CommitLineData
7da41ecd
NR
1package be.nikiroo.jvcard.launcher;
2
3import java.io.File;
4import java.io.IOException;
5import java.lang.reflect.Field;
7da41ecd
NR
6import java.net.Socket;
7import java.nio.charset.Charset;
8import java.util.LinkedList;
9import java.util.List;
10
11import be.nikiroo.jvcard.Card;
5ad0e17e 12import be.nikiroo.jvcard.launcher.CardResult.MergeCallback;
7da41ecd 13import be.nikiroo.jvcard.parsers.Format;
845fb1d7 14import be.nikiroo.jvcard.remote.Command;
7da41ecd
NR
15import be.nikiroo.jvcard.remote.SimpleSocket;
16import be.nikiroo.jvcard.resources.Bundles;
17import be.nikiroo.jvcard.resources.StringUtils;
18import be.nikiroo.jvcard.resources.Trans;
19import be.nikiroo.jvcard.resources.Trans.StringId;
20
21/**
22 * This class contains the runnable Main method. It will parse the user supplied
23 * parameters and take action based upon those. Most of the time, it will start
24 * a MainWindow.
25 *
26 * @author niki
27 *
28 */
29public class Main {
30 static public final String APPLICATION_TITLE = "jVcard";
4477025c 31 static public final String APPLICATION_VERSION = "1.0-beta3-dev";
7da41ecd
NR
32
33 static private final int ERR_NO_FILE = 1;
34 static private final int ERR_SYNTAX = 2;
35 static private final int ERR_INTERNAL = 3;
36 static private Trans transService;
37
38 /**
9b8cb729 39 * Translate the given {@link StringId} into user text.
7da41ecd 40 *
9b8cb729 41 * @param stringId
7da41ecd 42 * the ID to translate
9b8cb729
NR
43 * @param values
44 * the values to insert instead of the place holders in the
45 * translation
7da41ecd 46 *
9b8cb729 47 * @return the translated text with the given value where required
7da41ecd 48 */
9b8cb729
NR
49 static public String trans(StringId id, String... values) {
50 return transService.trans(id, (String[]) values);
7da41ecd
NR
51 }
52
53 /**
54 * Check if unicode characters should be used.
55 *
56 * @return TRUE to allow unicode
57 */
58 static public boolean isUnicode() {
59 return transService.isUnicode();
60 }
61
62 /**
63 * Start the application.
64 *
65 * <p>
66 * The returned exit codes are:
67 * <ul>
68 * <li>1: no files to open</li>
69 * <li>2: invalid syntax</li>
70 * <li>3: internal error</li>
71 * </ul>
72 * </p>
73 *
74 * @param args
75 * the parameters (see <tt>--help</tt> to know which are
76 * supported)
77 */
78 public static void main(String[] args) {
79 Boolean textMode = null;
80 boolean noMoreParams = false;
81 boolean filesTried = false;
82
83 // get the "system default" language to help translate the --help
84 // message if needed
85 String language = null;
86 transService = new Trans(language);
87
88 boolean unicode = transService.isUnicode();
89 String i18nDir = null;
90 List<String> files = new LinkedList<String>();
91 Integer port = null;
92 for (int index = 0; index < args.length; index++) {
93 String arg = args[index];
94 if (!noMoreParams && arg.equals("--")) {
95 noMoreParams = true;
96 } else if (!noMoreParams && arg.equals("--help")) {
97 System.out
98 .println("TODO: implement some help text.\n"
99 + "Usable switches:\n"
100 + "\t--: stop looking for switches\n"
101 + "\t--help: this here thingy\n"
102 + "\t--lang LANGUAGE: choose the language, for instance en_GB\n"
103 + "\t--tui: force pure text mode even if swing treminal is available\n"
104 + "\t--gui: force swing terminal mode\n"
105 + "\t--noutf: force non-utf8 mode if you need it\n"
106 + "\t--config DIRECTORY: force the given directory as a CONFIG_DIR\n"
107 + "\t--server PORT: start a remoting server instead of a client\n"
108 + "\t--i18n DIR: generate the translation file for the given language (can be \"\") to/from the .properties given dir\n"
109 + "everyhing else is either a file to open or a directory to open\n"
110 + "(we will only open 1st level files in given directories)\n"
111 + "('jvcard://hostname:8888/file' links -- or without 'file' -- are also ok)\n");
112 return;
113 } else if (!noMoreParams && arg.equals("--tui")) {
114 textMode = true;
115 } else if (!noMoreParams && arg.equals("--gui")) {
116 textMode = false;
117 } else if (!noMoreParams && arg.equals("--noutf")) {
118 unicode = false;
119 transService.setUnicode(unicode);
120 } else if (!noMoreParams && arg.equals("--lang")) {
121 index++;
122 if (index >= args.length) {
123 System.err.println("Syntax error: no language given");
124 System.exit(ERR_SYNTAX);
125 return;
126 }
127
128 language = args[index];
129 transService = new Trans(language);
130 transService.setUnicode(unicode);
131 } else if (!noMoreParams && arg.equals("--config")) {
132 index++;
133 if (index >= args.length) {
134 System.err
135 .println("Syntax error: no config directory given");
136 System.exit(ERR_SYNTAX);
137 return;
138 }
139
140 Bundles.setDirectory(args[index]);
141 transService = new Trans(language);
142 transService.setUnicode(unicode);
143 } else if (!noMoreParams && arg.equals("--server")) {
144 index++;
145 if (index >= args.length) {
146 System.err.println("Syntax error: no port given");
147 System.exit(ERR_SYNTAX);
148 return;
149 }
150
151 try {
152 port = Integer.parseInt(args[index]);
153 } catch (NumberFormatException e) {
154 System.err.println("Invalid port number: " + args[index]);
155 System.exit(ERR_SYNTAX);
156 return;
157 }
158 } else if (!noMoreParams && arg.equals("--i18n")) {
159 index++;
160 if (index >= args.length) {
161 System.err
162 .println("Syntax error: no .properties directory given");
163 System.exit(ERR_SYNTAX);
164 return;
165 }
9b8cb729 166
7da41ecd
NR
167 i18nDir = args[index];
168 } else {
169 filesTried = true;
170 files.addAll(open(arg));
171 }
172 }
9b8cb729 173
f578f3af
NR
174 // Force headless mode if we run in forced-text mode
175 if (textMode != null && textMode) {
176 // same as -Djava.awt.headless=true
177 System.setProperty("java.awt.headless", "true");
178 }
7da41ecd
NR
179
180 if (unicode) {
181 utf8();
182 }
183
184 // Error management:
185 if (port != null && files.size() > 0) {
186 System.err
187 .println("Invalid syntax: you cannot both use --server and provide card files");
188 System.exit(ERR_SYNTAX);
189 } else if (i18nDir != null && files.size() > 0) {
190 System.err
191 .println("Invalid syntax: you cannot both use --i18n and provide card files");
192 System.exit(ERR_SYNTAX);
193 } else if (port != null && i18nDir != null) {
194 System.err
195 .println("Invalid syntax: you cannot both use --server and --i18n");
196 System.exit(ERR_SYNTAX);
197 } else if (i18nDir != null && language == null) {
198 System.err
199 .println("Invalid syntax: you cannot use --i18n without --lang");
200 System.exit(ERR_SYNTAX);
201 } else if (port == null && i18nDir == null && files.size() == 0) {
202 if (files.size() == 0 && !filesTried) {
203 files.addAll(open("."));
204 }
205
206 if (files.size() == 0) {
207 System.err.println("No files to open");
208 System.exit(ERR_NO_FILE);
209 return;
210 }
211 }
212 //
213
214 if (port != null) {
215 try {
02b341aa 216 Optional.runServer(port);
7da41ecd
NR
217 } catch (Exception e) {
218 if (e instanceof IOException) {
219 System.err
220 .println("I/O Exception: Cannot start the server");
221 } else {
4298276a 222 System.err.println("Remoting support not available");
7da41ecd
NR
223 System.exit(ERR_INTERNAL);
224 }
225 }
226 } else if (i18nDir != null) {
227 try {
228 Trans.generateTranslationFile(i18nDir, language);
229 } catch (IOException e) {
230 System.err
231 .println("I/O Exception: Cannot create/update a language in directory: "
232 + i18nDir);
233 }
234 } else {
235 try {
02b341aa 236 Optional.startTui(textMode, files);
7da41ecd
NR
237 } catch (Exception e) {
238 if (e instanceof IOException) {
239 System.err
240 .println("I/O Exception: Cannot start the program with the given cards");
241 } else {
4298276a 242 System.err.println("TUI support not available");
7da41ecd
NR
243 System.exit(ERR_INTERNAL);
244 }
245 }
246 }
247 }
248
249 /**
250 * Return the {@link Card} corresponding to the given resource name -- a
251 * file or a remote jvcard URL
252 *
253 * @param input
254 * a filename or a remote jvcard url with named resource (e.g.:
255 * <tt>jvcard://localhost:4444/coworkers.vcf</tt>)
5ad0e17e
NR
256 * @param callback
257 * the {@link MergeCallback} to call in case of conflict, or NULL
258 * to disallow conflict management (the {@link Card} will not be
259 * allowed to synchronise in case of conflicts)
7da41ecd
NR
260 *
261 * @return the {@link Card}
262 *
263 * @throws IOException
264 * in case of IO error or remoting not available
265 */
5ad0e17e
NR
266 static public CardResult getCard(String input, MergeCallback callback)
267 throws IOException {
7da41ecd
NR
268 boolean remote = false;
269 Format format = Format.Abook;
270 String ext = input;
271 if (ext.contains(".")) {
272 String tab[] = ext.split("\\.");
273 if (tab.length > 1 && tab[tab.length - 1].equalsIgnoreCase("vcf")) {
274 format = Format.VCard21;
275 }
276 }
277
278 if (input.contains("://")) {
279 format = Format.VCard21;
280 remote = true;
281 }
282
5ad0e17e 283 CardResult card = null;
7da41ecd
NR
284 try {
285 if (remote) {
5ad0e17e 286 card = Optional.syncCard(input, callback);
7da41ecd 287 } else {
5ad0e17e
NR
288 card = new CardResult(new Card(new File(input), format), false,
289 false, false);
7da41ecd
NR
290 }
291 } catch (IOException ioe) {
292 throw ioe;
293 } catch (Exception e) {
4298276a 294 throw new IOException("Remoting support not available", e);
7da41ecd
NR
295 }
296
297 return card;
298 }
299
7da41ecd
NR
300 /**
301 * Open the given path and add all its files if it is a directory or just
302 * this one if not to the returned list.
303 *
304 * @param path
305 * the path to open
306 *
307 * @return the list of opened files
308 */
309 static private List<String> open(String path) {
310 List<String> files = new LinkedList<String>();
311
312 if (path != null && path.startsWith("jvcard://")) {
313 if (path.endsWith("/")) {
314 files.addAll(list(path));
315 } else {
316 files.add(path);
317 }
318 } else {
319 File file = new File(path);
320 if (file.exists()) {
321 if (file.isDirectory()) {
322 for (File subfile : file.listFiles()) {
323 if (!subfile.isDirectory())
324 files.add(subfile.getAbsolutePath());
325 }
326 } else {
327 files.add(file.getAbsolutePath());
328 }
329 } else {
330 System.err.println("File or directory not found: \"" + path
331 + "\"");
332 }
333 }
334
335 return files;
336 }
337
338 /**
339 * List all the available {@link Card}s on the given network location (which
340 * is expected to be a jVCard remote server, obviously).
341 *
342 * @param path
343 * the jVCard remote server path (e.g.:
344 * <tt>jvcard://localhost:4444/</tt>)
345 *
346 * @return the list of {@link Card}s
347 */
348 static private List<String> list(String path) {
349 List<String> files = new LinkedList<String>();
350
351 try {
352 String host = path.split("\\:")[1].substring(2);
353 int port = Integer.parseInt(path.split("\\:")[2].replaceAll("/$",
354 ""));
355 SimpleSocket s = new SimpleSocket(new Socket(host, port),
356 "sync client");
357 s.open(true);
358
845fb1d7 359 s.sendCommand(Command.LIST_CARD);
7da41ecd
NR
360 for (String p : s.receiveBlock()) {
361 files.add(path
362 + p.substring(StringUtils.fromTime(0).length() + 1));
363 }
364 s.close();
365 } catch (Exception e) {
366 e.printStackTrace();
367 }
368
369 return files;
370 }
371
372 /**
373 * Really, really ask for UTF-8 encoding.
374 */
375 static private void utf8() {
376 try {
377 System.setProperty("file.encoding", "UTF-8");
378 Field charset = Charset.class.getDeclaredField("defaultCharset");
379 charset.setAccessible(true);
380 charset.set(null, null);
381 } catch (SecurityException e) {
382 } catch (NoSuchFieldException e) {
383 } catch (IllegalArgumentException e) {
384 } catch (IllegalAccessException e) {
385 }
386 }
387}