1 package nom.tam.fits;
2
3 /*-
4 * #%L
5 * nom.tam.fits
6 * %%
7 * Copyright (C) 1996 - 2024 nom-tam-fits
8 * %%
9 * This is free and unencumbered software released into the public domain.
10 *
11 * Anyone is free to copy, modify, publish, use, compile, sell, or
12 * distribute this software, either in source code form or as a compiled
13 * binary, for any purpose, commercial or non-commercial, and by any
14 * means.
15 *
16 * In jurisdictions that recognize copyright laws, the author or authors
17 * of this software dedicate any and all copyright interest in the
18 * software to the public domain. We make this dedication for the benefit
19 * of the public at large and to the detriment of our heirs and
20 * successors. We intend this dedication to be an overt act of
21 * relinquishment in perpetuity of all present and future rights to this
22 * software under copyright law.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
28 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
29 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30 * OTHER DEALINGS IN THE SOFTWARE.
31 * #L%
32 */
33
34 import java.io.IOException;
35 import java.lang.reflect.Array;
36
37 import nom.tam.fits.header.Bitpix;
38 import nom.tam.fits.header.Standard;
39 import nom.tam.util.ArrayDataInput;
40 import nom.tam.util.ArrayDataOutput;
41 import nom.tam.util.ArrayFuncs;
42 import nom.tam.util.Cursor;
43 import nom.tam.util.FitsEncoder;
44
45 /**
46 * Random Groups data. The use of random groups is discouraged, even by the FITS standard. Some old radio data may be
47 * packaged in this format. Thus apart from provided limited support for reading such data, users should not create
48 * random groups anew. {@link BinaryTable} offers a much more flexible and capable way for storing an ensemble of
49 * parameters, arrays, and more.
50 * <p>
51 * Random groups are instantiated as a two-dimensional array of objects. The first dimension of the array is the number
52 * of groups. The second dimension is 2. The first object in every row is a one dimensional parameter array. The second
53 * element is the n-dimensional data array.
54 *
55 * @see BinaryTable
56 */
57 public class RandomGroupsData extends Data {
58
59 private int groups;
60 private Object[] sampleRow;
61 private Object[][] dataArray;
62
63 /**
64 * Create the equivalent of a null data element.
65 */
66 public RandomGroupsData() {
67 dataArray = new Object[0][];
68 }
69
70 /**
71 * Constructor used by RandomGroupsHDU only...
72 *
73 * @param gcount The number of parameter groups
74 * @param sampleRow The 2-element array of a sample group.
75 *
76 * @since 1.18
77 */
78 RandomGroupsData(int gcount, Object[] sampleRow) {
79 this();
80 groups = gcount;
81 this.sampleRow = sampleRow;
82 }
83
84 /**
85 * Create a RandomGroupsData object using the specified object to initialize the data array.
86 *
87 * @param x The initial data array. This should a two-d array of objects as described above.
88 *
89 * @throws IllegalArgumentException if the second array dimension is specified and it is not 2, or if the parameter
90 * arrya is not 1-dimensional, or if the parameter and data types differ.
91 */
92 public RandomGroupsData(Object[][] x) throws IllegalArgumentException {
93 dataArray = x == null ? new Object[0][] : x;
94 groups = dataArray.length;
95 if (groups > 0) {
96
97 if (dataArray[0].length != 2) {
98 throw new IllegalArgumentException("Second array dimension must be 2");
99 }
100
101 if (Array.getLength(ArrayFuncs.getDimensions(dataArray[0][0])) != 1) {
102 throw new IllegalArgumentException("Expected 1D parameter array.");
103 }
104
105 if (dataArray[0][1] != null) {
106 Class<?> pbase = ArrayFuncs.getBaseClass(dataArray[0][0]);
107 Class<?> dbase = ArrayFuncs.getBaseClass(dataArray[0][1]);
108
109 if (pbase != dbase) {
110 throw new IllegalArgumentException(
111 "Mismatched parameters and data types (" + pbase.getName() + " vs " + dbase.getName() + ")");
112 }
113 }
114
115 sampleRow = new Object[2];
116 sampleRow[0] = ArrayFuncs.deepClone(dataArray[0][0]);
117 sampleRow[1] = ArrayFuncs.deepClone(dataArray[0][1]);
118 }
119 }
120
121 /**
122 * Returns the Java class of the the parameter and data array elements.
123 *
124 * @return The java class of the parameter and data elements.
125 *
126 * @since 1.18
127 */
128 public Class<?> getElementType() {
129 return sampleRow == null ? null : ArrayFuncs.getBaseClass(sampleRow[0]);
130 }
131
132 /**
133 * Returns the dimensions of the grouped parameters
134 *
135 * @return The dimensions of the parameters or -1 if not defined.
136 *
137 * @see #getDataDims()
138 *
139 * @since 1.18
140 */
141 public int getParameterCount() {
142 return sampleRow == null ? -1 : Array.getLength(sampleRow[0]);
143 }
144
145 /**
146 * Returns the dimensions of the grouped data
147 *
148 * @return The dimensions of the parameters, or <code>null</code> if not defined.
149 *
150 * @see #getParameterCount()
151 *
152 * @since 1.18
153 */
154 public int[] getDataDims() {
155 return sampleRow == null ? null : ArrayFuncs.getDimensions(sampleRow[1]);
156 }
157
158 @SuppressWarnings("deprecation")
159 @Override
160 protected void fillHeader(Header h) throws FitsException {
161 if (groups <= 0) {
162 throw new FitsException("Invalid (empty) random group data");
163 }
164 Standard.context(RandomGroupsData.class);
165
166 // We'll assume it's a primary image, until we know better...
167 // Just in case, we don't want an XTENSION key lingering around...
168 h.deleteKey(Standard.XTENSION);
169
170 Cursor<String, HeaderCard> c = h.iterator();
171 c.add(HeaderCard.create(Standard.SIMPLE, true));
172 c.add(HeaderCard.create(Standard.BITPIX, Bitpix.forPrimitiveType(getElementType()).getHeaderValue()));
173
174 int[] dims = getDataDims();
175 c.add(HeaderCard.create(Standard.NAXIS, dims.length + 1));
176 h.addValue(Standard.NAXIS1, 0);
177
178 for (int i = 1; i <= dims.length; i++) {
179 c.add(HeaderCard.create(Standard.NAXISn.n(i + 1), dims[dims.length - i]));
180 }
181
182 // Just in case!
183 c.add(HeaderCard.create(Standard.GROUPS, true));
184 c.add(HeaderCard.create(Standard.PCOUNT, getParameterCount()));
185 c.add(HeaderCard.create(Standard.GCOUNT, groups));
186 c.add(HeaderCard.create(Standard.EXTEND, true));
187
188 Standard.context(null);
189 }
190
191 @Override
192 protected long getTrueSize() {
193 if (sampleRow == null) {
194 return 0;
195 }
196 return (FitsEncoder.computeSize(sampleRow[0]) + FitsEncoder.computeSize(sampleRow[1])) * groups;
197 }
198
199 @Override
200 public boolean isEmpty() {
201 return dataArray.length == 0;
202 }
203
204 @Override
205 protected void loadData(ArrayDataInput in) throws IOException {
206 dataArray = new Object[groups][2];
207
208 for (int i = 0; i < groups; i++) {
209 dataArray[i][0] = ((Object[]) ArrayFuncs.deepClone(sampleRow))[0];
210 dataArray[i][1] = ((Object[]) ArrayFuncs.deepClone(sampleRow))[1];
211 }
212
213 in.readImage(dataArray);
214 }
215
216 @Override
217 protected Object[][] getCurrentData() {
218 return dataArray;
219 }
220
221 @Override
222 public Object[][] getData() throws FitsException {
223 return (Object[][]) super.getData();
224 }
225
226 @SuppressWarnings({"resource", "deprecation"})
227 @Override
228 public void write(ArrayDataOutput str) throws FitsException {
229 if (getTrueSize() <= 0) {
230 return;
231 }
232
233 if (str != getRandomAccessInput()) {
234 ensureData();
235 }
236
237 try {
238 str.writeArray(dataArray);
239 FitsUtil.pad(str, getTrueSize());
240 } catch (IOException e) {
241 throw new FitsException("IO error writing random groups data ", e);
242 }
243 }
244
245 @SuppressWarnings("deprecation")
246 @Override
247 public RandomGroupsHDU toHDU() throws FitsException {
248 Header h = new Header();
249 fillHeader(h);
250 return new RandomGroupsHDU(h, this);
251 }
252
253 /**
254 * Returns the image component stored in the specified group.
255 *
256 * @param group The zero-based group index
257 *
258 * @return The image array for the specified group
259 *
260 * @throws ArrayIndexOutOfBoundsException if the group index is out of bounds
261 * @throws FitsException if the deferred data could not be loaded.
262 *
263 * @see RandomGroupsHDU#getParameter(String, int)
264 *
265 * @since 1.19
266 */
267 public Object getImage(int group) throws ArrayIndexOutOfBoundsException, FitsException {
268 ensureData();
269 return dataArray[group][1];
270 }
271
272 Object getParameterArray(int group) throws ArrayIndexOutOfBoundsException, FitsException {
273 ensureData();
274 return dataArray[group][0];
275 }
276
277 }