View Javadoc
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 }