001/* ***** BEGIN LICENSE BLOCK *****
002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003 *
004 * The contents of this file are subject to the Mozilla Public License Version
005 * 1.1 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 * http://www.mozilla.org/MPL/
008 *
009 * Software distributed under the License is distributed on an "AS IS" basis,
010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011 * for the specific language governing rights and limitations under the
012 * License.
013 *
014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
015 * Java(TM), hosted at https://github.com/gunterze/dcm4che.
016 *
017 * The Initial Developer of the Original Code is
018 * Agfa Healthcare.
019 * Portions created by the Initial Developer are Copyright (C) 2011
020 * the Initial Developer. All Rights Reserved.
021 *
022 * Contributor(s):
023 * See @authors listed below
024 *
025 * Alternatively, the contents of this file may be used under the terms of
026 * either the GNU General Public License Version 2 or later (the "GPL"), or
027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
028 * in which case the provisions of the GPL or the LGPL are applicable instead
029 * of those above. If you wish to allow use of your version of this file only
030 * under the terms of either the GPL or the LGPL, and not to allow others to
031 * use your version of this file under the terms of the MPL, indicate your
032 * decision by deleting the provisions above and replace them with the notice
033 * and other provisions required by the GPL or the LGPL. If you do not delete
034 * the provisions above, a recipient may use your version of this file under
035 * the terms of any one of the MPL, the GPL or the LGPL.
036 *
037 * ***** END LICENSE BLOCK ***** */
038
039package org.dcm4che3.data;
040
041import java.io.IOException;
042import java.util.ArrayList;
043import java.util.Arrays;
044import java.util.Collection;
045import java.util.ListIterator;
046
047import org.dcm4che3.io.DicomEncodingOptions;
048import org.dcm4che3.io.DicomOutputStream;
049
050/**
051 * Fragments are used for encapsulation of an encoded (=compressed) pixel data
052 * stream into the Pixel Data (7FE0,0010) portion of the DICOM Data Set. They
053 * are encoded as a sequence of items with Value Representation OB.
054 * 
055 * <p>
056 * Each item is either a byte[], {@link BulkData} or {@link Value#NULL}.
057 * 
058 * <p>
059 * The first Item in the sequence of items before the encoded Pixel Data Stream
060 * is a Basic Offset Table item. The value of the Basic Offset Table, however,
061 * is not required to be present. The first item is then {@link Value#NULL}.
062 * 
063 * <p>
064 * Depending on the transfer syntax, a frame may be entirely contained within a
065 * single fragment, or may span multiple fragments to support buffering during
066 * compression or to avoid exceeding the maximum size of a fixed length
067 * fragment. A recipient can detect fragmentation of frames by comparing the
068 * number of fragments (the number of Items minus one for the Basic Offset
069 * Table) with the number of frames.
070 * 
071 * <p>
072 * See also <a href=
073 * "http://medical.nema.org/medical/dicom/current/output/chtml/part05/sect_A.4.html">
074 * DICOM Part 5: A.4 TRANSFER SYNTAXES FOR ENCAPSULATION OF ENCODED PIXEL
075 * DATA</a> and <a href=
076 * "http://medical.nema.org/medical/dicom/current/output/chtml/part05/sect_8.2.html">
077 * DICOM Part 5: 8.2 Native or Encapsulated Format Encoding</a>
078 * 
079 * @author Gunter Zeilinger <gunterze@gmail.com>
080 */
081public class Fragments extends ArrayList<Object> implements Value {
082
083    private static final long serialVersionUID = -6667210062541083610L;
084
085    private final String privateCreator;
086    private final int tag;
087    private final VR vr;
088    private final boolean bigEndian;
089
090    public Fragments(String privateCreator, int tag, VR vr, boolean bigEndian, int initialCapacity) {
091        super(initialCapacity);
092        this.privateCreator = privateCreator;
093        this.tag = tag;
094        this.vr = vr;
095        this.bigEndian = bigEndian;
096    }
097
098    public String privateCreator() {
099        return privateCreator;
100    }
101
102    public final int tag() {
103        return tag;
104    }
105
106    public final VR vr() {
107        return vr;
108    }
109
110    public final boolean bigEndian() {
111        return bigEndian;
112    }
113
114    @Override
115    public String toString() {
116        return "" + size() + " Fragments";
117    }
118
119    @Override
120    public boolean add(Object frag) {
121        add(size(), frag);
122        return true;
123    }
124
125    @Override
126    public void add(int index, Object frag) {
127        super.add(index, 
128                frag == null || (frag instanceof byte[]) && ((byte[]) frag).length == 0
129                    ? Value.NULL
130                    : frag);
131    }
132
133    @Override
134    public boolean addAll(Collection<? extends Object> c) {
135        return addAll(size(), c);
136    }
137
138    @Override
139    public boolean addAll(int index, Collection<? extends Object> c) {
140        for (Object o : c)
141            add(index++, o);
142        return !c.isEmpty();
143    }
144
145    @Override
146    public void writeTo(DicomOutputStream out, VR vr)
147            throws IOException {
148        for (Object frag : this)
149            out.writeAttribute(Tag.Item, vr, frag, null);
150    }
151
152    @Override
153    public int calcLength(DicomEncodingOptions encOpts, boolean explicitVR, VR vr) {
154        int len = 0;
155        for (Object frag : this) {
156            len += 8;
157            if (frag instanceof Value)
158                len += ((Value) frag).calcLength(encOpts, explicitVR, vr);
159            else
160                len += (((byte[]) frag).length + 1) & ~1;
161        }
162        return len;
163    }
164
165    @Override
166    public int getEncodedLength(DicomEncodingOptions encOpts, boolean explicitVR, VR vr) {
167        return -1;
168    }
169
170    @Override
171    public byte[] toBytes(VR vr, boolean bigEndian) throws IOException {
172        throw new UnsupportedOperationException();
173    }
174
175    @Override
176    public boolean equals(Object obj) {
177        if (this == obj)
178            return true;
179        if (obj == null)
180            return false;
181        if (getClass() != obj.getClass())
182            return false;
183
184        Fragments other = (Fragments) obj;
185        if (bigEndian != other.bigEndian)
186            return false;
187        if (privateCreator == null) {
188            if (other.privateCreator != null)
189                return false;
190        } else if (!privateCreator.equals(other.privateCreator))
191            return false;
192        if (tag != other.tag)
193            return false;
194        if (vr != other.vr)
195            return false;
196
197        ListIterator<Object> e1 = listIterator();
198        ListIterator<Object> e2 = other.listIterator();
199        while (e1.hasNext() && e2.hasNext()) {
200            Object o1 = e1.next();
201            Object o2 = e2.next();
202            if (!itemsEqual(o1, o2))
203                return false;
204        }
205        if (e1.hasNext() || e2.hasNext())
206            return false;
207
208        return true;
209    }
210
211    @Override
212    public int hashCode() {
213        final int prime = 31;
214        
215        int hashCode = 1;
216        for (Object e : this)
217            hashCode = prime * hashCode + itemHashCode(e);
218        
219        hashCode = prime * hashCode + (bigEndian ? 1231 : 1237);
220        hashCode = prime * hashCode + ((privateCreator == null) ? 0 : privateCreator.hashCode());
221        hashCode = prime * hashCode + tag;
222        hashCode = prime * hashCode + ((vr == null) ? 0 : vr.hashCode());
223        return hashCode;
224    }
225
226    private boolean itemsEqual(Object o1, Object o2) {
227
228        if (o1 == null) {
229            return o2 == null;
230        } else {
231            if (o1 instanceof byte[]) {
232                if (o2 instanceof byte[] && ((byte[]) o1).length == ((byte[]) o2).length) {
233                    return Arrays.equals((byte[]) o1, (byte[]) o2);
234                } else {
235                    return false;
236                }
237            } else {
238                return o1.equals(o2);
239            }
240        }
241    }
242
243    private int itemHashCode(Object e) {
244        if (e == null) {
245            return 0;
246        } else {
247            if (e instanceof byte[])
248                return Arrays.hashCode((byte[]) e);
249            else
250                return e.hashCode();
251        }
252    }
253
254}