/* ***** 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) 2013
* 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.dcm4che3.json;
import org.dcm4che3.data.*;
import org.dcm4che3.data.PersonName.Group;
import org.dcm4che3.util.Base64;
import org.dcm4che3.util.TagUtils;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonParser.Event;
import javax.json.stream.JsonParsingException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
/**
* @author Gunter Zeilinger <gunterze@gmail.com>
*
*/
public class JSONReader {
public interface Callback {
void onDataset(Attributes fmi, Attributes dataset);
}
private final JsonParser parser;
private Attributes fmi;
private Event event;
private String s;
private final ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
private final EnumMap<Group, String> pnGroups = new EnumMap<Group, String>(PersonName.Group.class);
public JSONReader(JsonParser parser) {
this.parser = parser;
}
public Attributes getFileMetaInformation() {
return fmi;
}
private Event next() {
s = null;
return event = parser.next();
}
private String getString() {
if (s == null)
s = parser.getString();
return s;
}
private void expect(Event expected) {
if (this.event != expected)
throw new JsonParsingException("Unexpected " + event + ", expected " + expected, parser.getLocation());
}
private String valueString() {
next();
expect(JsonParser.Event.VALUE_STRING);
return getString();
}
public Attributes readDataset(Attributes attrs) {
next();
expect(Event.START_OBJECT);
if (attrs == null) {
attrs = new Attributes();
}
fmi = null;
next();
doReadDataset(attrs);
return attrs;
}
public void readDatasets(Callback callback) {
next();
expect(Event.START_ARRAY);
Attributes attrs;
while (next() == JsonParser.Event.START_OBJECT) {
fmi = null;
attrs = new Attributes();
next();
doReadDataset(attrs);
callback.onDataset(fmi, attrs);
}
expect(JsonParser.Event.END_ARRAY);
}
private Attributes doReadDataset(Attributes attrs) {
while (event == JsonParser.Event.KEY_NAME) {
readAttribute(attrs);
next();
}
expect(JsonParser.Event.END_OBJECT);
attrs.trimToSize();
return attrs;
}
private void readAttribute(Attributes attrs) {
int tag = (int) Long.parseLong(getString(), 16);
if (TagUtils.isFileMetaInformation(tag)) {
if (fmi == null)
fmi = new Attributes();
attrs = fmi;
}
next();
expect(Event.START_OBJECT);
Element el = new Element();
while (next() == JsonParser.Event.KEY_NAME) {
String key = getString();
if (key.equals("vr"))
try {
el.vr = VR.valueOf(valueString());
} catch (IllegalArgumentException e) {
throw new JsonParsingException("Invalid vr: " + key, parser.getLocation());
}
else if (key.equals("Value"))
el.values = readValues();
else if (key.equals("InlineBinary"))
el.bytes = readInlineBinary();
else if (key.equals("BulkDataURI"))
el.bulkDataURI = valueString();
else if (key.equals("DataFragment"))
el.values = readDataFragments();
else
throw new JsonParsingException("Unexpected \"" + key
+ "\", expected \"Value\" or \"InlineBinary\""
+ " or \"BulkDataURI\" or \"DataFragment\"", parser.getLocation());
}
expect(JsonParser.Event.END_OBJECT);
if (el.vr == null)
throw new JsonParsingException("Missing property: vr", parser.getLocation());
if (el.isEmpty())
attrs.setNull(tag, el.vr);
else switch (el.vr) {
case AE:
case AS:
case AT:
case CS:
case DA:
case DT:
case LO:
case LT:
case PN:
case SH:
case ST:
case TM:
case UC:
case UI:
case UR:
case UT:
attrs.setString(tag, el.vr, el.toStrings());
break;
case DS:
case FL:
case FD:
attrs.setDouble(tag, el.vr, el.toDoubles());
break;
case IS:
case SL:
case SS:
case UL:
case US:
attrs.setInt(tag, el.vr, el.toInts());
break;
case SQ:
el.toItems(attrs.newSequence(tag, el.values.size()));
break;
case OB:
case OD:
case OF:
case OL:
case OW:
case UN:
if (el.bytes != null)
attrs.setBytes(tag, el.vr, el.bytes);
else if (el.bulkDataURI != null) {
BulkData bulkData = new BulkData(null, el.bulkDataURI, false);
attrs.setValue(tag, el.vr, bulkData.hasFragments()
? bulkData.toFragments(null, tag, el.vr)
: bulkData);
} else
el.toFragments(attrs.newFragments(tag, el.vr, el.values.size()));
}
}
private List<Object> readValues() {
ArrayList<Object> list = new ArrayList<Object>();
next();
expect(Event.START_ARRAY);
while (next() != Event.END_ARRAY) {
switch (event) {
case START_OBJECT:
list.add(readItemOrPersonName());
break;
case VALUE_STRING:
list.add(parser.getString());
break;
case VALUE_NUMBER:
list.add(parser.getBigDecimal());
break;
case VALUE_NULL:
list.add(null);
break;
default:
throw new JsonParsingException("Unexpected " + event, parser.getLocation());
}
}
return list;
}
private List<Object> readDataFragments() {
ArrayList<Object> list = new ArrayList<Object>();
next();
expect(Event.START_ARRAY);
while (next() != Event.END_ARRAY) {
switch (event) {
case START_OBJECT:
list.add(readDataFragment());
break;
case VALUE_NULL:
list.add(null);
break;
default:
throw new JsonParsingException("Unexpected " + event, parser.getLocation());
}
}
return list;
}
private Object readItemOrPersonName() {
if (next() != JsonParser.Event.KEY_NAME)
return null;
return (getString().length() == 8)
? doReadDataset(new Attributes())
: readPersonName();
}
private String readPersonName() {
pnGroups.clear();
while (event == JsonParser.Event.KEY_NAME) {
Group key;
try {
key = PersonName.Group.valueOf(getString());
} catch (IllegalArgumentException e) {
throw new JsonParsingException("Unexpected \"" + getString()
+ "\", expected \"Alphabetic\" or \"Ideographic\""
+ " or \"Phonetic\"", parser.getLocation());
}
pnGroups.put(key, valueString());
next();
}
expect(JsonParser.Event.END_OBJECT);
String s = pnGroups.get(PersonName.Group.Alphabetic);
if (s != null && pnGroups.size() == 1)
return s;
StringBuilder sb = new StringBuilder(64);
if (s != null)
sb.append(s);
sb.append('=');
s = pnGroups.get(PersonName.Group.Ideographic);
if (s != null)
sb.append(s);
s = pnGroups.get(PersonName.Group.Phonetic);
if (s != null)
sb.append('=').append(s);
return sb.toString();
}
private byte[] readInlineBinary() {
char[] base64 = valueString().toCharArray();
bout.reset();
try {
Base64.decode(base64, 0, base64.length, bout);
} catch (IOException e) {
throw new RuntimeException(e);
}
return bout.toByteArray();
}
private Object readDataFragment() {
next();
byte[] bytes = null;
String bulkDataURI = null;
while (next() != Event.KEY_NAME) {
String key = getString();
if (key.equals("BulkDataURI"))
bulkDataURI = valueString();
else if (key.equals("InlineBinary"))
bytes = readInlineBinary();
else
throw new JsonParsingException("Unexpected \"" + key
+ "\", expected \"InlineBinary\""
+ " or \"BulkDataURI\"", parser.getLocation());
}
expect(Event.END_OBJECT);
return bulkDataURI != null
? new BulkData(null, bulkDataURI, false)
: bytes;
}
private static class Element {
VR vr;
List<Object> values;
byte[] bytes;
String bulkDataURI;
boolean isEmpty() {
return (values == null || values.isEmpty()) && (bytes == null || bytes.length == 0) && bulkDataURI == null;
}
String[] toStrings() {
String[] ss = new String[values.size()];
for (int i = 0; i < ss.length; i++) {
Object value = values.get(i);
ss[i] = value != null ? value.toString() : null;
}
return ss;
}
double[] toDoubles() {
double[] ds = new double[values.size()];
for (int i = 0; i < ds.length; i++) {
ds[i] = ((Number) values.get(i)).doubleValue();
}
return ds;
}
int[] toInts() {
int[] is = new int[values.size()];
for (int i = 0; i < is.length; i++) {
is[i] = ((Number) values.get(i)).intValue();
}
return is;
}
void toItems(Sequence seq) {
for (Object value : values) {
seq.add(value != null ? (Attributes) value : new Attributes(0));
}
}
void toFragments(Fragments fragments) {
for (Object value : values) {
fragments.add(value);
}
}
}
}