001/* ***** BEGIN LICENSE BLOCK *****
002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003 *
004 * The contents of this file are subject to the Mozilla Public License Version
005 * 1.1 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 * http://www.mozilla.org/MPL/
008 *
009 * Software distributed under the License is distributed on an "AS IS" basis,
010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011 * for the specific language governing rights and limitations under the
012 * License.
013 *
014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
015 * Java(TM), hosted at https://github.com/gunterze/dcm4che.
016 *
017 * The Initial Developer of the Original Code is
018 * Agfa Healthcare.
019 * Portions created by the Initial Developer are Copyright (C) 2011
020 * the Initial Developer. All Rights Reserved.
021 *
022 * Contributor(s):
023 * See @authors listed below
024 *
025 * Alternatively, the contents of this file may be used under the terms of
026 * either the GNU General Public License Version 2 or later (the "GPL"), or
027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
028 * in which case the provisions of the GPL or the LGPL are applicable instead
029 * of those above. If you wish to allow use of your version of this file only
030 * under the terms of either the GPL or the LGPL, and not to allow others to
031 * use your version of this file under the terms of the MPL, indicate your
032 * decision by deleting the provisions above and replace them with the notice
033 * and other provisions required by the GPL or the LGPL. If you do not delete
034 * the provisions above, a recipient may use your version of this file under
035 * the terms of any one of the MPL, the GPL or the LGPL.
036 *
037 * ***** END LICENSE BLOCK ***** */
038
039package org.dcm4che3.tool.common;
040
041import java.io.File;
042import java.io.IOException;
043import java.io.InputStream;
044import java.security.GeneralSecurityException;
045import java.text.MessageFormat;
046import java.util.Properties;
047import java.util.ResourceBundle;
048
049import org.apache.commons.cli.CommandLine;
050import org.apache.commons.cli.CommandLineParser;
051import org.apache.commons.cli.HelpFormatter;
052import org.apache.commons.cli.MissingOptionException;
053import org.apache.commons.cli.OptionBuilder;
054import org.apache.commons.cli.OptionGroup;
055import org.apache.commons.cli.Options;
056import org.apache.commons.cli.ParseException;
057import org.apache.commons.cli.PosixParser;
058import org.dcm4che3.data.Tag;
059import org.dcm4che3.data.UID;
060import org.dcm4che3.data.Attributes;
061import org.dcm4che3.data.ElementDictionary;
062import org.dcm4che3.data.Sequence;
063import org.dcm4che3.data.VR;
064import org.dcm4che3.io.DicomEncodingOptions;
065import org.dcm4che3.net.ApplicationEntity;
066import org.dcm4che3.net.Connection;
067import org.dcm4che3.net.Device;
068import org.dcm4che3.net.Priority;
069import org.dcm4che3.net.SSLManagerFactory;
070import org.dcm4che3.net.pdu.AAssociateRQ;
071import org.dcm4che3.net.pdu.UserIdentityRQ;
072import org.dcm4che3.util.SafeClose;
073import org.dcm4che3.util.StreamUtils;
074import org.dcm4che3.util.StringUtils;
075
076/**
077 * @author Gunter Zeilinger <gunterze@gmail.com>
078 *
079 */
080public class CLIUtils {
081
082    public static ResourceBundle rb =
083        ResourceBundle.getBundle("org.dcm4che3.tool.common.messages");
084
085    public static void addCommonOptions(Options opts) {
086        opts.addOption("h", "help", false, rb.getString("help"));
087        opts.addOption("V", "version", false, rb.getString("version"));
088    }
089
090    @SuppressWarnings("static-access")
091    public static void addBindOption(Options opts, String defAET) {
092        opts.addOption(OptionBuilder
093                .hasArg()
094                .withArgName("aet[@ip][:port]")
095                .withDescription(
096                        MessageFormat.format(rb.getString("bind"), defAET))
097                .withLongOpt("bind")
098                .create("b"));
099    }
100
101    @SuppressWarnings("static-access")
102    public static void addBindServerOption(Options opts) {
103        opts.addOption(OptionBuilder
104                .hasArg()
105                .withArgName("[aet[@ip]:]port")
106                .withDescription(rb.getString("bind-server"))
107                .withLongOpt("bind")
108                .create("b"));
109        addRequestTimeoutOption(opts);
110    }
111
112    @SuppressWarnings("static-access")
113    public static void addConnectOption(Options opts) {
114        opts.addOption(OptionBuilder
115                .hasArg()
116                .withArgName("aet@host:port")
117                .withDescription(rb.getString("connect"))
118                .withLongOpt("connect")
119                .create("c"));
120        opts.addOption(OptionBuilder
121                .hasArg()
122                .withArgName("[user:password@]host:port")
123                .withDescription(rb.getString("proxy"))
124                .withLongOpt("proxy")
125                .create(null));
126        opts.addOption(OptionBuilder
127                .hasArg()
128                .withArgName("name")
129                .withDescription(rb.getString("user"))
130                .withLongOpt("user")
131                .create(null));
132        opts.addOption(OptionBuilder
133                .hasArg()
134                .withArgName("password")
135                .withDescription(rb.getString("user-pass"))
136                .withLongOpt("user-pass")
137                .create(null));
138        opts.addOption(null, "user-rsp", false, rb.getString("user-rsp"));
139        addConnectTimeoutOption(opts);
140        addAcceptTimeoutOption(opts);
141    }
142
143    @SuppressWarnings("static-access")
144    public static void addAEOptions(Options opts) {
145        opts.addOption(OptionBuilder
146                .hasArg()
147                .withArgName("length")
148                .withDescription(rb.getString("max-pdulen-rcv"))
149                .withLongOpt("max-pdulen-rcv")
150                .create(null));
151        opts.addOption(OptionBuilder
152                .hasArg()
153                .withArgName("length")
154                .withDescription(rb.getString("max-pdulen-snd"))
155                .withLongOpt("max-pdulen-snd")
156                .create(null));
157        opts.addOption(OptionBuilder
158                .hasArg()
159                .withArgName("no")
160                .withDescription(rb.getString("max-ops-invoked"))
161                .withLongOpt("max-ops-invoked")
162                .create(null));
163        opts.addOption(OptionBuilder
164                .hasArg()
165                .withArgName("no")
166                .withDescription(rb.getString("max-ops-performed"))
167                .withLongOpt("max-ops-performed")
168                .create(null));
169        opts.addOption(null, "not-async", false, rb.getString("not-async"));
170        opts.addOption(null, "not-pack-pdv", false, rb.getString("not-pack-pdv"));
171        opts.addOption(OptionBuilder
172                .hasArg()
173                .withArgName("ms")
174                .withDescription(rb.getString("idle-timeout"))
175                .withLongOpt("idle-timeout")
176                .create(null));
177        opts.addOption(OptionBuilder
178                .hasArg()
179                .withArgName("ms")
180                .withDescription(rb.getString("release-timeout"))
181                .withLongOpt("release-timeout")
182                .create(null));
183        opts.addOption(OptionBuilder
184                .hasArg()
185                .withArgName("ms")
186                .withDescription(rb.getString("soclose-delay"))
187                .withLongOpt("soclose-delay")
188                .create(null));
189        addSocketOptions(opts);
190        addTLSOptions(opts);
191    }
192
193    @SuppressWarnings("static-access")
194    public static void addRequestTimeoutOption(Options opts) {
195        opts.addOption(OptionBuilder
196            .hasArg()
197            .withArgName("ms")
198            .withDescription(rb.getString("request-timeout"))
199            .withLongOpt("request-timeout")
200            .create(null));
201    }
202
203    @SuppressWarnings("static-access")
204    public static void addAcceptTimeoutOption(Options opts) {
205        opts.addOption(OptionBuilder
206                .hasArg()
207                .withArgName("ms")
208                .withDescription(rb.getString("accept-timeout"))
209                .withLongOpt("accept-timeout")
210                .create(null));
211    }
212
213    @SuppressWarnings("static-access")
214    public static void addSocketOptions(Options opts) {
215        opts.addOption(OptionBuilder
216                .hasArg()
217                .withArgName("length")
218                .withDescription(rb.getString("sosnd-buffer"))
219                .withLongOpt("sosnd-buffer")
220                .create(null));
221        opts.addOption(OptionBuilder
222                .hasArg()
223                .withArgName("length")
224                .withDescription(rb.getString("sorcv-buffer"))
225                .withLongOpt("sorcv-buffer")
226                .create(null));
227        opts.addOption(null, "tcp-delay", false, rb.getString("tcp-delay"));
228    }
229
230    @SuppressWarnings("static-access")
231    public static void addConnectTimeoutOption(Options opts) {
232        opts.addOption(OptionBuilder
233                .hasArg()
234                .withArgName("ms")
235                .withDescription(rb.getString("connect-timeout"))
236                .withLongOpt("connect-timeout")
237                .create(null));
238    }
239
240    @SuppressWarnings("static-access")
241    public static void addResponseTimeoutOption(Options opts) {
242        opts.addOption(OptionBuilder
243            .hasArg()
244            .withArgName("ms")
245            .withDescription(rb.getString("response-timeout"))
246            .withLongOpt("response-timeout")
247            .create(null));
248    }
249
250    @SuppressWarnings("static-access")
251    public static void addRetrieveTimeoutOption(Options opts) {
252        opts.addOption(OptionBuilder
253            .hasArg()
254            .withArgName("ms")
255            .withDescription(rb.getString("retrieve-timeout"))
256            .withLongOpt("retrieve-timeout")
257            .create(null));
258    }
259
260    @SuppressWarnings("static-access")
261    public static void addTLSOptions(Options opts) {
262        opts.addOption(OptionBuilder
263                .hasArg()
264                .withArgName("cipher")
265                .withDescription(rb.getString("tls-cipher"))
266                .withLongOpt("tls-cipher")
267                .create(null));
268        opts.addOption(null, "tls", false, rb.getString("tls"));
269        opts.addOption(null, "tls-null", false, rb.getString("tls-null"));
270        opts.addOption(null, "tls-3des", false, rb.getString("tls-3des"));
271        opts.addOption(null, "tls-aes", false, rb.getString("tls-aes"));
272        opts.addOption(OptionBuilder
273                .hasArg()
274                .withArgName("protocol")
275                .withDescription(rb.getString("tls-protocol"))
276                .withLongOpt("tls-protocol")
277                .create(null));
278        opts.addOption(null, "tls1", false, rb.getString("tls1"));
279        opts.addOption(null, "tls11", false, rb.getString("tls11"));
280        opts.addOption(null, "tls12", false, rb.getString("tls12"));
281        opts.addOption(null, "ssl3", false, rb.getString("ssl3"));
282        opts.addOption(null, "ssl2Hello", false, rb.getString("ssl2Hello"));
283        opts.addOption(null, "tls-noauth", false, rb.getString("tls-noauth"));
284        opts.addOption(OptionBuilder
285                .hasArg()
286                .withArgName("file|url")
287                .withDescription(rb.getString("key-store"))
288                .withLongOpt("key-store")
289                .create(null));
290        opts.addOption(OptionBuilder
291                .hasArg()
292                .withArgName("storetype")
293                .withDescription(rb.getString("key-store-type"))
294                .withLongOpt("key-store-type")
295                .create(null));
296        opts.addOption(OptionBuilder
297                .hasArg()
298                .withArgName("password")
299                .withDescription(rb.getString("key-store-pass"))
300                .withLongOpt("key-store-pass")
301                .create(null));
302        opts.addOption(OptionBuilder
303                .hasArg()
304                .withArgName("password")
305                .withDescription(rb.getString("key-pass"))
306                .withLongOpt("key-pass")
307                .create(null));
308        opts.addOption(OptionBuilder
309                .hasArg()
310                .withArgName("file|url")
311                .withDescription(rb.getString("trust-store"))
312                .withLongOpt("trust-store")
313                .create(null));
314        opts.addOption(OptionBuilder
315                .hasArg()
316                .withArgName("storetype")
317                .withDescription(rb.getString("trust-store-type"))
318                .withLongOpt("trust-store-type")
319                .create(null));
320        opts.addOption(OptionBuilder
321                .hasArg()
322                .withArgName("password")
323                .withDescription(rb.getString("trust-store-pass"))
324                .withLongOpt("trust-store-pass")
325                .create(null));
326    }
327
328    @SuppressWarnings("static-access")
329    public static void addPriorityOption(Options opts) {
330        OptionGroup group = new OptionGroup();
331        group.addOption(OptionBuilder
332                .withLongOpt("prior-high")
333                .withDescription(rb.getString("prior-high"))
334                .create());
335        group.addOption(OptionBuilder
336                .withLongOpt("prior-low")
337                .withDescription(rb.getString("prior-low"))
338                .create());
339        opts.addOptionGroup(group);
340    }
341
342    public static CommandLine parseComandLine(String[] args, Options opts, 
343            ResourceBundle rb2, Class<?> clazz) throws ParseException {
344        CommandLineParser parser = new PosixParser();
345        CommandLine cl = parser.parse(opts, args);
346        if (cl.hasOption("h")) {
347            HelpFormatter formatter = new HelpFormatter();
348            formatter.printHelp(
349                    rb2.getString("usage"),
350                    rb2.getString("description"), opts,
351                    rb2.getString("example"));
352            System.exit(0);
353        }
354        if (cl.hasOption("V")) {
355            Package p = clazz.getPackage();
356            String s = p.getName();
357            System.out.println(s.substring(s.lastIndexOf('.')+1) + ": " +
358                   p.getImplementationVersion());
359            System.exit(0);
360        }
361        return cl;
362    }
363
364    public static void configureConnect(Connection conn,
365            AAssociateRQ rq, CommandLine cl) throws ParseException {
366        if (!cl.hasOption("c"))
367            throw new MissingOptionException(
368                    rb.getString("missing-connect-opt"));
369        String aeAtHostPort = cl.getOptionValue("c");
370        String[] aeHostPort = split(aeAtHostPort , '@', 0);
371        if (aeHostPort[1] == null)
372            throw new ParseException(rb.getString("invalid-connect-opt"));
373        
374        String[] hostPort = split(aeHostPort[1], ':', 0);
375        if (hostPort[1] == null)
376            throw new ParseException(rb.getString("invalid-connect-opt"));
377
378        rq.setCalledAET(aeHostPort[0]);
379        conn.setHostname(hostPort[0]);
380        conn.setPort(Integer.parseInt(hostPort[1]));
381
382        conn.setHttpProxy(cl.getOptionValue("proxy"));
383
384        if (cl.hasOption("user"))
385            rq.setUserIdentityRQ(cl.hasOption("user-pass")
386                    ? new UserIdentityRQ(cl.getOptionValue("user"),
387                            cl.getOptionValue("user-pass").toCharArray())
388                    : new UserIdentityRQ(cl.getOptionValue("user"),
389                            cl.hasOption("user-rsp")));
390    }
391
392    public static void configureBind(Connection conn,
393            ApplicationEntity ae, CommandLine cl) throws ParseException {
394        if (cl.hasOption("b")) {
395            String aeAtHostPort = cl.getOptionValue("b");
396            String[] aeAtHostAndPort = split(aeAtHostPort, ':', 0);
397            String[] aeHost = split(aeAtHostAndPort[0], '@', 0);
398            ae.setAETitle(aeHost[0]);
399            if (aeHost[1] != null)
400                conn.setHostname(aeHost[1]);
401            if (aeAtHostAndPort[1] != null)
402                conn.setPort(Integer.parseInt(aeAtHostAndPort[1]));
403        }
404    }
405
406    public static void configureBindServer(Connection conn,
407            ApplicationEntity ae, CommandLine cl) throws ParseException {
408        if (!cl.hasOption("b"))
409            throw new MissingOptionException(rb.getString("missing-bind-opt"));
410        String aeAtHostPort = cl.getOptionValue("b");
411        String[] aeAtHostAndPort = split(aeAtHostPort, ':', 1);
412        conn.setPort(Integer.parseInt(aeAtHostAndPort[1]));
413        if (aeAtHostAndPort[0] != null) {
414            String[] aeHost = split(aeAtHostAndPort[0], '@', 0);
415            ae.setAETitle(aeHost[0]);
416            if (aeHost[1] != null)
417                conn.setHostname(aeHost[1]);
418        }
419    }
420
421    private static String[] split(String s, char delim, int defPos) {
422        String[] s2 = new String[2];
423        int pos = s.indexOf(delim);
424        if (pos != -1) {
425            s2[0] = s.substring(0, pos);
426            s2[1] = s.substring(pos + 1);
427        } else {
428            s2[defPos] = s;
429        }
430        return s2;
431    }
432
433    public static int priorityOf(CommandLine cl) {
434        return cl.hasOption("prior-high")
435                ? Priority.HIGH
436                : cl.hasOption("prior-low") 
437                        ? Priority.LOW
438                        : Priority.NORMAL;
439    }
440
441    public static int getIntOption(CommandLine cl, String opt, int defVal) {
442        String optVal = cl.getOptionValue(opt);
443        if (optVal == null)
444            return defVal;
445        
446        return optVal.endsWith("H")
447                ? Integer.parseInt(optVal.substring(0, optVal.length() - 1), 16)
448                : Integer.parseInt(optVal);
449    }
450
451    public static void configure(Connection conn, CommandLine cl)
452            throws ParseException, IOException {
453        conn.setReceivePDULength(
454                getIntOption(cl, "max-pdulen-rcv", Connection.DEF_MAX_PDU_LENGTH));
455        conn.setSendPDULength(
456                getIntOption(cl, "max-pdulen-snd", Connection.DEF_MAX_PDU_LENGTH));
457        if(cl.hasOption("not-async")) {
458            conn.setMaxOpsInvoked(1);
459            conn.setMaxOpsPerformed(1);
460        } else {
461            conn.setMaxOpsInvoked(getIntOption(cl, "max-ops-invoked", 0));
462            conn.setMaxOpsPerformed(getIntOption(cl, "max-ops-performed", 0));
463        }
464        conn.setPackPDV(!cl.hasOption("not-pack-pdv"));
465        conn.setConnectTimeout(getIntOption(cl, "connect-timeout", 0));
466        conn.setRequestTimeout(getIntOption(cl, "request-timeout", 0));
467        conn.setAcceptTimeout(getIntOption(cl, "accept-timeout", 0));
468        conn.setReleaseTimeout(getIntOption(cl, "release-timeout", 0));
469        conn.setResponseTimeout(getIntOption(cl, "response-timeout", 0));
470        conn.setRetrieveTimeout(getIntOption(cl, "retrieve-timeout", 0));
471        conn.setIdleTimeout(getIntOption(cl, "idle-timeout", 0));
472        conn.setSocketCloseDelay(getIntOption(cl, "soclose-delay", 
473                Connection.DEF_SOCKETDELAY));
474        conn.setSendBufferSize(getIntOption(cl, "sosnd-buffer", 0));
475        conn.setReceiveBufferSize(getIntOption(cl, "sorcv-buffer", 0));
476        conn.setTcpNoDelay(!cl.hasOption("tcp-delay"));
477        configureTLS(conn, cl);
478    }
479
480    private static void configureTLS(Connection conn, CommandLine cl)
481            throws ParseException, IOException {
482        if (cl.hasOption("tls"))
483            conn.setTlsCipherSuites(
484                    "SSL_RSA_WITH_NULL_SHA",
485                    "TLS_RSA_WITH_AES_128_CBC_SHA",
486                    "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
487        else if (cl.hasOption("tls-null"))
488            conn.setTlsCipherSuites("SSL_RSA_WITH_NULL_SHA");
489        else if (cl.hasOption("tls-3des"))
490            conn.setTlsCipherSuites("SSL_RSA_WITH_3DES_EDE_CBC_SHA");
491        else if (cl.hasOption("tls-aes"))
492            conn.setTlsCipherSuites(
493                    "TLS_RSA_WITH_AES_128_CBC_SHA",
494                    "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
495        else if (cl.hasOption("tls-cipher"))
496            conn.setTlsCipherSuites(cl.getOptionValues("tls-cipher"));
497        else
498            return;
499
500        if (cl.hasOption("tls12"))
501            conn.setTlsProtocols("TLSv1.2");
502        else if (cl.hasOption("tls11"))
503            conn.setTlsProtocols("TLSv1.1");
504        else if (cl.hasOption("tls1"))
505            conn.setTlsProtocols("TLSv1");
506        else if (cl.hasOption("ssl3"))
507            conn.setTlsProtocols("SSLv3");
508        else if (cl.hasOption("ssl2Hello"))
509            conn.setTlsProtocols("SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2");
510        else if (cl.hasOption("tls-protocol"))
511            conn.setTlsProtocols(cl.getOptionValues("tls-protocol"));
512
513        conn.setTlsNeedClientAuth(!cl.hasOption("tls-noauth"));
514
515        String keyStoreURL = cl.getOptionValue("key-store", "resource:key.jks");
516        String keyStoreType =  cl.getOptionValue("key-store-type", "JKS");
517        String keyStorePass = cl.getOptionValue("key-store-pass", "secret");
518        String keyPass = cl.getOptionValue("key-pass", keyStorePass);
519        String trustStoreURL = cl.getOptionValue("trust-store", "resource:cacerts.jks");
520        String trustStoreType =  cl.getOptionValue("trust-store-type", "JKS");
521        String trustStorePass = cl.getOptionValue("trust-store-pass", "secret");
522
523        Device device = conn.getDevice();
524        try {
525            device.setKeyManager(SSLManagerFactory.createKeyManager(
526                    keyStoreType, keyStoreURL, keyStorePass, keyPass));
527            device.setTrustManager(SSLManagerFactory.createTrustManager(
528                    trustStoreType, trustStoreURL, trustStorePass));
529        } catch (GeneralSecurityException e) {
530            throw new IOException(e);
531        }
532    }
533
534    public static Properties loadProperties(String url, Properties p)
535            throws IOException {
536        if (p == null)
537            p = new Properties();
538        InputStream in = StreamUtils.openFileOrURL(url);
539        try {
540            p.load(in);
541        } finally {
542            SafeClose.close(in);
543        }
544        return p;
545    }
546
547    @SuppressWarnings("static-access")
548    public static void addEncodingOptions(Options opts) {
549        opts.addOption(null, "group-len", false, rb.getString("group-len"));
550        OptionGroup sqlenGroup = new OptionGroup();
551        sqlenGroup.addOption(OptionBuilder
552                .withLongOpt("expl-seq-len")
553                .withDescription(rb.getString("expl-seq-len"))
554                .create(null));
555        sqlenGroup.addOption(OptionBuilder
556                .withLongOpt("undef-seq-len")
557                .withDescription(rb.getString("undef-seq-len"))
558                .create(null));
559        opts.addOptionGroup(sqlenGroup);
560        OptionGroup itemlenGroup = new OptionGroup();
561        itemlenGroup.addOption(OptionBuilder
562                .withLongOpt("expl-item-len")
563                .withDescription(rb.getString("expl-item-len"))
564                .create(null));
565        itemlenGroup.addOption(OptionBuilder
566                .withLongOpt("undef-item-len")
567                .withDescription(rb.getString("undef-item-len"))
568                .create(null));
569        opts.addOptionGroup(itemlenGroup);
570    }
571
572    public static DicomEncodingOptions encodingOptionsOf(CommandLine cl)
573            throws ParseException {
574        if (cl.hasOption("expl-item-len") && cl.hasOption("undef-item-len")
575                || cl.hasOption("expl-seq-len") && cl.hasOption("undef-seq-len"))
576                throw new ParseException(
577                        rb.getString("conflicting-enc-opts"));
578        return new DicomEncodingOptions(
579                cl.hasOption("group-len"),
580                !cl.hasOption("expl-seq-len"),
581                cl.hasOption("undef-seq-len"),
582                !cl.hasOption("expl-item-len"),
583                cl.hasOption("undef-item-len"));
584    }
585
586    public static int[] toTags(String[] tagOrKeywords) {
587        int[] tags = new int[tagOrKeywords.length];
588        for (int i = 0; i < tags.length; i++)
589            tags[i] = toTag(tagOrKeywords[i]);
590        return tags;
591    }
592
593    public static int toTag(String tagOrKeyword) {
594        try {
595            return Integer.parseInt(tagOrKeyword, 16);
596        } catch (IllegalArgumentException e) {
597            int tag = ElementDictionary.tagForKeyword(tagOrKeyword, null);
598            if (tag == -1)
599                throw new IllegalArgumentException(tagOrKeyword);
600            return tag;
601        }
602    }
603
604    @SuppressWarnings("static-access")
605    public static void addFilesetInfoOptions(Options opts) {
606        opts.addOption(OptionBuilder
607                .withLongOpt("fs-desc")
608                .hasArg()
609                .withArgName("txtfile")
610                .withDescription(rb.getString("fs-desc"))
611                .create());
612        opts.addOption(OptionBuilder
613                .withLongOpt("fs-desc-cs")
614                .hasArg()
615                .withArgName("code")
616                .withDescription(rb.getString("fs-desc-cs"))
617                .create());
618        opts.addOption(OptionBuilder
619                .withLongOpt("fs-id")
620                .hasArg()
621                .withArgName("id")
622                .withDescription(rb.getString("fs-id"))
623                .create());
624        opts.addOption(OptionBuilder
625                .withLongOpt("fs-uid")
626                .hasArg()
627                .withArgName("uid")
628                .withDescription(rb.getString("fs-uid"))
629                .create());
630    }
631
632    public static void configure(FilesetInfo fsInfo, CommandLine cl) {
633        fsInfo.setFilesetUID(cl.getOptionValue("fs-uid"));
634        fsInfo.setFilesetID(cl.getOptionValue("fs-id"));
635        if (cl.hasOption("fs-desc"))
636            fsInfo.setDescriptorFile(new File(cl.getOptionValue("fs-desc")));
637        fsInfo.setDescriptorFileCharset(cl.getOptionValue("fs-desc-cs"));
638    }
639
640    @SuppressWarnings("static-access")
641    public static void addTransferSyntaxOptions(Options opts) {
642        OptionGroup group = new OptionGroup();
643        group.addOption(OptionBuilder
644                .withLongOpt("explicit-vr")
645                .withDescription(rb.getString("explicit-vr"))
646                .create());
647        group.addOption(OptionBuilder
648                .withLongOpt("big-endian")
649                .withDescription(rb.getString("big-endian"))
650                .create());
651        group.addOption(OptionBuilder
652                .withLongOpt("implicit-vr")
653                .withDescription(rb.getString("implicit-vr"))
654                .create());
655        opts.addOptionGroup(group);
656    }
657
658    private static String[] IVR_LE_FIRST = {
659        UID.ImplicitVRLittleEndian,
660        UID.ExplicitVRLittleEndian,
661        UID.ExplicitVRBigEndianRetired
662    };
663
664    private static String[] EVR_LE_FIRST = {
665        UID.ExplicitVRLittleEndian,
666        UID.ExplicitVRBigEndianRetired,
667        UID.ImplicitVRLittleEndian
668    };
669
670    private static String[] EVR_BE_FIRST = {
671        UID.ExplicitVRBigEndianRetired,
672        UID.ExplicitVRLittleEndian,
673        UID.ImplicitVRLittleEndian
674    };
675
676    private static String[] IVR_LE_ONLY = {
677        UID.ImplicitVRLittleEndian
678    };
679
680    public static String[] transferSyntaxesOf(CommandLine cl) {
681        if (cl.hasOption("explicit-vr"))
682            return EVR_LE_FIRST;
683        if (cl.hasOption("big-endian"))
684            return EVR_BE_FIRST;
685        if (cl.hasOption("implicit-vr"))
686            return IVR_LE_ONLY;
687        return IVR_LE_FIRST;
688    }
689
690    public static void addAttributes(Attributes attrs, int[] tags, String... ss) {
691        Attributes item = attrs;
692        for (int i = 0; i < tags.length-1; i++) {
693            int tag = tags[i];
694            Sequence sq = item.getSequence(tag);
695            if (sq == null)
696                sq = item.newSequence(tag, 1);
697            if (sq.isEmpty())
698                sq.add(new Attributes());
699            item = sq.get(0);
700        }
701        int tag = tags[tags.length-1];
702        VR vr = ElementDictionary.vrOf(tag,
703                item.getPrivateCreator(tag));
704        if (ss.length == 0)
705            if (vr == VR.SQ)
706                item.newSequence(tag, 1).add(new Attributes(0));
707            else
708                item.setNull(tag, vr);
709        else
710            item.setString(tag, vr, ss);
711    }
712
713    public static void addAttributes(Attributes attrs, String[] optVals) {
714        if (optVals != null)
715            for (int i = 1; i < optVals.length; i++, i++)
716                addAttributes(attrs,
717                        toTags(
718                                StringUtils.split(optVals[i-1], '/')),
719                                StringUtils.split(optVals[i], '/'));
720    }
721
722    public static void addEmptyAttributes(Attributes attrs, String[] optVals) {
723        if (optVals != null)
724            for (int i = 0; i < optVals.length; i++)
725                addAttributes(attrs,
726                        toTags(StringUtils.split(optVals[i], '/')));
727    }
728
729    public static boolean updateAttributes(Attributes data, Attributes attrs,
730            String uidSuffix) {
731        if (attrs.isEmpty() && uidSuffix == null)
732            return false;
733        if (uidSuffix != null ) {
734            data.setString(Tag.StudyInstanceUID, VR.UI,
735                    data.getString(Tag.StudyInstanceUID) + uidSuffix);
736            data.setString(Tag.SeriesInstanceUID, VR.UI,
737                    data.getString(Tag.SeriesInstanceUID) + uidSuffix);
738            data.setString(Tag.SOPInstanceUID, VR.UI, 
739                    data.getString(Tag.SOPInstanceUID) + uidSuffix);
740        }
741        data.update(attrs, null);
742        return true;
743    }
744
745    public static String[] toUIDs(String s) {
746        if (s.equals("*"))
747            return new String[] { "*" };
748
749        String[] uids = StringUtils.split(s, ',');
750        for (int i = 0; i < uids.length; i++)
751            uids[i] = toUID(uids[i]);
752        return uids ;
753    }
754
755    public static String toUID(String uid) {
756        uid = uid.trim();
757        return (uid.equals("*") || Character.isDigit(uid.charAt(0)))
758                ? uid
759                : UID.forName(uid);
760    }
761}