001    /* @(#)FitsColumn.java     $Revision: 1.5 $    $Date: 2003/04/11 08:41:15 $
002     *
003     * Copyright (C) 1999 European Southern Observatory 
004     * License:  GNU General Public License version 2 or later
005     */
006    package org.eso.fits;
007    
008    import java.lang.*;
009    import java.io.*;
010    import java.util.*;
011    
012    /** FitsColumn class represents a FITS table column in either ASCII or
013     *  BINARY table format. Note: only binary data formats A,L,I,J,E,D
014     *  are fully supported.
015     *
016     *  @version $Revision: 1.5 $ $Date: 2003/04/11 08:41:15 $
017     *  @author  P.Grosbol, DMD/ESO, <pgrosbol@eso.org>
018     */
019    public class FitsColumn {
020    
021        private String stringNull = null;
022        private long    intNull;
023        private boolean intNullDefined = false;
024        private int repeat = 1;
025        private char dataType = '\0';
026        private long columnOffset = 0;
027        private int recordSize = 0;
028        private FitsTform format;
029        private int bytesPerData = 0;
030        private long noRows = 0;
031        private boolean binColumn = true;
032        private String display;
033        private String label;
034        private String unit;
035        private double zero;
036        private double scale;
037        private boolean scaling = false;
038    
039        private RandomAccessFile dataFile = null;
040        private long dataOffset = 0;
041        private boolean isRAFile = true;
042        private byte[] dataArray = null;
043    
044        /** Constructor for FitsColumn class from a file
045         *
046         *  @param type storage type of column i.e. Fits.ATABLE or
047         *              Fits.BTABLE, by default Fits.BTABLE is assumed
048         *  @param tform storage format of data in column 
049         *  @param label name of column
050         *  @param rows  no. of rows in the column
051         *  @exception FitsException
052         */
053        public FitsColumn(int type, String tform, String label, int rows)
054            throws FitsException {
055    
056            this.format = new FitsTform(tform);
057            this.dataType = format.getDataType();
058            this.repeat = format.getRepeat();
059            this.bytesPerData = format.getWidth();
060            this.label = label;
061            this.noRows = rows;
062            this.binColumn = (type == Fits.BTABLE);
063        }
064    
065        /** Sets data matrix with the table data as a RandomAcessFile.
066         *
067         *  @param file  associated RandomAccessFile with FITS data
068         *  @param dataOffset byte offset of the table data unit in the FITS file
069         *  @param position relative byte start position of of column data
070         *                  within record (first byte is 0)
071         *  @param recordSize byte size of table record
072         */
073        public void setData(RandomAccessFile file, long dataOffset,
074                            int position, int recordSize) {
075            dataFile = file;
076            this.dataOffset = dataOffset;
077            isRAFile = true;
078            this.columnOffset = position;
079            this.recordSize = recordSize;
080        }
081    
082        /** Sets data matrix with the table data as a byte array
083         *
084         *  @param array  byte array with table data matrix
085         *  @param position relative byte start position of of column data
086         *                  within record (first byte is 0)
087         *  @param recordSize byte size of table record
088         */
089        public void setData(byte[] array, int position, int recordSize) {
090            dataArray = array;
091            isRAFile = false;
092            this.columnOffset = position;
093            this.recordSize = recordSize;
094        }
095    
096        /** Read single column element as integer value.  An ndefined
097         *  value is returned as Integer.MIN_VALUE. The following column
098         *  types are supported i.e. binary I/J formats.  For array type
099         *  elements only the first element is read. Note: ASCII table
100         *  columns are read with free format and does not conform to the
101         *  FITS standard in the current implementation.
102         *
103         *  @param row no. of element in column (starting with 0)
104         */
105        public int getInt(int row) {
106            int value = Integer.MIN_VALUE;
107            byte[] dbuf = getBytes(row);
108    
109            try {
110                if (binColumn) {
111                    DataInputStream di = 
112                        new DataInputStream(new ByteArrayInputStream(dbuf));
113                    switch (dataType) {
114                    case 'I' :
115                        value = (int) di.readShort();
116                        break;
117                    case 'J' :
118                        value = di.readInt();
119                        break;
120                    }
121                    if (intNullDefined && value==intNull) {
122                        value = Integer.MIN_VALUE;
123                    } else if (scaling) {
124                        value = (int) (zero + scale*value);
125                    }
126                } else {
127                    String str = new String(dbuf);
128                    if (stringNull == null || !str.startsWith(stringNull)) {
129                        value = (Integer.valueOf(str)).intValue();
130                        if (scaling) {
131                            value = (int) (zero + scale*value);
132                        }
133                    }
134                }
135            } catch (Exception e) {
136            }
137    
138            return value;
139        }
140    
141        /** Read single column element as integer array.  Undefined values
142         *  are returned as Integer.MIN_VALUE. The following column types
143         *  are supported i.e. binary I/J formats. Note: ASCII table
144         *  columns are read with free format and does not conform to the
145         *  FITS standard in the current implementation.
146         *
147         *  @param row no. of element in column (starting with 0)
148         */
149        public int[] getInts(int row) {
150            int[] arr = new int[repeat];
151            byte[] dbuf = getBytes(row);
152    
153            try {
154                if (binColumn) {
155                    DataInputStream di = 
156                        new DataInputStream(new ByteArrayInputStream(dbuf));
157                    int value;
158                    for (int n=0; n<repeat; n++) {
159                        value = Integer.MIN_VALUE;
160                        switch (dataType) {
161                        case 'I' :
162                            value = (int) di.readShort();
163                            break;
164                        case 'J' :
165                            value =  di.readInt();
166                            break;
167                        }
168                        if (intNullDefined && value==intNull) {
169                            value = Integer.MIN_VALUE;
170                        } else if (scaling) {
171                                value = (int) (zero + scale*value);
172                            }
173                        arr[n] = value;
174                    }
175                } else {
176                    String str = new String(dbuf);
177                    arr[0] = Integer.MIN_VALUE;
178                    if (stringNull == null || !str.startsWith(stringNull)) {
179                        arr[0] = (Integer.valueOf(str)).intValue();
180                        if (scaling) {
181                            arr[0] = (int) (zero + scale*arr[0]);
182                        }
183                    }
184                }
185            } catch (Exception e) {
186            }
187    
188            return arr;
189        }
190    
191        /** Read single column element as double value.  An undefined
192         *  value is returned as Double.NaN. The following column types
193         *  are supported i.e. binary I/J/E/D formats.  For array type
194         *  elements only the first element is read.  Note: ASCII table
195         *  columns are read with free format and does not conform to the
196         *  FITS standard in the current implementation.
197         *
198         *  @param row no. of element in column (starting with 0)
199         */
200        public double getReal(int row) {
201            double value = Double.NaN;
202            byte[] dbuf = getBytes(row);
203    
204            try {
205                if (binColumn) {
206                    DataInputStream di = 
207                        new DataInputStream(new ByteArrayInputStream(dbuf));
208                    switch (dataType) {
209                    case 'I' :
210                        value = (double) di.readShort();
211                        break;
212                    case 'J' :
213                        value = (double) di.readInt();
214                        break;
215                    case 'E' :
216                        value = (double) di.readFloat();
217                        break;
218                    case 'D' :
219                        value = di.readDouble();
220                        break;
221                    }
222                    if (scaling) {
223                        value = zero + scale*value;
224                    }
225                }
226                else {
227                    String str = new String(dbuf);
228                    if ((stringNull == null) || !str.startsWith(stringNull)) {
229                        value = (Double.valueOf(str)).doubleValue();
230                        if (scaling) {
231                            value = zero + scale*value;
232                        }
233                    }
234                }
235            } catch (Exception e) {
236            }
237    
238            return value;
239        }
240    
241        /** Read single column element as double array. Undefined value
242         *  are returned as Double.NaN. The following column types are
243         *  supported: binary I/J/E/D formats. Note: ASCII table columns
244         *  are read with free format and does not conform to the FITS
245         *  standard in the current implementation.
246         *
247         *  @param row no. of element in column (starting with 0)
248         */
249        public double[] getReals(int row) {
250            double[] arr = new double[repeat];
251            byte[] dbuf = getBytes(row);
252            
253            try {
254                if (binColumn) {
255                    DataInputStream di = 
256                        new DataInputStream(new ByteArrayInputStream(dbuf));
257                    for (int n=0; n<repeat; n++) {
258                        switch (dataType) {
259                        case 'I' :
260                            arr[n] = (double) di.readShort();
261                            break;
262                        case 'J' :
263                            arr[n] = (double) di.readInt();
264                            break;
265                        case 'E' :
266                            arr[n] = (double) di.readFloat();
267                            break;
268                        case 'D' :
269                            arr[n] = di.readDouble();
270                            break;
271                        }
272                        if (scaling) {
273                            arr[n] = zero + scale*arr[n];
274                        }
275                    }
276                } else {
277                    double value = Double.NaN;
278                    String str = new String(dbuf);
279                    if ((stringNull == null) || !str.startsWith(stringNull)) {
280                        arr[0] = (Double.valueOf(str)).doubleValue();
281                        if (scaling) {
282                            arr[0] = zero + scale*arr[0];
283                        }
284                    }
285                }
286            } catch (Exception e) {
287            }
288    
289            return arr;
290        }
291    
292        /** Read single column element as string. An undefined value
293         *  is returned as a NULL string. The following column types are
294         *  supported: A/L formats.
295         *
296         *  @param row number of element in column (starting with 0)
297         */
298        public String getString(int row) {
299            String str = null;
300            if ((dataType == 'A') || (dataType == 'L')) {
301                byte[] dbuf = getBytes(row);
302                if (dbuf != null) {
303                    if ((stringNull == null) || !str.startsWith(stringNull)) {
304                        str = new String(dbuf);
305                    }
306                }
307            }
308            return str;
309        }
310    
311        /** Extract a column element from the 'file' as byte array.
312         *
313         *  @param row number of element in column (starting with 0)
314         */
315        private byte[] getBytes(int row) {
316            if (row<0 || noRows<=row) return null;
317            byte[] dbuf = new byte[getWidth()];
318    
319            if (isRAFile) {
320                try {
321                if (dataFile == null) return null;
322                    dataFile.seek(dataOffset + columnOffset + row*recordSize);
323                    dataFile.read(dbuf);
324                } catch (IOException e) {
325                    return null;
326                }
327            } else {
328                if (dataArray == null) return null;
329                int k = 0;
330                int n = (int) (columnOffset + row*recordSize);
331                int nsize = getWidth();
332                while (0 < nsize--) dbuf[k++] = dataArray[n++];
333            }
334    
335            return dbuf;
336        }
337    
338        /** Define NULL string for ASCII table column.
339         *
340         *  @param nullValue string with null value
341         */
342        public void setNull(String nullValue){
343            this.stringNull = nullValue;
344        }
345    
346        /** Define NULL value for interger format Binary table columns
347         *  @param nullValue value of NULL integer */
348        public void setNull(int nullValue){
349            this.intNull = nullValue;
350            intNullDefined = true;
351        }
352    
353        /** Define dimension of binary table column. Note: This is not
354         *  used in the current implementation
355         *
356         *  @param dim string with dimension soecification for column
357         */
358        public void setDim(String dim){
359        }
360    
361        /** Retrieve repeat factor that is number of values per column element */
362        public int getRepeat() {
363            return this.repeat;
364        }
365    
366        /** Get the data type character for column as given in the TFORM
367         *  FITS keyword. */
368        public char getDataType() {
369            return this.dataType;
370        }
371    
372        /** Calculate the number of bytes associated to a column entry. */
373        protected int getWidth() {
374            return repeat*bytesPerData;
375        }
376    
377        /** Retrieve the display format for the column (ref. TDISP keyword). */
378        public String getDisplay(){
379            return display;
380        }
381    
382        /** Set the display format of the column.
383         *
384         *  @param  display  string with the display format for the column */
385        public void setDisplay(String display){
386            this.display = display;
387        }
388    
389        /** Retrieve unit string for the column. */
390        public String getUnit(){
391            return unit;
392        }
393    
394        /** Set unit string for column.
395         *
396         *  @param  unit  string with the unit of the column */
397        public void setUnit(String unit){
398            this.unit = unit;
399        }
400    
401        /** Retrieve column label. */
402        public String getLabel(){
403            return label;
404        }
405    
406        /** Set label of the column.
407         *
408         *  @param label  string with the column label */
409        public void setLabel(String label){
410            this.label = label;
411        }
412    
413        /** Get scaling zero point for table column. */
414        public double getZero(){
415            return zero;
416        }
417    
418        /** Define scaling zero point for table column.
419         *
420         *  @param zero  scaling zero point applied on raw data values */
421        public void setZero(double zero){
422            this.zero = zero;
423            this.scaling = true;
424        }
425    
426        /** Get scaling factor for table column. */
427        public double getScale(){
428            return scale;
429        }
430    
431        /** Define scaling factor for table column.
432         *
433         *  @param scale  scaling factor applied on raw data values */
434        public void setScale(double scale){
435            this.scale = scale;
436            this.scaling = true;
437        }
438    }