org.dcm4che.tool.common.CLIUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.dcm4che.tool.common.CLIUtils.java

Source

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
 * Java(TM), hosted at https://github.com/gunterze/dcm4che.
 *
 * The Initial Developer of the Original Code is
 * Agfa Healthcare.
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * See @authors listed below
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

package org.dcm4che.tool.common;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.text.MessageFormat;
import java.util.Properties;
import java.util.ResourceBundle;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.dcm4che.data.Attributes;
import org.dcm4che.data.ElementDictionary;
import org.dcm4che.data.Sequence;
import org.dcm4che.data.Tag;
import org.dcm4che.data.UID;
import org.dcm4che.data.VR;
import org.dcm4che.io.DicomEncodingOptions;
import org.dcm4che.net.ApplicationEntity;
import org.dcm4che.net.Connection;
import org.dcm4che.net.Device;
import org.dcm4che.net.Priority;
import org.dcm4che.net.SSLManagerFactory;
import org.dcm4che.net.pdu.AAssociateRQ;
import org.dcm4che.net.pdu.UserIdentityRQ;
import org.dcm4che.util.SafeClose;
import org.dcm4che.util.StreamUtils;
import org.dcm4che.util.StringUtils;

/**
 * @author Gunter Zeilinger <gunterze@gmail.com>
 *
 */
public class CLIUtils {

    public static ResourceBundle rb = ResourceBundle.getBundle("org.dcm4che.tool.common.messages");

    public static void addCommonOptions(Options opts) {
        opts.addOption("h", "help", false, rb.getString("help"));
        opts.addOption("V", "version", false, rb.getString("version"));
    }

    @SuppressWarnings("static-access")
    public static void addBindOption(Options opts, String defAET) {
        opts.addOption(OptionBuilder.hasArg().withArgName("aet[@ip][:port]")
                .withDescription(MessageFormat.format(rb.getString("bind"), defAET)).withLongOpt("bind")
                .create("b"));
    }

    @SuppressWarnings("static-access")
    public static void addBindServerOption(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("[aet[@ip]:]port")
                .withDescription(rb.getString("bind-server")).withLongOpt("bind").create("b"));
        addRequestTimeoutOption(opts);
    }

    @SuppressWarnings("static-access")
    public static void addConnectOption(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("aet@host:port").withDescription(rb.getString("connect"))
                .withLongOpt("connect").create("c"));
        opts.addOption(OptionBuilder.hasArg().withArgName("[user:password@]host:port")
                .withDescription(rb.getString("proxy")).withLongOpt("proxy").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("name").withDescription(rb.getString("user"))
                .withLongOpt("user").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("password").withDescription(rb.getString("user-pass"))
                .withLongOpt("user-pass").create(null));
        opts.addOption(null, "user-rsp", false, rb.getString("user-rsp"));
        addConnectTimeoutOption(opts);
        addAcceptTimeoutOption(opts);
    }

    @SuppressWarnings("static-access")
    public static void addAEOptions(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("length").withDescription(rb.getString("max-pdulen-rcv"))
                .withLongOpt("max-pdulen-rcv").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("length").withDescription(rb.getString("max-pdulen-snd"))
                .withLongOpt("max-pdulen-snd").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("no").withDescription(rb.getString("max-ops-invoked"))
                .withLongOpt("max-ops-invoked").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("no").withDescription(rb.getString("max-ops-performed"))
                .withLongOpt("max-ops-performed").create(null));
        opts.addOption(null, "not-async", false, rb.getString("not-async"));
        opts.addOption(null, "not-pack-pdv", false, rb.getString("not-pack-pdv"));
        opts.addOption(OptionBuilder.hasArg().withArgName("ms").withDescription(rb.getString("idle-timeout"))
                .withLongOpt("idle-timeout").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("ms").withDescription(rb.getString("release-timeout"))
                .withLongOpt("release-timeout").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("ms").withDescription(rb.getString("soclose-delay"))
                .withLongOpt("soclose-delay").create(null));
        addSocketOptions(opts);
        addTLSOptions(opts);
    }

    @SuppressWarnings("static-access")
    public static void addRequestTimeoutOption(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("ms").withDescription(rb.getString("request-timeout"))
                .withLongOpt("request-timeout").create(null));
    }

    @SuppressWarnings("static-access")
    public static void addAcceptTimeoutOption(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("ms").withDescription(rb.getString("accept-timeout"))
                .withLongOpt("accept-timeout").create(null));
    }

    @SuppressWarnings("static-access")
    public static void addSocketOptions(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("length").withDescription(rb.getString("sosnd-buffer"))
                .withLongOpt("sosnd-buffer").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("length").withDescription(rb.getString("sorcv-buffer"))
                .withLongOpt("sorcv-buffer").create(null));
        opts.addOption(null, "tcp-delay", false, rb.getString("tcp-delay"));
    }

    @SuppressWarnings("static-access")
    public static void addConnectTimeoutOption(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("ms").withDescription(rb.getString("connect-timeout"))
                .withLongOpt("connect-timeout").create(null));
    }

    @SuppressWarnings("static-access")
    public static void addResponseTimeoutOption(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("ms").withDescription(rb.getString("response-timeout"))
                .withLongOpt("response-timeout").create(null));
    }

    @SuppressWarnings("static-access")
    public static void addRetrieveTimeoutOption(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("ms").withDescription(rb.getString("retrieve-timeout"))
                .withLongOpt("retrieve-timeout").create(null));
    }

    @SuppressWarnings("static-access")
    public static void addTLSOptions(Options opts) {
        opts.addOption(OptionBuilder.hasArg().withArgName("cipher").withDescription(rb.getString("tls-cipher"))
                .withLongOpt("tls-cipher").create(null));
        opts.addOption(null, "tls", false, rb.getString("tls"));
        opts.addOption(null, "tls-null", false, rb.getString("tls-null"));
        opts.addOption(null, "tls-3des", false, rb.getString("tls-3des"));
        opts.addOption(null, "tls-aes", false, rb.getString("tls-aes"));
        opts.addOption(OptionBuilder.hasArg().withArgName("protocol").withDescription(rb.getString("tls-protocol"))
                .withLongOpt("tls-protocol").create(null));
        opts.addOption(null, "tls1", false, rb.getString("tls1"));
        opts.addOption(null, "ssl3", false, rb.getString("ssl3"));
        opts.addOption(null, "ssl2Hello", false, rb.getString("ssl2Hello"));
        opts.addOption(null, "tls-noauth", false, rb.getString("tls-noauth"));
        opts.addOption(OptionBuilder.hasArg().withArgName("file|url").withDescription(rb.getString("key-store"))
                .withLongOpt("key-store").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("storetype")
                .withDescription(rb.getString("key-store-type")).withLongOpt("key-store-type").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("password")
                .withDescription(rb.getString("key-store-pass")).withLongOpt("key-store-pass").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("password").withDescription(rb.getString("key-pass"))
                .withLongOpt("key-pass").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("file|url").withDescription(rb.getString("trust-store"))
                .withLongOpt("trust-store").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("storetype")
                .withDescription(rb.getString("trust-store-type")).withLongOpt("trust-store-type").create(null));
        opts.addOption(OptionBuilder.hasArg().withArgName("password")
                .withDescription(rb.getString("trust-store-pass")).withLongOpt("trust-store-pass").create(null));
    }

    @SuppressWarnings("static-access")
    public static void addPriorityOption(Options opts) {
        OptionGroup group = new OptionGroup();
        group.addOption(
                OptionBuilder.withLongOpt("prior-high").withDescription(rb.getString("prior-high")).create());
        group.addOption(OptionBuilder.withLongOpt("prior-low").withDescription(rb.getString("prior-low")).create());
        opts.addOptionGroup(group);
    }

    public static CommandLine parseComandLine(String[] args, Options opts, ResourceBundle rb2, Class<?> clazz)
            throws ParseException {
        CommandLineParser parser = new PosixParser();
        CommandLine cl = parser.parse(opts, args);
        if (cl.hasOption("h")) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp(rb2.getString("usage"), rb2.getString("description"), opts,
                    rb2.getString("example"));
            System.exit(0);
        }
        if (cl.hasOption("V")) {
            Package p = clazz.getPackage();
            String s = p.getName();
            System.out.println(s.substring(s.lastIndexOf('.') + 1) + ": " + p.getImplementationVersion());
            System.exit(0);
        }
        return cl;
    }

    public static void configureConnect(Connection conn, AAssociateRQ rq, CommandLine cl) throws ParseException {
        if (!cl.hasOption("c"))
            throw new MissingOptionException(rb.getString("missing-connect-opt"));
        String aeAtHostPort = cl.getOptionValue("c");
        String[] aeHostPort = split(aeAtHostPort, '@', 0);
        if (aeHostPort[1] == null)
            throw new ParseException(rb.getString("invalid-connect-opt"));

        String[] hostPort = split(aeHostPort[1], ':', 0);
        if (hostPort[1] == null)
            throw new ParseException(rb.getString("invalid-connect-opt"));

        rq.setCalledAET(aeHostPort[0]);
        conn.setHostname(hostPort[0]);
        conn.setPort(Integer.parseInt(hostPort[1]));

        conn.setHttpProxy(cl.getOptionValue("proxy"));

        if (cl.hasOption("user"))
            rq.setUserIdentityRQ(cl.hasOption("user-pass")
                    ? new UserIdentityRQ(cl.getOptionValue("user"), cl.getOptionValue("user-pass").toCharArray())
                    : new UserIdentityRQ(cl.getOptionValue("user"), cl.hasOption("user-rsp")));
    }

    public static void configureBind(Connection conn, ApplicationEntity ae, CommandLine cl) throws ParseException {
        if (cl.hasOption("b")) {
            String aeAtHostPort = cl.getOptionValue("b");
            String[] aeAtHostAndPort = split(aeAtHostPort, ':', 0);
            String[] aeHost = split(aeAtHostAndPort[0], '@', 0);
            ae.setAETitle(aeHost[0]);
            if (aeHost[1] != null)
                conn.setHostname(aeHost[1]);
            if (aeAtHostAndPort[1] != null)
                conn.setPort(Integer.parseInt(aeAtHostAndPort[1]));
        }
    }

    public static void configureBindServer(Connection conn, ApplicationEntity ae, CommandLine cl)
            throws ParseException {
        if (!cl.hasOption("b"))
            throw new MissingOptionException(rb.getString("missing-bind-opt"));
        String aeAtHostPort = cl.getOptionValue("b");
        String[] aeAtHostAndPort = split(aeAtHostPort, ':', 1);
        conn.setPort(Integer.parseInt(aeAtHostAndPort[1]));
        if (aeAtHostAndPort[0] != null) {
            String[] aeHost = split(aeAtHostAndPort[0], '@', 0);
            ae.setAETitle(aeHost[0]);
            if (aeHost[1] != null)
                conn.setHostname(aeHost[1]);
        }
    }

    private static String[] split(String s, char delim, int defPos) {
        String[] s2 = new String[2];
        int pos = s.indexOf(delim);
        if (pos != -1) {
            s2[0] = s.substring(0, pos);
            s2[1] = s.substring(pos + 1);
        } else {
            s2[defPos] = s;
        }
        return s2;
    }

    public static int priorityOf(CommandLine cl) {
        return cl.hasOption("prior-high") ? Priority.HIGH
                : cl.hasOption("prior-low") ? Priority.LOW : Priority.NORMAL;
    }

    public static int getIntOption(CommandLine cl, String opt, int defVal) {
        String optVal = cl.getOptionValue(opt);
        if (optVal == null)
            return defVal;

        return optVal.endsWith("H") ? Integer.parseInt(optVal.substring(0, optVal.length() - 1), 16)
                : Integer.parseInt(optVal);
    }

    public static void configure(Connection conn, CommandLine cl) throws ParseException, IOException {
        if (cl.hasOption("max-pdulen-rcv"))
            conn.setReceivePDULength(Integer.parseInt(cl.getOptionValue("max-pdulen-rcv")));
        if (cl.hasOption("max-pdulen-snd"))
            conn.setSendPDULength(Integer.parseInt(cl.getOptionValue("max-pdulen-snd")));
        if (cl.hasOption("not-async")) {
            conn.setMaxOpsInvoked(1);
            conn.setMaxOpsPerformed(1);
        } else {
            int maxOpsInvoked = 0;
            if (cl.hasOption("max-ops-invoked"))
                maxOpsInvoked = Integer.parseInt(cl.getOptionValue("max-ops-invoked"));
            conn.setMaxOpsInvoked(maxOpsInvoked);
            int maxOpsPerformed = 0;
            if (cl.hasOption("max-ops-performed"))
                maxOpsPerformed = Integer.parseInt(cl.getOptionValue("max-ops-performed"));
            conn.setMaxOpsPerformed(maxOpsPerformed);
        }
        conn.setPackPDV(!cl.hasOption("not-pack-pdv"));
        if (cl.hasOption("connect-timeout"))
            conn.setConnectTimeout(Integer.parseInt(cl.getOptionValue("connect-timeout")));
        if (cl.hasOption("request-timeout"))
            conn.setRequestTimeout(Integer.parseInt(cl.getOptionValue("request-timeout")));
        if (cl.hasOption("accept-timeout"))
            conn.setAcceptTimeout(Integer.parseInt(cl.getOptionValue("accept-timeout")));
        if (cl.hasOption("release-timeout"))
            conn.setReleaseTimeout(Integer.parseInt(cl.getOptionValue("release-timeout")));
        if (cl.hasOption("response-timeout"))
            conn.setResponseTimeout(Integer.parseInt(cl.getOptionValue("response-timeout")));
        if (cl.hasOption("retrieve-timeout"))
            conn.setRetrieveTimeout(Integer.parseInt(cl.getOptionValue("retrieve-timeout")));
        if (cl.hasOption("idle-timeout"))
            conn.setIdleTimeout(Integer.parseInt(cl.getOptionValue("idle-timeout")));
        if (cl.hasOption("soclose-delay"))
            conn.setSocketCloseDelay(Integer.parseInt(cl.getOptionValue("soclose-delay")));
        if (cl.hasOption("sosnd-buffer"))
            conn.setSendBufferSize(Integer.parseInt(cl.getOptionValue("sosnd-buffer")));
        if (cl.hasOption("sorcv-buffer"))
            conn.setReceiveBufferSize(Integer.parseInt(cl.getOptionValue("sorcv-buffer")));
        conn.setTcpNoDelay(!cl.hasOption("tcp-delay"));
        configureTLS(conn, cl);
    }

    private static void configureTLS(Connection conn, CommandLine cl) throws ParseException, IOException {
        if (cl.hasOption("tls"))
            conn.setTlsCipherSuites("SSL_RSA_WITH_NULL_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA",
                    "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
        else if (cl.hasOption("tls-null"))
            conn.setTlsCipherSuites("SSL_RSA_WITH_NULL_SHA");
        else if (cl.hasOption("tls-3des"))
            conn.setTlsCipherSuites("SSL_RSA_WITH_3DES_EDE_CBC_SHA");
        else if (cl.hasOption("tls-aes"))
            conn.setTlsCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
        else if (cl.hasOption("tls-cipher"))
            conn.setTlsCipherSuites(cl.getOptionValues("tls-cipher"));
        else
            return;

        if (cl.hasOption("tls1"))
            conn.setTlsProtocols("TLSv1");
        else if (cl.hasOption("ssl3"))
            conn.setTlsProtocols("SSLv3");
        else if (cl.hasOption("ssl2Hello"))
            conn.setTlsProtocols("SSLv2Hello", "SSLv3", "TLSv1");
        else if (cl.hasOption("tls-protocol"))
            conn.setTlsProtocols(cl.getOptionValues("tls-protocol"));

        conn.setTlsNeedClientAuth(!cl.hasOption("tls-noauth"));

        String keyStoreURL = cl.getOptionValue("key-store", "resource:key.jks");
        String keyStoreType = cl.getOptionValue("key-store-type", "JKS");
        String keyStorePass = cl.getOptionValue("key-store-pass", "secret");
        String keyPass = cl.getOptionValue("key-pass", keyStorePass);
        String trustStoreURL = cl.getOptionValue("trust-store", "resource:cacerts.jks");
        String trustStoreType = cl.getOptionValue("trust-store-type", "JKS");
        String trustStorePass = cl.getOptionValue("trust-store-pass", "secret");

        Device device = conn.getDevice();
        try {
            device.setKeyManager(
                    SSLManagerFactory.createKeyManager(keyStoreType, keyStoreURL, keyStorePass, keyPass));
            device.setTrustManager(
                    SSLManagerFactory.createTrustManager(trustStoreType, trustStoreURL, trustStorePass));
        } catch (GeneralSecurityException e) {
            throw new IOException(e);
        }
    }

    public static Properties loadProperties(String url, Properties p) throws IOException {
        if (p == null)
            p = new Properties();
        InputStream in = StreamUtils.openFileOrURL(url);
        try {
            p.load(in);
        } finally {
            SafeClose.close(in);
        }
        return p;
    }

    @SuppressWarnings("static-access")
    public static void addEncodingOptions(Options opts) {
        opts.addOption(null, "group-len", false, rb.getString("group-len"));
        OptionGroup sqlenGroup = new OptionGroup();
        sqlenGroup.addOption(OptionBuilder.withLongOpt("expl-seq-len").withDescription(rb.getString("expl-seq-len"))
                .create(null));
        sqlenGroup.addOption(OptionBuilder.withLongOpt("undef-seq-len")
                .withDescription(rb.getString("undef-seq-len")).create(null));
        opts.addOptionGroup(sqlenGroup);
        OptionGroup itemlenGroup = new OptionGroup();
        itemlenGroup.addOption(OptionBuilder.withLongOpt("expl-item-len")
                .withDescription(rb.getString("expl-item-len")).create(null));
        itemlenGroup.addOption(OptionBuilder.withLongOpt("undef-item-len")
                .withDescription(rb.getString("undef-item-len")).create(null));
        opts.addOptionGroup(itemlenGroup);
    }

    public static DicomEncodingOptions encodingOptionsOf(CommandLine cl) throws ParseException {
        if (cl.hasOption("expl-item-len") && cl.hasOption("undef-item-len")
                || cl.hasOption("expl-seq-len") && cl.hasOption("undef-seq-len"))
            throw new ParseException(rb.getString("conflicting-enc-opts"));
        return new DicomEncodingOptions(cl.hasOption("group-len"), !cl.hasOption("expl-seq-len"),
                cl.hasOption("undef-seq-len"), !cl.hasOption("expl-item-len"), cl.hasOption("undef-item-len"));
    }

    public static int[] toTags(String[] tagOrKeywords) {
        int[] tags = new int[tagOrKeywords.length];
        for (int i = 0; i < tags.length; i++)
            tags[i] = toTag(tagOrKeywords[i]);
        return tags;
    }

    public static int toTag(String tagOrKeyword) {
        try {
            return Integer.parseInt(tagOrKeyword, 16);
        } catch (IllegalArgumentException e) {
            int tag = ElementDictionary.tagForKeyword(tagOrKeyword, null);
            if (tag == -1)
                throw new IllegalArgumentException(tagOrKeyword);
            return tag;
        }
    }

    @SuppressWarnings("static-access")
    public static void addFilesetInfoOptions(Options opts) {
        opts.addOption(OptionBuilder.withLongOpt("fs-desc").hasArg().withArgName("txtfile")
                .withDescription(rb.getString("fs-desc")).create());
        opts.addOption(OptionBuilder.withLongOpt("fs-desc-cs").hasArg().withArgName("code")
                .withDescription(rb.getString("fs-desc-cs")).create());
        opts.addOption(OptionBuilder.withLongOpt("fs-id").hasArg().withArgName("id")
                .withDescription(rb.getString("fs-id")).create());
        opts.addOption(OptionBuilder.withLongOpt("fs-uid").hasArg().withArgName("uid")
                .withDescription(rb.getString("fs-uid")).create());
    }

    public static void configure(FilesetInfo fsInfo, CommandLine cl) {
        fsInfo.setFilesetUID(cl.getOptionValue("fs-uid"));
        fsInfo.setFilesetID(cl.getOptionValue("fs-id"));
        if (cl.hasOption("fs-desc"))
            fsInfo.setDescriptorFile(new File(cl.getOptionValue("fs-desc")));
        fsInfo.setDescriptorFileCharset(cl.getOptionValue("fs-desc-cs"));
    }

    @SuppressWarnings("static-access")
    public static void addTransferSyntaxOptions(Options opts) {
        OptionGroup group = new OptionGroup();
        group.addOption(
                OptionBuilder.withLongOpt("explicit-vr").withDescription(rb.getString("explicit-vr")).create());
        group.addOption(
                OptionBuilder.withLongOpt("big-endian").withDescription(rb.getString("big-endian")).create());
        group.addOption(
                OptionBuilder.withLongOpt("implicit-vr").withDescription(rb.getString("implicit-vr")).create());
        opts.addOptionGroup(group);
    }

    private static String[] IVR_LE_FIRST = { UID.ImplicitVRLittleEndian, UID.ExplicitVRLittleEndian,
            UID.ExplicitVRBigEndian };

    private static String[] EVR_LE_FIRST = { UID.ExplicitVRLittleEndian, UID.ExplicitVRBigEndian,
            UID.ImplicitVRLittleEndian };

    private static String[] EVR_BE_FIRST = { UID.ExplicitVRBigEndian, UID.ExplicitVRLittleEndian,
            UID.ImplicitVRLittleEndian };

    private static String[] IVR_LE_ONLY = { UID.ImplicitVRLittleEndian };

    public static String[] transferSyntaxesOf(CommandLine cl) {
        if (cl.hasOption("explicit-vr"))
            return EVR_LE_FIRST;
        if (cl.hasOption("big-endian"))
            return EVR_BE_FIRST;
        if (cl.hasOption("implicit-vr"))
            return IVR_LE_ONLY;
        return IVR_LE_FIRST;
    }

    public static void addAttributes(Attributes attrs, int[] tags, String... ss) {
        Attributes item = attrs;
        for (int i = 0; i < tags.length - 1; i++) {
            int tag = tags[i];
            Sequence sq = item.getSequence(tag);
            if (sq == null)
                sq = item.newSequence(tag, 1);
            if (sq.isEmpty())
                sq.add(new Attributes());
            item = sq.get(0);
        }
        int tag = tags[tags.length - 1];
        VR vr = ElementDictionary.vrOf(tag, item.getPrivateCreator(tag));
        if (ss.length == 0)
            if (vr == VR.SQ)
                item.newSequence(tag, 1).add(new Attributes(0));
            else
                item.setNull(tag, vr);
        else
            item.setString(tag, vr, ss);
    }

    public static void addAttributes(Attributes attrs, String[] optVals) {
        if (optVals != null)
            for (int i = 1; i < optVals.length; i++, i++)
                addAttributes(attrs, toTags(StringUtils.split(optVals[i - 1], '/')),
                        StringUtils.split(optVals[i], '/'));
    }

    public static void addEmptyAttributes(Attributes attrs, String[] optVals) {
        if (optVals != null)
            for (int i = 0; i < optVals.length; i++)
                addAttributes(attrs, toTags(StringUtils.split(optVals[i], '/')));
    }

    public static boolean updateAttributes(Attributes data, Attributes attrs, String uidSuffix) {
        if (attrs.isEmpty() && uidSuffix == null)
            return false;
        if (uidSuffix != null) {
            data.setString(Tag.StudyInstanceUID, VR.UI, data.getString(Tag.StudyInstanceUID) + uidSuffix);
            data.setString(Tag.SeriesInstanceUID, VR.UI, data.getString(Tag.SeriesInstanceUID) + uidSuffix);
            data.setString(Tag.SOPInstanceUID, VR.UI, data.getString(Tag.SOPInstanceUID) + uidSuffix);
        }
        data.update(attrs, null);
        return true;
    }

}