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) 2012 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.data; 040 041import java.io.File; 042import java.io.FileNotFoundException; 043import java.io.IOException; 044import java.io.Serializable; 045import java.util.ArrayList; 046import java.util.Arrays; 047import java.util.HashMap; 048import java.util.LinkedList; 049import java.util.List; 050import java.util.Map; 051 052import javax.xml.parsers.ParserConfigurationException; 053import javax.xml.parsers.SAXParser; 054import javax.xml.parsers.SAXParserFactory; 055 056import org.dcm4che3.util.ByteUtils; 057import org.dcm4che3.util.ResourceLocator; 058import org.dcm4che3.util.StringUtils; 059import org.xml.sax.Locator; 060import org.xml.sax.SAXException; 061import org.xml.sax.helpers.DefaultHandler; 062 063/** 064 * IOD stands for Information Object Definition. 065 * 066 * @author Gunter Zeilinger <gunterze@gmail.com> 067 * 068 */ 069public class IOD extends ArrayList<IOD.DataElement> { 070 071 private static final long serialVersionUID = -5065822488885801576L; 072 073 public enum DataElementType { 074 TYPE_0, TYPE_1, TYPE_2, TYPE_3 075 } 076 077 public static class DataElement implements Serializable { 078 079 private static final long serialVersionUID = -7460474415381086525L; 080 081 public final int tag; 082 public final VR vr; 083 public final DataElementType type; 084 public final int minVM; 085 public final int maxVM; 086 public final int valueNumber; 087 private Condition condition; 088 private Object values; 089 private int lineNumber = -1; 090 091 public DataElement(int tag, VR vr, DataElementType type, 092 int minVM, int maxVM, int valueNumber) { 093 this.tag = tag; 094 this.vr = vr; 095 this.type = type; 096 this.minVM = minVM; 097 this.maxVM = maxVM; 098 this.valueNumber = valueNumber; 099 } 100 101 public DataElement setCondition(Condition condition) { 102 this.condition = condition; 103 return this; 104 } 105 106 public Condition getCondition() { 107 return condition; 108 } 109 110 public int getValueNumber() { 111 return valueNumber; 112 } 113 114 public DataElement setValues(String... values) { 115 if (vr == VR.SQ) 116 throw new IllegalStateException("vr=SQ"); 117 this.values = values; 118 return this; 119 } 120 121 public DataElement setValues(int... values) { 122 if (!vr.isIntType()) 123 throw new IllegalStateException("vr=" + vr); 124 this.values = values; 125 return this; 126 } 127 128 public DataElement setValues(Code... values) { 129 if (vr != VR.SQ) 130 throw new IllegalStateException("vr=" + vr); 131 this.values = values; 132 return this; 133 } 134 135 public DataElement addItemIOD(IOD iod) { 136 if (this.values == null) { 137 this.values = new IOD[] { iod }; 138 } else { 139 IOD[] iods = (IOD[]) this.values; 140 iods = Arrays.copyOf(iods, iods.length+1); 141 iods[iods.length - 1] = iod; 142 this.values = iods; 143 } 144 return this; 145 } 146 147 public Object getValues() { 148 return values; 149 } 150 151 public int getLineNumber() { 152 return lineNumber; 153 } 154 155 public DataElement setLineNumber(int lineNumber) { 156 this.lineNumber = lineNumber; 157 return this; 158 } 159 160 } 161 162 public abstract static class Condition { 163 protected String id; 164 protected boolean not; 165 166 public Condition id(String id) { 167 this.id = id; 168 return this; 169 } 170 171 public final String id() { 172 return id; 173 } 174 175 public final Condition not() { 176 this.not = !not; 177 return this; 178 } 179 180 public abstract boolean match(Attributes attrs); 181 182 public void addChild(Condition child) { 183 throw new UnsupportedOperationException(); 184 } 185 186 public Condition trim() { 187 return this; 188 } 189 190 public boolean isEmpty() { 191 return false; 192 } 193 194 } 195 196 abstract static class CompositeCondition extends Condition { 197 protected final ArrayList<Condition> childs = new ArrayList<Condition>(); 198 199 public abstract boolean match(Attributes attrs); 200 201 @Override 202 public void addChild(Condition child) { 203 childs.add(child); 204 } 205 206 @Override 207 public Condition trim() { 208 int size = childs.size(); 209 if (size == 1) { 210 Condition child = childs.get(0).id(id); 211 return not ? child.not() : child; 212 } 213 childs.trimToSize(); 214 return this; 215 } 216 217 @Override 218 public boolean isEmpty() { 219 return childs.isEmpty(); 220 } 221 } 222 223 public static class And extends CompositeCondition { 224 225 public boolean match(Attributes attrs) { 226 for (Condition child : childs) { 227 if (!child.match(attrs)) 228 return not; 229 } 230 return !not; 231 } 232 } 233 234 public static class Or extends CompositeCondition { 235 236 public boolean match(Attributes attrs) { 237 for (Condition child : childs) { 238 if (child.match(attrs)) 239 return !not; 240 } 241 return not; 242 } 243 } 244 245 public static class Present extends Condition { 246 protected final int tag; 247 protected final int[] itemPath; 248 249 public Present(int tag, int... itemPath) { 250 this.tag = tag; 251 this.itemPath = itemPath; 252 } 253 254 public boolean match(Attributes attrs) { 255 return not ? !item(attrs).containsValue(tag) 256 : item(attrs).containsValue(tag); 257 } 258 259 protected Attributes item(Attributes attrs) { 260 for (int sqtag : itemPath) { 261 if (sqtag == -1) 262 attrs = (sqtag == -1) 263 ? attrs.getParent() 264 : attrs.getNestedDataset(sqtag); 265 } 266 return attrs; 267 } 268 } 269 270 public static class MemberOf extends Present { 271 private final VR vr; 272 private final int valueIndex; 273 private final boolean matchNotPresent; 274 private Object values; 275 276 public MemberOf(int tag, VR vr, int valueIndex, 277 boolean matchNotPresent, int... itemPath) { 278 super(tag, itemPath); 279 this.vr = vr; 280 this.valueIndex = valueIndex; 281 this.matchNotPresent = matchNotPresent; 282 } 283 284 public VR vr() { 285 return vr; 286 } 287 288 public MemberOf setValues(String... values) { 289 if (vr == VR.SQ) 290 throw new IllegalStateException("vr=SQ"); 291 this.values = values; 292 return this; 293 } 294 295 public MemberOf setValues(int... values) { 296 if (!vr.isIntType()) 297 throw new IllegalStateException("vr=" + vr); 298 this.values = values; 299 return this; 300 } 301 302 public MemberOf setValues(Code... values) { 303 if (vr != VR.SQ) 304 throw new IllegalStateException("vr=" + vr); 305 this.values = values; 306 return this; 307 } 308 309 public boolean match(Attributes attrs) { 310 if (values == null) 311 throw new IllegalStateException("values not initialized"); 312 Attributes item = item(attrs); 313 if (item == null) 314 return matchNotPresent; 315 316 if (values instanceof int[]) 317 return not ? !match(item, ((int[]) values)) 318 : match(item, ((int[]) values)); 319 else if (values instanceof Code[]) 320 return not ? !match(item, ((Code[]) values)) 321 : match(item, ((Code[]) values)); 322 else 323 return not ? !match(item, ((String[]) values)) 324 : match(item, ((String[]) values)); 325 } 326 327 private boolean match(Attributes item, String[] ss) { 328 String val = item.getString(tag, valueIndex); 329 if (val == null) 330 return not ? !matchNotPresent : matchNotPresent; 331 for (String s : ss) { 332 if (s.equals(val)) 333 return !not; 334 } 335 return not; 336 } 337 338 private boolean match(Attributes item, Code[] codes) { 339 Sequence seq = item.getSequence(tag); 340 if (seq != null) 341 for (Attributes codeItem : seq) { 342 try { 343 Code val = new Code(codeItem); 344 for (Code code : codes) { 345 if (code.equals(val)) 346 return !not; 347 } 348 } catch (NullPointerException npe) {} 349 } 350 return not; 351 } 352 353 private boolean match(Attributes item, int[] is) { 354 int val = item.getInt(tag, valueIndex, Integer.MIN_VALUE); 355 if (val == Integer.MIN_VALUE) 356 return matchNotPresent; 357 for (int i : is) { 358 if (i == val) 359 return true; 360 } 361 return false; 362 } 363 } 364 365 private DataElementType type; 366 private Condition condition; 367 private int lineNumber = -1; 368 369 public void setType(DataElementType type) { 370 this.type = type; 371 } 372 373 public DataElementType getType() { 374 return type; 375 } 376 377 public void setCondition(Condition condition) { 378 this.condition = condition; 379 } 380 381 public Condition getCondition() { 382 return condition; 383 } 384 385 public int getLineNumber() { 386 return lineNumber; 387 } 388 389 public void setLineNumber(int lineNumber) { 390 this.lineNumber = lineNumber; 391 } 392 393 public void parse(String uri) throws IOException { 394 try { 395 SAXParserFactory f = SAXParserFactory.newInstance(); 396 SAXParser parser = f.newSAXParser(); 397 parser.parse(uri, new SAXHandler(this)); 398 } catch (SAXException e) { 399 throw new IOException("Failed to parse " + uri, e); 400 } catch (ParserConfigurationException e) { 401 throw new RuntimeException(e); 402 } 403 } 404 405 private static class SAXHandler extends DefaultHandler { 406 407 private StringBuilder sb = new StringBuilder(); 408 private boolean processCharacters; 409 private boolean elementConditions; 410 private boolean itemConditions; 411 private String idref; 412 private List<String> values = new ArrayList<String>(); 413 private List<Code> codes = new ArrayList<Code>(); 414 private LinkedList<IOD> iodStack = new LinkedList<IOD>(); 415 private LinkedList<Condition> conditionStack = new LinkedList<Condition>(); 416 private Map<String, IOD> id2iod = new HashMap<String, IOD>(); 417 private Map<String, Condition> id2cond = new HashMap<String, Condition>(); 418 private Locator locator; 419 420 public SAXHandler(IOD iod) { 421 iodStack.add(iod); 422 } 423 424 @Override 425 public void setDocumentLocator(Locator locator) { 426 this.locator = locator; 427 } 428 429 @Override 430 public void startElement(String uri, String localName, String qName, 431 org.xml.sax.Attributes atts) throws SAXException { 432 switch (qName.charAt(0)) { 433 case 'A': 434 if (qName.equals("And")) 435 startCondition(qName, new And()); 436 break; 437 case 'C': 438 if (qName.equals("Code")) 439 startCode( 440 atts.getValue("codeValue"), 441 atts.getValue("codingSchemeDesignator"), 442 atts.getValue("codingSchemeVersion"), 443 atts.getValue("codeMeaning")); 444 case 'D': 445 if (qName.equals("DataElement")) 446 startDataElement( 447 atts.getValue("tag"), 448 atts.getValue("vr"), 449 atts.getValue("type"), 450 atts.getValue("vm"), 451 atts.getValue("items"), 452 atts.getValue("valueNumber")); 453 break; 454 case 'I': 455 if (qName.equals("If")) 456 startIf(atts.getValue("id"), atts.getValue("idref")); 457 else if (qName.equals("Item")) 458 startItem(atts.getValue("id"), 459 atts.getValue("idref"), 460 atts.getValue("type")); 461 break; 462 case 'M': 463 if (qName.equals("MemberOf")) 464 startCondition(qName, memberOf(atts)); 465 break; 466 case 'N': 467 if (qName.equals("NotAnd")) 468 startCondition(qName, new And().not()); 469 else if (qName.equals("NotMemberOf")) 470 startCondition(qName, memberOf(atts).not()); 471 else if (qName.equals("NotOr")) 472 startCondition(qName, new Or().not()); 473 else if (qName.equals("NotPresent")) 474 startCondition(qName, present(atts).not()); 475 break; 476 case 'O': 477 if (qName.equals("Or")) 478 startCondition(qName, new Or()); 479 break; 480 case 'P': 481 if (qName.equals("Present")) 482 startCondition(qName, present(atts)); 483 break; 484 case 'V': 485 if (qName.equals("Value")) 486 startValue(); 487 break; 488 } 489 } 490 491 private Present present(org.xml.sax.Attributes atts) 492 throws SAXException { 493 int[] tagPath = tagPathOf(atts.getValue("tag")); 494 int lastIndex = tagPath.length-1; 495 return new Present(tagPath[lastIndex], 496 lastIndex > 0 ? Arrays.copyOf(tagPath, lastIndex) 497 : ByteUtils.EMPTY_INTS); 498 } 499 500 private MemberOf memberOf(org.xml.sax.Attributes atts) 501 throws SAXException { 502 int[] tagPath = tagPathOf(atts.getValue("tag")); 503 int lastIndex = tagPath.length-1; 504 return new MemberOf( 505 tagPath[lastIndex], 506 vrOf(atts.getValue("vr")), 507 valueNumberOf(atts.getValue("valueNumber"), 1) - 1, 508 matchNotPresentOf(atts.getValue("matchNotPresent")), 509 lastIndex > 0 ? Arrays.copyOf(tagPath, lastIndex) 510 : ByteUtils.EMPTY_INTS); 511 } 512 513 private void startCode(String codeValue, 514 String codingSchemeDesignator, 515 String codingSchemeVersion, 516 String codeMeaning) throws SAXException { 517 if (codeValue == null) 518 throw new SAXException("missing codeValue attribute"); 519 if (codingSchemeDesignator == null) 520 throw new SAXException("missing codingSchemeDesignator attribute"); 521 if (codeMeaning == null) 522 throw new SAXException("missing codeMeaning attribute"); 523 codes.add(new Code(codeValue, codingSchemeDesignator, 524 codingSchemeVersion, codeMeaning)); 525 } 526 527 @Override 528 public void endElement(String uri, String localName, String qName) 529 throws SAXException { 530 switch (qName.charAt(0)) { 531 case 'A': 532 if (qName.equals("And")) 533 endCondition(qName); 534 break; 535 case 'D': 536 if (qName.equals("DataElement")) 537 endDataElement(); 538 break; 539 case 'I': 540 if (qName.equals("If")) 541 endCondition(qName); 542 else if (qName.equals("Item")) 543 endItem(); 544 break; 545 case 'M': 546 if (qName.equals("MemberOf")) 547 endCondition(qName); 548 break; 549 case 'N': 550 if (qName.equals("NotAnd")) 551 endCondition(qName); 552 else if (qName.equals("NotMemberOf")) 553 endCondition(qName); 554 else if (qName.equals("NotOr")) 555 endCondition(qName); 556 else if (qName.equals("NotPresent")) 557 endCondition(qName); 558 break; 559 case 'O': 560 if (qName.equals("Or")) 561 endCondition(qName); 562 break; 563 case 'P': 564 if (qName.equals("Present")) 565 endCondition(qName); 566 break; 567 case 'V': 568 if (qName.equals("Value")) 569 endValue(); 570 break; 571 } 572 processCharacters = false; 573 idref = null; 574 } 575 576 @Override 577 public void characters(char[] ch, int start, int length) 578 throws SAXException { 579 if (processCharacters) 580 sb.append(ch, start, length); 581 } 582 583 private void startDataElement(String tagStr, String vrStr, 584 String typeStr, String vmStr, String items, 585 String valueNumberStr) throws SAXException { 586 if (idref != null) 587 throw new SAXException("<Item> with idref must be empty"); 588 589 IOD iod = iodStack.getLast(); 590 int tag = tagOf(tagStr); 591 VR vr = vrOf(vrStr); 592 DataElementType type = typeOf(typeStr); 593 594 int minVM = -1; 595 int maxVM = -1; 596 String vm = vr == VR.SQ ? items : vmStr; 597 if (vm != null) { 598 try { 599 String[] ss = StringUtils.split(vm, '-'); 600 if (ss[0].charAt(0) != 'n') { 601 minVM = Integer.parseInt(ss[0]); 602 if (ss.length > 1) { 603 if (ss[1].charAt(0) != 'n') 604 maxVM = Integer.parseInt(ss[1]); 605 } else { 606 maxVM = minVM; 607 } 608 } 609 } catch (IllegalArgumentException e) { 610 throw new SAXException( 611 (vr == VR.SQ ? "invalid items=\"" 612 : "invalid vm=\"") 613 + vm + '"'); 614 } 615 } 616 DataElement el = new DataElement(tag, vr, type, minVM, maxVM, 617 valueNumberOf(valueNumberStr, 0)); 618 if (locator != null) 619 el.setLineNumber(locator.getLineNumber()); 620 iod.add(el); 621 elementConditions = true; 622 itemConditions = false; 623 } 624 625 private DataElementType typeOf(String s) throws SAXException { 626 if (s == null) 627 throw new SAXException("missing type attribute"); 628 try { 629 return DataElementType.valueOf("TYPE_" + s); 630 } catch (IllegalArgumentException e) { 631 throw new SAXException("unrecognized type=\"" + s + '"'); 632 } 633 } 634 635 private VR vrOf(String s) throws SAXException { 636 try { 637 return VR.valueOf(s); 638 } catch (NullPointerException e) { 639 throw new SAXException("missing vr attribute"); 640 } catch (IllegalArgumentException e) { 641 throw new SAXException("unrecognized vr=\"" + s + '"'); 642 } 643 } 644 645 private int tagOf(String s) throws SAXException { 646 try { 647 return (int) Long.parseLong(s, 16); 648 } catch (NullPointerException e) { 649 throw new SAXException("missing tag attribute"); 650 } catch (IllegalArgumentException e) { 651 throw new SAXException("invalid tag=\"" + s + '"'); 652 } 653 } 654 655 private int[] tagPathOf(String s) throws SAXException { 656 String[] ss = StringUtils.split(s, '/'); 657 if (ss.length == 0) 658 throw new SAXException("missing tag attribute"); 659 660 try { 661 int[] tagPath = new int[ss.length]; 662 for (int i = 0; i < tagPath.length; i++) 663 tagPath[i] = ss[i].equals("..") 664 ? -1 665 : (int) Long.parseLong(s, 16); 666 return tagPath; 667 } catch (IllegalArgumentException e) { 668 throw new SAXException("invalid tag=\"" + s + '"'); 669 } 670 } 671 672 673 private int valueNumberOf(String s, int def) throws SAXException { 674 try { 675 return s != null ? Integer.parseInt(s) : def; 676 } catch (IllegalArgumentException e) { 677 throw new SAXException("invalid valueNumber=\"" + s + '"'); 678 } 679 } 680 681 private boolean matchNotPresentOf(String s) { 682 return s != null && s.equalsIgnoreCase("true"); 683 } 684 685 686 private DataElement getLastDataElement() { 687 IOD iod = iodStack.getLast(); 688 return iod.get(iod.size()-1); 689 } 690 691 private void endDataElement() throws SAXException { 692 DataElement el = getLastDataElement(); 693 if (!values.isEmpty()) { 694 try { 695 if (el.vr.isIntType()) 696 el.setValues(parseInts(values)); 697 else 698 el.setValues(values.toArray(new String[values.size()])); 699 } catch (IllegalStateException e) { 700 throw new SAXException("unexpected <Value>"); 701 } 702 values.clear(); 703 } 704 if (!codes.isEmpty()) { 705 try { 706 el.setValues(codes.toArray(new Code[codes.size()])); 707 } catch (IllegalStateException e) { 708 throw new SAXException("unexpected <Code>"); 709 } 710 codes.clear(); 711 } 712 elementConditions = false; 713 } 714 715 private int[] parseInts(List<String> list) { 716 int[] is = new int[list.size()]; 717 for (int i = 0; i < is.length; i++) 718 is[i] = Integer.parseInt(list.get(i)); 719 return is; 720 } 721 722 private void startValue() { 723 sb.setLength(0); 724 processCharacters = true; 725 } 726 727 private void endValue() { 728 values.add(sb.toString()); 729 } 730 731 private void startItem(String id, String idref, String type) throws SAXException { 732 IOD iod; 733 if (idref != null) { 734 if (type != null) 735 throw new SAXException("<Item> with idref must not specify type"); 736 737 iod = id2iod.get(idref); 738 if (iod == null) 739 throw new SAXException( 740 "could not resolve <Item idref:\"" + idref + "\"/>"); 741 } else { 742 iod = new IOD(); 743 if (type != null) 744 iod.setType(typeOf(type)); 745 if (locator != null) 746 iod.setLineNumber(locator.getLineNumber()); 747 } 748 getLastDataElement().addItemIOD(iod); 749 iodStack.add(iod); 750 if (id != null) 751 id2iod.put(id, iod); 752 753 this.idref = idref; 754 itemConditions = true; 755 elementConditions = false; 756 } 757 758 private void endItem() { 759 iodStack.removeLast().trimToSize(); 760 itemConditions = false; 761 } 762 763 private void startIf(String id, String idref) throws SAXException { 764 if (!conditionStack.isEmpty()) 765 throw new SAXException("unexpected <If>"); 766 767 Condition cond; 768 if (idref != null) { 769 cond = id2cond.get(idref); 770 if (cond == null) 771 throw new SAXException( 772 "could not resolve <If idref:\"" + idref + "\"/>"); 773 } else { 774 cond = new And().id(id); 775 } 776 conditionStack.add(cond); 777 if (id != null) 778 id2cond.put(id, cond); 779 this.idref = idref; 780 } 781 782 private void startCondition(String name, Condition cond) 783 throws SAXException { 784 if (!(elementConditions || itemConditions)) 785 throw new SAXException("unexpected <" + name + '>'); 786 787 conditionStack.add(cond); 788 } 789 790 private void endCondition(String name) throws SAXException { 791 Condition cond = conditionStack.removeLast(); 792 if (cond.isEmpty()) 793 throw new SAXException('<' + name + "> must not be empty"); 794 795 if (!values.isEmpty()) { 796 try { 797 MemberOf memberOf = (MemberOf) cond; 798 if (memberOf.vr.isIntType()) 799 memberOf.setValues(parseInts(values)); 800 else 801 memberOf.setValues(values.toArray(new String[values.size()])); 802 } catch (Exception e) { 803 throw new SAXException("unexpected <Value> contained by <" 804 + name + ">"); 805 } 806 values.clear(); 807 } 808 809 if (!codes.isEmpty()) { 810 try { 811 ((MemberOf) cond).setValues(codes.toArray(new Code[codes.size()])); 812 } catch (Exception e) { 813 throw new SAXException("unexpected <Code> contained by <" 814 + name + ">"); 815 } 816 codes.clear(); 817 } 818 819 if (conditionStack.isEmpty()) { 820 if (elementConditions) 821 getLastDataElement().setCondition(cond.trim()); 822 else 823 iodStack.getLast().setCondition(cond.trim()); 824 elementConditions = false; 825 itemConditions = false; 826 } else 827 conditionStack.getLast().addChild(cond.trim()); 828 } 829 } 830 831 public static IOD load(String uri) throws IOException { 832 if (uri.startsWith("resource:")) { 833 try { 834 uri = ResourceLocator.getResource(uri.substring(9), IOD.class); 835 } catch (NullPointerException npe) { 836 throw new FileNotFoundException(uri); 837 } 838 } else if (uri.indexOf(':') < 2) { 839 uri = new File(uri).toURI().toString(); 840 } 841 IOD iod = new IOD(); 842 iod.parse(uri); 843 iod.trimToSize(); 844 return iod; 845 } 846 847 public static IOD valueOf(Code code) { 848 IOD iod = new IOD(); 849 iod.add(new DataElement( 850 Tag.CodeValue, VR.SH, DataElementType.TYPE_1, 1, 1, 0) 851 .setValues(code.getCodeValue())); 852 iod.add(new DataElement( 853 Tag.CodingSchemeDesignator, VR.SH, DataElementType.TYPE_1, 1, 1, 0) 854 .setValues(code.getCodingSchemeDesignator())); 855 String codingSchemeVersion = code.getCodingSchemeVersion(); 856 if (codingSchemeVersion == null) 857 iod.add(new DataElement( 858 Tag.CodingSchemeVersion, VR.SH, DataElementType.TYPE_0, -1, -1, 0)); 859 else 860 iod.add(new DataElement( 861 Tag.CodingSchemeVersion, VR.SH, DataElementType.TYPE_1, 1, 1, 0)); 862 863 return iod; 864 } 865}