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}