1 package nom.tam.fits;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 import java.io.IOException;
35 import java.lang.reflect.Array;
36 import java.util.Arrays;
37
38 import nom.tam.fits.header.Bitpix;
39 import nom.tam.fits.header.IFitsHeader;
40 import nom.tam.fits.header.Standard;
41 import nom.tam.util.ArrayDataInput;
42 import nom.tam.util.ArrayDataOutput;
43 import nom.tam.util.ArrayFuncs;
44 import nom.tam.util.ByteFormatter;
45 import nom.tam.util.ByteParser;
46 import nom.tam.util.Cursor;
47 import nom.tam.util.FormatException;
48
49 import static nom.tam.fits.header.Standard.NAXIS1;
50 import static nom.tam.fits.header.Standard.NAXIS2;
51 import static nom.tam.fits.header.Standard.TBCOLn;
52 import static nom.tam.fits.header.Standard.TDMAXn;
53 import static nom.tam.fits.header.Standard.TDMINn;
54 import static nom.tam.fits.header.Standard.TFIELDS;
55 import static nom.tam.fits.header.Standard.TFORMn;
56 import static nom.tam.fits.header.Standard.TLMAXn;
57 import static nom.tam.fits.header.Standard.TLMINn;
58 import static nom.tam.fits.header.Standard.TNULLn;
59
60 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
61
62
63
64
65
66
67
68
69
70
71 @SuppressWarnings("deprecation")
72 public class AsciiTable extends AbstractTableData {
73
74 private static final int MAX_INTEGER_LENGTH = 10;
75
76 private static final int FLOAT_MAX_LENGTH = 16;
77
78 private static final int LONG_MAX_LENGTH = 20;
79
80 private static final int INT_MAX_LENGTH = 10;
81
82 private static final int DOUBLE_MAX_LENGTH = 24;
83
84
85 private static boolean isI10PreferInt = true;
86
87
88
89
90 private int nRows;
91
92
93 private int nFields;
94
95
96 private int rowLen;
97
98
99 private String[] nulls;
100
101
102 private Class<?>[] types;
103
104
105 private int[] offsets;
106
107
108 private int[] lengths;
109
110
111 private byte[] buffer;
112
113
114 private boolean[] isNull;
115
116
117 private String[] names;
118
119
120
121
122 private Object[] data;
123
124
125
126
127 private ByteParser bp;
128
129
130 private ArrayDataInput currInput;
131
132
133 public AsciiTable() {
134 data = new Object[0];
135 buffer = null;
136 nFields = 0;
137 nRows = 0;
138 rowLen = 0;
139 types = new Class[0];
140 lengths = new int[0];
141 offsets = new int[0];
142 nulls = new String[0];
143 names = new String[0];
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157 public AsciiTable(Header hdr) throws FitsException {
158 this(hdr, isI10PreferInt);
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 public AsciiTable(Header hdr, boolean preferInt) throws FitsException {
183 String ext = hdr.getStringValue(Standard.XTENSION, Standard.XTENSION_IMAGE);
184
185 if (!ext.equalsIgnoreCase(Standard.XTENSION_ASCIITABLE)) {
186 throw new FitsException("Not an ASCII table header (XTENSION = " + hdr.getStringValue(Standard.XTENSION) + ")");
187 }
188
189 nRows = hdr.getIntValue(NAXIS2);
190 nFields = hdr.getIntValue(TFIELDS);
191 rowLen = hdr.getIntValue(NAXIS1);
192
193 types = new Class[nFields];
194 offsets = new int[nFields];
195 lengths = new int[nFields];
196 nulls = new String[nFields];
197 names = new String[nFields];
198
199 for (int i = 0; i < nFields; i++) {
200 names[i] = hdr.getStringValue(Standard.TTYPEn.n(i + 1), TableHDU.getDefaultColumnName(i));
201 offsets[i] = hdr.getIntValue(TBCOLn.n(i + 1)) - 1;
202 String s = hdr.getStringValue(TFORMn.n(i + 1));
203 if (offsets[i] < 0 || s == null) {
204 throw new FitsException("Invalid Specification for column:" + (i + 1));
205 }
206 s = s.trim();
207 char c = s.charAt(0);
208 s = s.substring(1);
209 if (s.indexOf('.') > 0) {
210 s = s.substring(0, s.indexOf('.'));
211 }
212 lengths[i] = Integer.parseInt(s);
213
214 switch (c) {
215 case 'A':
216 types[i] = String.class;
217 break;
218 case 'I':
219 if (lengths[i] == MAX_INTEGER_LENGTH) {
220 types[i] = guessI10Type(i, hdr, preferInt);
221 } else {
222 types[i] = lengths[i] > MAX_INTEGER_LENGTH ? long.class : int.class;
223 }
224 break;
225 case 'F':
226 case 'E':
227 types[i] = float.class;
228 break;
229 case 'D':
230 types[i] = double.class;
231 break;
232 default:
233 throw new FitsException("could not parse column type of ascii table");
234 }
235
236 nulls[i] = hdr.getStringValue(TNULLn.n(i + 1));
237 if (nulls[i] != null) {
238 nulls[i] = nulls[i].trim();
239 }
240 }
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 public static AsciiTable fromColumnMajor(Object[] columns) throws FitsException {
263 AsciiTable t = new AsciiTable();
264 for (int i = 0; i < columns.length; i++) {
265 try {
266 t.addColumn(columns[i]);
267 } catch (Exception e) {
268 throw new FitsException("col[" + i + "]: " + e.getMessage(), e);
269 }
270 }
271 return t;
272 }
273
274 void setColumnName(int col, String value)
275 throws IllegalArgumentException, IndexOutOfBoundsException, HeaderCardException {
276 HeaderCard.validateChars(value);
277 names[col] = value;
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291 private boolean requiresLong(Header h, IFitsHeader key, Long dft) {
292 long l = h.getLongValue(key, dft);
293 if (l == dft) {
294 return false;
295 }
296
297 return (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE);
298 }
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318 private Class<?> guessI10Type(int col, Header h, boolean preferInt) {
319 col++;
320
321 if (requiresLong(h, TLMINn.n(col), Long.MAX_VALUE) || requiresLong(h, TLMAXn.n(col), Long.MIN_VALUE)
322 || requiresLong(h, TDMINn.n(col), Long.MAX_VALUE) || requiresLong(h, TDMAXn.n(col), Long.MIN_VALUE)) {
323 return long.class;
324 }
325
326 if ((h.containsKey(TLMINn.n(col)) || h.containsKey(TDMINn.n(col)))
327 && (h.containsKey(TLMAXn.n(col)) || h.containsKey(TDMAXn.n(col)))) {
328
329 return int.class;
330 }
331
332 return preferInt ? int.class : long.class;
333 }
334
335
336
337
338
339
340
341
342
343
344 public final Class<?> getColumnType(int col) {
345 return types[col];
346 }
347
348 int addColInfo(int col, Cursor<String, HeaderCard> iter) {
349 String tform = null;
350 if (types[col] == String.class) {
351 tform = "A" + lengths[col];
352 } else if (types[col] == int.class || types[col] == long.class) {
353 tform = "I" + lengths[col];
354 } else if (types[col] == float.class) {
355 tform = "E" + lengths[col] + ".0";
356 } else if (types[col] == double.class) {
357 tform = "D" + lengths[col] + ".0";
358 }
359
360 Standard.context(AsciiTable.class);
361 if (names[col] != null) {
362 iter.add(HeaderCard.create(Standard.TTYPEn.n(col + 1), names[col]));
363 }
364 iter.add(HeaderCard.create(Standard.TFORMn.n(col + 1), tform));
365 iter.add(HeaderCard.create(Standard.TBCOLn.n(col + 1), offsets[col] + 1));
366 Standard.context(null);
367 return lengths[col];
368 }
369
370 @Override
371 public int addColumn(Object newCol) throws FitsException, IllegalArgumentException {
372 if (newCol == null) {
373 throw new FitsException("data is null");
374 }
375
376 if (!newCol.getClass().isArray()) {
377 throw new IllegalArgumentException("Not an array: " + newCol.getClass().getName());
378 }
379
380 int maxLen = 1;
381 if (newCol instanceof String[]) {
382 String[] sa = (String[]) newCol;
383 for (String element : sa) {
384 if (element != null && element.length() > maxLen) {
385 maxLen = element.length();
386 }
387 }
388 } else if (newCol instanceof double[]) {
389 maxLen = DOUBLE_MAX_LENGTH;
390 } else if (newCol instanceof int[]) {
391 maxLen = INT_MAX_LENGTH;
392 } else if (newCol instanceof long[]) {
393 maxLen = LONG_MAX_LENGTH;
394 } else if (newCol instanceof float[]) {
395 maxLen = FLOAT_MAX_LENGTH;
396 } else {
397 throw new FitsException(
398 "No AsciiTable support for elements of " + newCol.getClass().getComponentType().getName());
399 }
400 addColumn(newCol, maxLen);
401
402
403 buffer = null;
404
405 return nFields;
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427 public int addColumn(Object newCol, int width) throws FitsException, IllegalArgumentException {
428 if (width < 1) {
429 throw new IllegalArgumentException("Illegal ASCII column width: " + width);
430 }
431
432 if (!newCol.getClass().isArray()) {
433 throw new IllegalArgumentException("Not an array: " + newCol.getClass().getName());
434 }
435
436 if (nFields > 0 && Array.getLength(newCol) != nRows) {
437 throw new FitsException(
438 "Mismatched number of rows: expected " + nRows + ", got " + Array.getLength(newCol) + "rows.");
439 }
440
441 if (nFields == 0) {
442 nRows = Array.getLength(newCol);
443 }
444
445 Class<?> type = ArrayFuncs.getBaseClass(newCol);
446 if (type != int.class && type != long.class && type != float.class && type != double.class
447 && type != String.class) {
448 throw new FitsException("No AsciiTable support for elements of " + type.getName());
449 }
450
451 data = Arrays.copyOf(data, nFields + 1);
452 offsets = Arrays.copyOf(offsets, nFields + 1);
453 lengths = Arrays.copyOf(lengths, nFields + 1);
454 types = Arrays.copyOf(types, nFields + 1);
455 nulls = Arrays.copyOf(nulls, nFields + 1);
456 names = Arrays.copyOf(names, nFields + 1);
457
458 data[nFields] = newCol;
459 offsets[nFields] = rowLen + 1;
460 lengths[nFields] = width;
461 types[nFields] = ArrayFuncs.getBaseClass(newCol);
462 names[nFields] = TableHDU.getDefaultColumnName(nFields);
463
464 rowLen += width + 1;
465 if (isNull != null) {
466 boolean[] newIsNull = new boolean[nRows * (nFields + 1)];
467
468 int add = 0;
469 for (int i = 0; i < isNull.length; i++) {
470 if (i % nFields == 0) {
471 add++;
472 }
473 if (isNull[i]) {
474 newIsNull[i + add] = true;
475 }
476 }
477 isNull = newIsNull;
478 }
479 nFields++;
480
481
482 buffer = null;
483
484 return nFields;
485 }
486
487
488
489
490 @Override
491 public int addRow(Object[] newRow) throws FitsException {
492 try {
493
494
495
496 if (nFields == 0) {
497 for (Object element : newRow) {
498 addColumn(element);
499 }
500 } else {
501 for (int i = 0; i < nFields; i++) {
502 Object o = ArrayFuncs.newInstance(types[i], nRows + 1);
503 System.arraycopy(data[i], 0, o, 0, nRows);
504 System.arraycopy(newRow[i], 0, o, nRows, 1);
505 data[i] = o;
506 }
507 nRows++;
508 }
509
510 buffer = null;
511 return nRows;
512 } catch (Exception e) {
513 throw new FitsException("Error adding row:" + e.getMessage(), e);
514 }
515 }
516
517 @Override
518 public void deleteColumns(int start, int len) throws FitsException {
519 ensureData();
520
521 Object[] newData = new Object[nFields - len];
522 int[] newOffsets = new int[nFields - len];
523 int[] newLengths = new int[nFields - len];
524 Class<?>[] newTypes = new Class[nFields - len];
525 String[] newNulls = new String[nFields - len];
526
527
528 System.arraycopy(data, 0, newData, 0, start);
529
530 System.arraycopy(lengths, 0, newLengths, 0, start);
531 System.arraycopy(types, 0, newTypes, 0, start);
532 System.arraycopy(nulls, 0, newNulls, 0, start);
533
534
535 System.arraycopy(data, start + len, newData, start, nFields - start - len);
536
537 System.arraycopy(lengths, start + len, newLengths, start, nFields - start - len);
538 System.arraycopy(types, start + len, newTypes, start, nFields - start - len);
539 System.arraycopy(nulls, start + len, newNulls, start, nFields - start - len);
540
541 for (int i = start; i < start + len; i++) {
542 rowLen -= lengths[i] + 1;
543 }
544
545 data = newData;
546 offsets = newOffsets;
547 lengths = newLengths;
548 types = newTypes;
549 nulls = newNulls;
550
551 if (isNull != null) {
552 boolean found = false;
553
554 boolean[] newIsNull = new boolean[nRows * (nFields - len)];
555 for (int i = 0; i < nRows; i++) {
556 int oldOff = nFields * i;
557 int newOff = (nFields - len) * i;
558 for (int col = 0; col < start; col++) {
559 newIsNull[newOff + col] = isNull[oldOff + col];
560 found = found || isNull[oldOff + col];
561 }
562 for (int col = start + len; col < nFields; col++) {
563 newIsNull[newOff + col - len] = isNull[oldOff + col];
564 found = found || isNull[oldOff + col];
565 }
566 }
567 if (found) {
568 isNull = newIsNull;
569 } else {
570 isNull = null;
571 }
572 }
573
574
575 buffer = null;
576
577 nFields -= len;
578 }
579
580
581
582
583
584 @Override
585 public void deleteRows(int start, int len) throws FitsException {
586 if (nRows == 0 || start < 0 || start >= nRows || len <= 0) {
587 return;
588 }
589 if (start + len > nRows) {
590 len = nRows - start;
591 }
592
593 ensureData();
594
595 for (int i = 0; i < nFields; i++) {
596 try {
597 Object o = ArrayFuncs.newInstance(types[i], nRows - len);
598 System.arraycopy(data[i], 0, o, 0, start);
599 System.arraycopy(data[i], start + len, o, start, nRows - len - start);
600 data[i] = o;
601 } catch (Exception e) {
602 throw new FitsException("Error deleting row: " + e.getMessage(), e);
603 }
604
605 }
606 nRows -= len;
607 }
608
609 @Override
610 protected void loadData(ArrayDataInput in) throws IOException, FitsException {
611 currInput = in;
612
613 if (buffer == null) {
614 getBuffer((long) nRows * rowLen, 0);
615 }
616
617 data = new Object[nFields];
618 for (int i = 0; i < nFields; i++) {
619 data[i] = ArrayFuncs.newInstance(types[i], nRows);
620 }
621
622 bp.setOffset(0);
623
624 int rowOffset;
625 for (int i = 0; i < nRows; i++) {
626 rowOffset = rowLen * i;
627 for (int j = 0; j < nFields; j++) {
628 try {
629 if (!extractElement(rowOffset + offsets[j], lengths[j], data, j, i, nulls[j])) {
630 if (isNull == null) {
631 isNull = new boolean[nRows * nFields];
632 }
633
634 isNull[j + i * nFields] = true;
635 }
636 } catch (ArrayIndexOutOfBoundsException e) {
637 throw new FitsException("not enough data: " + e, e);
638 }
639 }
640 }
641 }
642
643 @Override
644 public void read(ArrayDataInput in) throws FitsException {
645 currInput = in;
646 super.read(in);
647 }
648
649
650
651
652
653
654
655
656
657
658
659
660
661 private boolean extractElement(int offset, int length, Object[] array, int col, int row, String nullFld)
662 throws FitsException {
663
664 bp.setOffset(offset);
665
666 if (nullFld != null) {
667 String s = bp.getString(length);
668 if (s.trim().equals(nullFld)) {
669 return false;
670 }
671 bp.skip(-length);
672 }
673 try {
674 if (array[col] instanceof String[]) {
675 ((String[]) array[col])[row] = bp.getString(length);
676 } else if (array[col] instanceof int[]) {
677 ((int[]) array[col])[row] = bp.getInt(length);
678 } else if (array[col] instanceof float[]) {
679 ((float[]) array[col])[row] = bp.getFloat(length);
680 } else if (array[col] instanceof double[]) {
681 ((double[]) array[col])[row] = bp.getDouble(length);
682 } else if (array[col] instanceof long[]) {
683 ((long[]) array[col])[row] = bp.getLong(length);
684 } else {
685 throw new FitsException("Invalid type for ASCII table conversion:" + array[col]);
686 }
687 } catch (FormatException e) {
688 throw new FitsException("Error parsing data at row,col:" + row + "," + col + " ", e);
689 }
690 return true;
691 }
692
693 @Override
694 protected void fillHeader(Header h) {
695 h.deleteKey(Standard.SIMPLE);
696 h.deleteKey(Standard.EXTEND);
697
698 Standard.context(AsciiTable.class);
699
700 Cursor<String, HeaderCard> c = h.iterator();
701 c.add(HeaderCard.create(Standard.XTENSION, Standard.XTENSION_ASCIITABLE));
702 c.add(HeaderCard.create(Standard.BITPIX, Bitpix.BYTE.getHeaderValue()));
703 c.add(HeaderCard.create(Standard.NAXIS, 2));
704 c.add(HeaderCard.create(Standard.NAXIS1, rowLen));
705 c.add(HeaderCard.create(Standard.NAXIS2, nRows));
706 c.add(HeaderCard.create(Standard.PCOUNT, 0));
707 c.add(HeaderCard.create(Standard.GCOUNT, 1));
708 c.add(HeaderCard.create(Standard.TFIELDS, nFields));
709
710 for (int i = 0; i < nFields; i++) {
711 addColInfo(i, c);
712 }
713
714 Standard.context(null);
715 }
716
717
718
719
720 private void getBuffer(long size, long offset) throws IOException, FitsException {
721
722 if (currInput == null) {
723 throw new IOException("No stream open to read");
724 }
725
726 if (size > Integer.MAX_VALUE) {
727 throw new FitsException("Cannot read ASCII table > 2 GB");
728 }
729
730 buffer = new byte[(int) size];
731 if (offset != 0) {
732 FitsUtil.reposition(currInput, offset);
733 }
734 currInput.readFully(buffer);
735 bp = new ByteParser(buffer);
736 }
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754 @Override
755 public Object getColumn(int col) throws FitsException {
756 ensureData();
757 return data[col];
758 }
759
760 @Override
761 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intended exposure of mutable data")
762 protected Object[] getCurrentData() {
763 return data;
764 }
765
766 @Override
767 public Object[] getData() throws FitsException {
768 return (Object[]) super.getData();
769 }
770
771 @Override
772 public Object getElement(int row, int col) throws FitsException {
773 if (data != null) {
774 return singleElement(row, col);
775 }
776 return parseSingleElement(row, col);
777 }
778
779 @Override
780 public int getNCols() {
781 return nFields;
782 }
783
784 @Override
785 public int getNRows() {
786 return nRows;
787 }
788
789 @Override
790 public Object[] getRow(int row) throws FitsException {
791
792 if (data != null) {
793 return singleRow(row);
794 }
795 return parseSingleRow(row);
796 }
797
798
799
800
801
802
803 public int getRowLen() {
804 return rowLen;
805 }
806
807 @Override
808 protected long getTrueSize() {
809 return (long) nRows * rowLen;
810 }
811
812
813
814
815
816
817
818
819
820 public boolean isNull(int row, int col) {
821 if (isNull != null) {
822 return isNull[row * nFields + col];
823 }
824 return false;
825 }
826
827
828
829
830
831
832 private Object parseSingleElement(int row, int col) throws FitsException {
833
834 Object[] res = new Object[1];
835 try {
836 getBuffer(lengths[col], getFileOffset() + (long) row * (long) rowLen + offsets[col]);
837 } catch (IOException e) {
838 buffer = null;
839 throw new FitsException("Unable to read element", e);
840 }
841 res[0] = ArrayFuncs.newInstance(types[col], 1);
842
843 boolean success = extractElement(0, lengths[col], res, 0, 0, nulls[col]);
844 buffer = null;
845
846 return success ? res[0] : null;
847 }
848
849
850
851
852
853
854 private Object[] parseSingleRow(int row) throws FitsException {
855
856 Object[] res = new Object[nFields];
857
858 try {
859 getBuffer(rowLen, getFileOffset() + (long) row * (long) rowLen);
860 } catch (IOException e) {
861 throw new FitsException("Unable to read row", e);
862 }
863
864 for (int i = 0; i < nFields; i++) {
865 res[i] = ArrayFuncs.newInstance(types[i], 1);
866 if (!extractElement(offsets[i], lengths[i], res, i, 0, nulls[i])) {
867 res[i] = null;
868 }
869 }
870
871
872 buffer = null;
873 return res;
874 }
875
876 @Override
877 public void setColumn(int col, Object newData) throws FitsException {
878 ensureData();
879 if (col < 0 || col >= nFields || newData.getClass() != data[col].getClass()
880 || Array.getLength(newData) != Array.getLength(data[col])) {
881 throw new FitsException("Invalid column/column mismatch:" + col);
882 }
883 data[col] = newData;
884
885
886 buffer = null;
887 }
888
889 @Override
890 public void setElement(int row, int col, Object newData) throws FitsException {
891 ensureData();
892 try {
893 System.arraycopy(newData, 0, data[col], row, 1);
894 } catch (Exception e) {
895 throw new FitsException("Incompatible element:" + row + "," + col, e);
896 }
897 setNull(row, col, false);
898
899
900 buffer = null;
901
902 }
903
904
905
906
907
908
909
910
911
912 public void setNull(int row, int col, boolean flag) {
913 if (flag) {
914 if (isNull == null) {
915 isNull = new boolean[nRows * nFields];
916 }
917 isNull[col + row * nFields] = true;
918 } else if (isNull != null) {
919 isNull[col + row * nFields] = false;
920 }
921
922
923 buffer = null;
924 }
925
926
927
928
929
930 void setNullString(int col, String newNull) {
931 if (col >= 0 && col < nulls.length) {
932 nulls[col] = newNull;
933 }
934 }
935
936 @Override
937 public void setRow(int row, Object[] newData) throws FitsException {
938 if (row < 0 || row > nRows) {
939 throw new FitsException("Invalid row in setRow");
940 }
941 ensureData();
942 for (int i = 0; i < nFields; i++) {
943 try {
944 System.arraycopy(newData[i], 0, data[i], row, 1);
945 } catch (Exception e) {
946 throw new FitsException("Unable to modify row: incompatible data:" + row, e);
947 }
948 setNull(row, i, false);
949 }
950
951
952 buffer = null;
953
954 }
955
956
957
958
959 private Object singleElement(int row, int col) {
960
961 Object res = null;
962 if (isNull == null || !isNull[row * nFields + col]) {
963 res = ArrayFuncs.newInstance(types[col], 1);
964 System.arraycopy(data[col], row, res, 0, 1);
965 }
966 return res;
967 }
968
969
970
971
972 private Object[] singleRow(int row) {
973
974 Object[] res = new Object[nFields];
975 for (int i = 0; i < nFields; i++) {
976 if (isNull == null || !isNull[row * nFields + i]) {
977 res[i] = ArrayFuncs.newInstance(types[i], 1);
978 System.arraycopy(data[i], row, res[i], 0, 1);
979 }
980 }
981 return res;
982 }
983
984
985
986
987
988
989 @Override
990 public void updateAfterDelete(int oldNCol, Header hdr) throws FitsException {
991
992 int offset = 0;
993 for (int i = 0; i < nFields; i++) {
994 offsets[i] = offset;
995 hdr.addValue(TBCOLn.n(i + 1), offset + 1);
996 offset += lengths[i] + 1;
997 }
998 for (int i = nFields; i < oldNCol; i++) {
999 hdr.deleteKey(TBCOLn.n(i + 1));
1000 }
1001
1002 hdr.addValue(NAXIS1, rowLen);
1003 }
1004
1005 @Override
1006 public void write(ArrayDataOutput str) throws FitsException {
1007
1008 if (str != currInput) {
1009 ensureData();
1010 }
1011
1012
1013
1014 if (data == null) {
1015 throw new FitsException("Attempt to write undefined ASCII Table");
1016 }
1017
1018 if ((long) nRows * rowLen > Integer.MAX_VALUE) {
1019 throw new FitsException("Cannot write ASCII table > 2 GB");
1020 }
1021
1022 buffer = new byte[nRows * rowLen];
1023
1024 bp = new ByteParser(buffer);
1025 for (int i = 0; i < buffer.length; i++) {
1026 buffer[i] = (byte) ' ';
1027 }
1028
1029 ByteFormatter bf = new ByteFormatter();
1030
1031 for (int i = 0; i < nRows; i++) {
1032
1033 for (int j = 0; j < nFields; j++) {
1034 int offset = i * rowLen + offsets[j];
1035 int len = lengths[j];
1036 if (isNull != null && isNull[i * nFields + j]) {
1037 if (nulls[j] == null) {
1038 throw new FitsException("No null value set when needed");
1039 }
1040 bf.format(nulls[j], buffer, offset, len);
1041 } else if (types[j] == String.class) {
1042 String[] s = (String[]) data[j];
1043 bf.format(s[i], buffer, offset, len);
1044 } else if (types[j] == int.class) {
1045 int[] ia = (int[]) data[j];
1046 bf.format(ia[i], buffer, offset, len);
1047 } else if (types[j] == float.class) {
1048 float[] fa = (float[]) data[j];
1049 bf.format(fa[i], buffer, offset, len);
1050 } else if (types[j] == double.class) {
1051 double[] da = (double[]) data[j];
1052 bf.format(da[i], buffer, offset, len);
1053 } else if (types[j] == long.class) {
1054 long[] la = (long[]) data[j];
1055 bf.format(la[i], buffer, offset, len);
1056 }
1057 }
1058 }
1059
1060
1061 try {
1062 str.write(buffer);
1063 FitsUtil.pad(str, buffer.length, (byte) ' ');
1064 } catch (IOException e) {
1065 throw new FitsException("Error writing ASCII Table data", e);
1066 }
1067 }
1068
1069 @Override
1070 public AsciiTableHDU toHDU() {
1071 Header h = new Header();
1072 fillHeader(h);
1073 return new AsciiTableHDU(h, this);
1074 }
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093 public static void setI10PreferInt(boolean value) {
1094 isI10PreferInt = value;
1095 }
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108 public static boolean isI10PreferInt() {
1109 return isI10PreferInt;
1110 }
1111 }