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) 2013
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.json;
040
041import org.dcm4che3.data.*;
042import org.dcm4che3.data.PersonName.Group;
043import org.dcm4che3.util.Base64;
044import org.dcm4che3.util.TagUtils;
045
046import javax.json.stream.JsonParser;
047import javax.json.stream.JsonParser.Event;
048import javax.json.stream.JsonParsingException;
049import java.io.ByteArrayOutputStream;
050import java.io.IOException;
051import java.util.ArrayList;
052import java.util.EnumMap;
053import java.util.List;
054
055/**
056 * @author Gunter Zeilinger <gunterze@gmail.com>
057 *
058 */
059public class JSONReader {
060
061    public interface Callback {
062
063        void onDataset(Attributes fmi, Attributes dataset);
064
065    }
066
067    private final JsonParser parser;
068    private Attributes fmi;
069    private Event event;
070    private String s;
071    private final ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
072    private final EnumMap<Group, String> pnGroups = new EnumMap<Group, String>(PersonName.Group.class);
073
074    public JSONReader(JsonParser parser) {
075        this.parser = parser;
076    }
077
078    public Attributes getFileMetaInformation() {
079        return fmi;
080    }
081
082    private Event next() {
083        s = null;
084        return event = parser.next();
085    }
086
087    private String getString() {
088        if (s == null)
089            s = parser.getString();
090        return s;
091    }
092
093    private void expect(Event expected) {
094        if (this.event != expected)
095            throw new JsonParsingException("Unexpected " + event + ", expected " + expected, parser.getLocation());
096    }
097
098    private String valueString() {
099        next();
100        expect(JsonParser.Event.VALUE_STRING);
101        return getString();
102    }
103
104    public Attributes readDataset(Attributes attrs) {
105        next();
106        expect(Event.START_OBJECT);
107        if (attrs == null) {
108            attrs = new Attributes();
109        }
110        fmi = null;
111        next();
112        doReadDataset(attrs);
113        return attrs;
114    }
115
116    public void readDatasets(Callback callback) {
117        next();
118        expect(Event.START_ARRAY);
119        Attributes attrs;
120        while (next() == JsonParser.Event.START_OBJECT) {
121            fmi = null;
122            attrs = new Attributes();
123            next();
124            doReadDataset(attrs);
125            callback.onDataset(fmi, attrs);
126        }
127        expect(JsonParser.Event.END_ARRAY);
128    }
129
130    private Attributes doReadDataset(Attributes attrs) {
131        while (event == JsonParser.Event.KEY_NAME) {
132            readAttribute(attrs);
133            next();
134        }
135        expect(JsonParser.Event.END_OBJECT);
136        attrs.trimToSize();
137        return attrs;
138    }
139
140    private void readAttribute(Attributes attrs) {
141        int tag = (int) Long.parseLong(getString(), 16);
142        if (TagUtils.isFileMetaInformation(tag)) {
143            if (fmi == null)
144                fmi = new Attributes();
145            attrs = fmi;
146        }
147        next();
148        expect(Event.START_OBJECT);
149        Element el = new Element();
150        while (next() == JsonParser.Event.KEY_NAME) {
151            String key = getString();
152            if (key.equals("vr"))
153                try {
154                    el.vr = VR.valueOf(valueString());
155                } catch (IllegalArgumentException e) {
156                    throw new JsonParsingException("Invalid vr: " + key, parser.getLocation());
157                }
158            else if (key.equals("Value"))
159                el.values = readValues();
160            else if (key.equals("InlineBinary"))
161                el.bytes = readInlineBinary();
162            else if (key.equals("BulkDataURI"))
163                el.bulkDataURI = valueString();
164            else if (key.equals("DataFragment"))
165                el.values = readDataFragments();
166            else
167                throw new JsonParsingException("Unexpected \"" + key
168                        + "\", expected \"Value\" or \"InlineBinary\""
169                        + " or \"BulkDataURI\" or  \"DataFragment\"", parser.getLocation());
170        }
171        expect(JsonParser.Event.END_OBJECT);
172        if (el.vr == null)
173            throw new JsonParsingException("Missing property: vr", parser.getLocation());
174
175        if (el.isEmpty())
176            attrs.setNull(tag, el.vr);
177        else switch (el.vr) {
178            case AE:
179            case AS:
180            case AT:
181            case CS:
182            case DA:
183            case DT:
184            case LO:
185            case LT:
186            case PN:
187            case SH:
188            case ST:
189            case TM:
190            case UC:
191            case UI:
192            case UR:
193            case UT:
194                attrs.setString(tag, el.vr, el.toStrings());
195                break;
196            case DS:
197            case FL:
198            case FD:
199                attrs.setDouble(tag, el.vr, el.toDoubles());
200                break;
201            case IS:
202            case SL:
203            case SS:
204            case UL:
205            case US:
206                attrs.setInt(tag, el.vr, el.toInts());
207                break;
208            case SQ:
209                el.toItems(attrs.newSequence(tag, el.values.size()));
210                break;
211            case OB:
212            case OD:
213            case OF:
214            case OL:
215            case OW:
216            case UN:
217                if (el.bytes != null)
218                    attrs.setBytes(tag, el.vr, el.bytes);
219                else if (el.bulkDataURI != null) {
220                    BulkData bulkData = new BulkData(null, el.bulkDataURI, false);
221                        attrs.setValue(tag, el.vr, bulkData.hasFragments()
222                                ? bulkData.toFragments(null, tag, el.vr)
223                                : bulkData);
224                } else
225                    el.toFragments(attrs.newFragments(tag, el.vr, el.values.size()));
226        }
227    }
228
229    private List<Object> readValues() {
230        ArrayList<Object> list = new ArrayList<Object>();
231        next();
232        expect(Event.START_ARRAY);
233        while (next() != Event.END_ARRAY) {
234            switch (event) {
235                case START_OBJECT:
236                    list.add(readItemOrPersonName());
237                    break;
238                case VALUE_STRING:
239                    list.add(parser.getString());
240                    break;
241                case VALUE_NUMBER:
242                    list.add(parser.getBigDecimal());
243                    break;
244                case VALUE_NULL:
245                    list.add(null);
246                    break;
247                default:
248                    throw new JsonParsingException("Unexpected " + event, parser.getLocation());
249            }
250        }
251        return list;
252    }
253
254    private List<Object> readDataFragments() {
255        ArrayList<Object> list = new ArrayList<Object>();
256        next();
257        expect(Event.START_ARRAY);
258        while (next() != Event.END_ARRAY) {
259            switch (event) {
260                case START_OBJECT:
261                    list.add(readDataFragment());
262                    break;
263                case VALUE_NULL:
264                    list.add(null);
265                    break;
266                default:
267                    throw new JsonParsingException("Unexpected " + event, parser.getLocation());
268            }
269        }
270        return list;
271    }
272
273    private Object readItemOrPersonName() {
274        if (next() != JsonParser.Event.KEY_NAME)
275            return null;
276
277        return (getString().length() == 8)
278                ? doReadDataset(new Attributes())
279                : readPersonName();
280    }
281
282    private String readPersonName() {
283        pnGroups.clear();
284        while (event == JsonParser.Event.KEY_NAME) {
285            Group key;
286            try {
287                key = PersonName.Group.valueOf(getString());
288            } catch (IllegalArgumentException e) {
289                throw new JsonParsingException("Unexpected \"" + getString()
290                        + "\", expected \"Alphabetic\" or \"Ideographic\""
291                        + " or \"Phonetic\"", parser.getLocation());
292            }
293            pnGroups.put(key, valueString());
294            next();
295        }
296        expect(JsonParser.Event.END_OBJECT);
297        String s = pnGroups.get(PersonName.Group.Alphabetic);
298        if (s != null && pnGroups.size() == 1)
299            return s;
300        
301        StringBuilder sb = new StringBuilder(64);
302        if (s != null)
303            sb.append(s);
304        
305        sb.append('=');
306        s = pnGroups.get(PersonName.Group.Ideographic);
307        if (s != null)
308            sb.append(s);
309
310        s = pnGroups.get(PersonName.Group.Phonetic);
311        if (s != null)
312            sb.append('=').append(s);
313        
314        return sb.toString();
315    }
316
317    private byte[] readInlineBinary() {
318        char[] base64 = valueString().toCharArray();
319        bout.reset();
320        try {
321            Base64.decode(base64, 0, base64.length, bout);
322        } catch (IOException e) {
323            throw new RuntimeException(e);
324        }
325        return bout.toByteArray();
326    }
327
328    private Object readDataFragment() {
329        next();
330        byte[] bytes = null;
331        String bulkDataURI = null;
332        while (next() != Event.KEY_NAME) {
333            String key = getString();
334            if (key.equals("BulkDataURI"))
335                bulkDataURI = valueString();
336            else if (key.equals("InlineBinary"))
337                bytes = readInlineBinary();
338            else
339                throw new JsonParsingException("Unexpected \"" + key
340                        + "\", expected \"InlineBinary\""
341                        + " or \"BulkDataURI\"", parser.getLocation());
342        }
343        expect(Event.END_OBJECT);
344        return bulkDataURI != null
345                ? new BulkData(null, bulkDataURI, false)
346                : bytes;
347    }
348
349    private static class Element {
350        VR vr;
351        List<Object> values;
352        byte[] bytes;
353        String bulkDataURI;
354
355        boolean isEmpty() {
356            return (values == null || values.isEmpty()) && (bytes == null || bytes.length == 0) && bulkDataURI == null;
357        }
358
359        String[] toStrings() {
360            String[] ss = new String[values.size()];
361            for (int i = 0; i < ss.length; i++) {
362                Object value = values.get(i);
363                ss[i] = value != null ? value.toString() : null;
364            }
365            return ss;
366        }
367
368        double[] toDoubles() {
369            double[] ds = new double[values.size()];
370            for (int i = 0; i < ds.length; i++) {
371                ds[i] = ((Number) values.get(i)).doubleValue();
372            }
373            return ds;
374        }
375
376        int[] toInts() {
377            int[] is = new int[values.size()];
378            for (int i = 0; i < is.length; i++) {
379                is[i] = ((Number) values.get(i)).intValue();
380            }
381            return is;
382        }
383
384        void toItems(Sequence seq) {
385            for (Object value : values) {
386                seq.add(value != null ? (Attributes) value : new Attributes(0));
387            }
388        }
389
390        void toFragments(Fragments fragments) {
391            for (Object value : values) {
392                fragments.add(value);
393            }
394        }
395
396    }
397}