1 package be
.nikiroo
.jvcard
.launcher
;
4 import java
.io
.IOException
;
5 import java
.lang
.reflect
.Field
;
6 import java
.lang
.reflect
.InvocationTargetException
;
7 import java
.lang
.reflect
.Method
;
8 import java
.net
.Socket
;
9 import java
.nio
.charset
.Charset
;
10 import java
.util
.LinkedList
;
11 import java
.util
.List
;
13 import be
.nikiroo
.jvcard
.Card
;
14 import be
.nikiroo
.jvcard
.parsers
.Format
;
15 import be
.nikiroo
.jvcard
.remote
.Command
.Verb
;
16 import be
.nikiroo
.jvcard
.remote
.SimpleSocket
;
17 import be
.nikiroo
.jvcard
.resources
.Bundles
;
18 import be
.nikiroo
.jvcard
.resources
.StringUtils
;
19 import be
.nikiroo
.jvcard
.resources
.Trans
;
20 import be
.nikiroo
.jvcard
.resources
.Trans
.StringId
;
23 * This class contains the runnable Main method. It will parse the user supplied
24 * parameters and take action based upon those. Most of the time, it will start
31 static public final String APPLICATION_TITLE
= "jVcard";
32 static public final String APPLICATION_VERSION
= "1.0-beta2-dev";
34 static private final int ERR_NO_FILE
= 1;
35 static private final int ERR_SYNTAX
= 2;
36 static private final int ERR_INTERNAL
= 3;
37 static private Trans transService
;
40 * Translate the given {@link StringId}.
45 * @return the translation
47 static public String
trans(StringId id
) {
48 return transService
.trans(id
);
52 * Check if unicode characters should be used.
54 * @return TRUE to allow unicode
56 static public boolean isUnicode() {
57 return transService
.isUnicode();
61 * Start the application.
64 * The returned exit codes are:
66 * <li>1: no files to open</li>
67 * <li>2: invalid syntax</li>
68 * <li>3: internal error</li>
73 * the parameters (see <tt>--help</tt> to know which are
76 public static void main(String
[] args
) {
77 Boolean textMode
= null;
78 boolean noMoreParams
= false;
79 boolean filesTried
= false;
81 // get the "system default" language to help translate the --help
83 String language
= null;
84 transService
= new Trans(language
);
86 boolean unicode
= transService
.isUnicode();
87 String i18nDir
= null;
88 List
<String
> files
= new LinkedList
<String
>();
90 for (int index
= 0; index
< args
.length
; index
++) {
91 String arg
= args
[index
];
92 if (!noMoreParams
&& arg
.equals("--")) {
94 } else if (!noMoreParams
&& arg
.equals("--help")) {
96 .println("TODO: implement some help text.\n"
97 + "Usable switches:\n"
98 + "\t--: stop looking for switches\n"
99 + "\t--help: this here thingy\n"
100 + "\t--lang LANGUAGE: choose the language, for instance en_GB\n"
101 + "\t--tui: force pure text mode even if swing treminal is available\n"
102 + "\t--gui: force swing terminal mode\n"
103 + "\t--noutf: force non-utf8 mode if you need it\n"
104 + "\t--config DIRECTORY: force the given directory as a CONFIG_DIR\n"
105 + "\t--server PORT: start a remoting server instead of a client\n"
106 + "\t--i18n DIR: generate the translation file for the given language (can be \"\") to/from the .properties given dir\n"
107 + "everyhing else is either a file to open or a directory to open\n"
108 + "(we will only open 1st level files in given directories)\n"
109 + "('jvcard://hostname:8888/file' links -- or without 'file' -- are also ok)\n");
111 } else if (!noMoreParams
&& arg
.equals("--tui")) {
113 } else if (!noMoreParams
&& arg
.equals("--gui")) {
115 } else if (!noMoreParams
&& arg
.equals("--noutf")) {
117 transService
.setUnicode(unicode
);
118 } else if (!noMoreParams
&& arg
.equals("--lang")) {
120 if (index
>= args
.length
) {
121 System
.err
.println("Syntax error: no language given");
122 System
.exit(ERR_SYNTAX
);
126 language
= args
[index
];
127 transService
= new Trans(language
);
128 transService
.setUnicode(unicode
);
129 } else if (!noMoreParams
&& arg
.equals("--config")) {
131 if (index
>= args
.length
) {
133 .println("Syntax error: no config directory given");
134 System
.exit(ERR_SYNTAX
);
138 Bundles
.setDirectory(args
[index
]);
139 transService
= new Trans(language
);
140 transService
.setUnicode(unicode
);
141 } else if (!noMoreParams
&& arg
.equals("--server")) {
143 if (index
>= args
.length
) {
144 System
.err
.println("Syntax error: no port given");
145 System
.exit(ERR_SYNTAX
);
150 port
= Integer
.parseInt(args
[index
]);
151 } catch (NumberFormatException e
) {
152 System
.err
.println("Invalid port number: " + args
[index
]);
153 System
.exit(ERR_SYNTAX
);
156 } else if (!noMoreParams
&& arg
.equals("--i18n")) {
158 if (index
>= args
.length
) {
160 .println("Syntax error: no .properties directory given");
161 System
.exit(ERR_SYNTAX
);
165 i18nDir
= args
[index
];
168 files
.addAll(open(arg
));
177 if (port
!= null && files
.size() > 0) {
179 .println("Invalid syntax: you cannot both use --server and provide card files");
180 System
.exit(ERR_SYNTAX
);
181 } else if (i18nDir
!= null && files
.size() > 0) {
183 .println("Invalid syntax: you cannot both use --i18n and provide card files");
184 System
.exit(ERR_SYNTAX
);
185 } else if (port
!= null && i18nDir
!= null) {
187 .println("Invalid syntax: you cannot both use --server and --i18n");
188 System
.exit(ERR_SYNTAX
);
189 } else if (i18nDir
!= null && language
== null) {
191 .println("Invalid syntax: you cannot use --i18n without --lang");
192 System
.exit(ERR_SYNTAX
);
193 } else if (port
== null && i18nDir
== null && files
.size() == 0) {
194 if (files
.size() == 0 && !filesTried
) {
195 files
.addAll(open("."));
198 if (files
.size() == 0) {
199 System
.err
.println("No files to open");
200 System
.exit(ERR_NO_FILE
);
209 } catch (Exception e
) {
210 if (e
instanceof IOException
) {
212 .println("I/O Exception: Cannot start the server");
214 System
.err
.println("Remoting support not available");
215 System
.exit(ERR_INTERNAL
);
218 } else if (i18nDir
!= null) {
220 Trans
.generateTranslationFile(i18nDir
, language
);
221 } catch (IOException e
) {
223 .println("I/O Exception: Cannot create/update a language in directory: "
228 startTui(textMode
, files
);
229 } catch (Exception e
) {
230 if (e
instanceof IOException
) {
232 .println("I/O Exception: Cannot start the program with the given cards");
234 System
.err
.println("TUI support not available");
235 System
.exit(ERR_INTERNAL
);
242 * Return the {@link Card} corresponding to the given resource name -- a
243 * file or a remote jvcard URL
246 * a filename or a remote jvcard url with named resource (e.g.:
247 * <tt>jvcard://localhost:4444/coworkers.vcf</tt>)
249 * @return the {@link Card}
251 * @throws IOException
252 * in case of IO error or remoting not available
254 static public Card
getCard(String input
) throws IOException
{
255 boolean remote
= false;
256 Format format
= Format
.Abook
;
258 if (ext
.contains(".")) {
259 String tab
[] = ext
.split("\\.");
260 if (tab
.length
> 1 && tab
[tab
.length
- 1].equalsIgnoreCase("vcf")) {
261 format
= Format
.VCard21
;
265 if (input
.contains("://")) {
266 format
= Format
.VCard21
;
273 card
= syncCard(input
);
275 card
= new Card(new File(input
), format
);
277 } catch (IOException ioe
) {
279 } catch (Exception e
) {
280 throw new IOException("Remoting support not available", e
);
287 * Create a new jVCard server on the given port, then run it.
292 * @throws SecurityException
293 * in case of internal error
294 * @throws NoSuchMethodException
295 * in case of internal error
296 * @throws ClassNotFoundException
297 * in case of internal error
298 * @throws IllegalAccessException
299 * in case of internal error
300 * @throws InstantiationException
301 * in case of internal error
302 * @throws InvocationTargetException
303 * in case of internal error
304 * @throws IllegalArgumentException
305 * in case of internal error
306 * @throws IOException
307 * in case of IO error
309 @SuppressWarnings("unchecked")
310 static private void runServer(int port
) throws NoSuchMethodException
,
311 SecurityException
, ClassNotFoundException
, InstantiationException
,
312 IllegalAccessException
, IllegalArgumentException
,
313 InvocationTargetException
{
314 @SuppressWarnings("rawtypes")
315 Class serverClass
= Class
.forName("be.nikiroo.jvcard.remote.Server");
316 Method run
= serverClass
.getDeclaredMethod("run", new Class
[] {});
317 run
.invoke(serverClass
.getConstructor(int.class).newInstance(port
));
321 * Start the TUI program.
324 * TRUE to force text mode, FALSE to force the Swing terminal
325 * emulator, null to automatically determine the best choice
327 * the files to show at startup
329 * @throws SecurityException
330 * in case of internal error
331 * @throws NoSuchMethodException
332 * in case of internal error
333 * @throws ClassNotFoundException
334 * in case of internal error
335 * @throws IllegalAccessException
336 * in case of internal error
337 * @throws InstantiationException
338 * in case of internal error
339 * @throws InvocationTargetException
340 * in case of internal error
341 * @throws IllegalArgumentException
342 * in case of internal error
343 * @throws IOException
344 * in case of IO error
346 @SuppressWarnings("unchecked")
347 static private void startTui(Boolean textMode
, List
<String
> files
)
348 throws NoSuchMethodException
, SecurityException
,
349 ClassNotFoundException
, InstantiationException
,
350 IllegalAccessException
, IllegalArgumentException
,
351 InvocationTargetException
{
352 @SuppressWarnings("rawtypes")
353 Class launcherClass
= Class
354 .forName("be.nikiroo.jvcard.tui.TuiLauncher");
355 Method start
= launcherClass
.getDeclaredMethod("start", new Class
[] {
356 Boolean
.class, List
.class });
357 start
.invoke(launcherClass
.newInstance(), textMode
, files
);
361 * Return the {@link Card} corresponding to the given URL, synchronised if
365 * the jvcard:// with resource name URL (e.g.:
366 * <tt>jvcard://localhost:4444/coworkers</tt>)
368 * @throws SecurityException
369 * in case of internal error
370 * @throws NoSuchMethodException
371 * in case of internal error
372 * @throws ClassNotFoundException
373 * in case of internal error
374 * @throws IllegalAccessException
375 * in case of internal error
376 * @throws InstantiationException
377 * in case of internal error
378 * @throws InvocationTargetException
379 * in case of internal error
380 * @throws IllegalArgumentException
381 * in case of internal error
382 * @throws IOException
383 * in case of IO error
385 @SuppressWarnings("unchecked")
386 static private Card
syncCard(String input
) throws ClassNotFoundException
,
387 NoSuchMethodException
, SecurityException
, InstantiationException
,
388 IllegalAccessException
, IllegalArgumentException
,
389 InvocationTargetException
, IOException
{
390 @SuppressWarnings("rawtypes")
391 Class syncClass
= Class
.forName("be.nikiroo.jvcard.remote.Sync");
392 Method getCache
= syncClass
.getDeclaredMethod("getCache",
394 Method sync
= syncClass
.getDeclaredMethod("sync", new Class
[] {
395 Card
.class, boolean.class });
397 Object o
= syncClass
.getConstructor(String
.class).newInstance(input
);
399 File file
= (File
) getCache
.invoke(o
);
400 Card card
= new Card(file
, Format
.VCard21
);
401 card
.setRemote(true);
402 sync
.invoke(o
, card
, false);
408 * Open the given path and add all its files if it is a directory or just
409 * this one if not to the returned list.
414 * @return the list of opened files
416 static private List
<String
> open(String path
) {
417 List
<String
> files
= new LinkedList
<String
>();
419 if (path
!= null && path
.startsWith("jvcard://")) {
420 if (path
.endsWith("/")) {
421 files
.addAll(list(path
));
426 File file
= new File(path
);
428 if (file
.isDirectory()) {
429 for (File subfile
: file
.listFiles()) {
430 if (!subfile
.isDirectory())
431 files
.add(subfile
.getAbsolutePath());
434 files
.add(file
.getAbsolutePath());
437 System
.err
.println("File or directory not found: \"" + path
446 * List all the available {@link Card}s on the given network location (which
447 * is expected to be a jVCard remote server, obviously).
450 * the jVCard remote server path (e.g.:
451 * <tt>jvcard://localhost:4444/</tt>)
453 * @return the list of {@link Card}s
455 static private List
<String
> list(String path
) {
456 List
<String
> files
= new LinkedList
<String
>();
459 String host
= path
.split("\\:")[1].substring(2);
460 int port
= Integer
.parseInt(path
.split("\\:")[2].replaceAll("/$",
462 SimpleSocket s
= new SimpleSocket(new Socket(host
, port
),
466 s
.sendCommand(Verb
.LIST
);
467 for (String p
: s
.receiveBlock()) {
469 + p
.substring(StringUtils
.fromTime(0).length() + 1));
472 } catch (Exception e
) {
480 * Really, really ask for UTF-8 encoding.
482 static private void utf8() {
484 System
.setProperty("file.encoding", "UTF-8");
485 Field charset
= Charset
.class.getDeclaredField("defaultCharset");
486 charset
.setAccessible(true);
487 charset
.set(null, null);
488 } catch (SecurityException e
) {
489 } catch (NoSuchFieldException e
) {
490 } catch (IllegalArgumentException e
) {
491 } catch (IllegalAccessException e
) {