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}