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