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.dcmdump;
040
041import java.io.File;
042import java.io.IOException;
043import java.text.MessageFormat;
044import java.util.List;
045import java.util.ResourceBundle;
046
047import org.apache.commons.cli.CommandLine;
048import org.apache.commons.cli.OptionBuilder;
049import org.apache.commons.cli.Options;
050import org.apache.commons.cli.ParseException;
051import org.dcm4che3.data.Tag;
052import org.dcm4che3.data.Attributes;
053import org.dcm4che3.data.ElementDictionary;
054import org.dcm4che3.data.Fragments;
055import org.dcm4che3.data.Sequence;
056import org.dcm4che3.data.VR;
057import org.dcm4che3.io.DicomInputHandler;
058import org.dcm4che3.io.DicomInputStream;
059import org.dcm4che3.tool.common.CLIUtils;
060import org.dcm4che3.util.TagUtils;
061
062/**
063 * @author Gunter Zeilinger <gunterze@gmail.com>
064 */
065public class DcmDump implements DicomInputHandler {
066
067    private static ResourceBundle rb =
068        ResourceBundle.getBundle("org.dcm4che3.tool.dcmdump.messages");
069
070    /** default number of characters per line */
071    private static final int DEFAULT_WIDTH = 78;
072
073    private int width = DEFAULT_WIDTH;
074
075    public final int getWidth() {
076        return width;
077    }
078
079    public final void setWidth(int width) {
080        if (width < 40)
081            throw new IllegalArgumentException();
082        this.width = width;
083    }
084
085    public void parse(DicomInputStream dis) throws IOException {
086        dis.setDicomInputHandler(this);
087        dis.readDataset(-1, -1);
088    }
089
090    @Override
091    public void startDataset(DicomInputStream dis) throws IOException {
092        promptPreamble(dis.getPreamble());
093    }
094
095    @Override
096    public void endDataset(DicomInputStream dis) throws IOException {
097    }
098
099    @Override
100    public void readValue(DicomInputStream dis, Attributes attrs)
101            throws IOException {
102        StringBuilder line = new StringBuilder(width + 30);
103        appendPrefix(dis, line);
104        appendHeader(dis, line);
105        VR vr = dis.vr();
106        int vallen = dis.length();
107        boolean undeflen = vallen == -1;
108        if (vr == VR.SQ || undeflen) {
109            appendKeyword(dis, line);
110            System.out.println(line);
111            dis.readValue(dis, attrs);
112            if (undeflen) {
113                line.setLength(0);
114                appendPrefix(dis, line);
115                appendHeader(dis, line);
116                appendKeyword(dis, line);
117                System.out.println(line);
118            }
119            return;
120        }
121        int tag = dis.tag();
122        byte[] b = dis.readValue();
123        line.append(" [");
124        if (vr.prompt(b, dis.bigEndian(),
125                attrs.getSpecificCharacterSet(),
126                width - line.length() - 1, line)) {
127            line.append(']');
128            appendKeyword(dis, line);
129        }
130        System.out.println(line);
131        if (tag == Tag.FileMetaInformationGroupLength)
132            dis.setFileMetaInformationGroupLength(b);
133        else if (tag == Tag.TransferSyntaxUID
134                || tag == Tag.SpecificCharacterSet
135                || TagUtils.isPrivateCreator(tag))
136            attrs.setBytes(tag, vr, b);
137    }
138
139    @Override
140    public void readValue(DicomInputStream dis, Sequence seq)
141            throws IOException {
142        StringBuilder line = new StringBuilder(width);
143        appendPrefix(dis, line);
144        appendHeader(dis, line);
145        appendKeyword(dis, line);
146        appendNumber(seq.size() + 1, line);
147        System.out.println(line);
148        boolean undeflen = dis.length() == -1;
149        dis.readValue(dis, seq);
150        if (undeflen) {
151            line.setLength(0);
152            appendPrefix(dis, line);
153            appendHeader(dis, line);
154            appendKeyword(dis, line);
155            System.out.println(line);
156        }
157    }
158
159    @Override
160    public void readValue(DicomInputStream dis, Fragments frags)
161            throws IOException {
162        StringBuilder line = new StringBuilder(width + 20);
163        appendPrefix(dis, line);
164        appendHeader(dis, line);
165        appendFragment(line, dis, frags.vr());
166        System.out.println(line);
167    }
168
169    private void appendPrefix(DicomInputStream dis, StringBuilder line) {
170        line.append(dis.getTagPosition()).append(": ");
171        int level = dis.level();
172        while (level-- > 0)
173            line.append('>');
174    }
175
176    private void appendHeader(DicomInputStream dis, StringBuilder line) {
177        line.append(TagUtils.toString(dis.tag())).append(' ');
178        VR vr = dis.vr();
179        if (vr != null)
180            line.append(vr).append(' ');
181        line.append('#').append(dis.length());
182    }
183
184    private void appendKeyword(DicomInputStream dis, StringBuilder line) {
185        if (line.length() < width) {
186            line.append(" ");
187            line.append(ElementDictionary.keywordOf(dis.tag(), null));
188            if (line.length() > width)
189                line.setLength(width);
190        }
191    }
192
193    private void appendNumber(int number, StringBuilder line) {
194        if (line.length() < width) {
195            line.append(" #");
196            line.append(number);
197            if (line.length() > width)
198                line.setLength(width);
199        }
200    }
201
202    private void appendFragment(StringBuilder line, DicomInputStream dis,
203            VR vr) throws IOException {
204        byte[] b = dis.readValue();
205        line.append(" [");
206        if (vr.prompt(b, dis.bigEndian(), null, 
207                width - line.length() - 1, line)) {
208            line.append(']');
209            appendKeyword(dis, line);
210        }
211    }
212
213    private void promptPreamble(byte[] preamble) {
214        if (preamble == null)
215            return;
216        
217        StringBuilder line = new StringBuilder(width);
218        line.append("0: [");
219        if (VR.OB.prompt(preamble, false, null, width - 5, line))
220            line.append(']');
221        System.out.println(line);
222    }
223
224    @SuppressWarnings("unchecked")
225    public static void main(String[] args) {
226        try {
227            CommandLine cl = parseComandLine(args);
228            DcmDump main = new DcmDump();
229            if (cl.hasOption("w")) {
230                String s = cl.getOptionValue("w");
231                try {
232                    main.setWidth(Integer.parseInt(s));
233                } catch (IllegalArgumentException e) {
234                    throw new ParseException(MessageFormat.format(
235                            rb.getString("illegal-width"), s));
236                }
237            }
238            String fname = fname(cl.getArgList());
239            if (fname.equals("-")) {
240                main.parse(new DicomInputStream(System.in));
241            } else {
242                DicomInputStream dis =
243                        new DicomInputStream(new File(fname));
244                try {
245                    main.parse(dis);
246                } finally {
247                    dis.close();
248                }
249            }
250        } catch (ParseException e) {
251            System.err.println("dcmdump: " + e.getMessage());
252            System.err.println(rb.getString("try"));
253            System.exit(2);
254        } catch (IOException e) {
255            System.err.println("dcmdump: " + e.getMessage());
256            System.exit(2);
257        }
258    }
259
260    private static String fname(List<String> argList) throws ParseException {
261        int numArgs = argList.size();
262        if (numArgs == 0)
263            throw new ParseException(rb.getString("missing"));
264        if (numArgs > 1)
265            throw new ParseException(rb.getString("too-many"));
266        return argList.get(0);
267    }
268
269    @SuppressWarnings("static-access")
270    private static CommandLine parseComandLine(String[] args)
271            throws ParseException{
272        Options opts = new Options();
273        CLIUtils.addCommonOptions(opts);
274        opts.addOption(OptionBuilder
275                .withLongOpt("width")
276                .hasArg()
277                .withArgName("col")
278                .withDescription(rb.getString("width"))
279                .create("w"));
280        return CLIUtils.parseComandLine(args, opts, rb, DcmDump.class);
281    }
282
283}