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