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.data; 040 041import org.dcm4che3.data.IOD.DataElement; 042import org.dcm4che3.data.IOD.DataElementType; 043import org.dcm4che3.io.BulkDataDescriptor; 044import org.dcm4che3.io.DicomEncodingOptions; 045import org.dcm4che3.io.DicomInputStream; 046import org.dcm4che3.io.DicomOutputStream; 047import org.dcm4che3.util.*; 048import org.slf4j.Logger; 049import org.slf4j.LoggerFactory; 050 051import java.io.IOException; 052import java.io.ObjectInputStream; 053import java.io.ObjectOutputStream; 054import java.io.Serializable; 055import java.util.*; 056import java.util.regex.Pattern; 057 058/** 059 * @author Gunter Zeilinger <gunterze@gmail.com> 060 */ 061public class Attributes implements Serializable { 062 063 064 065 public interface Visitor { 066 boolean visit(Attributes attrs, int tag, VR vr, Object value) 067 throws Exception; 068 } 069 070 private static final Logger LOG = 071 LoggerFactory.getLogger(Attributes.class); 072 073 private static final int INIT_CAPACITY = 16; 074 private static final int TO_STRING_LIMIT = 50; 075 private static final int TO_STRING_WIDTH = 78; 076 private transient Attributes parent; 077 private transient String parentSequencePrivateCreator; 078 private transient int parentSequenceTag; 079 private transient int[] tags; 080 private transient VR[] vrs; 081 private transient Object[] values; 082 private transient int size; 083 private transient SpecificCharacterSet cs; 084 private transient TimeZone tz; 085 private transient int length = -1; 086 private transient int[] groupLengths; 087 private transient int groupLengthIndex0; 088 089 private final boolean bigEndian; 090 private long itemPosition = -1; 091 private boolean containsSpecificCharacterSet; 092 private boolean containsTimezoneOffsetFromUTC; 093 private Map<String, Object> properties; 094 private TimeZone defaultTimeZone; 095 096 public Attributes() { 097 this(false, INIT_CAPACITY); 098 } 099 100 public Attributes(boolean bigEndian) { 101 this(bigEndian, INIT_CAPACITY); 102 } 103 104 public Attributes(int initialCapacity) { 105 this(false, initialCapacity); 106 } 107 108 public Attributes(boolean bigEndian, int initialCapacity) { 109 this.bigEndian = bigEndian; 110 init(initialCapacity); 111 } 112 113 public void clear() { 114 size = 0; 115 Arrays.fill(tags, 0); 116 Arrays.fill(vrs, null); 117 Arrays.fill(values, null); 118 } 119 120 private void init(int initialCapacity) { 121 this.tags = new int[initialCapacity]; 122 this.vrs = new VR[initialCapacity]; 123 this.values = new Object[initialCapacity]; 124 } 125 126 public Attributes(Attributes other) { 127 this(other, other.bigEndian); 128 } 129 130 public Attributes(Attributes other, boolean bigEndian) { 131 this(bigEndian, other.size); 132 if (other.properties != null) 133 properties = new HashMap<String, Object>(other.properties); 134 addAll(other); 135 } 136 137 public Attributes(Attributes other, int... selection) { 138 this(other, other.bigEndian, selection); 139 } 140 141 public Attributes(Attributes other, boolean bigEndian, int... selection) { 142 this(bigEndian, selection.length); 143 if (other.properties != null) 144 properties = new HashMap<String, Object>(other.properties); 145 addSelected(other, selection); 146 } 147 148 public Attributes(Attributes other, Attributes selection) { 149 this(selection.size()); 150 if (other.properties != null) 151 properties = new HashMap<String, Object>(other.properties); 152 addSelected(other, selection); 153 } 154 155 public Attributes(Attributes other, boolean bigEndian, Attributes selection) { 156 this(bigEndian, selection.size()); 157 if (other.properties != null) 158 properties = new HashMap<String, Object>(other.properties); 159 addSelected(other, selection); 160 } 161 162 public Map<String, Object> getProperties() { 163 return properties; 164 } 165 166 public void setProperties(Map<String, Object> properties) { 167 this.properties = properties; 168 } 169 170 public Object getProperty(String key, Object defVal) { 171 if (properties == null) 172 return defVal; 173 174 Object val = properties.get(key); 175 return val != null ? val : defVal; 176 } 177 178 public Object setProperty(String key, Object value) { 179 if (properties == null) 180 properties = new HashMap<String, Object>(); 181 return properties.put(key, value); 182 } 183 184 public Object clearProperty(String key) { 185 return properties != null ? properties.remove(key) : null; 186 } 187 188 public final boolean isRoot() { 189 return parent == null; 190 } 191 192 public Attributes getRoot() { 193 return isRoot() ? this : parent.getRoot(); 194 } 195 196 public final int getLevel() { 197 return isRoot() ? 0 : 1 + parent.getLevel(); 198 } 199 200 public final boolean bigEndian() { 201 return bigEndian; 202 } 203 204 public final Attributes getParent() { 205 return parent; 206 } 207 208 public String getParentSequencePrivateCreator() { 209 return parentSequencePrivateCreator; 210 } 211 212 public int getParentSequenceTag() { 213 return parentSequenceTag; 214 } 215 216 public final int getLength() { 217 return length; 218 } 219 220 Attributes setParent(Attributes parent, String parentSequencePrivateCreator, int parentSequenceTag) { 221 if (parent != null) { 222 if (parent.bigEndian != bigEndian) 223 throw new IllegalArgumentException( 224 "Endian of Item must match Endian of parent Data Set"); 225 if (this.parent != null) 226 throw new IllegalArgumentException( 227 "Item already contained by Sequence"); 228 if (!containsSpecificCharacterSet) 229 cs = null; 230 if (!containsTimezoneOffsetFromUTC) 231 tz = null; 232 } 233 this.parent = parent; 234 this.parentSequencePrivateCreator = parentSequencePrivateCreator; 235 this.parentSequenceTag = parentSequenceTag; 236 return this; 237 } 238 239 public final long getItemPosition() { 240 return itemPosition; 241 } 242 243 public final void setItemPosition(long itemPosition) { 244 this.itemPosition = itemPosition; 245 } 246 247 public final boolean isEmpty() { 248 return size == 0; 249 } 250 251 public final int size() { 252 return size; 253 } 254 255 public ItemPointer[] itemPointers() { 256 return itemPointers(0); 257 } 258 259 private ItemPointer[] itemPointers(int n) { 260 if (parent == null) 261 return new ItemPointer[n]; 262 263 ItemPointer[] itemPointers = parent.itemPointers(n + 1); 264 itemPointers[itemPointers.length - n - 1] = 265 new ItemPointer(parentSequencePrivateCreator, parentSequenceTag, itemIndex()); 266 return itemPointers; 267 } 268 269 public int itemIndex() { 270 if (parent == null) 271 return -1; 272 273 Sequence seq = parent.getSequence(parentSequencePrivateCreator, parentSequenceTag); 274 if (seq == null) 275 return -1; 276 277 return seq.indexOf(this); 278 } 279 280 public int[] tags() { 281 return Arrays.copyOf(tags, size); 282 } 283 284 public void trimToSize() { 285 trimToSize(false); 286 } 287 288 public void trimToSize(boolean recursive) { 289 int oldCapacity = tags.length; 290 if (size < oldCapacity) { 291 tags = Arrays.copyOf(tags, size); 292 vrs = Arrays.copyOf(vrs, size); 293 values = Arrays.copyOf(values, size); 294 } 295 if (recursive) 296 for (Object value : values) { 297 if (value instanceof Sequence) { 298 ((Sequence) value).trimToSize(recursive); 299 } else if (value instanceof Fragments) 300 ((Fragments) value).trimToSize(); 301 } 302 } 303 304 public void internalizeStringValues(boolean decode) { 305 SpecificCharacterSet cs = getSpecificCharacterSet(); 306 for (int i = 0; i < values.length; i++) { 307 VR vr = vrs[i]; 308 Object value = values[i]; 309 if (vr.isStringType()) { 310 if (value instanceof byte[]) { 311 if (!decode) 312 continue; 313 value = vr.toStrings((byte[]) value, bigEndian, cs); 314 } 315 if (value instanceof String) 316 values[i] = ((String) value).intern(); 317 else if (value instanceof String[]) { 318 String[] ss = (String[]) value; 319 for (int j = 0; j < ss.length; j++) 320 ss[j] = ss[j].intern(); 321 } 322 } else if (value instanceof Sequence) 323 for (Attributes item : (Sequence) value) 324 item.internalizeStringValues(decode); 325 } 326 } 327 328 private void decodeStringValuesUsingSpecificCharacterSet() { 329 Object value; 330 VR vr; 331 SpecificCharacterSet cs = getSpecificCharacterSet(); 332 for (int i = 0; i < size; i++) { 333 value = values[i]; 334 if (value instanceof Sequence) { 335 for (Attributes item : (Sequence) value) 336 item.decodeStringValuesUsingSpecificCharacterSet(); 337 } else if ((vr = vrs[i]).useSpecificCharacterSet()) 338 if (value instanceof byte[]) 339 values[i] = 340 vr.toStrings((byte[]) value, bigEndian, cs); 341 } 342 } 343 344 private void ensureCapacity(int minCapacity) { 345 int oldCapacity = tags.length; 346 if (minCapacity > oldCapacity) { 347 int newCapacity = Math.max(minCapacity, oldCapacity << 1); 348 tags = Arrays.copyOf(tags, newCapacity); 349 vrs = Arrays.copyOf(vrs, newCapacity); 350 values = Arrays.copyOf(values, newCapacity); 351 } 352 } 353 354 public Attributes getNestedDataset(int sequenceTag) { 355 return getNestedDataset(null, sequenceTag, 0); 356 } 357 358 public Attributes getNestedDataset(int sequenceTag, int itemIndex) { 359 return getNestedDataset(null, sequenceTag, itemIndex); 360 } 361 362 public Attributes getNestedDataset(String privateCreator, int sequenceTag) { 363 return getNestedDataset(privateCreator, sequenceTag, 0); 364 } 365 366 public Attributes getNestedDataset(String privateCreator, int sequenceTag, int itemIndex) { 367 Object value = getValue(privateCreator, sequenceTag); 368 if (!(value instanceof Sequence)) 369 return null; 370 371 Sequence sq = (Sequence) value; 372 if (itemIndex >= sq.size()) 373 return null; 374 375 return sq.get(itemIndex); 376 } 377 378 public Attributes getNestedDataset(ItemPointer... itemPointers) { 379 Attributes item = this; 380 for (ItemPointer ip : itemPointers) { 381 Object value = item.getValue(ip.privateCreator, ip.sequenceTag); 382 if (!(value instanceof Sequence)) 383 return null; 384 385 Sequence sq = (Sequence) value; 386 if (ip.itemIndex >= sq.size()) 387 return null; 388 389 item = sq.get(ip.itemIndex); 390 } 391 return item; 392 } 393 394 private int indexForInsertOf(int tag) { 395 return size == 0 ? -1 396 : tags[size-1] < tag ? -(size+1) 397 : indexOf(tag); 398 } 399 400 private int indexOf(int tag) { 401 return Arrays.binarySearch(tags, 0, size, tag); 402 } 403 404 private int indexOf(String privateCreator, int tag) { 405 if (privateCreator != null) { 406 int creatorTag = creatorTagOf(privateCreator, tag, false); 407 if (creatorTag == -1) 408 return -1; 409 tag = TagUtils.toPrivateTag(creatorTag, tag); 410 } 411 return indexOf(tag); 412 } 413 414 /** 415 * resolves to the actual private tag, 416 * given a private tag with placeholers (like 0011,xx13) 417 */ 418 public int tagOf(String privateCreator, int tag) { 419 if (privateCreator != null) { 420 int creatorTag = creatorTagOf(privateCreator, tag, false); 421 if (creatorTag == -1) 422 return -1; 423 tag = TagUtils.toPrivateTag(creatorTag, tag); 424 } 425 return tag; 426 } 427 428 429 private int creatorTagOf(String privateCreator, int tag, boolean reserve) { 430 if (!TagUtils.isPrivateGroup(tag)) 431 throw new IllegalArgumentException(TagUtils.toString(tag) 432 + " is not a private Data Element"); 433 434 int group = tag & 0xffff0000; 435 int creatorTag = group | 0x10; 436 int index = indexOf(creatorTag); 437 if (index < 0) 438 index = -index-1; 439 while (index < size && (tags[index] & 0xffffff00) == group) { 440 creatorTag = tags[index]; 441 if (vrs[index] == VR.LO) { 442 Object creatorID = decodeStringValue(index); 443 if (privateCreator.equals(creatorID)) 444 return creatorTag; 445 } 446 index++; 447 creatorTag++; 448 } 449 if (!reserve) 450 return -1; 451 452 if ((creatorTag & 0xff00) != 0) 453 throw new IllegalStateException("No free block for Private Element " 454 + TagUtils.toString(tag)); 455 setString(creatorTag, VR.LO, privateCreator); 456 return creatorTag; 457 } 458 459 private Object decodeStringValue(int index) { 460 Object value = values[index]; 461 if (value instanceof byte[]) { 462 value = vrs[index].toStrings((byte[]) value, bigEndian, 463 getSpecificCharacterSet(vrs[index])); 464 if (value instanceof String && ((String) value).isEmpty()) 465 value = Value.NULL; 466 values[index] = value; 467 } 468 return value; 469 } 470 471 public SpecificCharacterSet getSpecificCharacterSet(VR vr) { 472 return vr.useSpecificCharacterSet() 473 ? getSpecificCharacterSet() 474 : SpecificCharacterSet.ASCII; 475 } 476 477 private double[] decodeDSValue(int index) { 478 Object value = values[index]; 479 if (value == Value.NULL) 480 return ByteUtils.EMPTY_DOUBLES; 481 482 if (value instanceof double[]) 483 return (double[]) value; 484 485 double[] ds; 486 if (value instanceof byte[]) 487 value = vrs[index].toStrings((byte[]) value, bigEndian, 488 SpecificCharacterSet.ASCII); 489 if (value instanceof String) { 490 String s = (String) value; 491 if (s.isEmpty()) { 492 values[index] = Value.NULL; 493 return ByteUtils.EMPTY_DOUBLES; 494 } 495 ds = new double[] { StringUtils.parseDS(s) }; 496 } else { // value instanceof String[] 497 String[] ss = (String[]) value; 498 ds = new double[ss.length]; 499 for (int i = 0; i < ds.length; i++) { 500 String s = ss[i]; 501 ds[i] = (s != null && !s.isEmpty()) 502 ? StringUtils.parseDS(s) 503 : Double.NaN; 504 } 505 } 506 values[index] = ds; 507 return ds; 508 } 509 510 private int[] decodeISValue(int index) { 511 Object value = values[index]; 512 if (value == Value.NULL) 513 return ByteUtils.EMPTY_INTS; 514 515 if (value instanceof int[]) 516 return (int[]) value; 517 518 int[] is; 519 if (value instanceof byte[]) 520 value = vrs[index].toStrings((byte[]) value, bigEndian, 521 SpecificCharacterSet.ASCII); 522 if (value instanceof String) { 523 String s = (String) value; 524 if (s.isEmpty()) { 525 values[index] = Value.NULL; 526 return ByteUtils.EMPTY_INTS; 527 } 528 is = new int[] { StringUtils.parseIS(s) }; 529 } else { // value instanceof String[] 530 String[] ss = (String[]) value; 531 is = new int[ss.length]; 532 for (int i = 0; i < is.length; i++) { 533 String s = ss[i]; 534 is[i] = (s != null && !s.isEmpty()) 535 ? StringUtils.parseIS(s) 536 : Integer.MIN_VALUE; 537 } 538 } 539 values[index] = is; 540 return is; 541 } 542 543 private void updateVR(int index, VR vr) { 544 VR prev = vrs[index]; 545 if (vr == prev) 546 return; 547 548 Object value = values[index]; 549 if (!(value == Value.NULL 550 || value instanceof byte[] 551 || vr.isStringType() 552 && (value instanceof String 553 || value instanceof String[]))) 554 throw new IllegalStateException("value instanceof " + value.getClass()); 555 556 vrs[index] = vr; 557 } 558 559 private static boolean isEmpty(Object value) { 560 return (value instanceof Value) && ((Value) value).isEmpty(); 561 } 562 563 public boolean contains(int tag) { 564 return indexOf(tag) >= 0; 565 } 566 567 public boolean contains(String privateCreator, int tag) { 568 return indexOf(privateCreator, tag) >= 0; 569 } 570 571 public boolean containsValue(int tag) { 572 return containsValue(null, tag); 573 } 574 575 public boolean containsValue(String privateCreator, int tag) { 576 int index = indexOf(privateCreator, tag); 577 return index >= 0 578 && !isEmpty(vrs[index].isStringType() 579 ? decodeStringValue(index) 580 : values[index]); 581 } 582 583 public String privateCreatorOf(int tag) { 584 if (!TagUtils.isPrivateTag(tag)) 585 return null; 586 587 int creatorTag = (tag & 0xffff0000) | ((tag >>> 8) & 0xff); 588 int index = indexOf(creatorTag); 589 if (index < 0 || vrs[index] != VR.LO || values[index] == Value.NULL) 590 return null; 591 592 Object value = decodeStringValue(index); 593 if (value == Value.NULL) 594 return null; 595 596 return VR.LO.toString(value, false, 0, null); 597 } 598 599 public Object getValue(int tag) { 600 return getValue(null, tag, null); 601 } 602 603 public Object getValue(int tag, VR.Holder vr) { 604 return getValue(null, tag, vr); 605 } 606 607 public Object getValue(String privateCreator, int tag) { 608 return getValue(privateCreator, tag, null); 609 } 610 611 public Object getValue(String privateCreator, int tag, VR.Holder vr) { 612 int index = indexOf(privateCreator, tag); 613 if (index < 0) 614 return null; 615 616 if (vr != null) 617 vr.vr = vrs[index]; 618 return values[index]; 619 } 620 621 public VR getVR(int tag) { 622 return getVR(null, tag); 623 } 624 625 public VR getVR(String privateCreator, int tag) { 626 int index = indexOf(privateCreator, tag); 627 if (index < 0) 628 return null; 629 630 return vrs[index]; 631 } 632 633 public Sequence getSequence(int tag) { 634 return getSequence(null, tag); 635 } 636 637 public Sequence getSequence(String privateCreator, int tag) { 638 int index = indexOf(privateCreator, tag); 639 if (index < 0) 640 return null; 641 642 Object value = values[index]; 643 if (value == Value.NULL) 644 return (Sequence) (values[index] = new Sequence(this, privateCreator, tag, 0)); 645 return value instanceof Sequence ? (Sequence) value : null; 646 } 647 648 public byte[] getBytes(int tag) throws IOException { 649 return getBytes(null, tag); 650 } 651 652 public byte[] getBytes(String privateCreator, int tag) throws IOException { 653 int index = indexOf(privateCreator, tag); 654 if (index < 0) 655 return null; 656 657 Object value = values[index]; 658 VR vr = vrs[index]; 659 660 try { 661 if (value instanceof Value) 662 return ((Value) value).toBytes(vr, bigEndian); 663 664 return vr.toBytes(value, getSpecificCharacterSet(vr)); 665 } catch (UnsupportedOperationException e) { 666 LOG.info("Attempt to access {} {} as bytes", TagUtils.toString(tag), vr); 667 return null; 668 } 669 } 670 671 public byte[] getSafeBytes(int tag) { 672 return getSafeBytes(null, tag); 673 } 674 675 public byte[] getSafeBytes(String privateCreator, int tag) { 676 try { 677 return getBytes(privateCreator, tag); 678 } catch (IOException e) { 679 LOG.info("Access " + TagUtils.toString(tag) 680 + " throws i/o exception", e); 681 return null; 682 } 683 } 684 685 public String getString(int tag) { 686 return getString(null, tag, null, 0, null); 687 } 688 689 public String getString(int tag, String defVal) { 690 return getString(null, tag, null, 0, defVal); 691 } 692 693 public String getString(int tag, int valueIndex) { 694 return getString(null, tag, null, valueIndex, null); 695 } 696 697 public String getString(int tag, int valueIndex, String defVal) { 698 return getString(null, tag, null, valueIndex, defVal); 699 } 700 701 public String getString(String privateCreator, int tag) { 702 return getString(privateCreator, tag, null, 0, null); 703 } 704 705 public String getString(String privateCreator, int tag, String defVal) { 706 return getString(privateCreator, tag, null, 0, defVal); 707 } 708 709 public String getString(String privateCreator, int tag, VR vr) { 710 return getString(privateCreator, tag, vr, 0, null); 711 } 712 713 public String getString(String privateCreator, int tag, VR vr, String defVal) { 714 return getString(privateCreator, tag, vr, 0, defVal); 715 } 716 717 public String getString(String privateCreator, int tag, int valueIndex) { 718 return getString(privateCreator, tag, null, valueIndex, null); 719 } 720 721 public String getString(String privateCreator, int tag, int valueIndex, String defVal) { 722 return getString(privateCreator, tag, null, valueIndex, defVal); 723 } 724 725 public String getString(String privateCreator, int tag, VR vr, int valueIndex) { 726 return getString(privateCreator, tag, vr, valueIndex, null); 727 } 728 729 public String getString(String privateCreator, int tag, VR vr, int valueIndex, String defVal) { 730 int index = indexOf(privateCreator, tag); 731 if (index < 0) 732 return defVal; 733 734 Object value = values[index]; 735 if (value == Value.NULL) 736 return defVal; 737 738 if (vr == null) 739 vr = vrs[index]; 740 else 741 updateVR(index, vr); 742 if (vr.isStringType()) { 743 value = decodeStringValue(index); 744 if (value == Value.NULL) 745 return defVal; 746 } 747 748 try { 749 return vr.toString(value, bigEndian, valueIndex, defVal); 750 } catch (UnsupportedOperationException e) { 751 LOG.info("Attempt to access {} {} as string", TagUtils.toString(tag), vr); 752 return defVal; 753 } 754 } 755 756 public String[] getStrings(int tag) { 757 return getStrings(null, tag, null); 758 } 759 760 public String[] getStrings(String privateCreator, int tag) { 761 return getStrings(privateCreator, tag, null); 762 } 763 764 public String[] getStrings(String privateCreator, int tag, VR vr) { 765 int index = indexOf(privateCreator, tag); 766 if (index < 0) 767 return null; 768 769 Object value = values[index]; 770 if (value == Value.NULL) 771 return StringUtils.EMPTY_STRING; 772 773 if (vr == null) 774 vr = vrs[index]; 775 else 776 updateVR(index, vr); 777 if (vr.isStringType()) { 778 value = decodeStringValue(index); 779 if (value == Value.NULL) 780 return StringUtils.EMPTY_STRING; 781 } 782 try { 783 return toStrings(vr.toStrings(value, bigEndian, 784 getSpecificCharacterSet(vr))); 785 } catch (UnsupportedOperationException e) { 786 LOG.info("Attempt to access {} {} as string", TagUtils.toString(tag), vr); 787 return null; 788 } 789 } 790 791 private static String[] toStrings(Object val) { 792 return (val instanceof String) 793 ? new String[] { (String) val } 794 : (String[]) val; 795 } 796 797 public int getInt(int tag, int defVal) { 798 return getInt(null, tag, null, 0, defVal); 799 } 800 801 public int getInt(int tag, int valueIndex, int defVal) { 802 return getInt(null, tag, null, valueIndex, defVal); 803 } 804 805 public int getInt(String privateCreator, int tag, int defVal) { 806 return getInt(privateCreator, tag, null, 0, defVal); 807 } 808 809 public int getInt(String privateCreator, int tag, VR vr, int defVal) { 810 return getInt(privateCreator, tag, vr, 0, defVal); 811 } 812 813 public int getInt(String privateCreator, int tag, int valueIndex, int defVal) { 814 return getInt(privateCreator, tag, null, valueIndex, defVal); 815 } 816 817 public int getInt(String privateCreator, int tag, VR vr, int valueIndex, int defVal) { 818 int index = indexOf(privateCreator, tag); 819 if (index < 0) 820 return defVal; 821 822 Object value = values[index]; 823 if (value == Value.NULL) 824 return defVal; 825 826 if (vr == null) 827 vr = vrs[index]; 828 else 829 updateVR(index, vr); 830 if (vr == VR.IS) 831 value = decodeISValue(index); 832 833 try { 834 return vr.toInt(value, bigEndian, valueIndex, defVal); 835 } catch (UnsupportedOperationException e) { 836 LOG.info("Attempt to access {} {} as int", TagUtils.toString(tag), vr); 837 return defVal; 838 } catch (IllegalArgumentException e) { 839 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 840 return defVal; 841 } 842 } 843 844 public int[] getInts(int tag) { 845 return getInts(null, tag, null); 846 } 847 848 public int[] getInts(String privateCreator, int tag) { 849 return getInts(privateCreator, tag, null); 850 } 851 852 public int[] getInts(String privateCreator, int tag, VR vr) { 853 int index = indexOf(privateCreator, tag); 854 if (index < 0) 855 return null; 856 857 Object value = values[index]; 858 if (value == Value.NULL) 859 return ByteUtils.EMPTY_INTS; 860 861 if (vr == null) 862 vr = vrs[index]; 863 else 864 updateVR(index, vr); 865 if (vr == VR.IS) 866 value = decodeISValue(index); 867 868 try { 869 return vr.toInts(value, bigEndian); 870 } catch (UnsupportedOperationException e) { 871 LOG.info("Attempt to access {} {} as int", TagUtils.toString(tag), vr); 872 return null; 873 } catch (IllegalArgumentException e) { 874 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 875 return null; 876 } 877 } 878 879 public float getFloat(int tag, float defVal) { 880 return getFloat(null, tag, null, 0, defVal); 881 } 882 883 public float getFloat(int tag, int valueIndex, float defVal) { 884 return getFloat(null, tag, null, valueIndex, defVal); 885 } 886 887 public float getFloat(String privateCreator, int tag, float defVal) { 888 return getFloat(privateCreator, tag, null, 0, defVal); 889 } 890 891 public float getFloat(String privateCreator, int tag, VR vr, float defVal) { 892 return getFloat(privateCreator, tag, vr, 0, defVal); 893 } 894 895 public float getFloat(String privateCreator, int tag, int valueIndex, float defVal) { 896 return getFloat(privateCreator, tag, null, valueIndex, defVal); 897 } 898 899 public float getFloat(String privateCreator, int tag, VR vr, int valueIndex, float defVal) { 900 int index = indexOf(privateCreator, tag); 901 if (index < 0) 902 return defVal; 903 904 Object value = values[index]; 905 if (value == Value.NULL) 906 return defVal; 907 908 if (vr == null) 909 vr = vrs[index]; 910 else 911 updateVR(index, vr); 912 if (vr == VR.DS) 913 value = decodeDSValue(index); 914 915 try { 916 return vr.toFloat(value, bigEndian, valueIndex, defVal); 917 } catch (UnsupportedOperationException e) { 918 LOG.info("Attempt to access {} {} as float", TagUtils.toString(tag), vr); 919 return defVal; 920 } catch (IllegalArgumentException e) { 921 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 922 return defVal; 923 } 924 } 925 926 public float[] getFloats(int tag) { 927 return getFloats(null, tag, null); 928 } 929 930 public float[] getFloats(String privateCreator, int tag) { 931 return getFloats(privateCreator, tag, null); 932 } 933 934 public float[] getFloats(String privateCreator, int tag, VR vr) { 935 int index = indexOf(privateCreator, tag); 936 if (index < 0) 937 return null; 938 939 Object value = values[index]; 940 if (value == Value.NULL) 941 return ByteUtils.EMPTY_FLOATS; 942 943 if (vr == null) 944 vr = vrs[index]; 945 else 946 updateVR(index, vr); 947 if (vr == VR.DS) 948 value = decodeDSValue(index); 949 950 try { 951 return vr.toFloats(value, bigEndian); 952 } catch (UnsupportedOperationException e) { 953 LOG.info("Attempt to access {} {} as float", TagUtils.toString(tag), vr); 954 return null; 955 } catch (IllegalArgumentException e) { 956 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 957 return null; 958 } 959 } 960 961 public double getDouble(int tag, double defVal) { 962 return getDouble(null, tag, null, 0, defVal); 963 } 964 965 public double getDouble(int tag, int valueIndex, double defVal) { 966 return getDouble(null, tag, null, valueIndex, defVal); 967 } 968 969 public double getDouble(String privateCreator, int tag, double defVal) { 970 return getDouble(privateCreator, tag, null, 0, defVal); 971 } 972 973 public double getDouble(String privateCreator, int tag, VR vr, double defVal) { 974 return getDouble(privateCreator, tag, vr, 0, defVal); 975 } 976 977 public double getDouble(String privateCreator, int tag, int valueIndex, double defVal) { 978 return getDouble(privateCreator, tag, null, valueIndex, defVal); 979 } 980 981 public double getDouble(String privateCreator, int tag, VR vr, int valueIndex, double defVal) { 982 int index = indexOf(privateCreator, tag); 983 if (index < 0) 984 return defVal; 985 986 Object value = values[index]; 987 if (value == Value.NULL) 988 return defVal; 989 990 if (vr == null) 991 vr = vrs[index]; 992 else 993 updateVR(index, vr); 994 if (vr == VR.DS) 995 value = decodeDSValue(index); 996 997 try { 998 return vr.toDouble(value, bigEndian, valueIndex, defVal); 999 } catch (UnsupportedOperationException e) { 1000 LOG.info("Attempt to access {} {} as double", TagUtils.toString(tag), vr); 1001 return defVal; 1002 } catch (IllegalArgumentException e) { 1003 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 1004 return defVal; 1005 } 1006 } 1007 1008 public double[] getDoubles(int tag) { 1009 return getDoubles(null, tag, null); 1010 } 1011 1012 public double[] getDoubles(String privateCreator, int tag) { 1013 return getDoubles(privateCreator, tag, null); 1014 } 1015 1016 public double[] getDoubles(String privateCreator, int tag, VR vr) { 1017 int index = indexOf(privateCreator, tag); 1018 if (index < 0) 1019 return null; 1020 1021 Object value = values[index]; 1022 if (value == Value.NULL) 1023 return ByteUtils.EMPTY_DOUBLES; 1024 1025 if (vr == null) 1026 vr = vrs[index]; 1027 else 1028 updateVR(index, vr); 1029 if (vr == VR.DS) 1030 value = decodeDSValue(index); 1031 try { 1032 return vr.toDoubles(value, bigEndian); 1033 } catch (UnsupportedOperationException e) { 1034 LOG.info("Attempt to access {} {} as double", TagUtils.toString(tag), vr); 1035 return null; 1036 } catch (IllegalArgumentException e) { 1037 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 1038 return null; 1039 } 1040 } 1041 1042 public Date getDate(int tag) { 1043 return getDate(null, tag, null, 0, null, new DatePrecision()); 1044 } 1045 1046 public Date getDate(int tag, DatePrecision precision) { 1047 return getDate(null, tag, null, 0, null, precision); 1048 } 1049 1050 public Date getDate(int tag, Date defVal) { 1051 return getDate(null, tag, null, 0, defVal, new DatePrecision()); 1052 } 1053 1054 public Date getDate(int tag, Date defVal, DatePrecision precision) { 1055 return getDate(null, tag, null, 0, defVal, precision); 1056 } 1057 1058 public Date getDate(int tag, int valueIndex) { 1059 return getDate(null, tag, null, valueIndex, null, new DatePrecision()); 1060 } 1061 1062 public Date getDate(int tag, int valueIndex, DatePrecision precision) { 1063 return getDate(null, tag, null, valueIndex, null, precision); 1064 } 1065 1066 public Date getDate(int tag, int valueIndex, Date defVal) { 1067 return getDate(null, tag, null, valueIndex, defVal, new DatePrecision()); 1068 } 1069 1070 public Date getDate(int tag, int valueIndex, Date defVal, 1071 DatePrecision precision) { 1072 return getDate(null, tag, null, valueIndex, defVal, precision); 1073 } 1074 1075 public Date getDate(String privateCreator, int tag) { 1076 return getDate(privateCreator, tag, null, 0, null, new DatePrecision()); 1077 } 1078 1079 public Date getDate(String privateCreator, int tag, DatePrecision precision) { 1080 return getDate(privateCreator, tag, null, 0, null, precision); 1081 } 1082 1083 public Date getDate(String privateCreator, int tag, Date defVal, 1084 DatePrecision precision) { 1085 return getDate(privateCreator, tag, null, 0, defVal, precision); 1086 } 1087 1088 public Date getDate(String privateCreator, int tag, VR vr) { 1089 return getDate(privateCreator, tag, vr, 0, null, new DatePrecision()); 1090 } 1091 1092 public Date getDate(String privateCreator, int tag, VR vr, 1093 DatePrecision precision) { 1094 return getDate(privateCreator, tag, vr, 0, null, precision); 1095 } 1096 1097 public Date getDate(String privateCreator, int tag, VR vr, Date defVal) { 1098 return getDate(privateCreator, tag, vr, 0, defVal, new DatePrecision()); 1099 } 1100 1101 public Date getDate(String privateCreator, int tag, VR vr, Date defVal, 1102 DatePrecision precision) { 1103 return getDate(privateCreator, tag, vr, 0, defVal, precision); 1104 } 1105 1106 public Date getDate(String privateCreator, int tag, int valueIndex) { 1107 return getDate(privateCreator, tag, null, valueIndex, null, 1108 new DatePrecision()); 1109 } 1110 1111 public Date getDate(String privateCreator, int tag, int valueIndex, 1112 DatePrecision precision) { 1113 return getDate(privateCreator, tag, null, valueIndex, null, precision); 1114 } 1115 1116 public Date getDate(String privateCreator, int tag, int valueIndex, 1117 Date defVal) { 1118 return getDate(privateCreator, tag, null, valueIndex, defVal, 1119 new DatePrecision()); 1120 } 1121 1122 public Date getDate(String privateCreator, int tag, int valueIndex, 1123 Date defVal, DatePrecision precision) { 1124 return getDate(privateCreator, tag, null, valueIndex, defVal, precision); 1125 } 1126 1127 public Date getDate(String privateCreator, int tag, VR vr, int valueIndex) { 1128 return getDate(privateCreator, tag, vr, valueIndex, null, 1129 new DatePrecision()); 1130 } 1131 1132 public Date getDate(String privateCreator, int tag, VR vr, int valueIndex, 1133 DatePrecision precision) { 1134 return getDate(privateCreator, tag, vr, valueIndex, null, precision); 1135 } 1136 1137 public Date getDate(String privateCreator, int tag, VR vr, int valueIndex, 1138 Date defVal) { 1139 return getDate(privateCreator, tag, vr, valueIndex, defVal, 1140 new DatePrecision()); 1141 } 1142 1143 public Date getDate(String privateCreator, int tag, VR vr, int valueIndex, 1144 Date defVal, DatePrecision precision) { 1145 int index = indexOf(privateCreator, tag); 1146 if (index < 0) 1147 return defVal; 1148 1149 Object value = values[index]; 1150 if (value == Value.NULL) 1151 return defVal; 1152 1153 if (vr == null) 1154 vr = vrs[index]; 1155 else 1156 updateVR(index, vr); 1157 if (!vr.isTemporalType()) { 1158 LOG.info("Attempt to access {} {} as date", TagUtils.toString(tag), vr); 1159 return defVal; 1160 } 1161 try { 1162 value = decodeStringValue(index); 1163 if (value == Value.NULL) 1164 return defVal; 1165 1166 return vr.toDate(value, getTimeZone(), valueIndex, false, defVal, precision); 1167 } catch (IllegalArgumentException e) { 1168 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 1169 return defVal; 1170 } 1171 } 1172 1173 public Date getDate(long tag) { 1174 return getDate(null, tag, null, new DatePrecision()); 1175 } 1176 1177 public Date getDate(long tag, DatePrecision precision) { 1178 return getDate(null, tag, null, precision); 1179 } 1180 1181 public Date getDate(long tag, Date defVal) { 1182 return getDate(null, tag, defVal, new DatePrecision()); 1183 } 1184 1185 public Date getDate(long tag, Date defVal, DatePrecision precision) { 1186 return getDate(null, tag, defVal, precision); 1187 } 1188 1189 public Date getDate(String privateCreator, long tag) { 1190 return getDate(privateCreator, tag, null, new DatePrecision()); 1191 } 1192 1193 public Date getDate(String privateCreator, long tag, DatePrecision precision) { 1194 return getDate(privateCreator, tag, null, precision); 1195 } 1196 1197 public Date getDate(String privateCreator, long tag, Date defVal) { 1198 return getDate(privateCreator, tag, defVal, new DatePrecision()); 1199 } 1200 1201 public Date getDate(String privateCreator, long tag, Date defVal, 1202 DatePrecision precision) { 1203 int daTag = (int) (tag >>> 32); 1204 int tmTag = (int) tag; 1205 1206 String tm = getString(privateCreator, tmTag, VR.TM, null); 1207 if (tm == null) 1208 return getDate(daTag, defVal, precision); 1209 1210 String da = getString(privateCreator, daTag, VR.DA, null); 1211 if (da == null) 1212 return defVal; 1213 try { 1214 return VR.DT.toDate(da + tm, getTimeZone(), 0, false, null, 1215 precision); 1216 } catch (IllegalArgumentException e) { 1217 LOG.info("Invalid value of {} DA or {} TM", 1218 TagUtils.toString(daTag), 1219 TagUtils.toString(tmTag)); 1220 return defVal; 1221 } 1222 } 1223 1224 public Date[] getDates(int tag) { 1225 return getDates(null, tag, null, new DatePrecisions()); 1226 } 1227 1228 public Date[] getDates(int tag, DatePrecisions precisions) { 1229 return getDates(null, tag, null, precisions); 1230 } 1231 1232 public Date[] getDates(String privateCreator, int tag) { 1233 return getDates(privateCreator, tag, null, new DatePrecisions()); 1234 } 1235 1236 public Date[] getDates(String privateCreator, int tag, 1237 DatePrecisions precisions) { 1238 return getDates(privateCreator, tag, null, precisions); 1239 } 1240 1241 public Date[] getDates(String privateCreator, int tag, VR vr) { 1242 return getDates(privateCreator, tag, vr, new DatePrecisions()); 1243 } 1244 1245 public Date[] getDates(String privateCreator, int tag, VR vr, 1246 DatePrecisions precisions) { 1247 int index = indexOf(privateCreator, tag); 1248 if (index < 0) 1249 return null; 1250 1251 Object value = values[index]; 1252 if (value == Value.NULL) 1253 return DateUtils.EMPTY_DATES; 1254 1255 if (vr == null) 1256 vr = vrs[index]; 1257 else 1258 updateVR(index, vr); 1259 if (!vr.isTemporalType()) { 1260 LOG.info("Attempt to access {} {} as date", TagUtils.toString(tag), vr); 1261 return DateUtils.EMPTY_DATES; 1262 } 1263 try { 1264 value = decodeStringValue(index); 1265 if (value == Value.NULL) 1266 return DateUtils.EMPTY_DATES; 1267 1268 return vr.toDates(value, getTimeZone(), false, precisions); 1269 } catch (IllegalArgumentException e) { 1270 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 1271 return DateUtils.EMPTY_DATES; 1272 } 1273 } 1274 1275 public Date[] getDates(long tag) { 1276 return getDates(null, tag, new DatePrecisions()); 1277 } 1278 1279 public Date[] getDates(long tag, DatePrecisions precisions) { 1280 return getDates(null, tag, precisions); 1281 } 1282 1283 public Date[] getDates(String privateCreator, long tag) { 1284 return getDates(privateCreator, tag, new DatePrecisions()); 1285 } 1286 1287 public Date[] getDates(String privateCreator, long tag, 1288 DatePrecisions precisions) { 1289 int daTag = (int) (tag >>> 32); 1290 int tmTag = (int) tag; 1291 1292 String[] tm = getStrings(privateCreator, tmTag); 1293 if (tm == null || tm.length == 0) 1294 return getDates(daTag, precisions); 1295 1296 String[] da = getStrings(privateCreator, daTag); 1297 if (da == null || da.length == 0) 1298 return DateUtils.EMPTY_DATES; 1299 1300 Date[] dates = new Date[da.length]; 1301 precisions.precisions = new DatePrecision[da.length]; 1302 int i = 0; 1303 try { 1304 TimeZone tz = getTimeZone(); 1305 while (i < tm.length) 1306 dates[i++] = VR.DT.toDate(da[i] + tm[i], tz, 0, false, null, 1307 precisions.precisions[i] = new DatePrecision()); 1308 while (i < da.length) 1309 dates[i++] = VR.DA.toDate(da[i], tz, 0, false, null, 1310 precisions.precisions[i] = new DatePrecision()); 1311 } catch (IllegalArgumentException e) { 1312 LOG.info("Invalid value of {} DA or {} TM", 1313 TagUtils.toString(daTag), 1314 TagUtils.toString(tmTag)); 1315 dates = Arrays.copyOf(dates, i); 1316 } 1317 return dates; 1318 } 1319 1320 public DateRange getDateRange(int tag) { 1321 return getDateRange(null, tag, null, null); 1322 } 1323 1324 public DateRange getDateRange(int tag, DateRange defVal) { 1325 return getDateRange(null, tag, null, defVal); 1326 } 1327 1328 public DateRange getDateRange(String privateCreator, int tag) { 1329 return getDateRange(privateCreator, tag, null, null); 1330 } 1331 1332 public DateRange getDateRange(String privateCreator, int tag, DateRange defVal) { 1333 return getDateRange(privateCreator, tag, null, defVal); 1334 } 1335 1336 public DateRange getDateRange(String privateCreator, int tag, VR vr) { 1337 return getDateRange(privateCreator, tag, vr, null); 1338 } 1339 1340 public DateRange getDateRange(String privateCreator, int tag, VR vr, DateRange defVal) { 1341 int index = indexOf(privateCreator, tag); 1342 if (index < 0) 1343 return defVal; 1344 1345 Object value = values[index]; 1346 if (value == Value.NULL) 1347 return defVal; 1348 1349 if (vr == null) 1350 vr = vrs[index]; 1351 else 1352 updateVR(index, vr); 1353 if (!vr.isTemporalType()) { 1354 LOG.info("Attempt to access {} {} as date", TagUtils.toString(tag), vr); 1355 return defVal; 1356 } 1357 value = decodeStringValue(index); 1358 if (value == Value.NULL) 1359 return defVal; 1360 1361 try { 1362 return toDateRange((value instanceof String) 1363 ? (String) value : ((String[]) value)[0], vr); 1364 } catch (IllegalArgumentException e) { 1365 LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr); 1366 return defVal; 1367 } 1368 } 1369 1370 private DateRange toDateRange(String s, VR vr) { 1371 String[] range = splitRange(s); 1372 TimeZone tz = getTimeZone(); 1373 DatePrecision precision = new DatePrecision(); 1374 Date start = range[0] == null ? null 1375 : vr.toDate(range[0], tz, 0, false, null, precision); 1376 Date end = range[1] == null ? null 1377 : vr.toDate(range[1], tz, 0, true, null, precision); 1378 return new DateRange(start, end); 1379 } 1380 1381 private static String[] splitRange(String s) { 1382 String[] range = new String[2]; 1383 int delim = s.indexOf('-'); 1384 if (delim == -1) 1385 range[0] = range[1] = s; 1386 else { 1387 if (delim > 0) 1388 range[0] = s.substring(0, delim); 1389 if (delim < s.length() - 1) 1390 range[1] = s.substring(delim+1); 1391 } 1392 return range; 1393 } 1394 1395 public DateRange getDateRange(long tag) { 1396 return getDateRange(null, tag, null); 1397 } 1398 1399 public DateRange getDateRange(long tag, DateRange defVal) { 1400 return getDateRange(null, tag, defVal); 1401 } 1402 1403 public DateRange getDateRange(String privateCreator, long tag) { 1404 return getDateRange(privateCreator, tag, null); 1405 } 1406 1407 public DateRange getDateRange(String privateCreator, long tag, DateRange defVal) { 1408 int daTag = (int) (tag >>> 32); 1409 int tmTag = (int) tag; 1410 1411 String tm = getString(privateCreator, tmTag, VR.TM, null); 1412 if (tm == null) 1413 return getDateRange(daTag, defVal); 1414 1415 String da = getString(privateCreator, daTag, VR.DA, null); 1416 if (da == null) 1417 return defVal; 1418 1419 try { 1420 return toDateRange(da, tm); 1421 } catch (IllegalArgumentException e) { 1422 LOG.info("Invalid value of {} TM", TagUtils.toString((int) tag)); 1423 return defVal; 1424 } 1425 } 1426 1427 private DateRange toDateRange(String da, String tm) { 1428 String[] darange = splitRange(da); 1429 String[] tmrange = splitRange(tm); 1430 DatePrecision precision = new DatePrecision(); 1431 TimeZone tz = getTimeZone(); 1432 return new DateRange( 1433 darange[0] == null ? null 1434 : VR.DT.toDate(tmrange[0] == null 1435 ? darange[0] 1436 : darange[0] + tmrange[0], 1437 tz, 0, false, null, precision ), 1438 darange[1] == null ? null 1439 : VR.DT.toDate(tmrange[1] == null 1440 ? darange[1] 1441 : darange[1] + tmrange[1], 1442 tz, 0, true, null, precision)); 1443 } 1444 1445 /** 1446 * Set Specific Character Set (0008,0005) to specified code(s) and 1447 * re-encode contained LO, LT, PN, SH, ST, UT attributes 1448 * accordingly. 1449 * 1450 * @param codes new value(s) of Specific Character Set (0008,0005) 1451 */ 1452 public void setSpecificCharacterSet(String... codes) { 1453 decodeStringValuesUsingSpecificCharacterSet(); 1454 setString(Tag.SpecificCharacterSet, VR.CS, codes); 1455 } 1456 1457 public SpecificCharacterSet getSpecificCharacterSet() { 1458 if (cs != null) 1459 return cs; 1460 1461 if (containsSpecificCharacterSet) 1462 cs = SpecificCharacterSet.valueOf( 1463 getStrings(null, Tag.SpecificCharacterSet, VR.CS)); 1464 else if (parent != null) 1465 return parent.getSpecificCharacterSet(); 1466 else 1467 cs = SpecificCharacterSet.getDefaultCharacterSet(); 1468 1469 return cs; 1470 } 1471 1472 public boolean containsTimezoneOffsetFromUTC() { 1473 return containsTimezoneOffsetFromUTC; 1474 } 1475 1476 public void setDefaultTimeZone(TimeZone tz) { 1477 defaultTimeZone = tz; 1478 } 1479 1480 public TimeZone getDefaultTimeZone() { 1481 if (defaultTimeZone != null) 1482 return defaultTimeZone; 1483 1484 if (parent != null) 1485 return parent.getDefaultTimeZone(); 1486 1487 return TimeZone.getDefault(); 1488 } 1489 1490 public TimeZone getTimeZone() { 1491 if (tz != null) 1492 return tz; 1493 1494 if (containsTimezoneOffsetFromUTC) { 1495 String s = getString(Tag.TimezoneOffsetFromUTC); 1496 if (s != null) 1497 try { 1498 tz = DateUtils.timeZone(s); 1499 } catch (IllegalArgumentException e) { 1500 LOG.info(e.getMessage()); 1501 } 1502 } else if (parent != null) 1503 return parent.getTimeZone(); 1504 else 1505 tz = getDefaultTimeZone(); 1506 1507 return tz; 1508 } 1509 1510 /** 1511 * Set Timezone Offset From UTC (0008,0201) to specified value and 1512 * adjust contained DA, DT and TM attributs accordingly 1513 * 1514 * @param utcOffset offset from UTC as (+|-)HHMM 1515 */ 1516 public void setTimezoneOffsetFromUTC(String utcOffset) { 1517 TimeZone tz = DateUtils.timeZone(utcOffset); 1518 updateTimezone(getTimeZone(), tz); 1519 setString(Tag.TimezoneOffsetFromUTC, VR.SH, utcOffset); 1520 this.tz = tz; 1521 } 1522 1523 /** 1524 * Set the Default Time Zone to specified value and adjust contained DA, 1525 * DT and TM attributs accordingly. If the Time Zone does not use Daylight 1526 * Saving Time, attribute Timezone Offset From UTC (0008,0201) will be also 1527 * set accordingly. If the Time zone uses Daylight Saving Time, a previous 1528 * existing attribute Timezone Offset From UTC (0008,0201) will be removed. 1529 * 1530 * @param tz Time Zone 1531 * 1532 * @see #setDefaultTimeZone(TimeZone) 1533 * @see #setTimezoneOffsetFromUTC(String) 1534 */ 1535 public void setTimezone(TimeZone tz) { 1536 updateTimezone(getTimeZone(), tz); 1537 if (tz.useDaylightTime()) { 1538 remove(Tag.TimezoneOffsetFromUTC); 1539 setDefaultTimeZone(tz); 1540 } else { 1541 setString(Tag.TimezoneOffsetFromUTC, VR.SH, 1542 DateUtils.formatTimezoneOffsetFromUTC(tz)); 1543 } 1544 this.tz = tz; 1545 } 1546 1547 /** 1548 * Updates the time zone of a specific standard or private tag 1549 * 1550 * @param from Time Zone from 1551 * @param to Time Zone to 1552 * @param privateCreator private creator - null otherwise 1553 * @param tag Attribute tag to update time zone 1554 */ 1555 public void updateTimeZoneOfSpecificTag(TimeZone from, TimeZone to 1556 , String privateCreator, int tag) { 1557 1558 updateTimezone(from, to,indexOf(privateCreator, tag)); 1559 } 1560 1561 private void updateTimezone(TimeZone from, TimeZone to) { 1562 if (from.hasSameRules(to)) 1563 return; 1564 1565 for (int i = 0; i < size; i++) { 1566 Object val = values[i]; 1567 if (val instanceof Sequence) { 1568 Sequence new_name = (Sequence) val; 1569 for (Attributes item : new_name) { 1570 item.updateTimezone(item.getTimeZone(), to); 1571 item.remove(Tag.TimezoneOffsetFromUTC); 1572 } 1573 } else if (vrs[i] == VR.TM && tags[i] != Tag.PatientBirthTime 1574 || vrs[i] == VR.DT && tags[i] != Tag.ContextGroupVersion 1575 && tags[i] != Tag.ContextGroupLocalVersion) 1576 updateTimezone(from, to, i); 1577 } 1578 } 1579 1580 private void updateTimezone(TimeZone from, TimeZone to, int tmIndex) { 1581 Object tm = decodeStringValue(tmIndex); 1582 if (tm == Value.NULL) 1583 return; 1584 1585 int tmTag = tags[tmIndex]; 1586 if (vrs[tmIndex] == VR.DT) { 1587 if (tm instanceof String[]) { 1588 String[] tms = (String[]) tm; 1589 for (int i = 0; i < tms.length; i++) { 1590 tms[i] = updateTimeZoneDT(from, to, tms[i]); 1591 } 1592 } else 1593 values[tmIndex] = updateTimeZoneDT(from, to, (String) tm); 1594 } else { 1595 int daTag = ElementDictionary.getElementDictionary(privateCreatorOf(tmTag)).daTagOf(tmTag); 1596 int daIndex = daTag != 0 ? indexOf(daTag) : -1; 1597 Object da = daIndex >= 0 ? decodeStringValue(daIndex) : Value.NULL; 1598 1599 if (tm instanceof String[]) { 1600 String[] tms = (String[]) tm; 1601 if (da instanceof String[]) { 1602 String[] das = (String[]) da; 1603 for (int i = 0; i < tms.length; i++) { 1604 if (i < das.length) { 1605 String dt = updateTimeZoneDT( 1606 from, to, das[i] + tms[i]); 1607 das[i] = dt.substring(0,8); 1608 tms[i] = dt.substring(8); 1609 } else { 1610 tms[i] = updateTimeZoneTM(from, to, tms[i]); 1611 } 1612 } 1613 } else { 1614 if (da == Value.NULL) { 1615 tms[0] = updateTimeZoneTM(from, to, tms[0]); 1616 } else { 1617 String dt = updateTimeZoneDT( 1618 from, to, (String) da + tms[0]); 1619 values[daIndex] = dt.substring(0,8); 1620 tms[0] = dt.substring(8); 1621 } 1622 for (int i = 1; i < tms.length; i++) { 1623 tms[i] = updateTimeZoneTM(from, to, tms[i]); 1624 } 1625 } 1626 } else { 1627 if (da instanceof String[]) { 1628 String[] das = (String[]) da; 1629 String dt = updateTimeZoneDT( 1630 from, to, das[0] + (String) tm); 1631 das[0] = dt.substring(0,8); 1632 values[tmIndex] = dt.substring(8); 1633 } else { 1634 String[] tmRange = null; 1635 if (isRange((String) tm)) { 1636 tmRange = splitRange((String) tm); 1637 if (tmRange[0] == null) 1638 tmRange[0] = "000000.000"; 1639 if (tmRange[1] == null) 1640 tmRange[1] = "235959.999"; 1641 } 1642 if (da == Value.NULL) { 1643 if (tmRange != null) { 1644 tmRange[0] = updateTimeZoneTM( 1645 from, to, tmRange[0]); 1646 tmRange[1] = updateTimeZoneTM( 1647 from, to, tmRange[1]); 1648 values[tmIndex] = toDateRangeString( 1649 tmRange[0], tmRange[1]); 1650 } else { 1651 values[tmIndex] = updateTimeZoneTM( 1652 from, to, (String) tm); 1653 } 1654 } else { 1655 if (tmRange != null) { 1656 String[] daRange = splitRange((String) da); 1657 if (daRange[0] == null) { 1658 daRange[0] = ""; 1659 tmRange[0] = updateTimeZoneTM(from, to, tmRange[0]); 1660 } else { 1661 String dt = updateTimeZoneDT( 1662 from, to, daRange[0] + tmRange[0]); 1663 daRange[0] = dt.substring(0,8); 1664 tmRange[0] = dt.substring(8); 1665 } 1666 if (daRange[1] == null) { 1667 daRange[1] = ""; 1668 tmRange[1] = updateTimeZoneTM(from, to, tmRange[1]); 1669 } else { 1670 String dt = updateTimeZoneDT( 1671 from, to, daRange[1] + tmRange[1]); 1672 daRange[1] = dt.substring(0,8); 1673 tmRange[1] = dt.substring(8); 1674 } 1675 values[daIndex] = toDateRangeString( 1676 daRange[0], daRange[1]); 1677 values[tmIndex] = toDateRangeString( 1678 tmRange[0], tmRange[1]); 1679 } else { 1680 String dt = updateTimeZoneDT( 1681 from, to, (String) da + (String) tm); 1682 values[daIndex] = dt.substring(0,8); 1683 values[tmIndex] = dt.substring(8); 1684 } 1685 } 1686 } 1687 } 1688 } 1689 } 1690 1691 private static boolean isRange(String s) { 1692 return s.indexOf('-') >= 0; 1693 } 1694 1695 private String updateTimeZoneDT(TimeZone from, TimeZone to, String dt) { 1696 int dtlen = dt.length(); 1697 if (dtlen > 8) { 1698 char ch = dt.charAt(dtlen-5); 1699 if (ch == '+' || ch == '-') 1700 return dt; 1701 } 1702 try { 1703 DatePrecision precision = new DatePrecision(); 1704 Date date = DateUtils.parseDT(from, dt, false, precision); 1705 dt = DateUtils.formatDT(to, date, precision); 1706 } catch (IllegalArgumentException e) { 1707 // ignored 1708 } 1709 return dt; 1710 } 1711 1712 private String updateTimeZoneTM(TimeZone from, TimeZone to, String tm) { 1713 try { 1714 DatePrecision precision = new DatePrecision(); 1715 Date date = DateUtils.parseTM(from, tm, false, precision); 1716 tm = DateUtils.formatTM(to, date, precision); 1717 } catch (IllegalArgumentException e) { 1718 // ignored 1719 } 1720 return tm; 1721 } 1722 1723 public String getPrivateCreator(int tag) { 1724 return TagUtils.isPrivateTag(tag) 1725 ? getString(TagUtils.creatorTagOf(tag), null) 1726 : null; 1727 } 1728 1729 public Object remove(int tag) { 1730 return remove(null, tag); 1731 } 1732 1733 public Object remove(String privateCreator, int tag) { 1734 int index = indexOf(privateCreator, tag); 1735 if (index < 0) 1736 return null; 1737 1738 Object value = values[index]; 1739// if (value instanceof Sequence) 1740// ((Sequence) value).clear(); 1741 1742 int numMoved = size - index - 1; 1743 if (numMoved > 0) { 1744 System.arraycopy(tags, index+1, tags, index, numMoved); 1745 System.arraycopy(vrs, index+1, vrs, index, numMoved); 1746 System.arraycopy(values, index+1, values, index, numMoved); 1747 } 1748 values[--size] = null; 1749 1750 if (tag == Tag.SpecificCharacterSet) { 1751 containsSpecificCharacterSet = false; 1752 cs = null; 1753 } else if (tag == Tag.TimezoneOffsetFromUTC) { 1754 containsTimezoneOffsetFromUTC = false; 1755 tz = null; 1756 } 1757 1758 return value; 1759 } 1760 1761 public Object setNull(int tag, VR vr) { 1762 return setNull(null, tag, vr); 1763 } 1764 1765 public Object setNull(String privateCreator, int tag, VR vr) { 1766 return set(privateCreator, tag, vr, Value.NULL); 1767 } 1768 1769 public Object setBytes(int tag, VR vr, byte[] b) { 1770 return setBytes(null, tag, vr, b); 1771 } 1772 1773 public Object setBytes(String privateCreator, int tag, VR vr, byte[] b) { 1774 return set(privateCreator, tag, vr, vr.toValue(b)); 1775 } 1776 1777 public Object setString(int tag, VR vr, String s) { 1778 return setString(null, tag, vr, s); 1779 } 1780 1781 public Object setString(String privateCreator, int tag, VR vr, String s) { 1782 return set(privateCreator, tag, vr, vr.toValue(s, bigEndian)); 1783 } 1784 1785 public Object setString(int tag, VR vr, String... ss) { 1786 return setString(null, tag, vr, ss); 1787 } 1788 1789 public Object setString(String privateCreator, int tag, VR vr, String... ss) { 1790 return set(privateCreator, tag, vr, vr.toValue(ss, bigEndian)); 1791 } 1792 1793 public Object setInt(int tag, VR vr, int... is) { 1794 return setInt(null, tag, vr, is); 1795 } 1796 1797 public Object setInt(String privateCreator, int tag, VR vr, int... is) { 1798 return set(privateCreator, tag, vr, vr.toValue(is, bigEndian)); 1799 } 1800 1801 public Object setFloat(int tag, VR vr, float... fs) { 1802 return setFloat(null, tag, vr, fs); 1803 } 1804 1805 public Object setFloat(String privateCreator, int tag, VR vr, float... fs) { 1806 return set(privateCreator, tag, vr, vr.toValue(fs, bigEndian)); 1807 } 1808 1809 public Object setDouble(int tag, VR vr, double... ds) { 1810 return setDouble(null, tag, vr, ds); 1811 } 1812 1813 public Object setDouble(String privateCreator, int tag, VR vr, double... ds) { 1814 return set(privateCreator, tag, vr, vr.toValue(ds, bigEndian)); 1815 } 1816 1817 public Object setDate(int tag, VR vr, Date... ds) { 1818 return setDate(null, tag, vr, ds); 1819 } 1820 1821 public Object setDate(int tag, VR vr, DatePrecision precision, Date... ds) { 1822 return setDate(null, tag, vr, precision, ds); 1823 } 1824 1825 public Object setDate(String privateCreator, int tag, VR vr, 1826 Date... ds) { 1827 return setDate(privateCreator, tag, vr, new DatePrecision(), ds); 1828 } 1829 1830 public Object setDate(String privateCreator, int tag, VR vr, 1831 DatePrecision precision, Date... ds) { 1832 return set(privateCreator, tag, vr, vr.toValue(ds, getTimeZone(), precision)); 1833 } 1834 1835 public void setDate(long tag, Date dt) { 1836 setDate(null, tag, dt); 1837 } 1838 1839 public void setDate(long tag, DatePrecision precision, Date dt) { 1840 setDate(null, tag, precision, dt); 1841 } 1842 1843 public void setDate(String privateCreator, long tag, Date dt) { 1844 setDate(privateCreator, tag, new DatePrecision(), dt); 1845 } 1846 1847 public void setDate(String privateCreator, long tag, 1848 DatePrecision precision, Date dt) { 1849 int daTag = (int) (tag >>> 32); 1850 int tmTag = (int) tag; 1851 setDate(privateCreator, daTag, VR.DA, precision, dt); 1852 setDate(privateCreator, tmTag, VR.TM, precision, dt); 1853 } 1854 1855 public Object setDateRange(int tag, VR vr, DateRange range) { 1856 return setDateRange(null, tag, vr, range); 1857 } 1858 1859 public Object setDateRange(String privateCreator, int tag, VR vr, DateRange range) { 1860 return set(privateCreator, tag, vr, toString(range, vr, getTimeZone())); 1861 } 1862 1863 private static String toString(DateRange range, VR vr, TimeZone tz) { 1864 DatePrecision precision = new DatePrecision(); 1865 String start = range.getStartDate() != null 1866 ? (String) vr.toValue(new Date[]{range.getStartDate()}, tz, 1867 precision) 1868 : ""; 1869 String end = range.getEndDate() != null 1870 ? (String) vr.toValue(new Date[]{range.getEndDate()}, tz, 1871 precision) 1872 : ""; 1873 return toDateRangeString(start, end); 1874 } 1875 1876 private static String toDateRangeString(String start, String end) { 1877 return start.equals(end) ? start : (start + '-' + end); 1878 } 1879 1880 public void setDateRange(long tag, DateRange dr) { 1881 setDateRange(null, tag, dr); 1882 } 1883 1884 public void setDateRange(String privateCreator, long tag, DateRange range) { 1885 int daTag = (int) (tag >>> 32); 1886 int tmTag = (int) tag; 1887 setDateRange(privateCreator, daTag, VR.DA, range); 1888 setDateRange(privateCreator, tmTag, VR.TM, range); 1889 } 1890 1891 public Object setValue(int tag, VR vr, Object value) { 1892 return setValue(null, tag, vr, value); 1893 } 1894 1895 public Object setValue(String privateCreator, int tag, VR vr, Object value) { 1896 return set(privateCreator, tag, vr, value != null ? value : Value.NULL); 1897 } 1898 1899 public Sequence newSequence(int tag, int initialCapacity) { 1900 return newSequence(null, tag, initialCapacity); 1901 } 1902 1903 public Sequence newSequence(String privateCreator, int tag, int initialCapacity) { 1904 Sequence seq = new Sequence(this, privateCreator, tag, initialCapacity); 1905 set(privateCreator, tag, VR.SQ, seq); 1906 return seq; 1907 } 1908 1909 public Sequence ensureSequence(int tag, int initialCapacity) { 1910 return ensureSequence(null, tag, initialCapacity); 1911 } 1912 1913 public Sequence ensureSequence(String privateCreator, int tag, int initialCapacity) { 1914 if (privateCreator != null) { 1915 int creatorTag = creatorTagOf(privateCreator, tag, true); 1916 tag = TagUtils.toPrivateTag(creatorTag, tag); 1917 } 1918 1919 Sequence seq; 1920 int index = indexOf(tag); 1921 if (index >= 0) { 1922 Object oldValue = values[index]; 1923 if (oldValue instanceof Sequence) 1924 seq = (Sequence) oldValue; 1925 else 1926 values[index] = seq = new Sequence(this, privateCreator, tag, initialCapacity); 1927 } else { 1928 seq = new Sequence(this, privateCreator, tag, initialCapacity); 1929 insert(-index-1, tag, VR.SQ, seq); 1930 } 1931 return seq; 1932 } 1933 1934 1935 public Fragments newFragments(int tag, VR vr, int initialCapacity) { 1936 return newFragments(null, tag, vr, initialCapacity); 1937 } 1938 1939 public Fragments newFragments(String privateCreator, int tag, VR vr, 1940 int initialCapacity) { 1941 Fragments frags = new Fragments(privateCreator, tag, vr, bigEndian, initialCapacity); 1942 set(privateCreator, tag, vr, frags); 1943 return frags; 1944 } 1945 1946 private Object set(String privateCreator, int tag, VR vr, Object value) { 1947 if (vr == null) 1948 throw new NullPointerException("vr"); 1949 1950 if (privateCreator != null) { 1951 int creatorTag = creatorTagOf(privateCreator, tag, true); 1952 tag = TagUtils.toPrivateTag(creatorTag, tag); 1953 } 1954 1955 if (TagUtils.isGroupLength(tag)) 1956 return null; 1957 1958 Object oldValue = set(tag, vr, value); 1959 1960 if (tag == Tag.SpecificCharacterSet) { 1961 containsSpecificCharacterSet = true; 1962 cs = null; 1963 } else if (tag == Tag.TimezoneOffsetFromUTC) { 1964 containsTimezoneOffsetFromUTC = value != Value.NULL; 1965 tz = null; 1966 } 1967 1968 return oldValue; 1969 } 1970 1971 public void addBulkDataReference(String privateCreator, int tag, VR vr, BulkData bulkData, 1972 ItemPointer... itemPointers) { 1973 Sequence seq = ensureSequence(Tag.ReferencedBulkDataSequence, 8); 1974 Attributes item = new Attributes(bigEndian, 7); 1975 seq.add(item); 1976 item.setString(Tag.RetrieveURL, VR.UR, bulkData.uri); 1977 item.setInt(Tag.SelectorAttribute, VR.AT, privateCreator != null ? (tag & 0xffff00ff) : tag); 1978 item.setString(Tag.SelectorAttributeVR, VR.CS, vr.name()); 1979 if (privateCreator != null) 1980 item.setString(Tag.SelectorAttributePrivateCreator, VR.LO, privateCreator); 1981 if (itemPointers.length > 0) { 1982 int[] seqTags = new int[itemPointers.length]; 1983 int[] itemNumbers = new int[itemPointers.length]; 1984 String[] privateCreators = null; 1985 for (int i = 0; i < itemPointers.length; i++) { 1986 ItemPointer ip = itemPointers[i]; 1987 seqTags[i] = ip.privateCreator != null ? (ip.sequenceTag & 0xffff00ff) : ip.sequenceTag; 1988 itemNumbers[i] = ip.itemIndex + 1; 1989 if (ip.privateCreator != null) { 1990 if (privateCreators == null) 1991 privateCreators = new String[itemPointers.length]; 1992 privateCreators[i] = ip.privateCreator; 1993 } 1994 } 1995 item.setInt(Tag.SelectorSequencePointer, VR.AT, seqTags); 1996 if (privateCreators != null) 1997 item.setString(Tag.SelectorSequencePointerPrivateCreator, VR.LO, privateCreators); 1998 item.setInt(Tag.SelectorSequencePointerItems, VR.IS, itemNumbers); 1999 } 2000 item.trimToSize(); 2001 } 2002 2003 private Object set(int tag, VR vr, Object value) { 2004 int index = indexForInsertOf(tag); 2005 if (index >= 0) { 2006 Object oldValue = values[index]; 2007 vrs[index] = vr; 2008 values[index] = value; 2009 return oldValue; 2010 } 2011 insert(-index - 1, tag, vr, value); 2012 return null; 2013 } 2014 2015 private void insert(int index, int tag, VR vr, Object value) { 2016 ensureCapacity(size+1); 2017 int numMoved = size - index; 2018 if (numMoved > 0) { 2019 System.arraycopy(tags, index, tags, index+1, numMoved); 2020 System.arraycopy(vrs, index, vrs, index+1, numMoved); 2021 System.arraycopy(values, index, values, index+1, numMoved); 2022 } 2023 tags[index] = tag; 2024 vrs[index] = vr; 2025 values[index] = value; 2026 size++; 2027 } 2028 2029 2030 public boolean addAll(Attributes other) { 2031 return add(other, null, null, 0, 0, null, false, false, false, null); 2032 } 2033 2034 /** 2035 * Updates this Attributes object with all the attributes of 2036 * the "other" object, applying the same behaviour recursively 2037 * to the items of the Sequences (if the "other" attributes has 2038 * a sequence with only a part of the attributes, only those will 2039 * be updated in the original Sequence). 2040 * 2041 * Note: recursion will be applied only with Sequences containing one 2042 * item and having the original item not null. If this condition 2043 * is not supported, the complete sequence of the "other" Attributes 2044 * will be set to this one, as in addAll(Attributes other) 2045 * 2046 * @param other the other Attributes object 2047 * @return <tt>true</tt> if one ore more attribute are added or 2048 * overwritten with a different value 2049 */ 2050 public boolean updateRecursive (Attributes other) { 2051 2052 boolean toggleEndian = bigEndian != other.bigEndian; 2053 final int otherSize = other.size; 2054 int numAdd = 0; 2055 String privateCreator = null; 2056 int creatorTag = 0; 2057 for (int i = 0; i < otherSize; i++) { 2058 2059 int tag = other.tags[i]; 2060 VR vr = other.vrs[i]; 2061 Object value = other.values[i]; 2062 2063 if (TagUtils.isPrivateCreator(tag)) { 2064 continue; // private creators will be automatically added with the private tags 2065 } 2066 2067 if (TagUtils.isPrivateTag(tag)) { 2068 int tmp = TagUtils.creatorTagOf(tag); 2069 if (creatorTag != tmp) { 2070 creatorTag = tmp; 2071 privateCreator = other.privateCreatorOf(tag); 2072 } 2073 } else { 2074 creatorTag = 0; 2075 privateCreator = null; 2076 } 2077 2078 if (value instanceof Sequence) { 2079 2080 int indexOfOriginalSequence = indexOf(tag); 2081 2082 if (indexOfOriginalSequence < 0 ) { 2083 //Trying to recursively update an empty sequence, fallback to whole copy 2084 set(privateCreator, tag, (Sequence) value, null); 2085 } else { 2086 Sequence original = (Sequence) values[indexOfOriginalSequence]; 2087 2088 if (original.size() == 0) { 2089 //as above, fallback to whole copy 2090 set(privateCreator, tag, (Sequence) value, null); 2091 } 2092 else { 2093 Sequence updated = ((Sequence) value); 2094 2095 if (updated==null || updated.size() == 0) 2096 continue; 2097 2098 if (original.size() > 1 || updated.size()>1) 2099 //Trying to recursively update a sequence with more than 1 item: fallback to whole copy 2100 set(privateCreator, tag, updated, null); 2101 else 2102 //both original and updated sequences have 1 item 2103 original.get(0).updateRecursive(updated.get(0)); 2104 } 2105 } 2106 } else if (value instanceof Fragments) { 2107 set(privateCreator, tag, (Fragments) value); 2108 } else { 2109 set(privateCreator, tag, vr, 2110 toggleEndian(vr, value, toggleEndian)); 2111 } 2112 numAdd++; 2113 } 2114 return numAdd != 0; 2115 } 2116 2117 /** 2118 * Filters this Attributes object returning an Attributes containing 2119 * all the properties found in the selection object and the relative 2120 * ancestors, if any. 2121 * 2122 * Example: 2123 * 2124 * original: 2125 * (0010,0020) LO [PatientID] PatientID 2126 * (0010,0021) LO [IssuerOfPatientID] IssuerOfPatientID 2127 * (0010,1002) SQ [1 Items] OtherPatientIDsSequence 2128 * >Item #1 2129 * >(0010,0020) LO [OtherPatientID] PatientID 2130 * >(0010,0021) LO [OtherIssuerOfPatientID] IssuerOfPatientID 2131 * 2132 * selection: 2133 * (0010,0020) LO [OtherPatientID] PatientID 2134 * 2135 * result: 2136 * (0010,1002) SQ [1 Items] OtherPatientIDsSequence 2137 * >Item #1 2138 * >(0010,0020) LO [OtherPatientID] PatientID 2139 * 2140 * @param selection selection filter 2141 * @return filtered Attributes 2142 */ 2143 public Attributes filter (Attributes selection) { 2144 2145 Attributes filtered = new Attributes(); 2146 for (int tag : tags()) { 2147 if (selection.contains(getPrivateCreator(tag),tag)) { 2148 if (equalValues(selection, indexOf(getPrivateCreator(tag),tag), 2149 selection.indexOf(getPrivateCreator(tag),tag))) 2150 filtered.setValue(getPrivateCreator(tag),tag, getVR(tag), getValue(tag)); 2151 } 2152 Attributes nested; 2153 if (getVR(getPrivateCreator(tag),tag) == VR.SQ && 2154 (nested = getNestedDataset(getPrivateCreator(tag),tag))!=null) { 2155 Attributes seq = nested.filter(selection); 2156 if (seq.size()>0) { 2157 Sequence sequence = filtered.newSequence(getPrivateCreator(tag),tag,seq.size()); 2158 sequence.add(0,seq); 2159 } 2160 } 2161 } 2162 return filtered; 2163 } 2164 2165 public boolean merge(Attributes other) { 2166 return add(other, null, null, 0, 0, null, true, false, false, null); 2167 } 2168 2169 public boolean testMerge(Attributes other) { 2170 return add(other, null, null, 0, 0, null, true, false, true, null); 2171 } 2172 2173 public boolean addSelected(Attributes other, Attributes selection) { 2174 return add(other, null, null, 0, 0, selection, false, false, false, null); 2175 } 2176 2177 public boolean addSelected(Attributes other, String privateCreator, int tag) { 2178 int index = other.indexOf(privateCreator, tag); 2179 if (index < 0) 2180 return false; 2181 Object value = other.values[index]; 2182 if (value instanceof Sequence) { 2183 set(privateCreator, tag, (Sequence) value, null); 2184 } else if (value instanceof Fragments) { 2185 set(privateCreator, tag, (Fragments) value); 2186 } else { 2187 VR vr = other.vrs[index]; 2188 set(privateCreator, tag, vr, 2189 toggleEndian(vr, value, bigEndian != other.bigEndian)); 2190 } 2191 return true; 2192 } 2193 2194 public boolean addWithoutBulkData(Attributes other, BulkDataDescriptor descriptor) { 2195 final boolean toggleEndian = bigEndian != other.bigEndian; 2196 final int[] tags = other.tags; 2197 final VR[] srcVRs = other.vrs; 2198 final Object[] srcValues = other.values; 2199 final int otherSize = other.size; 2200 int numAdd = 0; 2201 String privateCreator = null; 2202 int creatorTag = 0; 2203 ItemPointer[] itemPointer = itemPointers(); 2204 for (int i = 0; i < otherSize; i++) { 2205 int tag = tags[i]; 2206 VR vr = srcVRs[i]; 2207 Object value = srcValues[i]; 2208 if (TagUtils.isPrivateCreator(tag)) { 2209 if (contains(tag)) 2210 continue; // do not overwrite private creator IDs 2211 2212 if (vr == VR.LO) { 2213 value = other.decodeStringValue(i); 2214 if ((value instanceof String) 2215 && creatorTagOf((String) value, tag, false) != -1) 2216 continue; // do not add duplicate private creator ID 2217 } 2218 } 2219 if (TagUtils.isPrivateTag(tag)) { 2220 int tmp = TagUtils.creatorTagOf(tag); 2221 if (creatorTag != tmp) { 2222 creatorTag = tmp; 2223 privateCreator = other.privateCreatorOf(tag); 2224 } 2225 } else { 2226 creatorTag = 0; 2227 privateCreator = null; 2228 } 2229 int vallen = (value instanceof byte[]) 2230 ? ((byte[])value).length 2231 : -1; 2232 if (descriptor.isBulkData(privateCreator, tag, vr, vallen, itemPointer)) 2233 continue; 2234 2235 if (value instanceof Sequence) { 2236 Sequence src = (Sequence) value; 2237 setWithoutBulkData(privateCreator, tag, src, descriptor); 2238 } else if (value instanceof Fragments) { 2239 set(privateCreator, tag, (Fragments) value); 2240 } else { 2241 set(privateCreator, tag, vr, 2242 toggleEndian(vr, value, toggleEndian)); 2243 } 2244 numAdd++; 2245 } 2246 return numAdd != 0; 2247 } 2248 2249 private void setWithoutBulkData(String privateCreator, int tag, Sequence seq, 2250 BulkDataDescriptor descriptor) { 2251 Sequence newSequence = newSequence(privateCreator, tag, seq.size()); 2252 for (Attributes item : seq) { 2253 Attributes newItem = new Attributes(bigEndian, item.size()); 2254 newSequence.add(newItem); 2255 newItem.addWithoutBulkData(item, descriptor); 2256 } 2257 } 2258 2259 /** 2260 * Add selected attributes from another Attributes object to this. 2261 * The specified array of tag values must be sorted (as by the 2262 * {@link java.util.Arrays#sort(int[])} method) prior to making this call. 2263 * 2264 * @param other the other Attributes object 2265 * @param selection sorted tag values 2266 * @return <tt>true</tt> if one ore more attributes were added 2267 */ 2268 public boolean addSelected(Attributes other, int... selection) { 2269 return addSelected(other, selection, 0, selection.length); 2270 } 2271 2272 /** 2273 * Add selected attributes from another Attributes object to this. 2274 * The specified array of tag values must be sorted (as by the 2275 * {@link java.util.Arrays#sort(int[], int, int)} method) prior to making this call. 2276 * 2277 * @param other the other Attributes object 2278 * @param selection sorted tag values 2279 * @param fromIndex the index of the first tag (inclusive) 2280 * @param toIndex the index of the last tag (exclusive) 2281 * @return <tt>true</tt> if one ore more attributes were added 2282 */ 2283 public boolean addSelected(Attributes other, int[] selection, 2284 int fromIndex, int toIndex) { 2285 return add(other, selection, null, fromIndex, toIndex, null, false, false, false, null); 2286 } 2287 2288 /** 2289 * Merge selected attributes from another Attributes object into this. 2290 * Does not overwrite existing non-empty attributes. 2291 * The specified array of tag values must be sorted (as by the 2292 * {@link java.util.Arrays#sort(int[])} method) prior to making this call. 2293 * 2294 * @param other the other Attributes object 2295 * @param selection sorted tag values 2296 * @return <tt>true</tt> if one ore more attributes were added 2297 */ 2298 public boolean mergeSelected(Attributes other, int... selection) { 2299 return add(other, selection, null, 0, selection.length, null, true, false, false, null); 2300 } 2301 2302 /** 2303 * Tests if {@link #mergeSelected} would modify attributes, without actually 2304 * modifying this attributes 2305 * 2306 * @param other the other Attributes object 2307 * @param selection sorted tag values 2308 * @return <tt>true</tt> if one ore more attributes would have been added 2309 */ 2310 public boolean testMergeSelected(Attributes other, int... selection) { 2311 return add(other, selection, null, 0, selection.length, null, true, false, true, null); 2312 } 2313 2314 /** 2315 * Add not selected attributes from another Attributes object to this. 2316 * The specified array of tag values must be sorted (as by the 2317 * {@link java.util.Arrays#sort(int[])} method) prior to making this call. 2318 * 2319 * @param other the other Attributes object 2320 * @param selection sorted tag values 2321 * @return <tt>true</tt> if one ore more attributes were added 2322 */ 2323 public boolean addNotSelected(Attributes other, int... selection) { 2324 return addNotSelected(other, selection, 0, selection.length); 2325 } 2326 2327 /** 2328 * Add not selected attributes from another Attributes object to this. 2329 * The specified array of tag values must be sorted (as by the 2330 * {@link java.util.Arrays#sort(int[])} method) prior to making this call. 2331 * 2332 * @param other the other Attributes object 2333 * @param selection sorted tag values 2334 * @param fromIndex the index of the first tag (inclusive) 2335 * @param toIndex the index of the last tag (exclusive) 2336 * @return <tt>true</tt> if one ore more attributes were added 2337 */ 2338 public boolean addNotSelected(Attributes other, int[] selection, 2339 int fromIndex, int toIndex) { 2340 return add(other, null, selection, fromIndex, toIndex, null, false, false, false, null); 2341 } 2342 2343 private boolean add(Attributes other, int[] include, int[] exclude, 2344 int fromIndex, int toIndex, Attributes selection, boolean merge, 2345 boolean update, boolean simulate, Attributes modified) { 2346 boolean toggleEndian = bigEndian != other.bigEndian; 2347 boolean modifiedToggleEndian = modified != null 2348 && bigEndian != modified.bigEndian; 2349 final int[] otherTags = other.tags; 2350 final VR[] srcVRs = other.vrs; 2351 final Object[] srcValues = other.values; 2352 final int otherSize = other.size; 2353 int numAdd = 0; 2354 String privateCreator = null; 2355 int creatorTag = 0; 2356 for (int i = 0; i < otherSize; i++) { 2357 int tag = otherTags[i]; 2358 VR vr = srcVRs[i]; 2359 Object value = srcValues[i]; 2360 if (TagUtils.isPrivateCreator(tag)) { 2361 if (vr != VR.LO) { 2362 LOG.info("Private Creator Element with wrong VR corrected! tag:{}, vr:{}, value:{}", 2363 TagUtils.toString(tag), vr, value); 2364 srcVRs[i] = VR.LO; 2365 } 2366 continue; // private creators will be automatically added with the private tags 2367 } 2368 2369 if (include != null && Arrays.binarySearch(include, fromIndex, toIndex, tag) < 0) 2370 continue; 2371 if (exclude != null && Arrays.binarySearch(exclude, fromIndex, toIndex, tag) >= 0) 2372 continue; 2373 2374 if (TagUtils.isPrivateTag(tag)) { 2375 int tmp = TagUtils.creatorTagOf(tag); 2376 if (creatorTag != tmp) { 2377 creatorTag = tmp; 2378 privateCreator = other.privateCreatorOf(tag); 2379 } 2380 } else { 2381 creatorTag = 0; 2382 privateCreator = null; 2383 } 2384 2385 if (selection != null && !selection.contains(privateCreator, tag)) 2386 continue; 2387 2388 if (merge || update) { 2389 int j = indexOf(tag); 2390 if (j >= 0) { 2391 if (update && equalValues(other, j, i)) { 2392 continue; 2393 } 2394 Object origValue = vrs[j].isStringType() 2395 ? decodeStringValue(j) 2396 : values[j]; 2397 if (!isEmpty(origValue)) { 2398 if (merge) { 2399 continue; 2400 } 2401 if (modified != null) { 2402 if (origValue instanceof Sequence) { 2403 modified.set(privateCreator, tag, (Sequence) origValue, null); 2404 } else if (origValue instanceof Fragments) { 2405 modified.set(privateCreator, tag, (Fragments) origValue); 2406 } else { 2407 modified.set(privateCreator, tag, vr, 2408 toggleEndian(vr, origValue, modifiedToggleEndian)); 2409 } 2410 } 2411 } 2412 } 2413 } 2414 if (!simulate) { 2415 if (value instanceof Sequence) { 2416 set(privateCreator, tag, (Sequence) value, 2417 selection != null 2418 ? selection.getNestedDataset(privateCreator, tag) 2419 : null); 2420 } else if (value instanceof Fragments) { 2421 set(privateCreator, tag, (Fragments) value); 2422 } else { 2423 set(privateCreator, tag, vr, 2424 toggleEndian(vr, value, toggleEndian)); 2425 } 2426 } 2427 numAdd++; 2428 } 2429 return numAdd != 0; 2430 } 2431 2432 public boolean update(Attributes newAttrs, Attributes modified) { 2433 return add(newAttrs, null, null, 0, 0, null, false, true, false, modified); 2434 } 2435 2436 public boolean testUpdate(Attributes newAttrs, Attributes modified) { 2437 return add(newAttrs, null, null, 0, 0, null, false, true, true, modified); 2438 } 2439 2440 /** 2441 * Add selected attributes from another Attributes object to this. 2442 * Optionally, the original values of overwritten existing non-empty 2443 * attributes are preserved in another Attributes object. 2444 * The specified array of tag values must be sorted (as by the 2445 * {@link java.util.Arrays#sort(int[])} method) prior to making this call. 2446 * 2447 * @param newAttrs the other Attributes object 2448 * @param modified Attributes object to collect overwritten non-empty 2449 * attributes with original values or <tt>null</tt> 2450 * @param selection sorted tag values 2451 * @return <tt>true</tt> if one ore more attribute were added or 2452 * overwritten with a different value 2453 */ 2454 public boolean updateSelected(Attributes newAttrs, 2455 Attributes modified, int... selection) { 2456 return add(newAttrs, selection, null, 0, selection.length, null, false, true, 2457 false, modified); 2458 } 2459 2460 /** 2461 * Tests if {@link #updateSelected} would modify attributes, without actually 2462 * modifying this attributes 2463 * 2464 * @param newAttrs the other Attributes object 2465 * @param modified Attributes object to collect overwritten non-empty 2466 * attributes with original values or <tt>null</tt> 2467 * @param selection sorted tag values 2468 * @return <tt>true</tt> if one ore more attribute would be added or 2469 * overwritten with a different value 2470 */ 2471 public boolean testUpdateSelected(Attributes newAttrs, Attributes modified, 2472 int... selection) { 2473 return add(newAttrs, selection, null, 0, selection.length, null, 2474 false, true, true, modified); 2475 } 2476 2477 private static Object toggleEndian(VR vr, Object value, boolean toggleEndian) { 2478 return (toggleEndian && value instanceof byte[]) 2479 ? vr.toggleEndian((byte[]) value, true) 2480 : value; 2481 } 2482 2483 @Override 2484 public boolean equals(Object o) { 2485 if (o == this) 2486 return true; 2487 2488 if (!(o instanceof Attributes)) 2489 return false; 2490 2491 final Attributes other = (Attributes) o; 2492 if (size != other.size) 2493 return false; 2494 2495 int creatorTag = 0; 2496 int otherCreatorTag = 0; 2497 for (int i = 0; i < size; i++) { 2498 int tag = tags[i]; 2499 if (!TagUtils.isPrivateGroup(tag)) { 2500 if (tag != other.tags[i] || !equalValues(other, i, i)) 2501 return false; 2502 } else if (TagUtils.isPrivateTag(tag)) { 2503 int tmp = TagUtils.creatorTagOf(tag); 2504 if (creatorTag != tmp) { 2505 creatorTag = tmp; 2506 otherCreatorTag = other.creatorTagOf(privateCreatorOf(tag), tag, false); 2507 if (otherCreatorTag == -1) 2508 return false; 2509 } 2510 int j = other.indexOf(TagUtils.toPrivateTag(otherCreatorTag, tag)); 2511 if (j < 0 || !equalValues(other, i, j)) 2512 return false; 2513 } 2514 } 2515 return true; 2516 } 2517 2518 private boolean equalValues(Attributes other, int index, int otherIndex) { 2519 VR vr = vrs[index]; 2520 if (vr != other.vrs[otherIndex]) 2521 return false; 2522 if (vr.isStringType()) 2523 if (vr == VR.IS) 2524 return equalISValues(other, index, otherIndex); 2525 else if (vr == VR.DS) 2526 return equalDSValues(other, index, otherIndex); 2527 else 2528 return equalStringValues(other, index, otherIndex); 2529 Object v1 = values[index]; 2530 Object v2 = other.values[otherIndex]; 2531 if (v1 instanceof byte[]) { 2532 if (v2 instanceof byte[] && ((byte[]) v1).length == ((byte[]) v2).length) { 2533 if (bigEndian != other.bigEndian) 2534 v2 = vr.toggleEndian((byte[]) v2, true); 2535 return Arrays.equals((byte[]) v1, (byte[]) v2); 2536 } 2537 } else 2538 return v1.equals(v2); 2539 return false; 2540 } 2541 2542 private boolean equalISValues(Attributes other, int index, int otherIndex) { 2543 try { 2544 return Arrays.equals(decodeISValue(index), other.decodeISValue(otherIndex)); 2545 } catch (NumberFormatException e) { 2546 return equalStringValues(other, index, otherIndex); 2547 } 2548 } 2549 2550 private boolean equalDSValues(Attributes other, int index, int otherIndex) { 2551 try { 2552 return Arrays.equals(decodeDSValue(index), other.decodeDSValue(otherIndex)); 2553 } catch (NumberFormatException e) { 2554 return equalStringValues(other, index, otherIndex); 2555 } 2556 } 2557 2558 private boolean equalStringValues(Attributes other, int index, int otherIndex) { 2559 Object v1 = decodeStringValue(index); 2560 Object v2 = other.decodeStringValue(otherIndex); 2561 if (v1 instanceof String[]) { 2562 if (v2 instanceof String[]) 2563 return Arrays.equals((String[]) v1, (String[]) v2); 2564 } else 2565 return v1.equals(v2); 2566 return false; 2567 } 2568 2569 @Override 2570 public int hashCode() { 2571 int h = 0; 2572 for (int i = 0; i < size; i++) { 2573 int tag = tags[i]; 2574 if (!TagUtils.isPrivateGroup(tag)) 2575 h = 31*h + tag; 2576 } 2577 return h; 2578 } 2579 2580 private void set(String privateCreator, int tag, Sequence src, 2581 Attributes selection) { 2582 Sequence dst = newSequence(privateCreator, tag, src.size()); 2583 for (Attributes item : src) 2584 dst.add(selection != null && !selection.isEmpty() 2585 ? new Attributes(item, bigEndian, selection) 2586 : new Attributes(item, bigEndian)); 2587 } 2588 2589 private void set(String privateCreator, int tag, Fragments src) { 2590 boolean toogleEndian = src.bigEndian() != bigEndian; 2591 VR vr = src.vr(); 2592 Fragments dst = newFragments(privateCreator, tag, vr, src.size()); 2593 for (Object frag : src) 2594 dst.add(toggleEndian(vr, frag, toogleEndian)); 2595 } 2596 2597 @Override 2598 public String toString() { 2599 return toString(TO_STRING_LIMIT, TO_STRING_WIDTH); 2600 } 2601 2602 public String toString(Deidentifier deidentifier) { 2603 return toString(TO_STRING_LIMIT, TO_STRING_WIDTH, deidentifier); 2604 } 2605 2606 public String toString(int limit, int maxWidth) { 2607 return toStringBuilder(limit, maxWidth, new StringBuilder(1024), null) 2608 .toString(); 2609 } 2610 2611 public String toString(int limit, int maxWidth, Deidentifier deidentifier) { 2612 return toStringBuilder(limit, maxWidth, new StringBuilder(1024), deidentifier) 2613 .toString(); 2614 } 2615 2616 public StringBuilder toStringBuilder(StringBuilder sb) { 2617 return toStringBuilder(TO_STRING_LIMIT, TO_STRING_WIDTH, sb, null); 2618 } 2619 2620 public StringBuilder toStringBuilder(int limit, int maxWidth, StringBuilder sb, Deidentifier deidentifier) { 2621 if (appendAttributes(limit, maxWidth, sb, "", deidentifier) > limit) 2622 sb.append("...\n"); 2623 return sb; 2624 } 2625 2626 private int appendAttributes(int limit, int maxWidth, StringBuilder sb, String prefix, Deidentifier deidentifier) { 2627 int lines = 0; 2628 int creatorTag = 0; 2629 String privateCreator = null; 2630 for (int i = 0; i < size; i++) { 2631 if (++lines > limit) 2632 break; 2633 int tag = tags[i]; 2634 if (TagUtils.isPrivateTag(tag)) { 2635 int tmp = TagUtils.creatorTagOf(tag); 2636 if (creatorTag != tmp) { 2637 creatorTag = tmp; 2638 privateCreator = getString(creatorTag, null); 2639 } 2640 } else { 2641 creatorTag = 0; 2642 privateCreator = null; 2643 } 2644 Object value = values[i]; 2645 appendAttribute(privateCreator, tag, vrs[i], value, sb.length() + maxWidth, sb, prefix, deidentifier); 2646 if (value instanceof Sequence) 2647 lines += appendItems((Sequence) value, limit - lines, maxWidth, sb, prefix + '>', deidentifier); 2648 } 2649 return lines; 2650 } 2651 2652 private int appendItems(Sequence sq, int limit, int maxWidth, StringBuilder sb, 2653 String prefix, Deidentifier deidentifier) { 2654 int lines = 0; 2655 int itemNo = 0; 2656 for (Attributes item : sq) { 2657 if (++lines > limit) 2658 break; 2659 sb.append(prefix).append("Item #").append(++itemNo).append('\n'); 2660 lines += item.appendAttributes(limit - lines, maxWidth, sb, prefix, deidentifier); 2661 } 2662 return lines ; 2663 } 2664 2665 private StringBuilder appendAttribute(String privateCreator, int tag, VR vr, Object value, 2666 int maxLength, StringBuilder sb, String prefix, Deidentifier deidentifier) { 2667 sb.append(prefix).append(TagUtils.toString(tag)).append(' ').append(vr).append(" ["); 2668 if (vr.prompt((deidentifier!=null ? deidentifier.deidentify(tag, vr, value) :value), bigEndian, 2669 getSpecificCharacterSet(vr), 2670 maxLength - sb.length() - 1, sb)) { 2671 sb.append("] ").append(ElementDictionary.keywordOf(tag, privateCreator)); 2672 if (sb.length() > maxLength) 2673 sb.setLength(maxLength); 2674 } 2675 sb.append('\n'); 2676 return sb; 2677 } 2678 2679 public int calcLength(DicomEncodingOptions encOpts, boolean explicitVR) { 2680 if (isEmpty()) 2681 return 0; 2682 2683 this.groupLengths = encOpts.groupLength 2684 ? new int[countGroups()] 2685 : null; 2686 this.length = calcLength(encOpts, explicitVR, 2687 getSpecificCharacterSet(), groupLengths); 2688 return this.length; 2689 } 2690 2691 private int calcLength(DicomEncodingOptions encOpts, boolean explicitVR, 2692 SpecificCharacterSet cs, int[] groupLengths) { 2693 int len, totlen = 0; 2694 int groupLengthTag = -1; 2695 int groupLengthIndex = -1; 2696 VR vr; 2697 Object val; 2698 for (int i = 0; i < size; i++) { 2699 vr = vrs[i]; 2700 val = values[i]; 2701 len = explicitVR ? vr.headerLength() : 8; 2702 if (val instanceof Value) 2703 len += ((Value) val).calcLength(encOpts, explicitVR, vr); 2704 else { 2705 if (!(val instanceof byte[])) 2706 values[i] = val = vr.toBytes(val, cs); 2707 len += (((byte[]) val).length + 1) & ~1; 2708 } 2709 totlen += len; 2710 if (groupLengths != null) { 2711 int tmp = TagUtils.groupLengthTagOf(tags[i]); 2712 if (groupLengthTag != tmp) { 2713 groupLengthTag = tmp; 2714 groupLengthIndex++; 2715 totlen += 12; 2716 } 2717 groupLengths[groupLengthIndex] += len; 2718 } 2719 } 2720 return totlen; 2721 } 2722 2723 private int countGroups() { 2724 int groupLengthTag = -1; 2725 int count = 0; 2726 for (int i = 0; i < size; i++) { 2727 int tmp = TagUtils.groupLengthTagOf(tags[i]); 2728 if (groupLengthTag != tmp) { 2729 if (groupLengthTag < 0) 2730 this.groupLengthIndex0 = count; 2731 groupLengthTag = tmp; 2732 count++; 2733 } 2734 } 2735 return count; 2736 } 2737 2738 public void writeTo(DicomOutputStream out) 2739 throws IOException { 2740 if (isEmpty()) 2741 return; 2742 2743 if (groupLengths == null && out.getEncodingOptions().groupLength) 2744 throw new IllegalStateException( 2745 "groupLengths not initialized by calcLength()"); 2746 2747 SpecificCharacterSet cs = getSpecificCharacterSet(); 2748 if (tags[0] < 0) { 2749 int index0 = -(1 + indexOf(0)); 2750 writeTo(out, cs, index0, size, groupLengthIndex0); 2751 writeTo(out, cs, 0, index0, 0); 2752 } else { 2753 writeTo(out, cs, 0, size, 0); 2754 } 2755 } 2756 2757 public void writeItemTo(DicomOutputStream out) throws IOException { 2758 DicomEncodingOptions encOpts = out.getEncodingOptions(); 2759 int len = getEncodedItemLength(encOpts, out.isExplicitVR()); 2760 out.writeHeader(Tag.Item, null, len); 2761 writeTo(out); 2762 if (len == -1) 2763 out.writeHeader(Tag.ItemDelimitationItem, null, 0); 2764 } 2765 2766 private int getEncodedItemLength(DicomEncodingOptions encOpts, 2767 boolean explicitVR) { 2768 if (isEmpty()) 2769 return encOpts.undefEmptyItemLength ? -1 : 0; 2770 2771 if (encOpts.undefItemLength) 2772 return -1; 2773 2774 if (length == -1) 2775 calcLength(encOpts, explicitVR); 2776 2777 return length; 2778 } 2779 2780 private void writeTo(DicomOutputStream out, SpecificCharacterSet cs, 2781 int start, int end, int groupLengthIndex) throws IOException { 2782 boolean groupLength = groupLengths != null; 2783 int groupLengthTag = -1; 2784 for (int i = start; i < end; i++) { 2785 int tag = tags[i]; 2786 if (groupLength) { 2787 int tmp = TagUtils.groupLengthTagOf(tag); 2788 if (groupLengthTag != tmp) { 2789 groupLengthTag = tmp; 2790 out.writeGroupLength(groupLengthTag, 2791 groupLengths[groupLengthIndex++]); 2792 } 2793 } 2794 out.writeAttribute(tag, vrs[i], values[i], cs); 2795 } 2796 } 2797 2798 /** 2799 * Invokes {@link Visitor#visit} for each attribute in this instance. The 2800 * operation will be aborted if <code>visitor.visit()</code> returns 2801 * <code>false</code> or throws an exception. 2802 * 2803 * @param visitor 2804 * @param visitNestedDatasets 2805 * controls if <code>visitor.visit()</code> is also invoked for 2806 * attributes in nested datasets 2807 * @return <code>true</code> if the operation was not aborted. 2808 * @throws Exception 2809 * exception thrown by {@link Visitor#visit} 2810 */ 2811 public boolean accept(Visitor visitor, boolean visitNestedDatasets) 2812 throws Exception{ 2813 if (isEmpty()) 2814 return true; 2815 2816 if (tags[0] < 0) { 2817 int index0 = -(1 + indexOf(0)); 2818 return accept(visitor, visitNestedDatasets, index0, size) 2819 && accept(visitor, visitNestedDatasets, 0, index0); 2820 } else { 2821 return accept(visitor, visitNestedDatasets, 0, size); 2822 } 2823 } 2824 2825 private boolean accept(Visitor visitor, boolean visitNestedDatasets, 2826 int start, int end) throws Exception { 2827 for (int i = start; i < end; i++) { 2828 if (!visitor.visit(this, tags[i], vrs[i], values[i])) 2829 return false; 2830 if (visitNestedDatasets && (values[i] instanceof Sequence)) { 2831 for (Attributes item : (Sequence) values[i]) { 2832 if (!item.accept(visitor, true)) 2833 return false; 2834 } 2835 } 2836 } 2837 return true; 2838 } 2839 2840 public void writeGroupTo(DicomOutputStream out, int groupLengthTag) 2841 throws IOException { 2842 if (isEmpty()) 2843 throw new IllegalStateException("No attributes"); 2844 2845 checkInGroup(0, groupLengthTag); 2846 checkInGroup(size-1, groupLengthTag); 2847 SpecificCharacterSet cs = getSpecificCharacterSet(); 2848 out.writeGroupLength(groupLengthTag, 2849 calcLength(out.getEncodingOptions(), out.isExplicitVR(), cs, null)); 2850 writeTo(out, cs, 0, size, 0); 2851 } 2852 2853 2854 private void checkInGroup(int i, int groupLengthTag) { 2855 int tag = tags[i]; 2856 if (TagUtils.groupLengthTagOf(tag) != groupLengthTag) 2857 throw new IllegalStateException(TagUtils.toString(tag) 2858 + " does not belong to group (" 2859 + TagUtils.shortToHexString( 2860 TagUtils.groupNumber(groupLengthTag)) 2861 + ",eeee)."); 2862 2863 } 2864 2865 public Attributes createFileMetaInformation(String tsuid) { 2866 return createFileMetaInformation( 2867 getString(Tag.SOPInstanceUID, null), 2868 getString(Tag.SOPClassUID, null), 2869 tsuid); 2870 } 2871 2872 public static Attributes createFileMetaInformation(String iuid, 2873 String cuid, String tsuid) { 2874 if (iuid == null || iuid.isEmpty()) 2875 throw new IllegalArgumentException("Missing SOP Instance UID"); 2876 if (cuid == null || cuid.isEmpty()) 2877 throw new IllegalArgumentException("Missing SOP Class UID"); 2878 if (tsuid == null || tsuid.isEmpty()) 2879 throw new IllegalArgumentException("Missing Transfer Syntax UID"); 2880 2881 Attributes fmi = new Attributes(6); 2882 fmi.setBytes(Tag.FileMetaInformationVersion, VR.OB, 2883 new byte[]{ 0, 1 }); 2884 fmi.setString(Tag.MediaStorageSOPClassUID, VR.UI, cuid); 2885 fmi.setString(Tag.MediaStorageSOPInstanceUID, VR.UI, iuid); 2886 fmi.setString(Tag.TransferSyntaxUID, VR.UI, tsuid); 2887 fmi.setString(Tag.ImplementationClassUID, VR.UI, 2888 Implementation.getClassUID()); 2889 fmi.setString(Tag.ImplementationVersionName, VR.SH, 2890 Implementation.getVersionName()); 2891 return fmi; 2892 } 2893 2894 public boolean matches(Attributes keys, boolean ignorePNCase, 2895 boolean matchNoValue) { 2896 int[] keyTags = keys.tags; 2897 VR[] keyVrs = keys.vrs; 2898 Object[] keyValues = keys.values; 2899 int keysSize = keys.size; 2900 String privateCreator = null; 2901 int creatorTag = 0; 2902 for (int i = 0; i < keysSize; i++) { 2903 int tag = keyTags[i]; 2904 if (TagUtils.isPrivateCreator(tag)) 2905 continue; 2906 2907 if (TagUtils.isPrivateGroup(tag)) { 2908 int tmp = TagUtils.creatorTagOf(tag); 2909 if (creatorTag != tmp) { 2910 creatorTag = tmp; 2911 privateCreator = keys.getString(creatorTag, null); 2912 } 2913 } else { 2914 creatorTag = 0; 2915 privateCreator = null; 2916 } 2917 2918 Object keyValue = keyValues[i]; 2919 if (isEmpty(keyValue)) 2920 continue; 2921 2922 if (keyVrs[i].isStringType()) { 2923 if (!matches(privateCreator, tag, keyVrs[i], ignorePNCase, 2924 matchNoValue, keys.getStrings(privateCreator, tag, null))) 2925 return false; 2926 } else if (keyValue instanceof Sequence) { 2927 if (!matches(privateCreator, tag, ignorePNCase, matchNoValue, 2928 (Sequence) keyValue)) 2929 return false; 2930 } else { 2931 throw new UnsupportedOperationException("Keys with VR: " 2932 + keyVrs[i] + " not supported"); 2933 } 2934 } 2935 return true; 2936 } 2937 2938 private boolean matches(String privateCreator, int tag, VR vr, 2939 boolean ignorePNCase, boolean matchNoValue, String[] keyVals) { 2940 String[] vals = getStrings(privateCreator, tag, null); 2941 if (vals == null || vals.length == 0) 2942 return matchNoValue; 2943 2944 boolean ignoreCase = ignorePNCase && vr == VR.PN; 2945 for (String keyVal : keyVals) { 2946 if (vr == VR.PN) 2947 keyVal = new PersonName(keyVals[0]).toString(); 2948 2949 if (StringUtils.containsWildCard(keyVal)) { 2950 Pattern pattern = StringUtils.compilePattern(keyVal, ignoreCase); 2951 for (String val : vals) { 2952 if (val == null) 2953 if (matchNoValue) 2954 return true; 2955 else 2956 continue; 2957 if (vr == VR.PN) 2958 val = new PersonName(val).toString(); 2959 if (pattern.matcher(val).matches()) 2960 return true; 2961 } 2962 } else { 2963 for (String val : vals) { 2964 if (val == null) 2965 if (matchNoValue) 2966 return true; 2967 else 2968 continue; 2969 if (vr == VR.PN) 2970 val = new PersonName(val).toString(); 2971 if (ignoreCase ? keyVal.equalsIgnoreCase(val) 2972 : keyVal.equals(val)) 2973 return true; 2974 } 2975 } 2976 } 2977 return false; 2978 } 2979 2980 private boolean matches(String privateCreator, int tag, boolean ignorePNCase, 2981 boolean matchNoValue, Sequence keySeq) { 2982 int n = keySeq.size(); 2983 if (n > 1) 2984 throw new IllegalArgumentException("Keys contain Sequence " 2985 + TagUtils.toString(tag) + " with " + n + " Items"); 2986 2987 Attributes keys = keySeq.get(0); 2988 if (keys.isEmpty()) 2989 return true; 2990 2991 Object value = getValue(privateCreator, tag); 2992 if (value == null || isEmpty(value)) 2993 return matchNoValue; 2994 2995 if (value instanceof Sequence) { 2996 Sequence sq = (Sequence) value; 2997 for (Attributes item : sq) 2998 if (item.matches(keys, ignorePNCase, matchNoValue)) 2999 return true; 3000 } 3001 return false; 3002 } 3003 3004 private static final long serialVersionUID = 7868714416968825241L; 3005 3006 private void writeObject(ObjectOutputStream out) throws IOException { 3007 out.defaultWriteObject(); 3008 out.writeInt(size); 3009 @SuppressWarnings("resource") 3010 DicomOutputStream dout = new DicomOutputStream(out, 3011 bigEndian ? UID.ExplicitVRBigEndianRetired 3012 : UID.ExplicitVRLittleEndian); 3013 dout.writeDataset(null, this); 3014 dout.writeHeader(Tag.ItemDelimitationItem, null, 0); 3015 } 3016 3017 private void readObject(ObjectInputStream in) 3018 throws IOException, ClassNotFoundException { 3019 in.defaultReadObject(); 3020 init(in.readInt()); 3021 @SuppressWarnings("resource") 3022 DicomInputStream din = new DicomInputStream(in, 3023 bigEndian ? UID.ExplicitVRBigEndianRetired 3024 : UID.ExplicitVRLittleEndian); 3025 din.readAttributes(this, -1, Tag.ItemDelimitationItem); 3026 } 3027 3028 public ValidationResult validate(IOD iod) { 3029 ValidationResult result = new ValidationResult(); 3030 HashMap<String,Boolean> resolvedConditions = new HashMap<String,Boolean>(); 3031 for (IOD.DataElement el : iod) { 3032 validate(el, result, resolvedConditions); 3033 } 3034 return result; 3035 } 3036 3037 public void validate(DataElement el, ValidationResult result) { 3038 validate(el, result, null); 3039 } 3040 3041 private void validate(DataElement el, ValidationResult result, 3042 Map<String, Boolean> processedConditions) { 3043 IOD.Condition condition = el.getCondition(); 3044 if (condition != null) { 3045 String id = condition.id(); 3046 Boolean match = id != null ? processedConditions.get(id) : null; 3047 if (match == null) { 3048 match = condition.match(this); 3049 if (id != null) 3050 processedConditions.put(id, match); 3051 } 3052 if (!match) 3053 return; 3054 } 3055 int index = indexOf(el.tag); 3056 if (index < 0) { 3057 if (el.type == IOD.DataElementType.TYPE_1 3058 || el.type == IOD.DataElementType.TYPE_2) { 3059 result.addMissingAttribute(el); 3060 } 3061 return; 3062 } 3063 Object value = values[index]; 3064 if (isEmpty(value)) { 3065 if (el.type == IOD.DataElementType.TYPE_1) { 3066 result.addMissingAttributeValue(el); 3067 } 3068 return; 3069 } 3070 if (el.type == IOD.DataElementType.TYPE_0) { 3071 result.addNotAllowedAttribute(el); 3072 return; 3073 } 3074 VR vr = vrs[index]; 3075 if (vr.isStringType()) { 3076 value = decodeStringValue(index); 3077 } 3078 3079 Object validVals = el.getValues(); 3080 if (el.vr == VR.SQ) { 3081 if (!(value instanceof Sequence)) { 3082 result.addInvalidAttributeValue(el, ValidationResult.Invalid.VR); 3083 return; 3084 } 3085 Sequence seq = (Sequence) value; 3086 int seqSize = seq.size(); 3087 if (el.maxVM > 0 && seqSize > el.maxVM) { 3088 result.addInvalidAttributeValue(el, 3089 ValidationResult.Invalid.MultipleItems); 3090 return; 3091 } 3092 if (validVals instanceof Code[]) { 3093 boolean invalidItem = false; 3094 ValidationResult[] itemValidationResults = new ValidationResult[seqSize]; 3095 for (int i = 0; i < seqSize; i++) { 3096 ValidationResult itemValidationResult = 3097 validateCode(seq.get(i), (Code[]) validVals); 3098 invalidItem = invalidItem || !itemValidationResult.isValid(); 3099 itemValidationResults[i] = itemValidationResult; 3100 } 3101 if (invalidItem) { 3102 result.addInvalidAttributeValue(el, 3103 ValidationResult.Invalid.Code, itemValidationResults, null); 3104 } 3105 } else if (validVals instanceof IOD[]) { 3106 IOD[] itemIODs = (IOD[]) validVals; 3107 int[] matchingItems = new int[itemIODs.length]; 3108 boolean invalidItem = false; 3109 ValidationResult[] itemValidationResults = new ValidationResult[seqSize]; 3110 for (int i = 0; i < seqSize; i++) { 3111 ValidationResult itemValidationResult = new ValidationResult(); 3112 HashMap<String,Boolean> resolvedItemConditions = 3113 new HashMap<String,Boolean>(); 3114 Attributes item = seq.get(i); 3115 for (int j = 0; j < itemIODs.length; j++) { 3116 IOD itemIOD = itemIODs[j]; 3117 IOD.Condition itemCondition = itemIOD.getCondition(); 3118 if (itemCondition != null) { 3119 String id = itemCondition.id(); 3120 Boolean match = id != null ? resolvedItemConditions.get(id) : null; 3121 if (match == null) { 3122 match = itemCondition.match(item); 3123 if (id != null) 3124 resolvedItemConditions.put(id, match); 3125 } 3126 if (!match) 3127 continue; 3128 } 3129 matchingItems[j]++; 3130 for (IOD.DataElement itemEl : itemIOD) { 3131 item.validate(itemEl, itemValidationResult, resolvedItemConditions); 3132 } 3133 } 3134 invalidItem = invalidItem || !itemValidationResult.isValid(); 3135 itemValidationResults[i] = itemValidationResult; 3136 } 3137 IOD[] missingItems = checkforMissingItems(matchingItems, itemIODs); 3138 if (invalidItem || missingItems != null) { 3139 result.addInvalidAttributeValue(el, 3140 ValidationResult.Invalid.Item, 3141 itemValidationResults, missingItems); 3142 } 3143 } 3144 return; 3145 } 3146 3147 if (el.maxVM > 0 || el.minVM > 1) { 3148 int vm = vr.vmOf(value); 3149 if (el.maxVM > 0 && vm > el.maxVM 3150 || el.minVM > 1 && vm < el.minVM) { 3151 result.addInvalidAttributeValue(el, ValidationResult.Invalid.VM); 3152 return; 3153 } 3154 } 3155 if (validVals == null) 3156 return; 3157 3158 if (validVals instanceof String[]) { 3159 if (!vr.isStringType()) { 3160 result.addInvalidAttributeValue(el, ValidationResult.Invalid.VR); 3161 return; 3162 } 3163 if (!isValidValue(toStrings(value), el.valueNumber, (String[]) validVals)) { 3164 result.addInvalidAttributeValue(el, ValidationResult.Invalid.Value); 3165 } 3166 } else if (validVals instanceof int[]) { 3167 if (vr == VR.IS) 3168 value = decodeISValue(index); 3169 else if (!vr.isIntType()) { 3170 result.addInvalidAttributeValue(el, ValidationResult.Invalid.VR); 3171 return; 3172 } 3173 if (!isValidValue(vr.toInts(value, bigEndian), el.valueNumber, (int[]) validVals)) { 3174 result.addInvalidAttributeValue(el, ValidationResult.Invalid.Value); 3175 } 3176 } 3177 } 3178 3179 private IOD[] checkforMissingItems(int[] matchingItems, IOD[] itemIODs) { 3180 IOD[] missingItems = new IOD[matchingItems.length]; 3181 int n = 0; 3182 for (int i = 0; i < matchingItems.length; i++) { 3183 IOD itemIOD = itemIODs[i]; 3184 if (matchingItems[i] == 0 3185 && itemIOD.getType() == DataElementType.TYPE_1) 3186 missingItems[n++] = itemIOD; 3187 } 3188 return n > 0 ? Arrays.copyOf(missingItems, n) : null; 3189 } 3190 3191 private ValidationResult validateCode(Attributes item, Code[] validVals) { 3192 ValidationResult result = null; 3193 for (Code code : validVals) { 3194 result = item.validate(IOD.valueOf(code)); 3195 if (result.isValid()) 3196 break; 3197 } 3198 return result; 3199 } 3200 3201 private boolean isValidValue(String[] val, int valueNumber, String[] validVals) { 3202 if (valueNumber != 0) 3203 return val.length < valueNumber || isOneOf(val[valueNumber-1], validVals); 3204 3205 for (int i = 0; i < val.length; i++) 3206 if (!isOneOf(val[i], validVals)) 3207 return false; 3208 return true; 3209 } 3210 3211 private <T> boolean isOneOf(Object val, T[] ss) { 3212 if (ss == null) 3213 return true; 3214 for (T s : ss) 3215 if (val.equals(s)) 3216 return true; 3217 return false; 3218 } 3219 3220 private boolean isValidValue(int[] val, int valueNumber, int[] validVals) { 3221 if (valueNumber != 0) 3222 return val.length < valueNumber || isOneOf(val[valueNumber-1], validVals); 3223 3224 for (int i = 0; i < val.length; i++) 3225 if (!isOneOf(val[i], validVals)) 3226 return false; 3227 return true; 3228 } 3229 3230 private boolean isOneOf(int val, int[] is) { 3231 if (is == null) 3232 return true; 3233 for (int i : is) 3234 if (val == i) 3235 return true; 3236 return false; 3237 } 3238 3239 /** 3240 * Add attributes of this data set which were replaced in 3241 * the specified other data set into the result data set. 3242 * If no result data set is passed, a new result set will be instantiated. 3243 * 3244 * @param other data set 3245 * @param result data set or {@code null} 3246 * 3247 * @return result data set. 3248 */ 3249 public Attributes getModified(Attributes other, Attributes result) { 3250 if (result == null) 3251 result = new Attributes(other.size); 3252 int creatorTag = -1; 3253 int prevOtherCreatorTag = -1; 3254 int otherCreatorTag = -1; 3255 String privateCreator = null; 3256 for (int i = 0; i < other.size; i++) { 3257 int tag = other.tags[i]; 3258 if ((tag & 0x00010000) != 0) { // private group 3259 if ((tag & 0x0000ff00) == 0) 3260 continue; // skip private creator 3261 3262 otherCreatorTag = TagUtils.creatorTagOf(tag); 3263 if (prevOtherCreatorTag != otherCreatorTag) { 3264 prevOtherCreatorTag = otherCreatorTag; 3265 creatorTag = -1; 3266 int k = other.indexOf(otherCreatorTag); 3267 if (k >= 0) { 3268 Object o = other.decodeStringValue(k); 3269 if (o instanceof String) { 3270 privateCreator = (String) o; 3271 creatorTag = creatorTagOf( 3272 privateCreator, tag, false); 3273 } 3274 } 3275 } 3276 if (creatorTag == -1) 3277 continue; // no matching Private Creator 3278 3279 tag = TagUtils.toPrivateTag(creatorTag, tag); 3280 } else { 3281 privateCreator = null; 3282 } 3283 3284 int j = indexOf(tag); 3285 if (j < 0) 3286 continue; 3287 3288 Object origValue = values[j]; 3289 if (origValue instanceof Value && ((Value) origValue).isEmpty()) 3290 continue; 3291 3292 if (equalValues(other, j, i)) 3293 continue; 3294 3295 if (origValue instanceof Sequence) { 3296 result.set(privateCreator, tag, (Sequence) origValue, null); 3297 } else if (origValue instanceof Fragments) { 3298 result.set(privateCreator, tag, (Fragments) origValue); 3299 } else { 3300 result.set(privateCreator, tag, vrs[j], origValue); 3301 } 3302 } 3303 return result; 3304 } 3305 3306 /** 3307 * Returns attributes of this data set which were removed or replaced in 3308 * the specified other data set. 3309 * 3310 * @param other data set 3311 * @return attributes of this data set which were removed or replaced in 3312 * the specified other data set. 3313 */ 3314 public Attributes getRemovedOrModified(Attributes other) { 3315 Attributes modified = new Attributes(size); 3316 int creatorTag = -1; 3317 int prevCreatorTag = -1; 3318 int otherCreatorTag = 0; 3319 String privateCreator = null; 3320 for (int i = 0; i < size; i++) { 3321 int tag = tags[i]; 3322 if ((tag & 0x00010000) != 0) { // private group 3323 if ((tag & 0x0000ff00) == 0) 3324 continue; // skip private creator 3325 3326 creatorTag = TagUtils.creatorTagOf(tag); 3327 if (prevCreatorTag != creatorTag) { 3328 prevCreatorTag = creatorTag; 3329 otherCreatorTag = -1; 3330 privateCreator = null; 3331 int k = indexOf(creatorTag); 3332 if (k >= 0) { 3333 Object o = decodeStringValue(k); 3334 if (o instanceof String) { 3335 privateCreator = (String) o; 3336 otherCreatorTag = other.creatorTagOf( 3337 privateCreator, tag, false); 3338 } 3339 } 3340 } 3341 if (privateCreator == null) 3342 continue; // no Private Creator 3343 3344 if (otherCreatorTag != -1) 3345 tag = TagUtils.toPrivateTag(otherCreatorTag, tag); 3346 } else { 3347 otherCreatorTag = 0; 3348 privateCreator = null; 3349 } 3350 3351 Object origValue = values[i]; 3352 if (origValue instanceof Value && ((Value) origValue).isEmpty()) 3353 continue; 3354 3355 if (otherCreatorTag >= 0) { 3356 int j = other.indexOf(tag); 3357 if (j >= 0 && equalValues(other, i, j)) 3358 continue; 3359 } 3360 3361 if (origValue instanceof Sequence) { 3362 modified.set(privateCreator, tag, (Sequence) origValue, null); 3363 } else if (origValue instanceof Fragments) { 3364 modified.set(privateCreator, tag, (Fragments) origValue); 3365 } else { 3366 modified.set(privateCreator, tag, vrs[i], origValue); 3367 } 3368 } 3369 return modified; 3370 } 3371 3372 private int creatorIndexOf(String privateCreator, int groupNumber) { 3373 if ((groupNumber & 1) == 0) 3374 throw new IllegalArgumentException( 3375 "(" + TagUtils.shortToHexString(groupNumber) + ",xxxx) is not a private Group"); 3376 3377 int group = groupNumber << 16; 3378 int creatorTag = group | 0x10; 3379 int index = indexOf(creatorTag); 3380 if (index < 0) 3381 index = -index-1; 3382 while (index < size && (tags[index] & 0xffffff00) == group) { 3383 if (vrs[index] == VR.LO) { 3384 Object creatorID = decodeStringValue(index); 3385 if (privateCreator.equals(creatorID)) 3386 return index; 3387 } 3388 index++; 3389 creatorTag++; 3390 } 3391 return -1; 3392 } 3393 3394 public int removePrivateAttributes(String privateCreator, int groupNumber) { 3395 int privateCreatorIndex = creatorIndexOf(privateCreator, groupNumber); 3396 if (privateCreatorIndex < 0) 3397 return 0; 3398 3399 int creatorTag = tags[privateCreatorIndex]; 3400 int privateTag = (creatorTag & 0xffff0000) | ((creatorTag & 0xff) << 8); 3401 int srcPos = privateCreatorIndex + 1; 3402 int start = srcPos; 3403 while (start < size && tags[start] < privateTag) 3404 start++; 3405 3406 int end = start; 3407 while (end < size && (tags[end] & 0xffffff00) == privateTag) 3408 end++; 3409 3410 int len1 = start - srcPos; 3411 if (len1 > 0) { 3412 System.arraycopy(tags, srcPos, tags, privateCreatorIndex, len1); 3413 System.arraycopy(vrs, srcPos, vrs, privateCreatorIndex, len1); 3414 System.arraycopy(values, srcPos, values, privateCreatorIndex, len1); 3415 } 3416 3417 int len2 = size - end; 3418 if (len2 > 0) { 3419 int destPos = start - 1; 3420 System.arraycopy(tags, end, tags, destPos, len2); 3421 System.arraycopy(vrs, end, vrs, destPos, len2); 3422 System.arraycopy(values, end, values, destPos, len2); 3423 } 3424 int removed = end - start; 3425 int size1 = size - removed - 1; 3426 Arrays.fill(tags, size1, size, 0); 3427 Arrays.fill(vrs, size1, size, null); 3428 Arrays.fill(values, size1, size, null); 3429 size = size1; 3430 return removed; 3431 } 3432 3433 public int removePrivateAttributes() { 3434 int size1 = size; 3435 for (int i = 0; i < size1; i++) { 3436 int j = i; 3437 while (TagUtils.isPrivateGroup(tags[j]) && j < size1) 3438 j++; 3439 if (j > i) { 3440 int len = size1 - j; 3441 if (len > 0) { 3442 System.arraycopy(tags, j, tags, i, len); 3443 System.arraycopy(vrs, j, vrs, i, len); 3444 System.arraycopy(values, j, values, i, len); 3445 } 3446 size1 -= j - i; 3447 } 3448 } 3449 int removed = size - size1; 3450 if (removed > 0) { 3451 Arrays.fill(tags, size1, size, 0); 3452 Arrays.fill(vrs, size1, size, null); 3453 Arrays.fill(values, size1, size, null); 3454 size = size1; 3455 } 3456 return removed; 3457 } 3458}