File: Variable.java

package info (click to toggle)
jcdf 1.2.3%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 540 kB
  • sloc: java: 5,325; makefile: 163; sh: 98
file content (593 lines) | stat: -rw-r--r-- 21,635 bytes parent folder | download | duplicates (3)
1
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
package uk.ac.bristol.star.cdf;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import uk.ac.bristol.star.cdf.record.Buf;
import uk.ac.bristol.star.cdf.record.DataReader;
import uk.ac.bristol.star.cdf.record.Record;
import uk.ac.bristol.star.cdf.record.RecordFactory;
import uk.ac.bristol.star.cdf.record.RecordMap;
import uk.ac.bristol.star.cdf.record.VariableDescriptorRecord;

/**
 * Provides the metadata and record data for a CDF variable.
 *
 * <p>At construction time, a map of where the records are stored is
 * constructed, but the record data itself is not read unless or until
 * one of the <code>read</code> methods is called.
 *
 * <p>This interface does not currently support data reading in such
 * a flexible way as the official CDF interface.
 * You can read a record's worth of data at a time using either
 * {@link #readRawRecord readRawRecord} (which should be fairly efficient) or
 * {@link #readShapedRecord readShapedRecord} (which may have to copy and
 * possibly re-order the array, and may not be so efficient).
 *
 * @author   Mark Taylor
 * @since    20 Jun 2013
 */
public class Variable {

    private final VariableDescriptorRecord vdr_;
    private final Buf buf_;
    private final RecordFactory recFact_;
    private final boolean isZVariable_;
    private final boolean recordVariance_;
    private final Shaper shaper_;
    private final int rvaleng_;
    private final DataType dataType_;
    private final DataReader dataReader_;
    private final Object padRawValueArray_;
    private final Object shapedPadValueRowMajor_;
    private final Object shapedPadValueColumnMajor_;
    private final String summaryTxt_;
    private RecordReader recordReader_;

    /**
     * Constructor.
     *
     * @param   vdr   variable descriptor record for the variable
     * @param   cdfInfo  global CDF information
     * @param   recFact  record factory
     */
    public Variable( VariableDescriptorRecord vdr, CdfInfo cdfInfo,
                     RecordFactory recFact ) throws IOException {

        // Prepare state for reading data.
        vdr_ = vdr;
        buf_ = vdr.getBuf();
        recFact_ = recFact;
        isZVariable_ = vdr.getRecordType() == 8;
        dataType_ = DataType.getDataType( vdr.dataType, cdfInfo );
        recordVariance_ = Record.hasBit( vdr_.flags, 0 );
        int[] dimSizes = isZVariable_ ? vdr.zDimSizes : cdfInfo.getRDimSizes();
        boolean[] dimVarys = vdr.dimVarys;
        boolean rowMajor = cdfInfo.getRowMajor();
        int numElems = vdr.numElems;

        // As far as I understand the internal formats document, only
        // character data types can have numElems>1 here.
        assert dataType_.hasMultipleElementsPerItem() || numElems == 1;
        shaper_ =
            Shaper.createShaper( dataType_, dimSizes, dimVarys, rowMajor );
        int nraw = shaper_.getRawItemCount();
        dataReader_ = new DataReader( dataType_, numElems, nraw );
        rvaleng_ = Array.getLength( dataReader_.createValueArray() );

        // Read pad value if present.
        long padOffset = vdr.getPadValueOffset();
        if ( padOffset >= 0 ) {
            DataReader padReader = new DataReader( dataType_, numElems, 1 );
            assert vdr.getPadValueSize() == padReader.getRecordSize();
            Object padValueArray = padReader.createValueArray();
            padReader.readValue( buf_, padOffset, padValueArray );
            Object rva = dataReader_.createValueArray();
            int ngrp = dataType_.getGroupSize();
            for ( int i = 0; i < nraw; i++ ) {
                System.arraycopy( padValueArray, 0, rva, i * ngrp, ngrp );
            }
            padRawValueArray_ = rva;
            shapedPadValueRowMajor_ = shaper_.shape( padRawValueArray_, true );
            shapedPadValueColumnMajor_ =
                shaper_.shape( padRawValueArray_, false );
        }
        else if ( vdr_.sRecords != 0 ) {
            Object padValueArray = dataType_.getDefaultPadValueArray();
            Object rva = dataReader_.createValueArray();
            int ngrp = dataType_.getGroupSize();
            for ( int i = 0; i < nraw; i++ ) {
                System.arraycopy( padValueArray, 0, rva, i * ngrp, ngrp );
            }
            padRawValueArray_ = rva;
            shapedPadValueRowMajor_ = shaper_.shape( padRawValueArray_, true );
            shapedPadValueColumnMajor_ = shapedPadValueRowMajor_;
        }
        else {
            padRawValueArray_ = null;
            shapedPadValueRowMajor_ = null;
            shapedPadValueColumnMajor_ = null;
        }

        // Assemble a short summary string.
        String shapeTxt = "";
        String varyTxt = "";
        for ( int idim = 0; idim < dimSizes.length; idim++ ) {
            if ( idim > 0 ) {
                shapeTxt += ',';
            }
            shapeTxt += dimSizes[ idim ];
            varyTxt += dimVarys[ idim ] ? 'T' : 'F';
        }
        summaryTxt_ = new StringBuffer()
            .append( dataType_.getName() )
            .append( ' ' )
            .append( isZVariable_ ? "(z)" : "(r)" )
            .append( ' ' )
            .append( dimSizes.length )
            .append( ':' )
            .append( '[' )
            .append( shapeTxt )
            .append( ']' )
            .append( ' ' )
            .append( recordVariance_ ? 'T' : 'F' )
            .append( '/' )
            .append( varyTxt )
            .toString();
    }

    /**
     * Returns this variable's name.
     *
     * @return   variable name
     */
    public String getName() {
        return vdr_.name;
    }

    /**
     * Returns the index number within the CDF of this variable.
     *
     * @return  variable num
     */
    public int getNum() {
        return vdr_.num;
    }

    /**
     * Indicates whether this variable is a zVariable or rVariable.
     *
     * @return   true for zVariable, false for rVariable
     */
    public boolean isZVariable() {
        return isZVariable_;
    }

    /**
     * Returns the upper limit of records that may have values.
     * The actual number of records may be lower than this in case of sparsity.
     *
     * @return   maximum record count
     */
    public int getRecordCount() {
        return vdr_.maxRec + 1;
    }

    /**
     * Returns the data type of this variable.
     *
     * @return  data type
     */
    public DataType getDataType() {
        return dataType_;
    }

    /**
     * Returns an object that knows about the array dimensions
     * of the data values.
     *
     * @return  shaper
     */
    public Shaper getShaper() {
        return shaper_;
    }

    /**
     * Indicates whether this variable has a value which is fixed for all
     * records or can vary per record.
     *
     * @return   false for fixed, true for varying
     */
    public boolean getRecordVariance() {
        return recordVariance_;
    }

    /**
     * Returns a short text string describing the type, shape and variance
     * of this variable.
     *
     * @return  text summary of variable characteristics
     */
    public String getSummary() {
        return summaryTxt_;
    }

    /**
     * Returns the VariableDescriptorRecord on which this Variable instance
     * is based.
     *
     * @return  variable descriptor record (rVDR or zVDR)
     */
    public VariableDescriptorRecord getDescriptor() {
        return vdr_;
    }

    /**
     * Creates a workspace array suitable for use with this variable's
     * reading methods.
     * The returned array is a 1-dimensional array of a primitive type
     * or of String.
     *
     * @return  workspace array for data reading
     */
    public Object createRawValueArray() {
        return dataReader_.createValueArray();
    }

    /**
     * Indicates whether a real distinct file-based record exists for
     * the given index.
     * Reading a record will give you a result in any case, but if this
     * returns false it will be some kind of fixed or default value.
     *
     * @param   irec  record index
     * @return   true iff a file-based record exists for irec
     */
    public boolean hasRecord( int irec ) throws IOException {
        return getRecordReader().hasRecord( irec );
    }

    /**
     * Reads the data from a single record into a supplied raw value array.
     * The values are read into the supplied array in the order in which
     * they are stored in the data stream, that is depending on the row/column
     * majority of the CDF.
     * <p>The raw value array is as obtained from {@link #createRawValueArray}.
     *
     * @param  irec  record index
     * @param  rawValueArray  workspace array, as created by the
     *                        <code>createRawValueArray</code> method
     */
    public void readRawRecord( int irec, Object rawValueArray )
             throws IOException {
         getRecordReader().readRawRecord( irec, rawValueArray );
    }

    /**
     * Reads the data from a single record and returns it as an object
     * of a suitable type for this variable.
     * If the variable type a scalar, then the return value will be
     * one of the primitive wrapper types (Integer etc),
     * otherwise it will be an array of primitive or String values.
     * If the majority of the stored data does not match the
     * <code>rowMajor</code> argument, the array elements will be
     * rordered appropriately.
     * If some of the dimension variances are false, the values will
     * be duplicated accordingly.
     * The Shaper returned from the {@link #getShaper} method
     * can provide more information on the return value from this method.
     *
     * <p>The workspace is as obtained from {@link #createRawValueArray}.
     *
     * @param  irec  record index
     * @param  rowMajor  required majority of output array; true for row major,
     *                   false for column major; only has an effect for
     *                   dimensionality &gt;=2
     * @param  rawValueArrayWorkspace  workspace array, as created by the
     *                                 <code>createRawValueArray</code> method
     * @return   a new object containing the shaped result
     *           (not the same object as <code>rawValueArray</code>
     */
    public Object readShapedRecord( int irec, boolean rowMajor,
                                    Object rawValueArrayWorkspace )
             throws IOException {
         return getRecordReader()
               .readShapedRecord( irec, rowMajor, rawValueArrayWorkspace );
    }

    /**
     * Returns an object that can read records for this variable.
     * Constructing it requires reading maps of where the record values
     * are stored, which might in principle involve a bit of work,
     * so do it lazily.
     *
     * @return  record reader
     */
    private synchronized RecordReader getRecordReader() throws IOException {
        if ( recordReader_ == null ) {
            recordReader_ = createRecordReader();
        }
        return recordReader_;
    }

    /**
     * Constructs a record reader.
     *
     * @return  new record reader
     */
    private RecordReader createRecordReader() throws IOException {
        RecordMap recMap =
            RecordMap.createRecordMap( vdr_, recFact_,
                                       dataReader_.getRecordSize() );
        if ( ! recordVariance_ ) {
            return new NoVaryRecordReader( recMap );
        }
        else {
            // Get sparse records type.  This is missing from the CDF Internal
            // Format Description document, but cdf.h says:
            //    #define NO_SPARSERECORDS                0L
            //    #define PAD_SPARSERECORDS               1L
            //    #define PREV_SPARSERECORDS              2L
            int sRecords = vdr_.sRecords;
            if ( sRecords == 0 ) {
                return new UnsparseRecordReader( recMap );
            }
            else if ( sRecords == 1 ) {
                assert padRawValueArray_ != null;
                return new PadRecordReader( recMap );
            }
            else if ( sRecords == 2 ) {
                assert padRawValueArray_ != null;
                return new PreviousRecordReader( recMap );
            }
            else {
                throw new CdfFormatException( "Unknown sparse record type "
                                            + sRecords );
            }
        }
    }

    /**
     * Object which can read record values for this variable.
     * This provides the implementations of several of the Variable methods.
     */
    private interface RecordReader {

        /**
         * Indicates whether a real file-based record exists for the given
         * record index.
         *
         * @param  irec  record index
         * @return  true iff a file-based record exists for irec
         */
        boolean hasRecord( int irec );

        /**
         * Reads the data from a single record into a supplied raw value array.
         *
         * @param  irec  record index
         * @param  rawValueArray  workspace array
         */
        void readRawRecord( int irec, Object rawValueArray )
            throws IOException;

        /**
         * Reads the data from a single record and returns it as an object
         * of a suitable type for this variable.
         *
         * @param  irec  record index
         * @param  rowMajor  required majority of output array
         * @param  rawValueArrayWorkspace  workspace array
         * @return   a new object containing shaped result
         */
        Object readShapedRecord( int irec, boolean rowMajor,
                                 Object rawValueArrayWorkspace )
            throws IOException;
    }

    /**
     * RecordReader implementation for non-record-varying variables.
     */
    private class NoVaryRecordReader implements RecordReader {
        private final Object rawValue_;
        private final Object rowMajorValue_;
        private final Object colMajorValue_;

        /**
         * Constructor.
         *
         * @param   recMap  record map
         */
        NoVaryRecordReader( RecordMap recMap ) throws IOException {

            // When record variance is false, the fixed value appears
            // to be located where you would otherwise expect to find record #0.
            // Read it once and store it in raw, row-major and column-major
            // versions for later use.
            RecordReader rt = new UnsparseRecordReader( recMap );
            rawValue_ = createRawValueArray();
            rt.readRawRecord( 0, rawValue_ );
            rowMajorValue_ = shaper_.shape( rawValue_, true );
            colMajorValue_ = shaper_.shape( rawValue_, false );
        }
        public boolean hasRecord( int irec ) {
            return false;
        }
        public void readRawRecord( int irec, Object rawValueArray ) {
            System.arraycopy( rawValue_, 0, rawValueArray, 0, rvaleng_ );
        }
        public Object readShapedRecord( int irec, boolean rowMajor,
                                        Object work ) {
            return rowMajor ? rowMajorValue_ : colMajorValue_;
        }
    }

    /**
     * RecordReader implementation for non-sparse variables.
     */
    private class UnsparseRecordReader implements RecordReader {
        private final RecordMap recMap_;
        private final int nrec_;
        private final Object zeros_;

        /**
         * Constructor.
         *
         * @param  recMap  record map
         */
        UnsparseRecordReader( RecordMap recMap ) {
            recMap_ = recMap;
            nrec_ = vdr_.maxRec + 1;
            zeros_ = createRawValueArray();
        }
        public boolean hasRecord( int irec ) {
            return irec < nrec_;
        }
        public void readRawRecord( int irec, Object rawValueArray )
                throws IOException {
            if ( hasRecord( irec ) ) {
                int ient = recMap_.getEntryIndex( irec );
                dataReader_.readValue( recMap_.getBuf( ient ),
                                       recMap_.getOffset( ient, irec ),
                                       rawValueArray );
            }
            else {
                System.arraycopy( zeros_, 0, rawValueArray, 0, rvaleng_ );
            }
        }
        public Object readShapedRecord( int irec, boolean rowMajor,
                                        Object work )
                throws IOException {
            if ( hasRecord( irec ) ) {
                int ient = recMap_.getEntryIndex( irec );
                dataReader_.readValue( recMap_.getBuf( ient ),
                                       recMap_.getOffset( ient, irec ),
                                       work );
                return shaper_.shape( work, rowMajor );
            }
            else {
                return null;
            }
        }
    }

    /**
     * RecordReader implementation for record-varying variables
     * with sparse padding or no padding.
     */
    private class PadRecordReader implements RecordReader {
        private final RecordMap recMap_;

        /**
         * Constructor.
         *
         * @param  recMap  record map
         */
        PadRecordReader( RecordMap recMap ) {
            recMap_ = recMap;
        }
        public boolean hasRecord( int irec ) {
            return hasRecord( irec, recMap_.getEntryIndex( irec ) );
        }
        public void readRawRecord( int irec, Object rawValueArray )
                throws IOException {
            int ient = recMap_.getEntryIndex( irec );
            if ( hasRecord( irec, ient ) ) {
                dataReader_.readValue( recMap_.getBuf( ient ),
                                       recMap_.getOffset( ient, irec ),
                                       rawValueArray );
            }
            else {
                System.arraycopy( padRawValueArray_, 0, rawValueArray, 0,
                                  rvaleng_ );
            }
        }
        public Object readShapedRecord( int irec, boolean rowMajor,
                                        Object work )
                throws IOException {
            int ient = recMap_.getEntryIndex( irec );
            if ( hasRecord( irec, ient ) ) {
                dataReader_.readValue( recMap_.getBuf( ient ),
                                       recMap_.getOffset( ient, irec ),
                                       work );
                return shaper_.shape( work, rowMajor );
            }
            else {
                return rowMajor ? shapedPadValueRowMajor_
                                : shapedPadValueColumnMajor_;
            }
        }
        private boolean hasRecord( int irec, int ient ) {
            return ient >= 0 && ient < recMap_.getEntryCount()
                && irec < getRecordCount();
        }
    }

    /**
     * RecordReader implementation for record-varying variables
     * with previous padding.
     */
    private class PreviousRecordReader implements RecordReader {
        private final RecordMap recMap_;

        /**
         * Constructor.
         *
         * @param  recMap  record map
         */
        PreviousRecordReader( RecordMap recMap ) {
            recMap_ = recMap;
        }
        public boolean hasRecord( int irec ) {
            // I'm not sure whether the constraint on getRecordCount ought
            // to be applied here - maybe for previous padding, non-existent
            // records are OK??
            return recMap_.getEntryIndex( irec ) >= 0
                && irec < getRecordCount();
        }
        public void readRawRecord( int irec, Object rawValueArray )
                throws IOException {
            int ient = recMap_.getEntryIndex( irec );
            if ( ient >= 0 ) {
                dataReader_.readValue( recMap_.getBuf( ient ),
                                       recMap_.getOffset( ient, irec ),
                                       rawValueArray );
            }
            else if ( ient == -1 ) {
                System.arraycopy( padRawValueArray_, 0, rawValueArray, 0,
                                  rvaleng_ );
            }
            else {
                int iPrevEnt = -ient - 2;
                long offset = recMap_.getFinalOffsetInEntry( iPrevEnt );
                dataReader_.readValue( recMap_.getBuf( iPrevEnt ), offset,
                                       rawValueArray );
            }
        }
        public Object readShapedRecord( int irec, boolean rowMajor,
                                        Object work )
                throws IOException {
            int ient = recMap_.getEntryIndex( irec );
            if ( ient >= 0 ) {
                dataReader_.readValue( recMap_.getBuf( ient ),
                                       recMap_.getOffset( ient, irec ),
                                       work );
                return shaper_.shape( work, rowMajor );
            }
            else if ( ient == -1 ) {
                return rowMajor ? shapedPadValueRowMajor_
                                : shapedPadValueColumnMajor_;
            }
            else {
                int iPrevEnt = -ient - 2;
                long offset = recMap_.getFinalOffsetInEntry( iPrevEnt );
                dataReader_.readValue( recMap_.getBuf( ient ),
                                       recMap_.getOffset( ient, irec ),
                                       work );
                return shaper_.shape( work, rowMajor );
            }
        }
    }
}