Remote support ~complete (need more tests at least)
[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";
31 static public final String APPLICATION_VERSION = "1.0-beta2-dev";
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 }
163
164 i18nDir = args[index];
165 } else {
166 filesTried = true;
167 files.addAll(open(arg));
168 }
169 }
170
171 if (unicode) {
172 utf8();
173 }
174
175 // Error management:
176 if (port != null && files.size() > 0) {
177 System.err
178 .println("Invalid syntax: you cannot both use --server and provide card files");
179 System.exit(ERR_SYNTAX);
180 } else if (i18nDir != null && files.size() > 0) {
181 System.err
182 .println("Invalid syntax: you cannot both use --i18n and provide card files");
183 System.exit(ERR_SYNTAX);
184 } else if (port != null && i18nDir != null) {
185 System.err
186 .println("Invalid syntax: you cannot both use --server and --i18n");
187 System.exit(ERR_SYNTAX);
188 } else if (i18nDir != null && language == null) {
189 System.err
190 .println("Invalid syntax: you cannot use --i18n without --lang");
191 System.exit(ERR_SYNTAX);
192 } else if (port == null && i18nDir == null && files.size() == 0) {
193 if (files.size() == 0 && !filesTried) {
194 files.addAll(open("."));
195 }
196
197 if (files.size() == 0) {
198 System.err.println("No files to open");
199 System.exit(ERR_NO_FILE);
200 return;
201 }
202 }
203 //
204
205 if (port != null) {
206 try {
02b341aa 207 Optional.runServer(port);
7da41ecd
NR
208 } catch (Exception e) {
209 if (e instanceof IOException) {
210 System.err
211 .println("I/O Exception: Cannot start the server");
212 } else {
4298276a 213 System.err.println("Remoting support not available");
7da41ecd
NR
214 System.exit(ERR_INTERNAL);
215 }
216 }
217 } else if (i18nDir != null) {
218 try {
219 Trans.generateTranslationFile(i18nDir, language);
220 } catch (IOException e) {
221 System.err
222 .println("I/O Exception: Cannot create/update a language in directory: "
223 + i18nDir);
224 }
225 } else {
226 try {
02b341aa 227 Optional.startTui(textMode, files);
7da41ecd
NR
228 } catch (Exception e) {
229 if (e instanceof IOException) {
230 System.err
231 .println("I/O Exception: Cannot start the program with the given cards");
232 } else {
4298276a 233 System.err.println("TUI support not available");
7da41ecd
NR
234 System.exit(ERR_INTERNAL);
235 }
236 }
237 }
238 }
239
240 /**
241 * Return the {@link Card} corresponding to the given resource name -- a
242 * file or a remote jvcard URL
243 *
244 * @param input
245 * a filename or a remote jvcard url with named resource (e.g.:
246 * <tt>jvcard://localhost:4444/coworkers.vcf</tt>)
5ad0e17e
NR
247 * @param callback
248 * the {@link MergeCallback} to call in case of conflict, or NULL
249 * to disallow conflict management (the {@link Card} will not be
250 * allowed to synchronise in case of conflicts)
7da41ecd
NR
251 *
252 * @return the {@link Card}
253 *
254 * @throws IOException
255 * in case of IO error or remoting not available
256 */
5ad0e17e
NR
257 static public CardResult getCard(String input, MergeCallback callback)
258 throws IOException {
7da41ecd
NR
259 boolean remote = false;
260 Format format = Format.Abook;
261 String ext = input;
262 if (ext.contains(".")) {
263 String tab[] = ext.split("\\.");
264 if (tab.length > 1 && tab[tab.length - 1].equalsIgnoreCase("vcf")) {
265 format = Format.VCard21;
266 }
267 }
268
269 if (input.contains("://")) {
270 format = Format.VCard21;
271 remote = true;
272 }
273
5ad0e17e 274 CardResult card = null;
7da41ecd
NR
275 try {
276 if (remote) {
5ad0e17e 277 card = Optional.syncCard(input, callback);
7da41ecd 278 } else {
5ad0e17e
NR
279 card = new CardResult(new Card(new File(input), format), false,
280 false, false);
7da41ecd
NR
281 }
282 } catch (IOException ioe) {
283 throw ioe;
284 } catch (Exception e) {
4298276a 285 throw new IOException("Remoting support not available", e);
7da41ecd
NR
286 }
287
288 return card;
289 }
290
7da41ecd
NR
291 /**
292 * Open the given path and add all its files if it is a directory or just
293 * this one if not to the returned list.
294 *
295 * @param path
296 * the path to open
297 *
298 * @return the list of opened files
299 */
300 static private List<String> open(String path) {
301 List<String> files = new LinkedList<String>();
302
303 if (path != null && path.startsWith("jvcard://")) {
304 if (path.endsWith("/")) {
305 files.addAll(list(path));
306 } else {
307 files.add(path);
308 }
309 } else {
310 File file = new File(path);
311 if (file.exists()) {
312 if (file.isDirectory()) {
313 for (File subfile : file.listFiles()) {
314 if (!subfile.isDirectory())
315 files.add(subfile.getAbsolutePath());
316 }
317 } else {
318 files.add(file.getAbsolutePath());
319 }
320 } else {
321 System.err.println("File or directory not found: \"" + path
322 + "\"");
323 }
324 }
325
326 return files;
327 }
328
329 /**
330 * List all the available {@link Card}s on the given network location (which
331 * is expected to be a jVCard remote server, obviously).
332 *
333 * @param path
334 * the jVCard remote server path (e.g.:
335 * <tt>jvcard://localhost:4444/</tt>)
336 *
337 * @return the list of {@link Card}s
338 */
339 static private List<String> list(String path) {
340 List<String> files = new LinkedList<String>();
341
342 try {
343 String host = path.split("\\:")[1].substring(2);
344 int port = Integer.parseInt(path.split("\\:")[2].replaceAll("/$",
345 ""));
346 SimpleSocket s = new SimpleSocket(new Socket(host, port),
347 "sync client");
348 s.open(true);
349
845fb1d7 350 s.sendCommand(Command.LIST_CARD);
7da41ecd
NR
351 for (String p : s.receiveBlock()) {
352 files.add(path
353 + p.substring(StringUtils.fromTime(0).length() + 1));
354 }
355 s.close();
356 } catch (Exception e) {
357 e.printStackTrace();
358 }
359
360 return files;
361 }
362
363 /**
364 * Really, really ask for UTF-8 encoding.
365 */
366 static private void utf8() {
367 try {
368 System.setProperty("file.encoding", "UTF-8");
369 Field charset = Charset.class.getDeclaredField("defaultCharset");
370 charset.setAccessible(true);
371 charset.set(null, null);
372 } catch (SecurityException e) {
373 } catch (NoSuchFieldException e) {
374 } catch (IllegalArgumentException e) {
375 } catch (IllegalAccessException e) {
376 }
377 }
378}