Java tutorial
/* ***** 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.dcmdir; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.List; import java.util.ResourceBundle; import org.apache.commons.cli.CommandLine; 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.dcm4che.data.Attributes; import org.dcm4che.data.Tag; import org.dcm4che.data.UID; import org.dcm4che.data.VR; import org.dcm4che.io.DicomEncodingOptions; import org.dcm4che.io.DicomInputStream; import org.dcm4che.io.DicomInputStream.IncludeBulkData; import org.dcm4che.media.DicomDirReader; import org.dcm4che.media.DicomDirWriter; import org.dcm4che.media.RecordFactory; import org.dcm4che.media.RecordType; import org.dcm4che.tool.common.CLIUtils; import org.dcm4che.tool.common.FilesetInfo; import org.dcm4che.util.SafeClose; import org.dcm4che.util.UIDUtils; /** * @author Gunter Zeilinger <gunterze@gmail.com> */ public class DcmDir { private static ResourceBundle rb = ResourceBundle.getBundle("org.dcm4che.tool.dcmdir.messages"); /** default number of characters per line */ private static final int DEFAULT_WIDTH = 78; private boolean inUse; private int width = DEFAULT_WIDTH; private DicomEncodingOptions encOpts = DicomEncodingOptions.DEFAULT; private final FilesetInfo fsInfo = new FilesetInfo(); private boolean origSeqLength; private boolean checkDuplicate; private File file; private DicomDirReader in; private DicomDirWriter out; private RecordFactory recFact; @SuppressWarnings("static-access") private static CommandLine parseComandLine(String[] args) throws ParseException { Options opts = new Options(); CLIUtils.addCommonOptions(opts); CLIUtils.addFilesetInfoOptions(opts); OptionGroup cmdGroup = new OptionGroup(); addCommandOptions(cmdGroup); opts.addOptionGroup(cmdGroup); opts.addOption(OptionBuilder.withLongOpt("width").hasArg().withArgName("col") .withDescription(rb.getString("width")).create("w")); opts.addOption(null, "in-use", false, rb.getString("in-use")); opts.addOption(null, "orig-seq-len", false, rb.getString("orig-seq-len")); CLIUtils.addEncodingOptions(opts); CommandLine cl = CLIUtils.parseComandLine(args, opts, rb, DcmDir.class); if (cmdGroup.getSelected() == null) throw new ParseException(rb.getString("missing")); return cl; } @SuppressWarnings("static-access") private static void addCommandOptions(OptionGroup cmdGroup) { cmdGroup.addOption( OptionBuilder.hasArg().withArgName("dicomdir").withDescription(rb.getString("list")).create("l")); cmdGroup.addOption( OptionBuilder.hasArg().withArgName("dicomdir").withDescription(rb.getString("create")).create("c")); cmdGroup.addOption( OptionBuilder.hasArg().withArgName("dicomdir").withDescription(rb.getString("update")).create("u")); cmdGroup.addOption( OptionBuilder.hasArg().withArgName("dicomdir").withDescription(rb.getString("delete")).create("d")); cmdGroup.addOption( OptionBuilder.hasArg().withArgName("dicomdir").withDescription(rb.getString("purge")).create("p")); cmdGroup.addOption(OptionBuilder.hasArg().withArgName("dicomdir").withDescription(rb.getString("compact")) .create("z")); } @SuppressWarnings("unchecked") public static void main(String[] args) { try { CommandLine cl = parseComandLine(args); DcmDir main = new DcmDir(); main.setInUse(cl.hasOption("in-use")); main.setEncodingOptions(CLIUtils.encodingOptionsOf(cl)); CLIUtils.configure(main.fsInfo, cl); main.setOriginalSequenceLength(cl.hasOption("orig-seq-len")); if (cl.hasOption("w")) { String s = cl.getOptionValue("w"); try { main.setWidth(Integer.parseInt(s)); } catch (IllegalArgumentException e) { throw new ParseException(MessageFormat.format(rb.getString("illegal-width"), s)); } } try { List<String> argList = cl.getArgList(); long start = System.currentTimeMillis(); if (cl.hasOption("l")) { main.openForReadOnly(new File(cl.getOptionValue("l"))); main.list(); } else if (cl.hasOption("d")) { main.open(new File(cl.getOptionValue("d"))); int num = 0; for (String arg : argList) num += main.removeReferenceTo(new File(arg)); main.close(); long end = System.currentTimeMillis(); System.out.println(); System.out.println( MessageFormat.format(rb.getString("deleted"), num, main.getFile(), (end - start))); } else if (cl.hasOption("p")) { main.open(new File(cl.getOptionValue("p"))); int num = main.purge(); main.close(); long end = System.currentTimeMillis(); System.out.println( MessageFormat.format(rb.getString("purged"), num, main.getFile(), (end - start))); } else if (cl.hasOption("z")) { String fpath = cl.getOptionValue("z"); File f = new File(fpath); File bak = new File(fpath + "~"); main.compact(f, bak); long end = System.currentTimeMillis(); System.out.println(MessageFormat.format(rb.getString("compacted"), f, bak.length(), f.length(), (end - start))); } else { if (cl.hasOption("c")) { main.create(new File(cl.getOptionValue("c"))); } else if (cl.hasOption("u")) { main.open(new File(cl.getOptionValue("u"))); } main.setRecordFactory(new RecordFactory()); int num = 0; for (String arg : argList) num += main.addReferenceTo(new File(arg)); main.close(); long end = System.currentTimeMillis(); System.out.println(); System.out.println( MessageFormat.format(rb.getString("added"), num, main.getFile(), (end - start))); } } finally { main.close(); } } catch (ParseException e) { System.err.println("dcmdir: " + e.getMessage()); System.err.println(rb.getString("try")); System.exit(2); } catch (IOException e) { System.err.println("dcmdir: " + e.getMessage()); e.printStackTrace(); System.exit(2); } } public void compact(File f, File bak) throws IOException { File tmp = File.createTempFile("DICOMDIR", null, f.getParentFile()); DicomDirReader r = new DicomDirReader(f); try { fsInfo.setFilesetUID(r.getFileSetUID()); fsInfo.setFilesetID(r.getFileSetID()); fsInfo.setDescriptorFile(r.getDescriptorFile()); fsInfo.setDescriptorFileCharset(r.getDescriptorFileCharacterSet()); create(tmp); copyFrom(r); } finally { close(); try { r.close(); } catch (IOException ignore) { } } bak.delete(); rename(f, bak); rename(tmp, f); } private void rename(File from, File to) throws IOException { if (!from.renameTo(to)) throw new IOException(MessageFormat.format(rb.getString("failed-to-rename"), from, to)); } public void copyFrom(DicomDirReader r) throws IOException { Attributes rec = r.findFirstRootDirectoryRecordInUse(false); while (rec != null) { copyChildsFrom(r, rec, out.addRootDirectoryRecord(new Attributes(rec))); rec = r.findNextDirectoryRecordInUse(rec, false); } } private void copyChildsFrom(DicomDirReader r, Attributes src, Attributes dst) throws IOException { Attributes rec = r.findLowerDirectoryRecordInUse(src, false); while (rec != null) { copyChildsFrom(r, rec, out.addLowerDirectoryRecord(dst, new Attributes(rec))); rec = r.findNextDirectoryRecordInUse(rec, false); } } public final File getFile() { return file; } public final void setInUse(boolean inUse) { this.inUse = inUse; } public final void setOriginalSequenceLength(boolean origSeqLength) { this.origSeqLength = origSeqLength; } public final void setEncodingOptions(DicomEncodingOptions encOpts) { this.encOpts = encOpts; } public final void setWidth(int width) { if (width < 40) throw new IllegalArgumentException(); this.width = width; } public final void setCheckDuplicate(boolean checkDuplicate) { this.checkDuplicate = checkDuplicate; } public final void setRecordFactory(RecordFactory recFact) { this.recFact = recFact; } public void close() { SafeClose.close(in); in = null; out = null; } public void openForReadOnly(File file) throws IOException { this.file = file; in = new DicomDirReader(file); } public void create(File file) throws IOException { this.file = file; DicomDirWriter.createEmptyDirectory(file, UIDUtils.createUIDIfNull(fsInfo.getFilesetUID()), fsInfo.getFilesetID(), fsInfo.getDescriptorFile(), fsInfo.getDescriptorFileCharset()); in = out = DicomDirWriter.open(file); out.setEncodingOptions(encOpts); setCheckDuplicate(false); } public void open(File file) throws IOException { this.file = file; in = out = DicomDirWriter.open(file); if (!origSeqLength) out.setEncodingOptions(encOpts); setCheckDuplicate(true); } public void list() throws IOException { checkIn(); list("File Meta Information:", in.getFileMetaInformation()); list("File-set Information:", in.getFileSetInformation()); list(inUse ? in.findFirstRootDirectoryRecordInUse(false) : in.readFirstRootDirectoryRecord(), new StringBuilder()); } private void list(final String header, final Attributes attrs) { System.out.println(header); System.out.println(attrs.toString(Integer.MAX_VALUE, width)); } private void list(Attributes rec, StringBuilder index) throws IOException { int indexLen = index.length(); int i = 1; while (rec != null) { index.append(i++).append('.'); list(heading(rec, index), rec); list(inUse ? in.findLowerDirectoryRecordInUse(rec, false) : in.readLowerDirectoryRecord(rec), index); rec = inUse ? in.findNextDirectoryRecordInUse(rec, false) : in.readNextDirectoryRecord(rec); index.setLength(indexLen); } ; } private String heading(Attributes rec, StringBuilder index) { int prefixLen = index.length(); try { return index.append(' ').append(rec.getString(Tag.DirectoryRecordType, "")).append(':').toString(); } finally { index.setLength(prefixLen); } } public int addReferenceTo(File f) throws IOException { checkOut(); checkRecordFactory(); int n = 0; if (f.isDirectory()) { for (String s : f.list()) n += addReferenceTo(new File(f, s)); return n; } // do not add reference to DICOMDIR if (f.equals(file)) return 0; Attributes fmi; Attributes dataset; DicomInputStream din = null; try { din = new DicomInputStream(f); din.setIncludeBulkData(IncludeBulkData.NO); fmi = din.readFileMetaInformation(); dataset = din.readDataset(-1, Tag.PixelData); } catch (IOException e) { System.out.println(); System.out.println(MessageFormat.format(rb.getString("failed-to-parse"), f, e.getMessage())); return 0; } finally { if (din != null) try { din.close(); } catch (Exception ignore) { } } char prompt = '.'; if (fmi == null) { fmi = dataset.createFileMetaInformation(UID.ImplicitVRLittleEndian); prompt = 'F'; } String iuid = fmi.getString(Tag.MediaStorageSOPInstanceUID, null); if (iuid == null) { System.out.println(); System.out.println(MessageFormat.format(rb.getString("skip-file"), f)); return 0; } String pid = dataset.getString(Tag.PatientID, null); String styuid = dataset.getString(Tag.StudyInstanceUID, null); String seruid = dataset.getString(Tag.SeriesInstanceUID, null); if (styuid != null && seruid != null) { if (pid == null) { dataset.setString(Tag.PatientID, VR.LO, pid = styuid); prompt = prompt == 'F' ? 'P' : 'p'; } Attributes patRec = in.findPatientRecord(pid); if (patRec == null) { patRec = recFact.createRecord(RecordType.PATIENT, null, dataset, null, null); out.addRootDirectoryRecord(patRec); n++; } Attributes studyRec = in.findStudyRecord(patRec, styuid); if (studyRec == null) { studyRec = recFact.createRecord(RecordType.STUDY, null, dataset, null, null); out.addLowerDirectoryRecord(patRec, studyRec); n++; } Attributes seriesRec = in.findSeriesRecord(studyRec, seruid); if (seriesRec == null) { seriesRec = recFact.createRecord(RecordType.SERIES, null, dataset, null, null); out.addLowerDirectoryRecord(studyRec, seriesRec); n++; } Attributes instRec; if (checkDuplicate) { instRec = in.findLowerInstanceRecord(seriesRec, false, iuid); if (instRec != null) { System.out.print('-'); return 0; } } instRec = recFact.createRecord(dataset, fmi, out.toFileIDs(f)); out.addLowerDirectoryRecord(seriesRec, instRec); } else { if (checkDuplicate) { if (in.findRootInstanceRecord(false, iuid) != null) { System.out.print('-'); return 0; } } Attributes instRec = recFact.createRecord(dataset, fmi, out.toFileIDs(f)); out.addRootDirectoryRecord(instRec); prompt = prompt == 'F' ? 'R' : 'r'; } System.out.print(prompt); return n + 1; } public int removeReferenceTo(File f) throws IOException { checkOut(); int n = 0; if (f.isDirectory()) { for (String s : f.list()) n += removeReferenceTo(new File(f, s)); return n; } String pid; String styuid; String seruid; String iuid; DicomInputStream din = null; try { din = new DicomInputStream(f); din.setIncludeBulkData(IncludeBulkData.NO); Attributes fmi = din.readFileMetaInformation(); Attributes dataset = din.readDataset(-1, Tag.StudyID); iuid = (fmi != null) ? fmi.getString(Tag.MediaStorageSOPInstanceUID, null) : dataset.getString(Tag.SOPInstanceUID, null); if (iuid == null) { System.out.println(); System.out.println(MessageFormat.format(rb.getString("skip-file"), f)); return 0; } pid = dataset.getString(Tag.PatientID, null); styuid = dataset.getString(Tag.StudyInstanceUID, null); seruid = dataset.getString(Tag.SeriesInstanceUID, null); } catch (IOException e) { System.out.println(); System.out.println(MessageFormat.format(rb.getString("failed-to-parse"), f, e.getMessage())); return 0; } finally { if (din != null) try { din.close(); } catch (Exception ignore) { } } Attributes instRec; if (styuid != null && seruid != null) { Attributes patRec = in.findPatientRecord(pid == null ? styuid : pid); if (patRec == null) { return 0; } Attributes studyRec = in.findStudyRecord(patRec, styuid); if (studyRec == null) { return 0; } Attributes seriesRec = in.findSeriesRecord(studyRec, seruid); if (seriesRec == null) { return 0; } instRec = in.findLowerInstanceRecord(seriesRec, false, iuid); } else { instRec = in.findRootInstanceRecord(false, iuid); } if (instRec == null) { return 0; } out.deleteRecord(instRec); System.out.print('x'); return 1; } public void commit() throws IOException { checkOut(); out.commit(); } public int purge() throws IOException { checkOut(); return out.purge(); } private void checkIn() { if (in == null) throw new IllegalStateException(rb.getString("no-open-file")); } private void checkOut() { checkIn(); if (out == null) throw new IllegalStateException(rb.getString("read-only")); } private void checkRecordFactory() { if (recFact == null) throw new IllegalStateException(rb.getString("no-record-factory")); } }