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.json2dcm;
040
041import java.io.BufferedOutputStream;
042import java.io.File;
043import java.io.FileDescriptor;
044import java.io.FileInputStream;
045import java.io.FileOutputStream;
046import java.io.IOException;
047import java.io.InputStream;
048import java.io.InputStreamReader;
049import java.io.OutputStream;
050import java.util.List;
051import java.util.ResourceBundle;
052
053import javax.json.Json;
054
055import org.apache.commons.cli.CommandLine;
056import org.apache.commons.cli.OptionBuilder;
057import org.apache.commons.cli.OptionGroup;
058import org.apache.commons.cli.Options;
059import org.apache.commons.cli.ParseException;
060import org.dcm4che3.data.Attributes;
061import org.dcm4che3.data.Tag;
062import org.dcm4che3.data.UID;
063import org.dcm4che3.io.BulkDataDescriptor;
064import org.dcm4che3.io.DicomEncodingOptions;
065import org.dcm4che3.io.DicomInputStream;
066import org.dcm4che3.io.DicomInputStream.IncludeBulkData;
067import org.dcm4che3.io.DicomOutputStream;
068import org.dcm4che3.json.JSONReader;
069import org.dcm4che3.tool.common.CLIUtils;
070import org.dcm4che3.util.SafeClose;
071
072/**
073 * Tool to convert JSON to XML.
074 * 
075 * @author Gunter Zeilinger <gunterze@gmail.com>
076 */
077public class Json2Dcm {
078
079    private static ResourceBundle rb =
080        ResourceBundle.getBundle("org.dcm4che3.tool.json2dcm.messages");
081
082    private IncludeBulkData includeBulkData = IncludeBulkData.URI;
083    private boolean catBlkFiles = false;
084    private String blkFilePrefix = "blk";
085    private String blkFileSuffix;
086    private File blkDirectory;
087    private Attributes blkAttrs;
088    private String tsuid;
089    private boolean withfmi;
090    private boolean nofmi;
091    private DicomEncodingOptions encOpts = DicomEncodingOptions.DEFAULT;
092    private List<File> bulkDataFiles;
093    private Attributes fmi;
094    private Attributes dataset;
095
096    public final void setIncludeBulkData(IncludeBulkData includeBulkData) {
097        this.includeBulkData = includeBulkData;
098    }
099
100    public final void setConcatenateBulkDataFiles(boolean catBlkFiles) {
101        this.catBlkFiles = catBlkFiles;
102    }
103
104    public final void setBulkDataFilePrefix(String blkFilePrefix) {
105        this.blkFilePrefix = blkFilePrefix;
106    }
107
108    public final void setBulkDataFileSuffix(String blkFileSuffix) {
109        this.blkFileSuffix = blkFileSuffix;
110    }
111
112    public final void setBulkDataDirectory(File blkDirectory) {
113        this.blkDirectory = blkDirectory;
114    }
115
116    public final void setBulkDataAttributes(Attributes blkAttrs) {
117        this.blkAttrs = blkAttrs;
118    }
119
120    public final void setTransferSyntax(String uid) {
121        this.tsuid = uid;
122    }
123
124    public final void setWithFileMetaInformation(boolean withfmi) {
125        this.withfmi = withfmi;
126    }
127
128    public final void setNoFileMetaInformation(boolean nofmi) {
129        this.nofmi = nofmi;
130    }
131
132    public final void setEncodingOptions(DicomEncodingOptions encOpts) {
133        this.encOpts = encOpts;
134    }
135
136     private static CommandLine parseComandLine(String[] args)
137            throws ParseException{
138        Options opts = new Options();
139        CLIUtils.addCommonOptions(opts);
140        addIOFileNameOptions(opts);
141        addBulkdataOptions(opts);
142        addFileEncodingOptions(opts);
143        CommandLine cl = CLIUtils.parseComandLine(args, opts, rb, Json2Dcm.class);
144        if (!(cl.hasOption("j") || cl.hasOption("i")))
145            throw new ParseException(rb.getString("missing-i-j"));
146        return cl;
147    }
148
149     @SuppressWarnings("static-access")
150     private static void addIOFileNameOptions(Options opts) {
151         opts.addOption(OptionBuilder
152                 .hasArg()
153                 .withArgName("json-file")
154                 .withDescription(rb.getString("j-file"))
155                 .create("j"));
156         opts.addOption(OptionBuilder
157                 .hasArg()
158                 .withArgName("dicom-file")
159                 .withDescription(rb.getString("i-file"))
160                 .create("i"));
161         opts.addOption(OptionBuilder
162                 .hasArg()
163                 .withArgName("dicom-file")
164                 .withDescription(rb.getString("o-file"))
165                 .create("o"));
166      }
167
168
169     @SuppressWarnings("static-access")
170     private static void addBulkdataOptions(Options opts) {
171         OptionGroup blkGroup = new OptionGroup();
172         blkGroup.addOption(OptionBuilder
173                 .withLongOpt("no-bulkdata")
174                 .withDescription(rb.getString("no-bulkdata"))
175                 .create("B"));
176         blkGroup.addOption(OptionBuilder
177                 .withLongOpt("alloc-bulkdata")
178                 .withDescription(rb.getString("alloc-bulkdata"))
179                 .create("b"));
180         opts.addOptionGroup(blkGroup);
181         opts.addOption(OptionBuilder
182                 .withLongOpt("blk-file-dir")
183                 .hasArg()
184                 .withArgName("directory")
185                 .withDescription(rb.getString("blk-file-dir"))
186                 .create("d"));
187         opts.addOption(OptionBuilder
188                 .withLongOpt("blk-file-prefix")
189                 .hasArg()
190                 .withArgName("prefix")
191                 .withDescription(rb.getString("blk-file-prefix"))
192                 .create());
193         opts.addOption(OptionBuilder
194                 .withLongOpt("blk-file-suffix")
195                 .hasArg()
196                 .withArgName("suffix")
197                 .withDescription(rb.getString("blk-file-suffix"))
198                 .create());
199         opts.addOption("c", "cat-blk-files", false,
200                  rb.getString("cat-blk-files"));
201         opts.addOption(null, "keep-blk-files", false,
202                 rb.getString("keep-blk-files"));
203         opts.addOption(OptionBuilder
204                 .withLongOpt("blk-spec")
205                 .hasArg()
206                 .withArgName("json-file")
207                 .withDescription(rb.getString("blk-spec"))
208                 .create("J"));
209     }
210
211     @SuppressWarnings("static-access")
212     private static void addFileEncodingOptions(Options opts) {
213        opts.addOption(OptionBuilder
214                .withLongOpt("transfer-syntax")
215                .hasArg()
216                .withArgName("uid")
217                .withDescription(rb.getString("transfer-syntax"))
218                .create("t"));
219        OptionGroup fmiGroup = new OptionGroup();
220        fmiGroup.addOption(OptionBuilder
221                .withLongOpt("no-fmi")
222                .withDescription(rb.getString("no-fmi"))
223                .create("F"));
224        fmiGroup.addOption(OptionBuilder
225                .withLongOpt("fmi")
226                .withDescription(rb.getString("fmi"))
227                .create("f"));
228        opts.addOptionGroup(fmiGroup);
229        CLIUtils.addEncodingOptions(opts);
230    }
231
232    public static void main(String[] args) {
233        try {
234            CommandLine cl = parseComandLine(args);
235            Json2Dcm main = new Json2Dcm();
236            configureBulkdata(main, cl);
237            if (cl.hasOption("t")) {
238                main.setTransferSyntax(cl.getOptionValue("t"));
239            }
240            main.setWithFileMetaInformation(cl.hasOption("f"));
241            main.setNoFileMetaInformation(cl.hasOption("F"));
242            main.setEncodingOptions(CLIUtils.encodingOptionsOf(cl));
243            try {
244                if (cl.hasOption("i")) {
245                    String fname = cl.getOptionValue("i");
246                    if (fname.equals("-")) {
247                        main.parse(new DicomInputStream(System.in));
248                    } else {
249                        DicomInputStream dis = 
250                                new DicomInputStream(new File(fname));
251                        try {
252                            main.parse(dis);
253                        } finally {
254                            dis.close();
255                        }
256                    }
257                }
258
259                if (cl.hasOption("j"))
260                    main.mergeJSON(cl.getOptionValue("j"));
261
262                OutputStream out = cl.hasOption("o") 
263                        ? new FileOutputStream(cl.getOptionValue("o"))
264                        : new FileOutputStream(FileDescriptor.out);
265                try {
266                    main.writeTo(out);
267                } finally {
268                    out.close();
269                }
270            } finally {
271                if (!cl.hasOption("keep-blk-files"))
272                    main.delBulkDataFiles();
273            }
274        } catch (ParseException e) {
275            System.err.println("json2dcm: " + e.getMessage());
276            System.err.println(rb.getString("try"));
277            System.exit(2);
278        } catch (Exception e) {
279            System.err.println("json2dcm: " + e.getMessage());
280            e.printStackTrace();
281            System.exit(2);
282        }
283    }
284
285    private static void configureBulkdata(Json2Dcm json2dcm, CommandLine cl)
286            throws Exception {
287        if (cl.hasOption("b")) {
288            json2dcm.setIncludeBulkData(IncludeBulkData.YES);
289        }
290        if (cl.hasOption("B")) {
291            json2dcm.setIncludeBulkData(IncludeBulkData.NO);
292        }
293        if (cl.hasOption("blk-file-prefix")) {
294            json2dcm.setBulkDataFilePrefix(
295                    cl.getOptionValue("blk-file-prefix"));
296        }
297        if (cl.hasOption("blk-file-suffix")) {
298            json2dcm.setBulkDataFileSuffix(
299                    cl.getOptionValue("blk-file-suffix"));
300        }
301        if (cl.hasOption("d")) {
302            File tempDir = new File(cl.getOptionValue("d"));
303            json2dcm.setBulkDataDirectory(tempDir);
304        }
305        json2dcm.setConcatenateBulkDataFiles(cl.hasOption("c"));
306        if (cl.hasOption("J")) {
307            json2dcm.setBulkDataAttributes(
308                    parseJSON(cl.getOptionValue("J")));
309        }
310    }
311
312    public void writeTo(OutputStream out) throws IOException {
313        if (nofmi)
314            fmi = null;
315        else if (fmi == null
316                ? withfmi
317                : tsuid != null && !tsuid.equals(
318                        fmi.getString(Tag.TransferSyntaxUID, null))) {
319            fmi = dataset.createFileMetaInformation(tsuid);
320        }
321        @SuppressWarnings("resource")
322        DicomOutputStream dos = new DicomOutputStream(
323                new BufferedOutputStream(out),
324                fmi != null
325                        ? UID.ExplicitVRLittleEndian
326                        : tsuid != null 
327                                ? tsuid
328                                : UID.ImplicitVRLittleEndian);
329        dos.setEncodingOptions(encOpts);
330        dos.writeDataset(fmi, dataset);
331        dos.finish();
332        dos.flush();
333    }
334
335    public void delBulkDataFiles() {
336        if (bulkDataFiles != null)
337            for (File f : bulkDataFiles)
338                f.delete();
339    }
340
341    public void parse(DicomInputStream dis) throws IOException {
342        dis.setIncludeBulkData(includeBulkData);
343        if (blkAttrs != null)
344            dis.setBulkDataDescriptor(BulkDataDescriptor.valueOf(blkAttrs));
345        dis.setBulkDataDirectory(blkDirectory);
346        dis.setBulkDataFilePrefix(blkFilePrefix);
347        dis.setBulkDataFileSuffix(blkFileSuffix);
348        dis.setConcatenateBulkDataFiles(catBlkFiles);
349        dataset = dis.readDataset(-1, -1);
350        fmi = dis.getFileMetaInformation();
351        bulkDataFiles = dis.getBulkDataFiles();
352    }
353
354    public void mergeJSON(String fname) throws Exception {
355        if (dataset == null)
356            dataset = new Attributes();
357        JSONReader reader = parseJSON(fname, dataset);
358        Attributes fmi2 = reader.getFileMetaInformation();
359        if (fmi2 != null)
360            fmi = fmi2;
361    }
362
363    public static Attributes parseJSON(String fname) throws Exception {
364        Attributes attrs = new Attributes();
365        parseJSON(fname, attrs);
366        return attrs;
367    }
368
369    private static JSONReader parseJSON(String fname, Attributes attrs)
370            throws IOException {
371        @SuppressWarnings("resource")
372        InputStream in = fname.equals("-") ? System.in : new FileInputStream(fname);
373        try {
374            JSONReader reader = new JSONReader(
375                    Json.createParser(new InputStreamReader(in, "UTF-8")));
376            reader.readDataset(attrs);
377            return reader;
378        } finally {
379            if (in != System.in)
380                SafeClose.close(in);
381        }
382    }
383
384}