Begin versioning.
[fits.git] / src / nom / tam / util / ColumnTable.java
1 package nom.tam.util;
2
3 /*
4  * Copyright: Thomas McGlynn 1997-1998.
5  * This code may be used for any purpose, non-commercial
6  * or commercial so long as this copyright notice is retained
7  * in the source code or included in or referred to in any
8  * derived software.
9  */
10 import java.io.*;
11 import java.lang.reflect.Array;
12
13 /** A data table is conventionally considered to consist of rows and
14  * columns, where the structure within each column is constant, but
15  * different columns may have different structures.  I.e., structurally
16  * columns may differ but rows are identical.
17  * Typically tabular data is usually stored in row order which can
18  * make it extremely difficult to access efficiently using Java.
19  * This class provides efficient
20  * access to data which is stored in row order and allows users to
21  * get and set the elements of the table.
22  * The table can consist only of arrays of primitive types.
23  * Data stored in column order can
24  * be efficiently read and written using the
25  * BufferedDataXputStream classes.
26  *
27  * The table is represented entirely as a set of one-dimensional primitive
28  * arrays.  For a given column, a row consists of some number of
29  * contiguous elements of the array.  Each column is required to have
30  * the same number of rows.
31  */
32 public class ColumnTable implements DataTable {
33
34     /** The columns to be read/written */
35     private Object[] arrays;
36     /** The number of elements in a row for each column */
37     private int[] sizes;
38     /** The number of rows */
39     private int nrow;
40     /** The number or rows to read/write in one I/O. */
41     private int chunk;
42     /** The size of a row in bytes */
43     private int rowSize;
44     /** The base type of each row (using the second character
45      * of the [x class names of the arrays.
46      */
47     private char[] types;
48     private Class[] bases;
49     // The following arrays are used to avoid having to check
50     // casts during the I/O loops.
51     // They point to elements of arrays.
52     private byte[][] bytePointers;
53     private short[][] shortPointers;
54     private int[][] intPointers;
55     private long[][] longPointers;
56     private float[][] floatPointers;
57     private double[][] doublePointers;
58     private char[][] charPointers;
59     private boolean[][] booleanPointers;
60
61     /** Create the object after checking consistency.
62      * @param arrays  An array of one-d primitive arrays.
63      * @param sizes   The number of elements in each row
64      *                for the corresponding column
65      */
66     public ColumnTable(Object[] arrays, int[] sizes) throws TableException {
67         setup(arrays, sizes);
68     }
69
70     /** Actually perform the initialization.
71      */
72     protected void setup(Object[] arrays, int[] sizes) throws TableException {
73
74         checkArrayConsistency(arrays, sizes);
75         getNumberOfRows();
76         initializePointers();
77
78     }
79
80     /** Get the number of rows in the table.
81      */
82     public int getNRows() {
83         return nrow;
84     }
85
86     /** Get the number of columns in the table.
87      */
88     public int getNCols() {
89         return arrays.length;
90     }
91
92     /** Get a particular column.
93      * @param col The column desired.
94      * @return an object containing the column data desired.
95      *         This will be an instance of a 1-d primitive array.
96      */
97     public Object getColumn(int col) {
98         return arrays[col];
99     }
100
101     /**
102      * Set the values in a particular column. The new values must match the old in
103      * length but not necessarily in type.
104      *
105      * @param col
106      *          The column to modify.
107      * @param newColumn
108      *          The new column data. This should be a primitive array.
109      * @exception TableException
110      *              Thrown when the new data is not commenserable with information
111      *              in the table.
112      */
113     public void setColumn(int col, Object newColumn) throws TableException {
114
115         boolean reset = newColumn.getClass() != arrays[col].getClass()
116                 || Array.getLength(newColumn) != Array.getLength(arrays[col]);
117         arrays[col] = newColumn;
118         if (reset) {
119             setup(arrays, sizes);
120         } else {
121             // This is required, because otherwise the typed pointer may point to the old
122             // array, which has been replaced by newColumn. Added by Jeroen de Jong, 1 Aug 2006
123             initializePointers();
124         }
125     }
126
127     /** Add a column */
128     public void addColumn(Object newColumn, int size) throws TableException {
129
130         String classname = newColumn.getClass().getName();
131         nrow = checkColumnConsistency(newColumn, classname, nrow, size);
132
133         rowSize += nrow * ArrayFuncs.getBaseLength(newColumn);
134
135         getNumberOfRows();
136
137         int ncol = arrays.length;
138
139         Object[] newArrays = new Object[ncol + 1];
140         int[] newSizes = new int[ncol + 1];
141         Class[] newBases = new Class[ncol + 1];
142         char[] newTypes = new char[ncol + 1];
143
144         System.arraycopy(arrays, 0, newArrays, 0, ncol);
145         System.arraycopy(sizes, 0, newSizes, 0, ncol);
146         System.arraycopy(bases, 0, newBases, 0, ncol);
147         System.arraycopy(types, 0, newTypes, 0, ncol);
148
149         arrays = newArrays;
150         sizes = newSizes;
151         bases = newBases;
152         types = newTypes;
153
154         arrays[ncol] = newColumn;
155         sizes[ncol] = size;
156         bases[ncol] = ArrayFuncs.getBaseClass(newColumn);
157         types[ncol] = classname.charAt(1);
158         addPointer(newColumn);
159     }
160
161     /** Add a row to the table.  This method is very inefficient
162      *  for adding multiple rows and should be avoided if possible.
163      */
164     public void addRow(Object[] row) throws TableException {
165
166         if (arrays.length == 0) {
167
168             for (int i = 0; i < row.length; i += 1) {
169                 addColumn(row[i], Array.getLength(row[i]));
170             }
171
172         } else {
173
174             if (row.length != arrays.length) {
175                 throw new TableException("Row length mismatch");
176             }
177
178             for (int i = 0; i < row.length; i += 1) {
179                 if (row[i].getClass() != arrays[i].getClass()
180                         || Array.getLength(row[i]) != sizes[i]) {
181                     throw new TableException("Row column mismatch at column:" + i);
182                 }
183                 Object xarray = ArrayFuncs.newInstance(bases[i], (nrow + 1) * sizes[i]);
184                 System.arraycopy(arrays[i], 0, xarray, 0, nrow * sizes[i]);
185                 System.arraycopy(row[i], 0, xarray, nrow * sizes[i], sizes[i]);
186                 arrays[i] = xarray;
187             }
188             initializePointers();
189             nrow += 1;
190         }
191     }
192
193     /** Get a element of the table.
194      * @param row The row desired.
195      * @param col The column desired.
196      * @return A primitive array containing the information.  Note
197      *         that an array will be returned even if the element
198      *         is a scalar.
199      */
200     public Object getElement(int row, int col) {
201
202         Object x = ArrayFuncs.newInstance(bases[col], sizes[col]);
203         System.arraycopy(arrays[col], sizes[col] * row, x, 0, sizes[col]);
204         return x;
205     }
206
207     /** Modify an element of the table.
208      * @param row The row containing the element.
209      * @param col The column containing the element.
210      * @param x   The new datum.  This should be 1-d primitive
211      *            array.
212      * @exception TableException Thrown when the new data
213      *                           is not of the same type as
214      *                           the data it replaces.
215      */
216     public void setElement(int row, int col, Object x)
217             throws TableException {
218
219         String classname = x.getClass().getName();
220
221         if (!classname.equals("[" + types[col])) {
222             throw new TableException("setElement: Incompatible element type");
223         }
224
225         if (Array.getLength(x) != sizes[col]) {
226             throw new TableException("setElement: Incompatible element size");
227         }
228
229         System.arraycopy(x, 0, arrays[col], sizes[col] * row, sizes[col]);
230     }
231
232     /** Get a row of data.
233      * @param The row desired.
234      * @return An array of objects each containing a primitive array.
235      */
236     public Object getRow(int row) {
237
238         Object[] x = new Object[arrays.length];
239         for (int col = 0; col < arrays.length; col += 1) {
240             x[col] = getElement(row, col);
241         }
242         return x;
243     }
244
245     /** Modify a row of data.
246      * @param row The row to be modified.
247      * @param x   The data to be modified.  This should be an
248      *            array of objects.  It is described as an Object
249      *            here since other table implementations may
250      *            use other methods to store the data (e.g.,
251      *            @see ColumnTable.getColumn.
252      */
253     public void setRow(int row, Object x) throws TableException {
254
255         if (!(x instanceof Object[])) {
256             throw new TableException("setRow: Incompatible row");
257         }
258
259         for (int col = 0; col < arrays.length; col += 1) {
260             setElement(row, col, ((Object[]) x)[col]);
261         }
262     }
263
264     /** Check that the columns and sizes are consistent.
265      * Inconsistencies include:
266      * <ul>
267      * <li> arrays and sizes have different lengths.
268      * <li> an element of arrays is not a primitive array.
269      * <li> the size of an array is not divisible by the sizes entry.
270      * <li> the number of rows differs for the columns.
271      * </ul>
272      * @param arrays The arrays defining the columns.
273      * @param sizes  The number of elements in each row for the column.
274      */
275     protected void checkArrayConsistency(Object[] arrays, int[] sizes)
276             throws TableException {
277
278         // This routine throws an error if it detects an inconsistency
279         // between the arrays being read in.
280
281         // First check that the lengths of the two arrays are the same.
282         if (arrays.length != sizes.length) {
283             throw new TableException("readArraysAsColumns: Incompatible arrays and sizes.");
284         }
285
286         // Now check that that we fill up all of the arrays exactly.
287         int ratio = 0;
288         int rowSize = 0;
289
290         this.types = new char[arrays.length];
291         this.bases = new Class[arrays.length];
292
293         // Check for a null table.
294         boolean nullTable = true;
295
296         for (int i = 0; i < arrays.length; i += 1) {
297
298             String classname = arrays[i].getClass().getName();
299
300             ratio = checkColumnConsistency(arrays[i], classname, ratio, sizes[i]);
301
302             rowSize += sizes[i] * ArrayFuncs.getBaseLength(arrays[i]);
303             types[i] = classname.charAt(1);
304             bases[i] = ArrayFuncs.getBaseClass(arrays[i]);
305         }
306
307         this.nrow = ratio;
308         this.rowSize = rowSize;
309         this.arrays = arrays;
310         this.sizes = sizes;
311     }
312
313     private int checkColumnConsistency(Object data, String classname, int ratio, int size)
314             throws TableException {
315
316
317         if (classname.charAt(0) != '[' || classname.length() != 2) {
318             throw new TableException("Non-primitive array for column");
319         }
320
321         int thisSize = Array.getLength(data);
322         if ((thisSize == 0 && size != 0 && ratio != 0)
323                 || (thisSize != 0 && size == 0)) {
324             throw new TableException("Size mismatch in column: " + thisSize + " != " + size);
325         }
326
327         // The row size must evenly divide the size of the array.
328         if (size != 0 && thisSize % size != 0) {
329             throw new TableException("Row size does not divide array for column");
330         }
331
332         // Finally the ratio of sizes must be the same for all columns -- this
333         // is the number of rows in the table.
334         int thisRatio = 0;
335         if (size > 0) {
336             thisRatio = thisSize / size;
337
338             if (ratio != 0 && (thisRatio != ratio)) {
339                 throw new TableException("Different number of rows in different columns");
340             }
341         }
342         if (thisRatio > 0) {
343             return thisRatio;
344         } else {
345             return ratio;
346         }
347
348     }
349
350     /** Calculate the number of rows to read/write at a time.
351      * @param rowSize The size of a row in bytes.
352      * @param nrows   The number of rows in the table.
353      */
354     protected void getNumberOfRows() {
355
356         int bufSize = 65536;
357
358         // If a row is larger than bufSize, then read one row at a time.
359         if (rowSize == 0) {
360             this.chunk = 0;
361
362         } else if (rowSize > bufSize) {
363             this.chunk = 1;
364
365             // If the entire set is not too big, just read it all.
366         } else if (bufSize / rowSize >= nrow) {
367             this.chunk = nrow;
368         } else {
369             this.chunk = bufSize / rowSize + 1;
370         }
371
372     }
373
374     /** Set the pointer arrays for the eight primitive types
375      * to point to the appropriate elements of arrays.
376      */
377     protected void initializePointers() {
378
379         int nbyte, nshort, nint, nlong, nfloat, ndouble, nchar, nboolean;
380
381         // Count how many of each type we have.
382         nbyte = 0;
383         nshort = 0;
384         nint = 0;
385         nlong = 0;
386         nfloat = 0;
387         ndouble = 0;
388         nchar = 0;
389         nboolean = 0;
390
391         for (int col = 0; col < arrays.length; col += 1) {
392             switch (types[col]) {
393
394                 case 'B':
395                     nbyte += 1;
396                     break;
397                 case 'S':
398                     nshort += 1;
399                     break;
400                 case 'I':
401                     nint += 1;
402                     break;
403                 case 'J':
404                     nlong += 1;
405                     break;
406                 case 'F':
407                     nfloat += 1;
408                     break;
409                 case 'D':
410                     ndouble += 1;
411                     break;
412                 case 'C':
413                     nchar += 1;
414                     break;
415                 case 'Z':
416                     nboolean += 1;
417                     break;
418             }
419         }
420
421         // Allocate the pointer arrays.  Note that many will be
422         // zero-length.
423
424         bytePointers = new byte[nbyte][];
425         shortPointers = new short[nshort][];
426         intPointers = new int[nint][];
427         longPointers = new long[nlong][];
428         floatPointers = new float[nfloat][];
429         doublePointers = new double[ndouble][];
430         charPointers = new char[nchar][];
431         booleanPointers = new boolean[nboolean][];
432
433         // Now set the pointers.
434         nbyte = 0;
435         nshort = 0;
436         nint = 0;
437         nlong = 0;
438         nfloat = 0;
439         ndouble = 0;
440         nchar = 0;
441         nboolean = 0;
442
443         for (int col = 0; col < arrays.length; col += 1) {
444             switch (types[col]) {
445
446                 case 'B':
447                     bytePointers[nbyte] = (byte[]) arrays[col];
448                     nbyte += 1;
449                     break;
450                 case 'S':
451                     shortPointers[nshort] = (short[]) arrays[col];
452                     nshort += 1;
453                     break;
454                 case 'I':
455                     intPointers[nint] = (int[]) arrays[col];
456                     nint += 1;
457                     break;
458                 case 'J':
459                     longPointers[nlong] = (long[]) arrays[col];
460                     nlong += 1;
461                     break;
462                 case 'F':
463                     floatPointers[nfloat] = (float[]) arrays[col];
464                     nfloat += 1;
465                     break;
466                 case 'D':
467                     doublePointers[ndouble] = (double[]) arrays[col];
468                     ndouble += 1;
469                     break;
470                 case 'C':
471                     charPointers[nchar] = (char[]) arrays[col];
472                     nchar += 1;
473                     break;
474                 case 'Z':
475                     booleanPointers[nboolean] = (boolean[]) arrays[col];
476                     nboolean += 1;
477                     break;
478             }
479         }
480     }
481
482     // Add a pointer in the pointer lists.
483     protected void addPointer(Object data) throws TableException {
484         String classname = data.getClass().getName();
485         char type = classname.charAt(1);
486
487         switch (type) {
488             case 'B': {
489                 byte[][] xb = new byte[bytePointers.length + 1][];
490                 System.arraycopy(bytePointers, 0, xb, 0, bytePointers.length);
491                 xb[bytePointers.length] = (byte[]) data;
492                 bytePointers = xb;
493                 break;
494             }
495             case 'Z': {
496                 boolean[][] xb = new boolean[booleanPointers.length + 1][];
497                 System.arraycopy(booleanPointers, 0, xb, 0, booleanPointers.length);
498                 xb[booleanPointers.length] = (boolean[]) data;
499                 booleanPointers = xb;
500                 break;
501             }
502             case 'S': {
503                 short[][] xb = new short[shortPointers.length + 1][];
504                 System.arraycopy(shortPointers, 0, xb, 0, shortPointers.length);
505                 xb[shortPointers.length] = (short[]) data;
506                 shortPointers = xb;
507                 break;
508             }
509             case 'C': {
510                 char[][] xb = new char[charPointers.length + 1][];
511                 System.arraycopy(charPointers, 0, xb, 0, charPointers.length);
512                 xb[charPointers.length] = (char[]) data;
513                 charPointers = xb;
514                 break;
515             }
516             case 'I': {
517                 int[][] xb = new int[intPointers.length + 1][];
518                 System.arraycopy(intPointers, 0, xb, 0, intPointers.length);
519                 xb[intPointers.length] = (int[]) data;
520                 intPointers = xb;
521                 break;
522             }
523             case 'J': {
524                 long[][] xb = new long[longPointers.length + 1][];
525                 System.arraycopy(longPointers, 0, xb, 0, longPointers.length);
526                 xb[longPointers.length] = (long[]) data;
527                 longPointers = xb;
528                 break;
529             }
530             case 'F': {
531                 float[][] xb = new float[floatPointers.length + 1][];
532                 System.arraycopy(floatPointers, 0, xb, 0, floatPointers.length);
533                 xb[floatPointers.length] = (float[]) data;
534                 floatPointers = xb;
535                 break;
536             }
537             case 'D': {
538                 double[][] xb = new double[doublePointers.length + 1][];
539                 System.arraycopy(doublePointers, 0, xb, 0, doublePointers.length);
540                 xb[doublePointers.length] = (double[]) data;
541                 doublePointers = xb;
542                 break;
543             }
544             default:
545                 throw new TableException("Invalid type for added column:" + classname);
546         }
547     }
548
549     /** Read a table.
550      * @param is The input stream to read from.
551      */
552     public int read(ArrayDataInput is) throws IOException {
553
554         int currRow = 0;
555
556         // While we have not finished reading the table..
557         for (int row = 0; row < nrow; row += 1) {
558
559             int ibyte = 0;
560             int ishort = 0;
561             int iint = 0;
562             int ilong = 0;
563             int ichar = 0;
564             int ifloat = 0;
565             int idouble = 0;
566             int iboolean = 0;
567
568             // Loop over the columns within the row.
569             for (int col = 0; col < arrays.length; col += 1) {
570
571                 int arrOffset = sizes[col] * row;
572                 int size = sizes[col];
573
574                 switch (types[col]) {
575                     // In anticpated order of use.
576                     case 'I':
577                         int[] ia = intPointers[iint];
578                         iint += 1;
579                         is.read(ia, arrOffset, size);
580                         break;
581
582                     case 'S':
583                         short[] s = shortPointers[ishort];
584                         ishort += 1;
585                         is.read(s, arrOffset, size);
586                         break;
587
588                     case 'B':
589                         byte[] b = bytePointers[ibyte];
590                         ibyte += 1;
591                         is.read(b, arrOffset, size);
592                         break;
593
594                     case 'F':
595                         float[] f = floatPointers[ifloat];
596                         ifloat += 1;
597                         is.read(f, arrOffset, size);
598                         break;
599
600                     case 'D':
601                         double[] d = doublePointers[idouble];
602                         idouble += 1;
603                         is.read(d, arrOffset, size);
604                         break;
605
606                     case 'C':
607                         char[] c = charPointers[ichar];
608                         ichar += 1;
609                         is.read(c, arrOffset, size);
610                         break;
611
612                     case 'J':
613                         long[] l = longPointers[ilong];
614                         ilong += 1;
615                         is.read(l, arrOffset, size);
616                         break;
617
618                     case 'Z':
619
620                         boolean[] bool = booleanPointers[iboolean];
621                         iboolean += 1;
622                         is.read(bool, arrOffset, size);
623                         break;
624                 }
625             }
626         }
627
628         // All done if we get here...
629         return rowSize * nrow;
630     }
631
632     /** Write a table.
633      * @param os the output stream to write to.
634      */
635     public int write(ArrayDataOutput os) throws IOException {
636
637         if (rowSize == 0) {
638             return 0;
639         }
640
641         for (int row = 0; row < nrow; row += 1) {
642
643             int ibyte = 0;
644             int ishort = 0;
645             int iint = 0;
646             int ilong = 0;
647             int ichar = 0;
648             int ifloat = 0;
649             int idouble = 0;
650             int iboolean = 0;
651
652             // Loop over the columns within the row.
653             for (int col = 0; col < arrays.length; col += 1) {
654
655                 int arrOffset = sizes[col] * row;
656                 int size = sizes[col];
657
658                 switch (types[col]) {
659                     // In anticpated order of use.
660                     case 'I':
661                         int[] ia = intPointers[iint];
662                         iint += 1;
663                         os.write(ia, arrOffset, size);
664                         break;
665
666                     case 'S':
667                         short[] s = shortPointers[ishort];
668                         ishort += 1;
669                         os.write(s, arrOffset, size);
670                         break;
671
672                     case 'B':
673                         byte[] b = bytePointers[ibyte];
674                         ibyte += 1;
675                         os.write(b, arrOffset, size);
676                         break;
677
678                     case 'F':
679                         float[] f = floatPointers[ifloat];
680                         ifloat += 1;
681                         os.write(f, arrOffset, size);
682                         break;
683
684                     case 'D':
685                         double[] d = doublePointers[idouble];
686                         idouble += 1;
687                         os.write(d, arrOffset, size);
688                         break;
689
690                     case 'C':
691                         char[] c = charPointers[ichar];
692                         ichar += 1;
693                         os.write(c, arrOffset, size);
694                         break;
695
696                     case 'J':
697                         long[] l = longPointers[ilong];
698                         ilong += 1;
699                         os.write(l, arrOffset, size);
700                         break;
701
702                     case 'Z':
703                         boolean[] bool = booleanPointers[iboolean];
704                         iboolean += 1;
705                         os.write(bool, arrOffset, size);
706                         break;
707                 }
708
709             }
710
711         }
712
713         // All done if we get here...
714         return rowSize * nrow;
715     }
716
717     /** Get the base classes of the columns.
718      * @return An array of Class objects, one for each column.
719      */
720     public Class[] getBases() {
721         return bases;
722     }
723
724     /** Get the characters describing the base classes of the columns.
725      * @return An array of char's, one for each column.
726      */
727     public char[] getTypes() {
728         return types;
729     }
730
731     /** Get the actual data arrays */
732     public Object[] getColumns() {
733         return arrays;
734     }
735
736     public int[] getSizes() {
737         return sizes;
738     }
739
740     /** Delete a row from the table.
741      *  @param row  The row (0-indexed) to be deleted.
742      */
743     public void deleteRow(int row) throws TableException {
744         deleteRows(row, 1);
745     }
746
747     /** Delete a contiguous set of rows from the table.
748      *  @param row    The row (0-indexed) to be deleted.
749      *  @param length The number of rows to be deleted.
750      *  @throws TableException if the request goes outside
751      *          the boundaries of the table or if the length is negative.
752      */
753     public void deleteRows(int row, int length) throws TableException {
754
755         if (row < 0 || length < 0 || row + length > nrow) {
756             throw new TableException("Invalid request to delete rows start: " + row + " length:" + length
757                     + " for table with " + nrow + " rows.");
758         }
759
760         if (length == 0) {
761             return;
762         }
763
764         for (int col = 0; col < arrays.length; col += 1) {
765
766             int sz = sizes[col];
767             int newSize = sz * (nrow - length);
768             Object newArr = ArrayFuncs.newInstance(bases[col], newSize);
769
770             // Copy whatever comes before the deletion
771             System.arraycopy(arrays[col], 0, newArr, 0, row * sz);
772
773             // Copy whatever comes after the deletion
774             System.arraycopy(arrays[col], (row + length) * sz, newArr, row * sz, (nrow - row - length) * sz);
775             arrays[col] = newArr;
776         }
777         nrow -= length;
778         initializePointers();
779     }
780
781     /** Delete a contiguous set of columns from the table.
782      *  @param col    The column (0-indexed) to be deleted.
783      *  @param length The number of rows to be deleted.
784      *  @throws TableException if the request goes outside
785      *          the boundaries of the table or if the length is negative.
786      */
787     public int deleteColumns(int start, int len) throws TableException {
788
789         int ncol = arrays.length;
790
791         if (start < 0 || len < 0 || start + len > ncol) {
792             throw new TableException("Invalid request to delete columns start: " + start + " length:" + len
793                     + " for table with " + ncol + " columns.");
794         }
795
796         if (len == 0) {
797             return rowSize;
798         }
799
800         for (int i = start; i < start + len; i += 1) {
801             rowSize -= sizes[i] * ArrayFuncs.getBaseLength(arrays[i]);
802         }
803
804         int ocol = ncol;
805         ncol -= len;
806
807         Object[] newArrays = new Object[ncol];
808         int[] newSizes = new int[ncol];
809         Class[] newBases = new Class[ncol];
810         char[] newTypes = new char[ncol];
811
812         System.arraycopy(arrays, 0, newArrays, 0, start);
813         System.arraycopy(sizes, 0, newSizes, 0, start);
814         System.arraycopy(bases, 0, newBases, 0, start);
815         System.arraycopy(types, 0, newTypes, 0, start);
816
817         int rem = ocol - (start + len);
818
819         System.arraycopy(arrays, start + len, newArrays, start, rem);
820         System.arraycopy(sizes, start + len, newSizes, start, rem);
821         System.arraycopy(bases, start + len, newBases, start, rem);
822         System.arraycopy(types, start + len, newTypes, start, rem);
823
824
825         arrays = newArrays;
826         sizes = newSizes;
827         bases = newBases;
828         types = newTypes;
829
830         initializePointers();
831         return rowSize;
832     }
833 }