/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is part of dcm4che, an implementation of DICOM(TM) in
* Java(TM), hosted at https://github.com/gunterze/dcm4che.
*
* The Initial Developer of the Original Code is
* Agfa Healthcare.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* See @authors listed below
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.dcm4che3.data;
import org.dcm4che3.data.IOD.DataElement;
import org.dcm4che3.data.IOD.DataElementType;
import org.dcm4che3.io.BulkDataDescriptor;
import org.dcm4che3.io.DicomEncodingOptions;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.io.DicomOutputStream;
import org.dcm4che3.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
import java.util.regex.Pattern;
/**
* @author Gunter Zeilinger <gunterze@gmail.com>
*/
public class Attributes implements Serializable {
public interface Visitor {
boolean visit(Attributes attrs, int tag, VR vr, Object value)
throws Exception;
}
private static final Logger LOG =
LoggerFactory.getLogger(Attributes.class);
private static final int INIT_CAPACITY = 16;
private static final int TO_STRING_LIMIT = 50;
private static final int TO_STRING_WIDTH = 78;
private transient Attributes parent;
private transient String parentSequencePrivateCreator;
private transient int parentSequenceTag;
private transient int[] tags;
private transient VR[] vrs;
private transient Object[] values;
private transient int size;
private transient SpecificCharacterSet cs;
private transient TimeZone tz;
private transient int length = -1;
private transient int[] groupLengths;
private transient int groupLengthIndex0;
private final boolean bigEndian;
private long itemPosition = -1;
private boolean containsSpecificCharacterSet;
private boolean containsTimezoneOffsetFromUTC;
private Map<String, Object> properties;
private TimeZone defaultTimeZone;
public Attributes() {
this(false, INIT_CAPACITY);
}
public Attributes(boolean bigEndian) {
this(bigEndian, INIT_CAPACITY);
}
public Attributes(int initialCapacity) {
this(false, initialCapacity);
}
public Attributes(boolean bigEndian, int initialCapacity) {
this.bigEndian = bigEndian;
init(initialCapacity);
}
public void clear() {
size = 0;
Arrays.fill(tags, 0);
Arrays.fill(vrs, null);
Arrays.fill(values, null);
}
private void init(int initialCapacity) {
this.tags = new int[initialCapacity];
this.vrs = new VR[initialCapacity];
this.values = new Object[initialCapacity];
}
public Attributes(Attributes other) {
this(other, other.bigEndian);
}
public Attributes(Attributes other, boolean bigEndian) {
this(bigEndian, other.size);
if (other.properties != null)
properties = new HashMap<String, Object>(other.properties);
addAll(other);
}
public Attributes(Attributes other, int... selection) {
this(other, other.bigEndian, selection);
}
public Attributes(Attributes other, boolean bigEndian, int... selection) {
this(bigEndian, selection.length);
if (other.properties != null)
properties = new HashMap<String, Object>(other.properties);
addSelected(other, selection);
}
public Attributes(Attributes other, Attributes selection) {
this(selection.size());
if (other.properties != null)
properties = new HashMap<String, Object>(other.properties);
addSelected(other, selection);
}
public Attributes(Attributes other, boolean bigEndian, Attributes selection) {
this(bigEndian, selection.size());
if (other.properties != null)
properties = new HashMap<String, Object>(other.properties);
addSelected(other, selection);
}
public Map<String, Object> getProperties() {
return properties;
}
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
public Object getProperty(String key, Object defVal) {
if (properties == null)
return defVal;
Object val = properties.get(key);
return val != null ? val : defVal;
}
public Object setProperty(String key, Object value) {
if (properties == null)
properties = new HashMap<String, Object>();
return properties.put(key, value);
}
public Object clearProperty(String key) {
return properties != null ? properties.remove(key) : null;
}
public final boolean isRoot() {
return parent == null;
}
public Attributes getRoot() {
return isRoot() ? this : parent.getRoot();
}
public final int getLevel() {
return isRoot() ? 0 : 1 + parent.getLevel();
}
public final boolean bigEndian() {
return bigEndian;
}
public final Attributes getParent() {
return parent;
}
public String getParentSequencePrivateCreator() {
return parentSequencePrivateCreator;
}
public int getParentSequenceTag() {
return parentSequenceTag;
}
public final int getLength() {
return length;
}
Attributes setParent(Attributes parent, String parentSequencePrivateCreator, int parentSequenceTag) {
if (parent != null) {
if (parent.bigEndian != bigEndian)
throw new IllegalArgumentException(
"Endian of Item must match Endian of parent Data Set");
if (this.parent != null)
throw new IllegalArgumentException(
"Item already contained by Sequence");
if (!containsSpecificCharacterSet)
cs = null;
if (!containsTimezoneOffsetFromUTC)
tz = null;
}
this.parent = parent;
this.parentSequencePrivateCreator = parentSequencePrivateCreator;
this.parentSequenceTag = parentSequenceTag;
return this;
}
public final long getItemPosition() {
return itemPosition;
}
public final void setItemPosition(long itemPosition) {
this.itemPosition = itemPosition;
}
public final boolean isEmpty() {
return size == 0;
}
public final int size() {
return size;
}
public ItemPointer[] itemPointers() {
return itemPointers(0);
}
private ItemPointer[] itemPointers(int n) {
if (parent == null)
return new ItemPointer[n];
ItemPointer[] itemPointers = parent.itemPointers(n + 1);
itemPointers[itemPointers.length - n - 1] =
new ItemPointer(parentSequencePrivateCreator, parentSequenceTag, itemIndex());
return itemPointers;
}
public int itemIndex() {
if (parent == null)
return -1;
Sequence seq = parent.getSequence(parentSequencePrivateCreator, parentSequenceTag);
if (seq == null)
return -1;
return seq.indexOf(this);
}
public int[] tags() {
return Arrays.copyOf(tags, size);
}
public void trimToSize() {
trimToSize(false);
}
public void trimToSize(boolean recursive) {
int oldCapacity = tags.length;
if (size < oldCapacity) {
tags = Arrays.copyOf(tags, size);
vrs = Arrays.copyOf(vrs, size);
values = Arrays.copyOf(values, size);
}
if (recursive)
for (Object value : values) {
if (value instanceof Sequence) {
((Sequence) value).trimToSize(recursive);
} else if (value instanceof Fragments)
((Fragments) value).trimToSize();
}
}
public void internalizeStringValues(boolean decode) {
SpecificCharacterSet cs = getSpecificCharacterSet();
for (int i = 0; i < values.length; i++) {
VR vr = vrs[i];
Object value = values[i];
if (vr.isStringType()) {
if (value instanceof byte[]) {
if (!decode)
continue;
value = vr.toStrings((byte[]) value, bigEndian, cs);
}
if (value instanceof String)
values[i] = ((String) value).intern();
else if (value instanceof String[]) {
String[] ss = (String[]) value;
for (int j = 0; j < ss.length; j++)
ss[j] = ss[j].intern();
}
} else if (value instanceof Sequence)
for (Attributes item : (Sequence) value)
item.internalizeStringValues(decode);
}
}
private void decodeStringValuesUsingSpecificCharacterSet() {
Object value;
VR vr;
SpecificCharacterSet cs = getSpecificCharacterSet();
for (int i = 0; i < size; i++) {
value = values[i];
if (value instanceof Sequence) {
for (Attributes item : (Sequence) value)
item.decodeStringValuesUsingSpecificCharacterSet();
} else if ((vr = vrs[i]).useSpecificCharacterSet())
if (value instanceof byte[])
values[i] =
vr.toStrings((byte[]) value, bigEndian, cs);
}
}
private void ensureCapacity(int minCapacity) {
int oldCapacity = tags.length;
if (minCapacity > oldCapacity) {
int newCapacity = Math.max(minCapacity, oldCapacity << 1);
tags = Arrays.copyOf(tags, newCapacity);
vrs = Arrays.copyOf(vrs, newCapacity);
values = Arrays.copyOf(values, newCapacity);
}
}
public Attributes getNestedDataset(int sequenceTag) {
return getNestedDataset(null, sequenceTag, 0);
}
public Attributes getNestedDataset(int sequenceTag, int itemIndex) {
return getNestedDataset(null, sequenceTag, itemIndex);
}
public Attributes getNestedDataset(String privateCreator, int sequenceTag) {
return getNestedDataset(privateCreator, sequenceTag, 0);
}
public Attributes getNestedDataset(String privateCreator, int sequenceTag, int itemIndex) {
Object value = getValue(privateCreator, sequenceTag);
if (!(value instanceof Sequence))
return null;
Sequence sq = (Sequence) value;
if (itemIndex >= sq.size())
return null;
return sq.get(itemIndex);
}
public Attributes getNestedDataset(ItemPointer... itemPointers) {
Attributes item = this;
for (ItemPointer ip : itemPointers) {
Object value = item.getValue(ip.privateCreator, ip.sequenceTag);
if (!(value instanceof Sequence))
return null;
Sequence sq = (Sequence) value;
if (ip.itemIndex >= sq.size())
return null;
item = sq.get(ip.itemIndex);
}
return item;
}
private int indexForInsertOf(int tag) {
return size == 0 ? -1
: tags[size-1] < tag ? -(size+1)
: indexOf(tag);
}
private int indexOf(int tag) {
return Arrays.binarySearch(tags, 0, size, tag);
}
private int indexOf(String privateCreator, int tag) {
if (privateCreator != null) {
int creatorTag = creatorTagOf(privateCreator, tag, false);
if (creatorTag == -1)
return -1;
tag = TagUtils.toPrivateTag(creatorTag, tag);
}
return indexOf(tag);
}
/**
* resolves to the actual private tag,
* given a private tag with placeholers (like 0011,xx13)
*/
public int tagOf(String privateCreator, int tag) {
if (privateCreator != null) {
int creatorTag = creatorTagOf(privateCreator, tag, false);
if (creatorTag == -1)
return -1;
tag = TagUtils.toPrivateTag(creatorTag, tag);
}
return tag;
}
private int creatorTagOf(String privateCreator, int tag, boolean reserve) {
if (!TagUtils.isPrivateGroup(tag))
throw new IllegalArgumentException(TagUtils.toString(tag)
+ " is not a private Data Element");
int group = tag & 0xffff0000;
int creatorTag = group | 0x10;
int index = indexOf(creatorTag);
if (index < 0)
index = -index-1;
while (index < size && (tags[index] & 0xffffff00) == group) {
creatorTag = tags[index];
if (vrs[index] == VR.LO) {
Object creatorID = decodeStringValue(index);
if (privateCreator.equals(creatorID))
return creatorTag;
}
index++;
creatorTag++;
}
if (!reserve)
return -1;
if ((creatorTag & 0xff00) != 0)
throw new IllegalStateException("No free block for Private Element "
+ TagUtils.toString(tag));
setString(creatorTag, VR.LO, privateCreator);
return creatorTag;
}
private Object decodeStringValue(int index) {
Object value = values[index];
if (value instanceof byte[]) {
value = vrs[index].toStrings((byte[]) value, bigEndian,
getSpecificCharacterSet(vrs[index]));
if (value instanceof String && ((String) value).isEmpty())
value = Value.NULL;
values[index] = value;
}
return value;
}
public SpecificCharacterSet getSpecificCharacterSet(VR vr) {
return vr.useSpecificCharacterSet()
? getSpecificCharacterSet()
: SpecificCharacterSet.ASCII;
}
private double[] decodeDSValue(int index) {
Object value = values[index];
if (value == Value.NULL)
return ByteUtils.EMPTY_DOUBLES;
if (value instanceof double[])
return (double[]) value;
double[] ds;
if (value instanceof byte[])
value = vrs[index].toStrings((byte[]) value, bigEndian,
SpecificCharacterSet.ASCII);
if (value instanceof String) {
String s = (String) value;
if (s.isEmpty()) {
values[index] = Value.NULL;
return ByteUtils.EMPTY_DOUBLES;
}
ds = new double[] { StringUtils.parseDS(s) };
} else { // value instanceof String[]
String[] ss = (String[]) value;
ds = new double[ss.length];
for (int i = 0; i < ds.length; i++) {
String s = ss[i];
ds[i] = (s != null && !s.isEmpty())
? StringUtils.parseDS(s)
: Double.NaN;
}
}
values[index] = ds;
return ds;
}
private int[] decodeISValue(int index) {
Object value = values[index];
if (value == Value.NULL)
return ByteUtils.EMPTY_INTS;
if (value instanceof int[])
return (int[]) value;
int[] is;
if (value instanceof byte[])
value = vrs[index].toStrings((byte[]) value, bigEndian,
SpecificCharacterSet.ASCII);
if (value instanceof String) {
String s = (String) value;
if (s.isEmpty()) {
values[index] = Value.NULL;
return ByteUtils.EMPTY_INTS;
}
is = new int[] { StringUtils.parseIS(s) };
} else { // value instanceof String[]
String[] ss = (String[]) value;
is = new int[ss.length];
for (int i = 0; i < is.length; i++) {
String s = ss[i];
is[i] = (s != null && !s.isEmpty())
? StringUtils.parseIS(s)
: Integer.MIN_VALUE;
}
}
values[index] = is;
return is;
}
private void updateVR(int index, VR vr) {
VR prev = vrs[index];
if (vr == prev)
return;
Object value = values[index];
if (!(value == Value.NULL
|| value instanceof byte[]
|| vr.isStringType()
&& (value instanceof String
|| value instanceof String[])))
throw new IllegalStateException("value instanceof " + value.getClass());
vrs[index] = vr;
}
private static boolean isEmpty(Object value) {
return (value instanceof Value) && ((Value) value).isEmpty();
}
public boolean contains(int tag) {
return indexOf(tag) >= 0;
}
public boolean contains(String privateCreator, int tag) {
return indexOf(privateCreator, tag) >= 0;
}
public boolean containsValue(int tag) {
return containsValue(null, tag);
}
public boolean containsValue(String privateCreator, int tag) {
int index = indexOf(privateCreator, tag);
return index >= 0
&& !isEmpty(vrs[index].isStringType()
? decodeStringValue(index)
: values[index]);
}
public String privateCreatorOf(int tag) {
if (!TagUtils.isPrivateTag(tag))
return null;
int creatorTag = (tag & 0xffff0000) | ((tag >>> 8) & 0xff);
int index = indexOf(creatorTag);
if (index < 0 || vrs[index] != VR.LO || values[index] == Value.NULL)
return null;
Object value = decodeStringValue(index);
if (value == Value.NULL)
return null;
return VR.LO.toString(value, false, 0, null);
}
public Object getValue(int tag) {
return getValue(null, tag, null);
}
public Object getValue(int tag, VR.Holder vr) {
return getValue(null, tag, vr);
}
public Object getValue(String privateCreator, int tag) {
return getValue(privateCreator, tag, null);
}
public Object getValue(String privateCreator, int tag, VR.Holder vr) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
if (vr != null)
vr.vr = vrs[index];
return values[index];
}
public VR getVR(int tag) {
return getVR(null, tag);
}
public VR getVR(String privateCreator, int tag) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
return vrs[index];
}
public Sequence getSequence(int tag) {
return getSequence(null, tag);
}
public Sequence getSequence(String privateCreator, int tag) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
Object value = values[index];
if (value == Value.NULL)
return (Sequence) (values[index] = new Sequence(this, privateCreator, tag, 0));
return value instanceof Sequence ? (Sequence) value : null;
}
public byte[] getBytes(int tag) throws IOException {
return getBytes(null, tag);
}
public byte[] getBytes(String privateCreator, int tag) throws IOException {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
Object value = values[index];
VR vr = vrs[index];
try {
if (value instanceof Value)
return ((Value) value).toBytes(vr, bigEndian);
return vr.toBytes(value, getSpecificCharacterSet(vr));
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as bytes", TagUtils.toString(tag), vr);
return null;
}
}
public byte[] getSafeBytes(int tag) {
return getSafeBytes(null, tag);
}
public byte[] getSafeBytes(String privateCreator, int tag) {
try {
return getBytes(privateCreator, tag);
} catch (IOException e) {
LOG.info("Access " + TagUtils.toString(tag)
+ " throws i/o exception", e);
return null;
}
}
public String getString(int tag) {
return getString(null, tag, null, 0, null);
}
public String getString(int tag, String defVal) {
return getString(null, tag, null, 0, defVal);
}
public String getString(int tag, int valueIndex) {
return getString(null, tag, null, valueIndex, null);
}
public String getString(int tag, int valueIndex, String defVal) {
return getString(null, tag, null, valueIndex, defVal);
}
public String getString(String privateCreator, int tag) {
return getString(privateCreator, tag, null, 0, null);
}
public String getString(String privateCreator, int tag, String defVal) {
return getString(privateCreator, tag, null, 0, defVal);
}
public String getString(String privateCreator, int tag, VR vr) {
return getString(privateCreator, tag, vr, 0, null);
}
public String getString(String privateCreator, int tag, VR vr, String defVal) {
return getString(privateCreator, tag, vr, 0, defVal);
}
public String getString(String privateCreator, int tag, int valueIndex) {
return getString(privateCreator, tag, null, valueIndex, null);
}
public String getString(String privateCreator, int tag, int valueIndex, String defVal) {
return getString(privateCreator, tag, null, valueIndex, defVal);
}
public String getString(String privateCreator, int tag, VR vr, int valueIndex) {
return getString(privateCreator, tag, vr, valueIndex, null);
}
public String getString(String privateCreator, int tag, VR vr, int valueIndex, String defVal) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return defVal;
Object value = values[index];
if (value == Value.NULL)
return defVal;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (vr.isStringType()) {
value = decodeStringValue(index);
if (value == Value.NULL)
return defVal;
}
try {
return vr.toString(value, bigEndian, valueIndex, defVal);
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as string", TagUtils.toString(tag), vr);
return defVal;
}
}
public String[] getStrings(int tag) {
return getStrings(null, tag, null);
}
public String[] getStrings(String privateCreator, int tag) {
return getStrings(privateCreator, tag, null);
}
public String[] getStrings(String privateCreator, int tag, VR vr) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
Object value = values[index];
if (value == Value.NULL)
return StringUtils.EMPTY_STRING;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (vr.isStringType()) {
value = decodeStringValue(index);
if (value == Value.NULL)
return StringUtils.EMPTY_STRING;
}
try {
return toStrings(vr.toStrings(value, bigEndian,
getSpecificCharacterSet(vr)));
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as string", TagUtils.toString(tag), vr);
return null;
}
}
private static String[] toStrings(Object val) {
return (val instanceof String)
? new String[] { (String) val }
: (String[]) val;
}
public int getInt(int tag, int defVal) {
return getInt(null, tag, null, 0, defVal);
}
public int getInt(int tag, int valueIndex, int defVal) {
return getInt(null, tag, null, valueIndex, defVal);
}
public int getInt(String privateCreator, int tag, int defVal) {
return getInt(privateCreator, tag, null, 0, defVal);
}
public int getInt(String privateCreator, int tag, VR vr, int defVal) {
return getInt(privateCreator, tag, vr, 0, defVal);
}
public int getInt(String privateCreator, int tag, int valueIndex, int defVal) {
return getInt(privateCreator, tag, null, valueIndex, defVal);
}
public int getInt(String privateCreator, int tag, VR vr, int valueIndex, int defVal) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return defVal;
Object value = values[index];
if (value == Value.NULL)
return defVal;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (vr == VR.IS)
value = decodeISValue(index);
try {
return vr.toInt(value, bigEndian, valueIndex, defVal);
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as int", TagUtils.toString(tag), vr);
return defVal;
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return defVal;
}
}
public int[] getInts(int tag) {
return getInts(null, tag, null);
}
public int[] getInts(String privateCreator, int tag) {
return getInts(privateCreator, tag, null);
}
public int[] getInts(String privateCreator, int tag, VR vr) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
Object value = values[index];
if (value == Value.NULL)
return ByteUtils.EMPTY_INTS;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (vr == VR.IS)
value = decodeISValue(index);
try {
return vr.toInts(value, bigEndian);
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as int", TagUtils.toString(tag), vr);
return null;
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return null;
}
}
public float getFloat(int tag, float defVal) {
return getFloat(null, tag, null, 0, defVal);
}
public float getFloat(int tag, int valueIndex, float defVal) {
return getFloat(null, tag, null, valueIndex, defVal);
}
public float getFloat(String privateCreator, int tag, float defVal) {
return getFloat(privateCreator, tag, null, 0, defVal);
}
public float getFloat(String privateCreator, int tag, VR vr, float defVal) {
return getFloat(privateCreator, tag, vr, 0, defVal);
}
public float getFloat(String privateCreator, int tag, int valueIndex, float defVal) {
return getFloat(privateCreator, tag, null, valueIndex, defVal);
}
public float getFloat(String privateCreator, int tag, VR vr, int valueIndex, float defVal) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return defVal;
Object value = values[index];
if (value == Value.NULL)
return defVal;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (vr == VR.DS)
value = decodeDSValue(index);
try {
return vr.toFloat(value, bigEndian, valueIndex, defVal);
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as float", TagUtils.toString(tag), vr);
return defVal;
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return defVal;
}
}
public float[] getFloats(int tag) {
return getFloats(null, tag, null);
}
public float[] getFloats(String privateCreator, int tag) {
return getFloats(privateCreator, tag, null);
}
public float[] getFloats(String privateCreator, int tag, VR vr) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
Object value = values[index];
if (value == Value.NULL)
return ByteUtils.EMPTY_FLOATS;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (vr == VR.DS)
value = decodeDSValue(index);
try {
return vr.toFloats(value, bigEndian);
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as float", TagUtils.toString(tag), vr);
return null;
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return null;
}
}
public double getDouble(int tag, double defVal) {
return getDouble(null, tag, null, 0, defVal);
}
public double getDouble(int tag, int valueIndex, double defVal) {
return getDouble(null, tag, null, valueIndex, defVal);
}
public double getDouble(String privateCreator, int tag, double defVal) {
return getDouble(privateCreator, tag, null, 0, defVal);
}
public double getDouble(String privateCreator, int tag, VR vr, double defVal) {
return getDouble(privateCreator, tag, vr, 0, defVal);
}
public double getDouble(String privateCreator, int tag, int valueIndex, double defVal) {
return getDouble(privateCreator, tag, null, valueIndex, defVal);
}
public double getDouble(String privateCreator, int tag, VR vr, int valueIndex, double defVal) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return defVal;
Object value = values[index];
if (value == Value.NULL)
return defVal;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (vr == VR.DS)
value = decodeDSValue(index);
try {
return vr.toDouble(value, bigEndian, valueIndex, defVal);
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as double", TagUtils.toString(tag), vr);
return defVal;
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return defVal;
}
}
public double[] getDoubles(int tag) {
return getDoubles(null, tag, null);
}
public double[] getDoubles(String privateCreator, int tag) {
return getDoubles(privateCreator, tag, null);
}
public double[] getDoubles(String privateCreator, int tag, VR vr) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
Object value = values[index];
if (value == Value.NULL)
return ByteUtils.EMPTY_DOUBLES;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (vr == VR.DS)
value = decodeDSValue(index);
try {
return vr.toDoubles(value, bigEndian);
} catch (UnsupportedOperationException e) {
LOG.info("Attempt to access {} {} as double", TagUtils.toString(tag), vr);
return null;
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return null;
}
}
public Date getDate(int tag) {
return getDate(null, tag, null, 0, null, new DatePrecision());
}
public Date getDate(int tag, DatePrecision precision) {
return getDate(null, tag, null, 0, null, precision);
}
public Date getDate(int tag, Date defVal) {
return getDate(null, tag, null, 0, defVal, new DatePrecision());
}
public Date getDate(int tag, Date defVal, DatePrecision precision) {
return getDate(null, tag, null, 0, defVal, precision);
}
public Date getDate(int tag, int valueIndex) {
return getDate(null, tag, null, valueIndex, null, new DatePrecision());
}
public Date getDate(int tag, int valueIndex, DatePrecision precision) {
return getDate(null, tag, null, valueIndex, null, precision);
}
public Date getDate(int tag, int valueIndex, Date defVal) {
return getDate(null, tag, null, valueIndex, defVal, new DatePrecision());
}
public Date getDate(int tag, int valueIndex, Date defVal,
DatePrecision precision) {
return getDate(null, tag, null, valueIndex, defVal, precision);
}
public Date getDate(String privateCreator, int tag) {
return getDate(privateCreator, tag, null, 0, null, new DatePrecision());
}
public Date getDate(String privateCreator, int tag, DatePrecision precision) {
return getDate(privateCreator, tag, null, 0, null, precision);
}
public Date getDate(String privateCreator, int tag, Date defVal,
DatePrecision precision) {
return getDate(privateCreator, tag, null, 0, defVal, precision);
}
public Date getDate(String privateCreator, int tag, VR vr) {
return getDate(privateCreator, tag, vr, 0, null, new DatePrecision());
}
public Date getDate(String privateCreator, int tag, VR vr,
DatePrecision precision) {
return getDate(privateCreator, tag, vr, 0, null, precision);
}
public Date getDate(String privateCreator, int tag, VR vr, Date defVal) {
return getDate(privateCreator, tag, vr, 0, defVal, new DatePrecision());
}
public Date getDate(String privateCreator, int tag, VR vr, Date defVal,
DatePrecision precision) {
return getDate(privateCreator, tag, vr, 0, defVal, precision);
}
public Date getDate(String privateCreator, int tag, int valueIndex) {
return getDate(privateCreator, tag, null, valueIndex, null,
new DatePrecision());
}
public Date getDate(String privateCreator, int tag, int valueIndex,
DatePrecision precision) {
return getDate(privateCreator, tag, null, valueIndex, null, precision);
}
public Date getDate(String privateCreator, int tag, int valueIndex,
Date defVal) {
return getDate(privateCreator, tag, null, valueIndex, defVal,
new DatePrecision());
}
public Date getDate(String privateCreator, int tag, int valueIndex,
Date defVal, DatePrecision precision) {
return getDate(privateCreator, tag, null, valueIndex, defVal, precision);
}
public Date getDate(String privateCreator, int tag, VR vr, int valueIndex) {
return getDate(privateCreator, tag, vr, valueIndex, null,
new DatePrecision());
}
public Date getDate(String privateCreator, int tag, VR vr, int valueIndex,
DatePrecision precision) {
return getDate(privateCreator, tag, vr, valueIndex, null, precision);
}
public Date getDate(String privateCreator, int tag, VR vr, int valueIndex,
Date defVal) {
return getDate(privateCreator, tag, vr, valueIndex, defVal,
new DatePrecision());
}
public Date getDate(String privateCreator, int tag, VR vr, int valueIndex,
Date defVal, DatePrecision precision) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return defVal;
Object value = values[index];
if (value == Value.NULL)
return defVal;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (!vr.isTemporalType()) {
LOG.info("Attempt to access {} {} as date", TagUtils.toString(tag), vr);
return defVal;
}
try {
value = decodeStringValue(index);
if (value == Value.NULL)
return defVal;
return vr.toDate(value, getTimeZone(), valueIndex, false, defVal, precision);
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return defVal;
}
}
public Date getDate(long tag) {
return getDate(null, tag, null, new DatePrecision());
}
public Date getDate(long tag, DatePrecision precision) {
return getDate(null, tag, null, precision);
}
public Date getDate(long tag, Date defVal) {
return getDate(null, tag, defVal, new DatePrecision());
}
public Date getDate(long tag, Date defVal, DatePrecision precision) {
return getDate(null, tag, defVal, precision);
}
public Date getDate(String privateCreator, long tag) {
return getDate(privateCreator, tag, null, new DatePrecision());
}
public Date getDate(String privateCreator, long tag, DatePrecision precision) {
return getDate(privateCreator, tag, null, precision);
}
public Date getDate(String privateCreator, long tag, Date defVal) {
return getDate(privateCreator, tag, defVal, new DatePrecision());
}
public Date getDate(String privateCreator, long tag, Date defVal,
DatePrecision precision) {
int daTag = (int) (tag >>> 32);
int tmTag = (int) tag;
String tm = getString(privateCreator, tmTag, VR.TM, null);
if (tm == null)
return getDate(daTag, defVal, precision);
String da = getString(privateCreator, daTag, VR.DA, null);
if (da == null)
return defVal;
try {
return VR.DT.toDate(da + tm, getTimeZone(), 0, false, null,
precision);
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} DA or {} TM",
TagUtils.toString(daTag),
TagUtils.toString(tmTag));
return defVal;
}
}
public Date[] getDates(int tag) {
return getDates(null, tag, null, new DatePrecisions());
}
public Date[] getDates(int tag, DatePrecisions precisions) {
return getDates(null, tag, null, precisions);
}
public Date[] getDates(String privateCreator, int tag) {
return getDates(privateCreator, tag, null, new DatePrecisions());
}
public Date[] getDates(String privateCreator, int tag,
DatePrecisions precisions) {
return getDates(privateCreator, tag, null, precisions);
}
public Date[] getDates(String privateCreator, int tag, VR vr) {
return getDates(privateCreator, tag, vr, new DatePrecisions());
}
public Date[] getDates(String privateCreator, int tag, VR vr,
DatePrecisions precisions) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
Object value = values[index];
if (value == Value.NULL)
return DateUtils.EMPTY_DATES;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (!vr.isTemporalType()) {
LOG.info("Attempt to access {} {} as date", TagUtils.toString(tag), vr);
return DateUtils.EMPTY_DATES;
}
try {
value = decodeStringValue(index);
if (value == Value.NULL)
return DateUtils.EMPTY_DATES;
return vr.toDates(value, getTimeZone(), false, precisions);
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return DateUtils.EMPTY_DATES;
}
}
public Date[] getDates(long tag) {
return getDates(null, tag, new DatePrecisions());
}
public Date[] getDates(long tag, DatePrecisions precisions) {
return getDates(null, tag, precisions);
}
public Date[] getDates(String privateCreator, long tag) {
return getDates(privateCreator, tag, new DatePrecisions());
}
public Date[] getDates(String privateCreator, long tag,
DatePrecisions precisions) {
int daTag = (int) (tag >>> 32);
int tmTag = (int) tag;
String[] tm = getStrings(privateCreator, tmTag);
if (tm == null || tm.length == 0)
return getDates(daTag, precisions);
String[] da = getStrings(privateCreator, daTag);
if (da == null || da.length == 0)
return DateUtils.EMPTY_DATES;
Date[] dates = new Date[da.length];
precisions.precisions = new DatePrecision[da.length];
int i = 0;
try {
TimeZone tz = getTimeZone();
while (i < tm.length)
dates[i++] = VR.DT.toDate(da[i] + tm[i], tz, 0, false, null,
precisions.precisions[i] = new DatePrecision());
while (i < da.length)
dates[i++] = VR.DA.toDate(da[i], tz, 0, false, null,
precisions.precisions[i] = new DatePrecision());
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} DA or {} TM",
TagUtils.toString(daTag),
TagUtils.toString(tmTag));
dates = Arrays.copyOf(dates, i);
}
return dates;
}
public DateRange getDateRange(int tag) {
return getDateRange(null, tag, null, null);
}
public DateRange getDateRange(int tag, DateRange defVal) {
return getDateRange(null, tag, null, defVal);
}
public DateRange getDateRange(String privateCreator, int tag) {
return getDateRange(privateCreator, tag, null, null);
}
public DateRange getDateRange(String privateCreator, int tag, DateRange defVal) {
return getDateRange(privateCreator, tag, null, defVal);
}
public DateRange getDateRange(String privateCreator, int tag, VR vr) {
return getDateRange(privateCreator, tag, vr, null);
}
public DateRange getDateRange(String privateCreator, int tag, VR vr, DateRange defVal) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return defVal;
Object value = values[index];
if (value == Value.NULL)
return defVal;
if (vr == null)
vr = vrs[index];
else
updateVR(index, vr);
if (!vr.isTemporalType()) {
LOG.info("Attempt to access {} {} as date", TagUtils.toString(tag), vr);
return defVal;
}
value = decodeStringValue(index);
if (value == Value.NULL)
return defVal;
try {
return toDateRange((value instanceof String)
? (String) value : ((String[]) value)[0], vr);
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} {}", TagUtils.toString(tag), vr);
return defVal;
}
}
private DateRange toDateRange(String s, VR vr) {
String[] range = splitRange(s);
TimeZone tz = getTimeZone();
DatePrecision precision = new DatePrecision();
Date start = range[0] == null ? null
: vr.toDate(range[0], tz, 0, false, null, precision);
Date end = range[1] == null ? null
: vr.toDate(range[1], tz, 0, true, null, precision);
return new DateRange(start, end);
}
private static String[] splitRange(String s) {
String[] range = new String[2];
int delim = s.indexOf('-');
if (delim == -1)
range[0] = range[1] = s;
else {
if (delim > 0)
range[0] = s.substring(0, delim);
if (delim < s.length() - 1)
range[1] = s.substring(delim+1);
}
return range;
}
public DateRange getDateRange(long tag) {
return getDateRange(null, tag, null);
}
public DateRange getDateRange(long tag, DateRange defVal) {
return getDateRange(null, tag, defVal);
}
public DateRange getDateRange(String privateCreator, long tag) {
return getDateRange(privateCreator, tag, null);
}
public DateRange getDateRange(String privateCreator, long tag, DateRange defVal) {
int daTag = (int) (tag >>> 32);
int tmTag = (int) tag;
String tm = getString(privateCreator, tmTag, VR.TM, null);
if (tm == null)
return getDateRange(daTag, defVal);
String da = getString(privateCreator, daTag, VR.DA, null);
if (da == null)
return defVal;
try {
return toDateRange(da, tm);
} catch (IllegalArgumentException e) {
LOG.info("Invalid value of {} TM", TagUtils.toString((int) tag));
return defVal;
}
}
private DateRange toDateRange(String da, String tm) {
String[] darange = splitRange(da);
String[] tmrange = splitRange(tm);
DatePrecision precision = new DatePrecision();
TimeZone tz = getTimeZone();
return new DateRange(
darange[0] == null ? null
: VR.DT.toDate(tmrange[0] == null
? darange[0]
: darange[0] + tmrange[0],
tz, 0, false, null, precision ),
darange[1] == null ? null
: VR.DT.toDate(tmrange[1] == null
? darange[1]
: darange[1] + tmrange[1],
tz, 0, true, null, precision));
}
/**
* Set Specific Character Set (0008,0005) to specified code(s) and
* re-encode contained LO, LT, PN, SH, ST, UT attributes
* accordingly.
*
* @param codes new value(s) of Specific Character Set (0008,0005)
*/
public void setSpecificCharacterSet(String... codes) {
decodeStringValuesUsingSpecificCharacterSet();
setString(Tag.SpecificCharacterSet, VR.CS, codes);
}
public SpecificCharacterSet getSpecificCharacterSet() {
if (cs != null)
return cs;
if (containsSpecificCharacterSet)
cs = SpecificCharacterSet.valueOf(
getStrings(null, Tag.SpecificCharacterSet, VR.CS));
else if (parent != null)
return parent.getSpecificCharacterSet();
else
cs = SpecificCharacterSet.getDefaultCharacterSet();
return cs;
}
public boolean containsTimezoneOffsetFromUTC() {
return containsTimezoneOffsetFromUTC;
}
public void setDefaultTimeZone(TimeZone tz) {
defaultTimeZone = tz;
}
public TimeZone getDefaultTimeZone() {
if (defaultTimeZone != null)
return defaultTimeZone;
if (parent != null)
return parent.getDefaultTimeZone();
return TimeZone.getDefault();
}
public TimeZone getTimeZone() {
if (tz != null)
return tz;
if (containsTimezoneOffsetFromUTC) {
String s = getString(Tag.TimezoneOffsetFromUTC);
if (s != null)
try {
tz = DateUtils.timeZone(s);
} catch (IllegalArgumentException e) {
LOG.info(e.getMessage());
}
} else if (parent != null)
return parent.getTimeZone();
else
tz = getDefaultTimeZone();
return tz;
}
/**
* Set Timezone Offset From UTC (0008,0201) to specified value and
* adjust contained DA, DT and TM attributs accordingly
*
* @param utcOffset offset from UTC as (+|-)HHMM
*/
public void setTimezoneOffsetFromUTC(String utcOffset) {
TimeZone tz = DateUtils.timeZone(utcOffset);
updateTimezone(getTimeZone(), tz);
setString(Tag.TimezoneOffsetFromUTC, VR.SH, utcOffset);
this.tz = tz;
}
/**
* Set the Default Time Zone to specified value and adjust contained DA,
* DT and TM attributs accordingly. If the Time Zone does not use Daylight
* Saving Time, attribute Timezone Offset From UTC (0008,0201) will be also
* set accordingly. If the Time zone uses Daylight Saving Time, a previous
* existing attribute Timezone Offset From UTC (0008,0201) will be removed.
*
* @param tz Time Zone
*
* @see #setDefaultTimeZone(TimeZone)
* @see #setTimezoneOffsetFromUTC(String)
*/
public void setTimezone(TimeZone tz) {
updateTimezone(getTimeZone(), tz);
if (tz.useDaylightTime()) {
remove(Tag.TimezoneOffsetFromUTC);
setDefaultTimeZone(tz);
} else {
setString(Tag.TimezoneOffsetFromUTC, VR.SH,
DateUtils.formatTimezoneOffsetFromUTC(tz));
}
this.tz = tz;
}
/**
* Updates the time zone of a specific standard or private tag
*
* @param from Time Zone from
* @param to Time Zone to
* @param privateCreator private creator - null otherwise
* @param tag Attribute tag to update time zone
*/
public void updateTimeZoneOfSpecificTag(TimeZone from, TimeZone to
, String privateCreator, int tag) {
updateTimezone(from, to,indexOf(privateCreator, tag));
}
private void updateTimezone(TimeZone from, TimeZone to) {
if (from.hasSameRules(to))
return;
for (int i = 0; i < size; i++) {
Object val = values[i];
if (val instanceof Sequence) {
Sequence new_name = (Sequence) val;
for (Attributes item : new_name) {
item.updateTimezone(item.getTimeZone(), to);
item.remove(Tag.TimezoneOffsetFromUTC);
}
} else if (vrs[i] == VR.TM && tags[i] != Tag.PatientBirthTime
|| vrs[i] == VR.DT && tags[i] != Tag.ContextGroupVersion
&& tags[i] != Tag.ContextGroupLocalVersion)
updateTimezone(from, to, i);
}
}
private void updateTimezone(TimeZone from, TimeZone to, int tmIndex) {
Object tm = decodeStringValue(tmIndex);
if (tm == Value.NULL)
return;
int tmTag = tags[tmIndex];
if (vrs[tmIndex] == VR.DT) {
if (tm instanceof String[]) {
String[] tms = (String[]) tm;
for (int i = 0; i < tms.length; i++) {
tms[i] = updateTimeZoneDT(from, to, tms[i]);
}
} else
values[tmIndex] = updateTimeZoneDT(from, to, (String) tm);
} else {
int daTag = ElementDictionary.getElementDictionary(privateCreatorOf(tmTag)).daTagOf(tmTag);
int daIndex = daTag != 0 ? indexOf(daTag) : -1;
Object da = daIndex >= 0 ? decodeStringValue(daIndex) : Value.NULL;
if (tm instanceof String[]) {
String[] tms = (String[]) tm;
if (da instanceof String[]) {
String[] das = (String[]) da;
for (int i = 0; i < tms.length; i++) {
if (i < das.length) {
String dt = updateTimeZoneDT(
from, to, das[i] + tms[i]);
das[i] = dt.substring(0,8);
tms[i] = dt.substring(8);
} else {
tms[i] = updateTimeZoneTM(from, to, tms[i]);
}
}
} else {
if (da == Value.NULL) {
tms[0] = updateTimeZoneTM(from, to, tms[0]);
} else {
String dt = updateTimeZoneDT(
from, to, (String) da + tms[0]);
values[daIndex] = dt.substring(0,8);
tms[0] = dt.substring(8);
}
for (int i = 1; i < tms.length; i++) {
tms[i] = updateTimeZoneTM(from, to, tms[i]);
}
}
} else {
if (da instanceof String[]) {
String[] das = (String[]) da;
String dt = updateTimeZoneDT(
from, to, das[0] + (String) tm);
das[0] = dt.substring(0,8);
values[tmIndex] = dt.substring(8);
} else {
String[] tmRange = null;
if (isRange((String) tm)) {
tmRange = splitRange((String) tm);
if (tmRange[0] == null)
tmRange[0] = "000000.000";
if (tmRange[1] == null)
tmRange[1] = "235959.999";
}
if (da == Value.NULL) {
if (tmRange != null) {
tmRange[0] = updateTimeZoneTM(
from, to, tmRange[0]);
tmRange[1] = updateTimeZoneTM(
from, to, tmRange[1]);
values[tmIndex] = toDateRangeString(
tmRange[0], tmRange[1]);
} else {
values[tmIndex] = updateTimeZoneTM(
from, to, (String) tm);
}
} else {
if (tmRange != null) {
String[] daRange = splitRange((String) da);
if (daRange[0] == null) {
daRange[0] = "";
tmRange[0] = updateTimeZoneTM(from, to, tmRange[0]);
} else {
String dt = updateTimeZoneDT(
from, to, daRange[0] + tmRange[0]);
daRange[0] = dt.substring(0,8);
tmRange[0] = dt.substring(8);
}
if (daRange[1] == null) {
daRange[1] = "";
tmRange[1] = updateTimeZoneTM(from, to, tmRange[1]);
} else {
String dt = updateTimeZoneDT(
from, to, daRange[1] + tmRange[1]);
daRange[1] = dt.substring(0,8);
tmRange[1] = dt.substring(8);
}
values[daIndex] = toDateRangeString(
daRange[0], daRange[1]);
values[tmIndex] = toDateRangeString(
tmRange[0], tmRange[1]);
} else {
String dt = updateTimeZoneDT(
from, to, (String) da + (String) tm);
values[daIndex] = dt.substring(0,8);
values[tmIndex] = dt.substring(8);
}
}
}
}
}
}
private static boolean isRange(String s) {
return s.indexOf('-') >= 0;
}
private String updateTimeZoneDT(TimeZone from, TimeZone to, String dt) {
int dtlen = dt.length();
if (dtlen > 8) {
char ch = dt.charAt(dtlen-5);
if (ch == '+' || ch == '-')
return dt;
}
try {
DatePrecision precision = new DatePrecision();
Date date = DateUtils.parseDT(from, dt, false, precision);
dt = DateUtils.formatDT(to, date, precision);
} catch (IllegalArgumentException e) {
// ignored
}
return dt;
}
private String updateTimeZoneTM(TimeZone from, TimeZone to, String tm) {
try {
DatePrecision precision = new DatePrecision();
Date date = DateUtils.parseTM(from, tm, false, precision);
tm = DateUtils.formatTM(to, date, precision);
} catch (IllegalArgumentException e) {
// ignored
}
return tm;
}
public String getPrivateCreator(int tag) {
return TagUtils.isPrivateTag(tag)
? getString(TagUtils.creatorTagOf(tag), null)
: null;
}
public Object remove(int tag) {
return remove(null, tag);
}
public Object remove(String privateCreator, int tag) {
int index = indexOf(privateCreator, tag);
if (index < 0)
return null;
Object value = values[index];
// if (value instanceof Sequence)
// ((Sequence) value).clear();
int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(tags, index+1, tags, index, numMoved);
System.arraycopy(vrs, index+1, vrs, index, numMoved);
System.arraycopy(values, index+1, values, index, numMoved);
}
values[--size] = null;
if (tag == Tag.SpecificCharacterSet) {
containsSpecificCharacterSet = false;
cs = null;
} else if (tag == Tag.TimezoneOffsetFromUTC) {
containsTimezoneOffsetFromUTC = false;
tz = null;
}
return value;
}
public Object setNull(int tag, VR vr) {
return setNull(null, tag, vr);
}
public Object setNull(String privateCreator, int tag, VR vr) {
return set(privateCreator, tag, vr, Value.NULL);
}
public Object setBytes(int tag, VR vr, byte[] b) {
return setBytes(null, tag, vr, b);
}
public Object setBytes(String privateCreator, int tag, VR vr, byte[] b) {
return set(privateCreator, tag, vr, vr.toValue(b));
}
public Object setString(int tag, VR vr, String s) {
return setString(null, tag, vr, s);
}
public Object setString(String privateCreator, int tag, VR vr, String s) {
return set(privateCreator, tag, vr, vr.toValue(s, bigEndian));
}
public Object setString(int tag, VR vr, String... ss) {
return setString(null, tag, vr, ss);
}
public Object setString(String privateCreator, int tag, VR vr, String... ss) {
return set(privateCreator, tag, vr, vr.toValue(ss, bigEndian));
}
public Object setInt(int tag, VR vr, int... is) {
return setInt(null, tag, vr, is);
}
public Object setInt(String privateCreator, int tag, VR vr, int... is) {
return set(privateCreator, tag, vr, vr.toValue(is, bigEndian));
}
public Object setFloat(int tag, VR vr, float... fs) {
return setFloat(null, tag, vr, fs);
}
public Object setFloat(String privateCreator, int tag, VR vr, float... fs) {
return set(privateCreator, tag, vr, vr.toValue(fs, bigEndian));
}
public Object setDouble(int tag, VR vr, double... ds) {
return setDouble(null, tag, vr, ds);
}
public Object setDouble(String privateCreator, int tag, VR vr, double... ds) {
return set(privateCreator, tag, vr, vr.toValue(ds, bigEndian));
}
public Object setDate(int tag, VR vr, Date... ds) {
return setDate(null, tag, vr, ds);
}
public Object setDate(int tag, VR vr, DatePrecision precision, Date... ds) {
return setDate(null, tag, vr, precision, ds);
}
public Object setDate(String privateCreator, int tag, VR vr,
Date... ds) {
return setDate(privateCreator, tag, vr, new DatePrecision(), ds);
}
public Object setDate(String privateCreator, int tag, VR vr,
DatePrecision precision, Date... ds) {
return set(privateCreator, tag, vr, vr.toValue(ds, getTimeZone(), precision));
}
public void setDate(long tag, Date dt) {
setDate(null, tag, dt);
}
public void setDate(long tag, DatePrecision precision, Date dt) {
setDate(null, tag, precision, dt);
}
public void setDate(String privateCreator, long tag, Date dt) {
setDate(privateCreator, tag, new DatePrecision(), dt);
}
public void setDate(String privateCreator, long tag,
DatePrecision precision, Date dt) {
int daTag = (int) (tag >>> 32);
int tmTag = (int) tag;
setDate(privateCreator, daTag, VR.DA, precision, dt);
setDate(privateCreator, tmTag, VR.TM, precision, dt);
}
public Object setDateRange(int tag, VR vr, DateRange range) {
return setDateRange(null, tag, vr, range);
}
public Object setDateRange(String privateCreator, int tag, VR vr, DateRange range) {
return set(privateCreator, tag, vr, toString(range, vr, getTimeZone()));
}
private static String toString(DateRange range, VR vr, TimeZone tz) {
DatePrecision precision = new DatePrecision();
String start = range.getStartDate() != null
? (String) vr.toValue(new Date[]{range.getStartDate()}, tz,
precision)
: "";
String end = range.getEndDate() != null
? (String) vr.toValue(new Date[]{range.getEndDate()}, tz,
precision)
: "";
return toDateRangeString(start, end);
}
private static String toDateRangeString(String start, String end) {
return start.equals(end) ? start : (start + '-' + end);
}
public void setDateRange(long tag, DateRange dr) {
setDateRange(null, tag, dr);
}
public void setDateRange(String privateCreator, long tag, DateRange range) {
int daTag = (int) (tag >>> 32);
int tmTag = (int) tag;
setDateRange(privateCreator, daTag, VR.DA, range);
setDateRange(privateCreator, tmTag, VR.TM, range);
}
public Object setValue(int tag, VR vr, Object value) {
return setValue(null, tag, vr, value);
}
public Object setValue(String privateCreator, int tag, VR vr, Object value) {
return set(privateCreator, tag, vr, value != null ? value : Value.NULL);
}
public Sequence newSequence(int tag, int initialCapacity) {
return newSequence(null, tag, initialCapacity);
}
public Sequence newSequence(String privateCreator, int tag, int initialCapacity) {
Sequence seq = new Sequence(this, privateCreator, tag, initialCapacity);
set(privateCreator, tag, VR.SQ, seq);
return seq;
}
public Sequence ensureSequence(int tag, int initialCapacity) {
return ensureSequence(null, tag, initialCapacity);
}
public Sequence ensureSequence(String privateCreator, int tag, int initialCapacity) {
if (privateCreator != null) {
int creatorTag = creatorTagOf(privateCreator, tag, true);
tag = TagUtils.toPrivateTag(creatorTag, tag);
}
Sequence seq;
int index = indexOf(tag);
if (index >= 0) {
Object oldValue = values[index];
if (oldValue instanceof Sequence)
seq = (Sequence) oldValue;
else
values[index] = seq = new Sequence(this, privateCreator, tag, initialCapacity);
} else {
seq = new Sequence(this, privateCreator, tag, initialCapacity);
insert(-index-1, tag, VR.SQ, seq);
}
return seq;
}
public Fragments newFragments(int tag, VR vr, int initialCapacity) {
return newFragments(null, tag, vr, initialCapacity);
}
public Fragments newFragments(String privateCreator, int tag, VR vr,
int initialCapacity) {
Fragments frags = new Fragments(privateCreator, tag, vr, bigEndian, initialCapacity);
set(privateCreator, tag, vr, frags);
return frags;
}
private Object set(String privateCreator, int tag, VR vr, Object value) {
if (vr == null)
throw new NullPointerException("vr");
if (privateCreator != null) {
int creatorTag = creatorTagOf(privateCreator, tag, true);
tag = TagUtils.toPrivateTag(creatorTag, tag);
}
if (TagUtils.isGroupLength(tag))
return null;
Object oldValue = set(tag, vr, value);
if (tag == Tag.SpecificCharacterSet) {
containsSpecificCharacterSet = true;
cs = null;
} else if (tag == Tag.TimezoneOffsetFromUTC) {
containsTimezoneOffsetFromUTC = value != Value.NULL;
tz = null;
}
return oldValue;
}
public void addBulkDataReference(String privateCreator, int tag, VR vr, BulkData bulkData,
ItemPointer... itemPointers) {
Sequence seq = ensureSequence(Tag.ReferencedBulkDataSequence, 8);
Attributes item = new Attributes(bigEndian, 7);
seq.add(item);
item.setString(Tag.RetrieveURL, VR.UR, bulkData.uri);
item.setInt(Tag.SelectorAttribute, VR.AT, privateCreator != null ? (tag & 0xffff00ff) : tag);
item.setString(Tag.SelectorAttributeVR, VR.CS, vr.name());
if (privateCreator != null)
item.setString(Tag.SelectorAttributePrivateCreator, VR.LO, privateCreator);
if (itemPointers.length > 0) {
int[] seqTags = new int[itemPointers.length];
int[] itemNumbers = new int[itemPointers.length];
String[] privateCreators = null;
for (int i = 0; i < itemPointers.length; i++) {
ItemPointer ip = itemPointers[i];
seqTags[i] = ip.privateCreator != null ? (ip.sequenceTag & 0xffff00ff) : ip.sequenceTag;
itemNumbers[i] = ip.itemIndex + 1;
if (ip.privateCreator != null) {
if (privateCreators == null)
privateCreators = new String[itemPointers.length];
privateCreators[i] = ip.privateCreator;
}
}
item.setInt(Tag.SelectorSequencePointer, VR.AT, seqTags);
if (privateCreators != null)
item.setString(Tag.SelectorSequencePointerPrivateCreator, VR.LO, privateCreators);
item.setInt(Tag.SelectorSequencePointerItems, VR.IS, itemNumbers);
}
item.trimToSize();
}
private Object set(int tag, VR vr, Object value) {
int index = indexForInsertOf(tag);
if (index >= 0) {
Object oldValue = values[index];
vrs[index] = vr;
values[index] = value;
return oldValue;
}
insert(-index - 1, tag, vr, value);
return null;
}
private void insert(int index, int tag, VR vr, Object value) {
ensureCapacity(size+1);
int numMoved = size - index;
if (numMoved > 0) {
System.arraycopy(tags, index, tags, index+1, numMoved);
System.arraycopy(vrs, index, vrs, index+1, numMoved);
System.arraycopy(values, index, values, index+1, numMoved);
}
tags[index] = tag;
vrs[index] = vr;
values[index] = value;
size++;
}
public boolean addAll(Attributes other) {
return add(other, null, null, 0, 0, null, false, false, false, null);
}
/**
* Updates this Attributes object with all the attributes of
* the "other" object, applying the same behaviour recursively
* to the items of the Sequences (if the "other" attributes has
* a sequence with only a part of the attributes, only those will
* be updated in the original Sequence).
*
* Note: recursion will be applied only with Sequences containing one
* item and having the original item not null. If this condition
* is not supported, the complete sequence of the "other" Attributes
* will be set to this one, as in addAll(Attributes other)
*
* @param other the other Attributes object
* @return <tt>true</tt> if one ore more attribute are added or
* overwritten with a different value
*/
public boolean updateRecursive (Attributes other) {
boolean toggleEndian = bigEndian != other.bigEndian;
final int otherSize = other.size;
int numAdd = 0;
String privateCreator = null;
int creatorTag = 0;
for (int i = 0; i < otherSize; i++) {
int tag = other.tags[i];
VR vr = other.vrs[i];
Object value = other.values[i];
if (TagUtils.isPrivateCreator(tag)) {
continue; // private creators will be automatically added with the private tags
}
if (TagUtils.isPrivateTag(tag)) {
int tmp = TagUtils.creatorTagOf(tag);
if (creatorTag != tmp) {
creatorTag = tmp;
privateCreator = other.privateCreatorOf(tag);
}
} else {
creatorTag = 0;
privateCreator = null;
}
if (value instanceof Sequence) {
int indexOfOriginalSequence = indexOf(tag);
if (indexOfOriginalSequence < 0 ) {
//Trying to recursively update an empty sequence, fallback to whole copy
set(privateCreator, tag, (Sequence) value, null);
} else {
Sequence original = (Sequence) values[indexOfOriginalSequence];
if (original.size() == 0) {
//as above, fallback to whole copy
set(privateCreator, tag, (Sequence) value, null);
}
else {
Sequence updated = ((Sequence) value);
if (updated==null || updated.size() == 0)
continue;
if (original.size() > 1 || updated.size()>1)
//Trying to recursively update a sequence with more than 1 item: fallback to whole copy
set(privateCreator, tag, updated, null);
else
//both original and updated sequences have 1 item
original.get(0).updateRecursive(updated.get(0));
}
}
} else if (value instanceof Fragments) {
set(privateCreator, tag, (Fragments) value);
} else {
set(privateCreator, tag, vr,
toggleEndian(vr, value, toggleEndian));
}
numAdd++;
}
return numAdd != 0;
}
/**
* Filters this Attributes object returning an Attributes containing
* all the properties found in the selection object and the relative
* ancestors, if any.
*
* Example:
*
* original:
* (0010,0020) LO [PatientID] PatientID
* (0010,0021) LO [IssuerOfPatientID] IssuerOfPatientID
* (0010,1002) SQ [1 Items] OtherPatientIDsSequence
* >Item #1
* >(0010,0020) LO [OtherPatientID] PatientID
* >(0010,0021) LO [OtherIssuerOfPatientID] IssuerOfPatientID
*
* selection:
* (0010,0020) LO [OtherPatientID] PatientID
*
* result:
* (0010,1002) SQ [1 Items] OtherPatientIDsSequence
* >Item #1
* >(0010,0020) LO [OtherPatientID] PatientID
*
* @param selection selection filter
* @return filtered Attributes
*/
public Attributes filter (Attributes selection) {
Attributes filtered = new Attributes();
for (int tag : tags()) {
if (selection.contains(getPrivateCreator(tag),tag)) {
if (equalValues(selection, indexOf(getPrivateCreator(tag),tag),
selection.indexOf(getPrivateCreator(tag),tag)))
filtered.setValue(getPrivateCreator(tag),tag, getVR(tag), getValue(tag));
}
Attributes nested;
if (getVR(getPrivateCreator(tag),tag) == VR.SQ &&
(nested = getNestedDataset(getPrivateCreator(tag),tag))!=null) {
Attributes seq = nested.filter(selection);
if (seq.size()>0) {
Sequence sequence = filtered.newSequence(getPrivateCreator(tag),tag,seq.size());
sequence.add(0,seq);
}
}
}
return filtered;
}
public boolean merge(Attributes other) {
return add(other, null, null, 0, 0, null, true, false, false, null);
}
public boolean testMerge(Attributes other) {
return add(other, null, null, 0, 0, null, true, false, true, null);
}
public boolean addSelected(Attributes other, Attributes selection) {
return add(other, null, null, 0, 0, selection, false, false, false, null);
}
public boolean addSelected(Attributes other, String privateCreator, int tag) {
int index = other.indexOf(privateCreator, tag);
if (index < 0)
return false;
Object value = other.values[index];
if (value instanceof Sequence) {
set(privateCreator, tag, (Sequence) value, null);
} else if (value instanceof Fragments) {
set(privateCreator, tag, (Fragments) value);
} else {
VR vr = other.vrs[index];
set(privateCreator, tag, vr,
toggleEndian(vr, value, bigEndian != other.bigEndian));
}
return true;
}
public boolean addWithoutBulkData(Attributes other, BulkDataDescriptor descriptor) {
final boolean toggleEndian = bigEndian != other.bigEndian;
final int[] tags = other.tags;
final VR[] srcVRs = other.vrs;
final Object[] srcValues = other.values;
final int otherSize = other.size;
int numAdd = 0;
String privateCreator = null;
int creatorTag = 0;
ItemPointer[] itemPointer = itemPointers();
for (int i = 0; i < otherSize; i++) {
int tag = tags[i];
VR vr = srcVRs[i];
Object value = srcValues[i];
if (TagUtils.isPrivateCreator(tag)) {
if (contains(tag))
continue; // do not overwrite private creator IDs
if (vr == VR.LO) {
value = other.decodeStringValue(i);
if ((value instanceof String)
&& creatorTagOf((String) value, tag, false) != -1)
continue; // do not add duplicate private creator ID
}
}
if (TagUtils.isPrivateTag(tag)) {
int tmp = TagUtils.creatorTagOf(tag);
if (creatorTag != tmp) {
creatorTag = tmp;
privateCreator = other.privateCreatorOf(tag);
}
} else {
creatorTag = 0;
privateCreator = null;
}
int vallen = (value instanceof byte[])
? ((byte[])value).length
: -1;
if (descriptor.isBulkData(privateCreator, tag, vr, vallen, itemPointer))
continue;
if (value instanceof Sequence) {
Sequence src = (Sequence) value;
setWithoutBulkData(privateCreator, tag, src, descriptor);
} else if (value instanceof Fragments) {
set(privateCreator, tag, (Fragments) value);
} else {
set(privateCreator, tag, vr,
toggleEndian(vr, value, toggleEndian));
}
numAdd++;
}
return numAdd != 0;
}
private void setWithoutBulkData(String privateCreator, int tag, Sequence seq,
BulkDataDescriptor descriptor) {
Sequence newSequence = newSequence(privateCreator, tag, seq.size());
for (Attributes item : seq) {
Attributes newItem = new Attributes(bigEndian, item.size());
newSequence.add(newItem);
newItem.addWithoutBulkData(item, descriptor);
}
}
/**
* Add selected attributes from another Attributes object to this.
* The specified array of tag values must be sorted (as by the
* {@link java.util.Arrays#sort(int[])} method) prior to making this call.
*
* @param other the other Attributes object
* @param selection sorted tag values
* @return <tt>true</tt> if one ore more attributes were added
*/
public boolean addSelected(Attributes other, int... selection) {
return addSelected(other, selection, 0, selection.length);
}
/**
* Add selected attributes from another Attributes object to this.
* The specified array of tag values must be sorted (as by the
* {@link java.util.Arrays#sort(int[], int, int)} method) prior to making this call.
*
* @param other the other Attributes object
* @param selection sorted tag values
* @param fromIndex the index of the first tag (inclusive)
* @param toIndex the index of the last tag (exclusive)
* @return <tt>true</tt> if one ore more attributes were added
*/
public boolean addSelected(Attributes other, int[] selection,
int fromIndex, int toIndex) {
return add(other, selection, null, fromIndex, toIndex, null, false, false, false, null);
}
/**
* Merge selected attributes from another Attributes object into this.
* Does not overwrite existing non-empty attributes.
* The specified array of tag values must be sorted (as by the
* {@link java.util.Arrays#sort(int[])} method) prior to making this call.
*
* @param other the other Attributes object
* @param selection sorted tag values
* @return <tt>true</tt> if one ore more attributes were added
*/
public boolean mergeSelected(Attributes other, int... selection) {
return add(other, selection, null, 0, selection.length, null, true, false, false, null);
}
/**
* Tests if {@link #mergeSelected} would modify attributes, without actually
* modifying this attributes
*
* @param other the other Attributes object
* @param selection sorted tag values
* @return <tt>true</tt> if one ore more attributes would have been added
*/
public boolean testMergeSelected(Attributes other, int... selection) {
return add(other, selection, null, 0, selection.length, null, true, false, true, null);
}
/**
* Add not selected attributes from another Attributes object to this.
* The specified array of tag values must be sorted (as by the
* {@link java.util.Arrays#sort(int[])} method) prior to making this call.
*
* @param other the other Attributes object
* @param selection sorted tag values
* @return <tt>true</tt> if one ore more attributes were added
*/
public boolean addNotSelected(Attributes other, int... selection) {
return addNotSelected(other, selection, 0, selection.length);
}
/**
* Add not selected attributes from another Attributes object to this.
* The specified array of tag values must be sorted (as by the
* {@link java.util.Arrays#sort(int[])} method) prior to making this call.
*
* @param other the other Attributes object
* @param selection sorted tag values
* @param fromIndex the index of the first tag (inclusive)
* @param toIndex the index of the last tag (exclusive)
* @return <tt>true</tt> if one ore more attributes were added
*/
public boolean addNotSelected(Attributes other, int[] selection,
int fromIndex, int toIndex) {
return add(other, null, selection, fromIndex, toIndex, null, false, false, false, null);
}
private boolean add(Attributes other, int[] include, int[] exclude,
int fromIndex, int toIndex, Attributes selection, boolean merge,
boolean update, boolean simulate, Attributes modified) {
boolean toggleEndian = bigEndian != other.bigEndian;
boolean modifiedToggleEndian = modified != null
&& bigEndian != modified.bigEndian;
final int[] otherTags = other.tags;
final VR[] srcVRs = other.vrs;
final Object[] srcValues = other.values;
final int otherSize = other.size;
int numAdd = 0;
String privateCreator = null;
int creatorTag = 0;
for (int i = 0; i < otherSize; i++) {
int tag = otherTags[i];
VR vr = srcVRs[i];
Object value = srcValues[i];
if (TagUtils.isPrivateCreator(tag)) {
if (vr != VR.LO) {
LOG.info("Private Creator Element with wrong VR corrected! tag:{}, vr:{}, value:{}",
TagUtils.toString(tag), vr, value);
srcVRs[i] = VR.LO;
}
continue; // private creators will be automatically added with the private tags
}
if (include != null && Arrays.binarySearch(include, fromIndex, toIndex, tag) < 0)
continue;
if (exclude != null && Arrays.binarySearch(exclude, fromIndex, toIndex, tag) >= 0)
continue;
if (TagUtils.isPrivateTag(tag)) {
int tmp = TagUtils.creatorTagOf(tag);
if (creatorTag != tmp) {
creatorTag = tmp;
privateCreator = other.privateCreatorOf(tag);
}
} else {
creatorTag = 0;
privateCreator = null;
}
if (selection != null && !selection.contains(privateCreator, tag))
continue;
if (merge || update) {
int j = indexOf(tag);
if (j >= 0) {
if (update && equalValues(other, j, i)) {
continue;
}
Object origValue = vrs[j].isStringType()
? decodeStringValue(j)
: values[j];
if (!isEmpty(origValue)) {
if (merge) {
continue;
}
if (modified != null) {
if (origValue instanceof Sequence) {
modified.set(privateCreator, tag, (Sequence) origValue, null);
} else if (origValue instanceof Fragments) {
modified.set(privateCreator, tag, (Fragments) origValue);
} else {
modified.set(privateCreator, tag, vr,
toggleEndian(vr, origValue, modifiedToggleEndian));
}
}
}
}
}
if (!simulate) {
if (value instanceof Sequence) {
set(privateCreator, tag, (Sequence) value,
selection != null
? selection.getNestedDataset(privateCreator, tag)
: null);
} else if (value instanceof Fragments) {
set(privateCreator, tag, (Fragments) value);
} else {
set(privateCreator, tag, vr,
toggleEndian(vr, value, toggleEndian));
}
}
numAdd++;
}
return numAdd != 0;
}
public boolean update(Attributes newAttrs, Attributes modified) {
return add(newAttrs, null, null, 0, 0, null, false, true, false, modified);
}
public boolean testUpdate(Attributes newAttrs, Attributes modified) {
return add(newAttrs, null, null, 0, 0, null, false, true, true, modified);
}
/**
* Add selected attributes from another Attributes object to this.
* Optionally, the original values of overwritten existing non-empty
* attributes are preserved in another Attributes object.
* The specified array of tag values must be sorted (as by the
* {@link java.util.Arrays#sort(int[])} method) prior to making this call.
*
* @param newAttrs the other Attributes object
* @param modified Attributes object to collect overwritten non-empty
* attributes with original values or <tt>null</tt>
* @param selection sorted tag values
* @return <tt>true</tt> if one ore more attribute were added or
* overwritten with a different value
*/
public boolean updateSelected(Attributes newAttrs,
Attributes modified, int... selection) {
return add(newAttrs, selection, null, 0, selection.length, null, false, true,
false, modified);
}
/**
* Tests if {@link #updateSelected} would modify attributes, without actually
* modifying this attributes
*
* @param newAttrs the other Attributes object
* @param modified Attributes object to collect overwritten non-empty
* attributes with original values or <tt>null</tt>
* @param selection sorted tag values
* @return <tt>true</tt> if one ore more attribute would be added or
* overwritten with a different value
*/
public boolean testUpdateSelected(Attributes newAttrs, Attributes modified,
int... selection) {
return add(newAttrs, selection, null, 0, selection.length, null,
false, true, true, modified);
}
private static Object toggleEndian(VR vr, Object value, boolean toggleEndian) {
return (toggleEndian && value instanceof byte[])
? vr.toggleEndian((byte[]) value, true)
: value;
}
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Attributes))
return false;
final Attributes other = (Attributes) o;
if (size != other.size)
return false;
int creatorTag = 0;
int otherCreatorTag = 0;
for (int i = 0; i < size; i++) {
int tag = tags[i];
if (!TagUtils.isPrivateGroup(tag)) {
if (tag != other.tags[i] || !equalValues(other, i, i))
return false;
} else if (TagUtils.isPrivateTag(tag)) {
int tmp = TagUtils.creatorTagOf(tag);
if (creatorTag != tmp) {
creatorTag = tmp;
otherCreatorTag = other.creatorTagOf(privateCreatorOf(tag), tag, false);
if (otherCreatorTag == -1)
return false;
}
int j = other.indexOf(TagUtils.toPrivateTag(otherCreatorTag, tag));
if (j < 0 || !equalValues(other, i, j))
return false;
}
}
return true;
}
private boolean equalValues(Attributes other, int index, int otherIndex) {
VR vr = vrs[index];
if (vr != other.vrs[otherIndex])
return false;
if (vr.isStringType())
if (vr == VR.IS)
return equalISValues(other, index, otherIndex);
else if (vr == VR.DS)
return equalDSValues(other, index, otherIndex);
else
return equalStringValues(other, index, otherIndex);
Object v1 = values[index];
Object v2 = other.values[otherIndex];
if (v1 instanceof byte[]) {
if (v2 instanceof byte[] && ((byte[]) v1).length == ((byte[]) v2).length) {
if (bigEndian != other.bigEndian)
v2 = vr.toggleEndian((byte[]) v2, true);
return Arrays.equals((byte[]) v1, (byte[]) v2);
}
} else
return v1.equals(v2);
return false;
}
private boolean equalISValues(Attributes other, int index, int otherIndex) {
try {
return Arrays.equals(decodeISValue(index), other.decodeISValue(otherIndex));
} catch (NumberFormatException e) {
return equalStringValues(other, index, otherIndex);
}
}
private boolean equalDSValues(Attributes other, int index, int otherIndex) {
try {
return Arrays.equals(decodeDSValue(index), other.decodeDSValue(otherIndex));
} catch (NumberFormatException e) {
return equalStringValues(other, index, otherIndex);
}
}
private boolean equalStringValues(Attributes other, int index, int otherIndex) {
Object v1 = decodeStringValue(index);
Object v2 = other.decodeStringValue(otherIndex);
if (v1 instanceof String[]) {
if (v2 instanceof String[])
return Arrays.equals((String[]) v1, (String[]) v2);
} else
return v1.equals(v2);
return false;
}
@Override
public int hashCode() {
int h = 0;
for (int i = 0; i < size; i++) {
int tag = tags[i];
if (!TagUtils.isPrivateGroup(tag))
h = 31*h + tag;
}
return h;
}
private void set(String privateCreator, int tag, Sequence src,
Attributes selection) {
Sequence dst = newSequence(privateCreator, tag, src.size());
for (Attributes item : src)
dst.add(selection != null && !selection.isEmpty()
? new Attributes(item, bigEndian, selection)
: new Attributes(item, bigEndian));
}
private void set(String privateCreator, int tag, Fragments src) {
boolean toogleEndian = src.bigEndian() != bigEndian;
VR vr = src.vr();
Fragments dst = newFragments(privateCreator, tag, vr, src.size());
for (Object frag : src)
dst.add(toggleEndian(vr, frag, toogleEndian));
}
@Override
public String toString() {
return toString(TO_STRING_LIMIT, TO_STRING_WIDTH);
}
public String toString(Deidentifier deidentifier) {
return toString(TO_STRING_LIMIT, TO_STRING_WIDTH, deidentifier);
}
public String toString(int limit, int maxWidth) {
return toStringBuilder(limit, maxWidth, new StringBuilder(1024), null)
.toString();
}
public String toString(int limit, int maxWidth, Deidentifier deidentifier) {
return toStringBuilder(limit, maxWidth, new StringBuilder(1024), deidentifier)
.toString();
}
public StringBuilder toStringBuilder(StringBuilder sb) {
return toStringBuilder(TO_STRING_LIMIT, TO_STRING_WIDTH, sb, null);
}
public StringBuilder toStringBuilder(int limit, int maxWidth, StringBuilder sb, Deidentifier deidentifier) {
if (appendAttributes(limit, maxWidth, sb, "", deidentifier) > limit)
sb.append("...\n");
return sb;
}
private int appendAttributes(int limit, int maxWidth, StringBuilder sb, String prefix, Deidentifier deidentifier) {
int lines = 0;
int creatorTag = 0;
String privateCreator = null;
for (int i = 0; i < size; i++) {
if (++lines > limit)
break;
int tag = tags[i];
if (TagUtils.isPrivateTag(tag)) {
int tmp = TagUtils.creatorTagOf(tag);
if (creatorTag != tmp) {
creatorTag = tmp;
privateCreator = getString(creatorTag, null);
}
} else {
creatorTag = 0;
privateCreator = null;
}
Object value = values[i];
appendAttribute(privateCreator, tag, vrs[i], value, sb.length() + maxWidth, sb, prefix, deidentifier);
if (value instanceof Sequence)
lines += appendItems((Sequence) value, limit - lines, maxWidth, sb, prefix + '>', deidentifier);
}
return lines;
}
private int appendItems(Sequence sq, int limit, int maxWidth, StringBuilder sb,
String prefix, Deidentifier deidentifier) {
int lines = 0;
int itemNo = 0;
for (Attributes item : sq) {
if (++lines > limit)
break;
sb.append(prefix).append("Item #").append(++itemNo).append('\n');
lines += item.appendAttributes(limit - lines, maxWidth, sb, prefix, deidentifier);
}
return lines ;
}
private StringBuilder appendAttribute(String privateCreator, int tag, VR vr, Object value,
int maxLength, StringBuilder sb, String prefix, Deidentifier deidentifier) {
sb.append(prefix).append(TagUtils.toString(tag)).append(' ').append(vr).append(" [");
if (vr.prompt((deidentifier!=null ? deidentifier.deidentify(tag, vr, value) :value), bigEndian,
getSpecificCharacterSet(vr),
maxLength - sb.length() - 1, sb)) {
sb.append("] ").append(ElementDictionary.keywordOf(tag, privateCreator));
if (sb.length() > maxLength)
sb.setLength(maxLength);
}
sb.append('\n');
return sb;
}
public int calcLength(DicomEncodingOptions encOpts, boolean explicitVR) {
if (isEmpty())
return 0;
this.groupLengths = encOpts.groupLength
? new int[countGroups()]
: null;
this.length = calcLength(encOpts, explicitVR,
getSpecificCharacterSet(), groupLengths);
return this.length;
}
private int calcLength(DicomEncodingOptions encOpts, boolean explicitVR,
SpecificCharacterSet cs, int[] groupLengths) {
int len, totlen = 0;
int groupLengthTag = -1;
int groupLengthIndex = -1;
VR vr;
Object val;
for (int i = 0; i < size; i++) {
vr = vrs[i];
val = values[i];
len = explicitVR ? vr.headerLength() : 8;
if (val instanceof Value)
len += ((Value) val).calcLength(encOpts, explicitVR, vr);
else {
if (!(val instanceof byte[]))
values[i] = val = vr.toBytes(val, cs);
len += (((byte[]) val).length + 1) & ~1;
}
totlen += len;
if (groupLengths != null) {
int tmp = TagUtils.groupLengthTagOf(tags[i]);
if (groupLengthTag != tmp) {
groupLengthTag = tmp;
groupLengthIndex++;
totlen += 12;
}
groupLengths[groupLengthIndex] += len;
}
}
return totlen;
}
private int countGroups() {
int groupLengthTag = -1;
int count = 0;
for (int i = 0; i < size; i++) {
int tmp = TagUtils.groupLengthTagOf(tags[i]);
if (groupLengthTag != tmp) {
if (groupLengthTag < 0)
this.groupLengthIndex0 = count;
groupLengthTag = tmp;
count++;
}
}
return count;
}
public void writeTo(DicomOutputStream out)
throws IOException {
if (isEmpty())
return;
if (groupLengths == null && out.getEncodingOptions().groupLength)
throw new IllegalStateException(
"groupLengths not initialized by calcLength()");
SpecificCharacterSet cs = getSpecificCharacterSet();
if (tags[0] < 0) {
int index0 = -(1 + indexOf(0));
writeTo(out, cs, index0, size, groupLengthIndex0);
writeTo(out, cs, 0, index0, 0);
} else {
writeTo(out, cs, 0, size, 0);
}
}
public void writeItemTo(DicomOutputStream out) throws IOException {
DicomEncodingOptions encOpts = out.getEncodingOptions();
int len = getEncodedItemLength(encOpts, out.isExplicitVR());
out.writeHeader(Tag.Item, null, len);
writeTo(out);
if (len == -1)
out.writeHeader(Tag.ItemDelimitationItem, null, 0);
}
private int getEncodedItemLength(DicomEncodingOptions encOpts,
boolean explicitVR) {
if (isEmpty())
return encOpts.undefEmptyItemLength ? -1 : 0;
if (encOpts.undefItemLength)
return -1;
if (length == -1)
calcLength(encOpts, explicitVR);
return length;
}
private void writeTo(DicomOutputStream out, SpecificCharacterSet cs,
int start, int end, int groupLengthIndex) throws IOException {
boolean groupLength = groupLengths != null;
int groupLengthTag = -1;
for (int i = start; i < end; i++) {
int tag = tags[i];
if (groupLength) {
int tmp = TagUtils.groupLengthTagOf(tag);
if (groupLengthTag != tmp) {
groupLengthTag = tmp;
out.writeGroupLength(groupLengthTag,
groupLengths[groupLengthIndex++]);
}
}
out.writeAttribute(tag, vrs[i], values[i], cs);
}
}
/**
* Invokes {@link Visitor#visit} for each attribute in this instance. The
* operation will be aborted if <code>visitor.visit()</code> returns
* <code>false</code> or throws an exception.
*
* @param visitor
* @param visitNestedDatasets
* controls if <code>visitor.visit()</code> is also invoked for
* attributes in nested datasets
* @return <code>true</code> if the operation was not aborted.
* @throws Exception
* exception thrown by {@link Visitor#visit}
*/
public boolean accept(Visitor visitor, boolean visitNestedDatasets)
throws Exception{
if (isEmpty())
return true;
if (tags[0] < 0) {
int index0 = -(1 + indexOf(0));
return accept(visitor, visitNestedDatasets, index0, size)
&& accept(visitor, visitNestedDatasets, 0, index0);
} else {
return accept(visitor, visitNestedDatasets, 0, size);
}
}
private boolean accept(Visitor visitor, boolean visitNestedDatasets,
int start, int end) throws Exception {
for (int i = start; i < end; i++) {
if (!visitor.visit(this, tags[i], vrs[i], values[i]))
return false;
if (visitNestedDatasets && (values[i] instanceof Sequence)) {
for (Attributes item : (Sequence) values[i]) {
if (!item.accept(visitor, true))
return false;
}
}
}
return true;
}
public void writeGroupTo(DicomOutputStream out, int groupLengthTag)
throws IOException {
if (isEmpty())
throw new IllegalStateException("No attributes");
checkInGroup(0, groupLengthTag);
checkInGroup(size-1, groupLengthTag);
SpecificCharacterSet cs = getSpecificCharacterSet();
out.writeGroupLength(groupLengthTag,
calcLength(out.getEncodingOptions(), out.isExplicitVR(), cs, null));
writeTo(out, cs, 0, size, 0);
}
private void checkInGroup(int i, int groupLengthTag) {
int tag = tags[i];
if (TagUtils.groupLengthTagOf(tag) != groupLengthTag)
throw new IllegalStateException(TagUtils.toString(tag)
+ " does not belong to group ("
+ TagUtils.shortToHexString(
TagUtils.groupNumber(groupLengthTag))
+ ",eeee).");
}
public Attributes createFileMetaInformation(String tsuid) {
return createFileMetaInformation(
getString(Tag.SOPInstanceUID, null),
getString(Tag.SOPClassUID, null),
tsuid);
}
public static Attributes createFileMetaInformation(String iuid,
String cuid, String tsuid) {
if (iuid == null || iuid.isEmpty())
throw new IllegalArgumentException("Missing SOP Instance UID");
if (cuid == null || cuid.isEmpty())
throw new IllegalArgumentException("Missing SOP Class UID");
if (tsuid == null || tsuid.isEmpty())
throw new IllegalArgumentException("Missing Transfer Syntax UID");
Attributes fmi = new Attributes(6);
fmi.setBytes(Tag.FileMetaInformationVersion, VR.OB,
new byte[]{ 0, 1 });
fmi.setString(Tag.MediaStorageSOPClassUID, VR.UI, cuid);
fmi.setString(Tag.MediaStorageSOPInstanceUID, VR.UI, iuid);
fmi.setString(Tag.TransferSyntaxUID, VR.UI, tsuid);
fmi.setString(Tag.ImplementationClassUID, VR.UI,
Implementation.getClassUID());
fmi.setString(Tag.ImplementationVersionName, VR.SH,
Implementation.getVersionName());
return fmi;
}
public boolean matches(Attributes keys, boolean ignorePNCase,
boolean matchNoValue) {
int[] keyTags = keys.tags;
VR[] keyVrs = keys.vrs;
Object[] keyValues = keys.values;
int keysSize = keys.size;
String privateCreator = null;
int creatorTag = 0;
for (int i = 0; i < keysSize; i++) {
int tag = keyTags[i];
if (TagUtils.isPrivateCreator(tag))
continue;
if (TagUtils.isPrivateGroup(tag)) {
int tmp = TagUtils.creatorTagOf(tag);
if (creatorTag != tmp) {
creatorTag = tmp;
privateCreator = keys.getString(creatorTag, null);
}
} else {
creatorTag = 0;
privateCreator = null;
}
Object keyValue = keyValues[i];
if (isEmpty(keyValue))
continue;
if (keyVrs[i].isStringType()) {
if (!matches(privateCreator, tag, keyVrs[i], ignorePNCase,
matchNoValue, keys.getStrings(privateCreator, tag, null)))
return false;
} else if (keyValue instanceof Sequence) {
if (!matches(privateCreator, tag, ignorePNCase, matchNoValue,
(Sequence) keyValue))
return false;
} else {
throw new UnsupportedOperationException("Keys with VR: "
+ keyVrs[i] + " not supported");
}
}
return true;
}
private boolean matches(String privateCreator, int tag, VR vr,
boolean ignorePNCase, boolean matchNoValue, String[] keyVals) {
String[] vals = getStrings(privateCreator, tag, null);
if (vals == null || vals.length == 0)
return matchNoValue;
boolean ignoreCase = ignorePNCase && vr == VR.PN;
for (String keyVal : keyVals) {
if (vr == VR.PN)
keyVal = new PersonName(keyVals[0]).toString();
if (StringUtils.containsWildCard(keyVal)) {
Pattern pattern = StringUtils.compilePattern(keyVal, ignoreCase);
for (String val : vals) {
if (val == null)
if (matchNoValue)
return true;
else
continue;
if (vr == VR.PN)
val = new PersonName(val).toString();
if (pattern.matcher(val).matches())
return true;
}
} else {
for (String val : vals) {
if (val == null)
if (matchNoValue)
return true;
else
continue;
if (vr == VR.PN)
val = new PersonName(val).toString();
if (ignoreCase ? keyVal.equalsIgnoreCase(val)
: keyVal.equals(val))
return true;
}
}
}
return false;
}
private boolean matches(String privateCreator, int tag, boolean ignorePNCase,
boolean matchNoValue, Sequence keySeq) {
int n = keySeq.size();
if (n > 1)
throw new IllegalArgumentException("Keys contain Sequence "
+ TagUtils.toString(tag) + " with " + n + " Items");
Attributes keys = keySeq.get(0);
if (keys.isEmpty())
return true;
Object value = getValue(privateCreator, tag);
if (value == null || isEmpty(value))
return matchNoValue;
if (value instanceof Sequence) {
Sequence sq = (Sequence) value;
for (Attributes item : sq)
if (item.matches(keys, ignorePNCase, matchNoValue))
return true;
}
return false;
}
private static final long serialVersionUID = 7868714416968825241L;
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(size);
@SuppressWarnings("resource")
DicomOutputStream dout = new DicomOutputStream(out,
bigEndian ? UID.ExplicitVRBigEndianRetired
: UID.ExplicitVRLittleEndian);
dout.writeDataset(null, this);
dout.writeHeader(Tag.ItemDelimitationItem, null, 0);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
init(in.readInt());
@SuppressWarnings("resource")
DicomInputStream din = new DicomInputStream(in,
bigEndian ? UID.ExplicitVRBigEndianRetired
: UID.ExplicitVRLittleEndian);
din.readAttributes(this, -1, Tag.ItemDelimitationItem);
}
public ValidationResult validate(IOD iod) {
ValidationResult result = new ValidationResult();
HashMap<String,Boolean> resolvedConditions = new HashMap<String,Boolean>();
for (IOD.DataElement el : iod) {
validate(el, result, resolvedConditions);
}
return result;
}
public void validate(DataElement el, ValidationResult result) {
validate(el, result, null);
}
private void validate(DataElement el, ValidationResult result,
Map<String, Boolean> processedConditions) {
IOD.Condition condition = el.getCondition();
if (condition != null) {
String id = condition.id();
Boolean match = id != null ? processedConditions.get(id) : null;
if (match == null) {
match = condition.match(this);
if (id != null)
processedConditions.put(id, match);
}
if (!match)
return;
}
int index = indexOf(el.tag);
if (index < 0) {
if (el.type == IOD.DataElementType.TYPE_1
|| el.type == IOD.DataElementType.TYPE_2) {
result.addMissingAttribute(el);
}
return;
}
Object value = values[index];
if (isEmpty(value)) {
if (el.type == IOD.DataElementType.TYPE_1) {
result.addMissingAttributeValue(el);
}
return;
}
if (el.type == IOD.DataElementType.TYPE_0) {
result.addNotAllowedAttribute(el);
return;
}
VR vr = vrs[index];
if (vr.isStringType()) {
value = decodeStringValue(index);
}
Object validVals = el.getValues();
if (el.vr == VR.SQ) {
if (!(value instanceof Sequence)) {
result.addInvalidAttributeValue(el, ValidationResult.Invalid.VR);
return;
}
Sequence seq = (Sequence) value;
int seqSize = seq.size();
if (el.maxVM > 0 && seqSize > el.maxVM) {
result.addInvalidAttributeValue(el,
ValidationResult.Invalid.MultipleItems);
return;
}
if (validVals instanceof Code[]) {
boolean invalidItem = false;
ValidationResult[] itemValidationResults = new ValidationResult[seqSize];
for (int i = 0; i < seqSize; i++) {
ValidationResult itemValidationResult =
validateCode(seq.get(i), (Code[]) validVals);
invalidItem = invalidItem || !itemValidationResult.isValid();
itemValidationResults[i] = itemValidationResult;
}
if (invalidItem) {
result.addInvalidAttributeValue(el,
ValidationResult.Invalid.Code, itemValidationResults, null);
}
} else if (validVals instanceof IOD[]) {
IOD[] itemIODs = (IOD[]) validVals;
int[] matchingItems = new int[itemIODs.length];
boolean invalidItem = false;
ValidationResult[] itemValidationResults = new ValidationResult[seqSize];
for (int i = 0; i < seqSize; i++) {
ValidationResult itemValidationResult = new ValidationResult();
HashMap<String,Boolean> resolvedItemConditions =
new HashMap<String,Boolean>();
Attributes item = seq.get(i);
for (int j = 0; j < itemIODs.length; j++) {
IOD itemIOD = itemIODs[j];
IOD.Condition itemCondition = itemIOD.getCondition();
if (itemCondition != null) {
String id = itemCondition.id();
Boolean match = id != null ? resolvedItemConditions.get(id) : null;
if (match == null) {
match = itemCondition.match(item);
if (id != null)
resolvedItemConditions.put(id, match);
}
if (!match)
continue;
}
matchingItems[j]++;
for (IOD.DataElement itemEl : itemIOD) {
item.validate(itemEl, itemValidationResult, resolvedItemConditions);
}
}
invalidItem = invalidItem || !itemValidationResult.isValid();
itemValidationResults[i] = itemValidationResult;
}
IOD[] missingItems = checkforMissingItems(matchingItems, itemIODs);
if (invalidItem || missingItems != null) {
result.addInvalidAttributeValue(el,
ValidationResult.Invalid.Item,
itemValidationResults, missingItems);
}
}
return;
}
if (el.maxVM > 0 || el.minVM > 1) {
int vm = vr.vmOf(value);
if (el.maxVM > 0 && vm > el.maxVM
|| el.minVM > 1 && vm < el.minVM) {
result.addInvalidAttributeValue(el, ValidationResult.Invalid.VM);
return;
}
}
if (validVals == null)
return;
if (validVals instanceof String[]) {
if (!vr.isStringType()) {
result.addInvalidAttributeValue(el, ValidationResult.Invalid.VR);
return;
}
if (!isValidValue(toStrings(value), el.valueNumber, (String[]) validVals)) {
result.addInvalidAttributeValue(el, ValidationResult.Invalid.Value);
}
} else if (validVals instanceof int[]) {
if (vr == VR.IS)
value = decodeISValue(index);
else if (!vr.isIntType()) {
result.addInvalidAttributeValue(el, ValidationResult.Invalid.VR);
return;
}
if (!isValidValue(vr.toInts(value, bigEndian), el.valueNumber, (int[]) validVals)) {
result.addInvalidAttributeValue(el, ValidationResult.Invalid.Value);
}
}
}
private IOD[] checkforMissingItems(int[] matchingItems, IOD[] itemIODs) {
IOD[] missingItems = new IOD[matchingItems.length];
int n = 0;
for (int i = 0; i < matchingItems.length; i++) {
IOD itemIOD = itemIODs[i];
if (matchingItems[i] == 0
&& itemIOD.getType() == DataElementType.TYPE_1)
missingItems[n++] = itemIOD;
}
return n > 0 ? Arrays.copyOf(missingItems, n) : null;
}
private ValidationResult validateCode(Attributes item, Code[] validVals) {
ValidationResult result = null;
for (Code code : validVals) {
result = item.validate(IOD.valueOf(code));
if (result.isValid())
break;
}
return result;
}
private boolean isValidValue(String[] val, int valueNumber, String[] validVals) {
if (valueNumber != 0)
return val.length < valueNumber || isOneOf(val[valueNumber-1], validVals);
for (int i = 0; i < val.length; i++)
if (!isOneOf(val[i], validVals))
return false;
return true;
}
private <T> boolean isOneOf(Object val, T[] ss) {
if (ss == null)
return true;
for (T s : ss)
if (val.equals(s))
return true;
return false;
}
private boolean isValidValue(int[] val, int valueNumber, int[] validVals) {
if (valueNumber != 0)
return val.length < valueNumber || isOneOf(val[valueNumber-1], validVals);
for (int i = 0; i < val.length; i++)
if (!isOneOf(val[i], validVals))
return false;
return true;
}
private boolean isOneOf(int val, int[] is) {
if (is == null)
return true;
for (int i : is)
if (val == i)
return true;
return false;
}
/**
* Add attributes of this data set which were replaced in
* the specified other data set into the result data set.
* If no result data set is passed, a new result set will be instantiated.
*
* @param other data set
* @param result data set or {@code null}
*
* @return result data set.
*/
public Attributes getModified(Attributes other, Attributes result) {
if (result == null)
result = new Attributes(other.size);
int creatorTag = -1;
int prevOtherCreatorTag = -1;
int otherCreatorTag = -1;
String privateCreator = null;
for (int i = 0; i < other.size; i++) {
int tag = other.tags[i];
if ((tag & 0x00010000) != 0) { // private group
if ((tag & 0x0000ff00) == 0)
continue; // skip private creator
otherCreatorTag = TagUtils.creatorTagOf(tag);
if (prevOtherCreatorTag != otherCreatorTag) {
prevOtherCreatorTag = otherCreatorTag;
creatorTag = -1;
int k = other.indexOf(otherCreatorTag);
if (k >= 0) {
Object o = other.decodeStringValue(k);
if (o instanceof String) {
privateCreator = (String) o;
creatorTag = creatorTagOf(
privateCreator, tag, false);
}
}
}
if (creatorTag == -1)
continue; // no matching Private Creator
tag = TagUtils.toPrivateTag(creatorTag, tag);
} else {
privateCreator = null;
}
int j = indexOf(tag);
if (j < 0)
continue;
Object origValue = values[j];
if (origValue instanceof Value && ((Value) origValue).isEmpty())
continue;
if (equalValues(other, j, i))
continue;
if (origValue instanceof Sequence) {
result.set(privateCreator, tag, (Sequence) origValue, null);
} else if (origValue instanceof Fragments) {
result.set(privateCreator, tag, (Fragments) origValue);
} else {
result.set(privateCreator, tag, vrs[j], origValue);
}
}
return result;
}
/**
* Returns attributes of this data set which were removed or replaced in
* the specified other data set.
*
* @param other data set
* @return attributes of this data set which were removed or replaced in
* the specified other data set.
*/
public Attributes getRemovedOrModified(Attributes other) {
Attributes modified = new Attributes(size);
int creatorTag = -1;
int prevCreatorTag = -1;
int otherCreatorTag = 0;
String privateCreator = null;
for (int i = 0; i < size; i++) {
int tag = tags[i];
if ((tag & 0x00010000) != 0) { // private group
if ((tag & 0x0000ff00) == 0)
continue; // skip private creator
creatorTag = TagUtils.creatorTagOf(tag);
if (prevCreatorTag != creatorTag) {
prevCreatorTag = creatorTag;
otherCreatorTag = -1;
privateCreator = null;
int k = indexOf(creatorTag);
if (k >= 0) {
Object o = decodeStringValue(k);
if (o instanceof String) {
privateCreator = (String) o;
otherCreatorTag = other.creatorTagOf(
privateCreator, tag, false);
}
}
}
if (privateCreator == null)
continue; // no Private Creator
if (otherCreatorTag != -1)
tag = TagUtils.toPrivateTag(otherCreatorTag, tag);
} else {
otherCreatorTag = 0;
privateCreator = null;
}
Object origValue = values[i];
if (origValue instanceof Value && ((Value) origValue).isEmpty())
continue;
if (otherCreatorTag >= 0) {
int j = other.indexOf(tag);
if (j >= 0 && equalValues(other, i, j))
continue;
}
if (origValue instanceof Sequence) {
modified.set(privateCreator, tag, (Sequence) origValue, null);
} else if (origValue instanceof Fragments) {
modified.set(privateCreator, tag, (Fragments) origValue);
} else {
modified.set(privateCreator, tag, vrs[i], origValue);
}
}
return modified;
}
private int creatorIndexOf(String privateCreator, int groupNumber) {
if ((groupNumber & 1) == 0)
throw new IllegalArgumentException(
"(" + TagUtils.shortToHexString(groupNumber) + ",xxxx) is not a private Group");
int group = groupNumber << 16;
int creatorTag = group | 0x10;
int index = indexOf(creatorTag);
if (index < 0)
index = -index-1;
while (index < size && (tags[index] & 0xffffff00) == group) {
if (vrs[index] == VR.LO) {
Object creatorID = decodeStringValue(index);
if (privateCreator.equals(creatorID))
return index;
}
index++;
creatorTag++;
}
return -1;
}
public int removePrivateAttributes(String privateCreator, int groupNumber) {
int privateCreatorIndex = creatorIndexOf(privateCreator, groupNumber);
if (privateCreatorIndex < 0)
return 0;
int creatorTag = tags[privateCreatorIndex];
int privateTag = (creatorTag & 0xffff0000) | ((creatorTag & 0xff) << 8);
int srcPos = privateCreatorIndex + 1;
int start = srcPos;
while (start < size && tags[start] < privateTag)
start++;
int end = start;
while (end < size && (tags[end] & 0xffffff00) == privateTag)
end++;
int len1 = start - srcPos;
if (len1 > 0) {
System.arraycopy(tags, srcPos, tags, privateCreatorIndex, len1);
System.arraycopy(vrs, srcPos, vrs, privateCreatorIndex, len1);
System.arraycopy(values, srcPos, values, privateCreatorIndex, len1);
}
int len2 = size - end;
if (len2 > 0) {
int destPos = start - 1;
System.arraycopy(tags, end, tags, destPos, len2);
System.arraycopy(vrs, end, vrs, destPos, len2);
System.arraycopy(values, end, values, destPos, len2);
}
int removed = end - start;
int size1 = size - removed - 1;
Arrays.fill(tags, size1, size, 0);
Arrays.fill(vrs, size1, size, null);
Arrays.fill(values, size1, size, null);
size = size1;
return removed;
}
public int removePrivateAttributes() {
int size1 = size;
for (int i = 0; i < size1; i++) {
int j = i;
while (TagUtils.isPrivateGroup(tags[j]) && j < size1)
j++;
if (j > i) {
int len = size1 - j;
if (len > 0) {
System.arraycopy(tags, j, tags, i, len);
System.arraycopy(vrs, j, vrs, i, len);
System.arraycopy(values, j, values, i, len);
}
size1 -= j - i;
}
}
int removed = size - size1;
if (removed > 0) {
Arrays.fill(tags, size1, size, 0);
Arrays.fill(vrs, size1, size, null);
Arrays.fill(values, size1, size, null);
size = size1;
}
return removed;
}
}