Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.file / org.gvsig.fmap.dal.file.csv / src / main / java / org / gvsig / fmap / dal / store / simplereader / virtualrows / RecordsFileImpl.java @ 47638

History | View | Annotate | Download (15.4 KB)

1
package org.gvsig.fmap.dal.store.simplereader.virtualrows;
2

    
3
import java.io.File;
4
import java.io.IOException;
5
import java.io.RandomAccessFile;
6
import java.nio.ByteBuffer;
7
import java.nio.MappedByteBuffer;
8
import java.nio.channels.FileChannel;
9
import java.sql.Timestamp;
10
import java.util.AbstractList;
11
import java.util.ArrayList;
12
import java.util.List;
13
import org.apache.commons.io.IOUtils;
14
import org.gvsig.fmap.dal.store.simplereader.virtualrows.RecordsFile.Record;
15
import static org.gvsig.fmap.dal.store.simplereader.virtualrows.RecordsFile.TYPE_BYTE;
16
import static org.gvsig.fmap.dal.store.simplereader.virtualrows.RecordsFile.TYPE_STRING;
17
import static org.gvsig.fmap.dal.store.simplereader.virtualrows.RecordsFile.TYPE_TIMESTAMP;
18

    
19
/**
20
 *
21
 * @author jjdelcerro
22
 */
23
public class RecordsFileImpl 
24
        extends AbstractList<Record>
25
        implements RecordsFile 
26
    {
27

    
28
    public static class RecordImpl implements Record {
29
        private final RecordType type;
30
        private final ByteBuffer buffer;
31
        
32
        public RecordImpl(RecordType type) {
33
            this.type = type;
34
            this.buffer = ByteBuffer.allocate(this.type.getSize());
35
        }
36
        
37
        @Override
38
        public RecordType getType() {
39
            return this.type;
40
        }
41

    
42
        @Override
43
        public byte[] getBytes() {
44
            return this.buffer.array();
45
        }
46

    
47
        @Override
48
        public ByteBuffer getBuffer() {
49
            return this.buffer;
50
        }
51
        
52
        @Override
53
        public void setBytes(byte[] bytes) {
54
            this.buffer.position(0);
55
            this.buffer.put(bytes);
56
        }
57

    
58
        @Override
59
        public int getInt(int n) {
60
            this.buffer.position(this.type.getFieldOffset(n));
61
            return this.buffer.getInt();
62
        }
63

    
64
        @Override
65
        public long getLong(int n) {
66
            this.buffer.position(this.type.getFieldOffset(n));
67
            return this.buffer.getLong();
68
        }
69

    
70
        @Override
71
        public double getDouble(int n) {
72
            this.buffer.position(this.type.getFieldOffset(n));
73
            return this.buffer.getDouble();
74
        }
75

    
76
        @Override
77
        public Timestamp getTimestamp(int n) {
78
            this.buffer.position(this.type.getFieldOffset(n));
79
            long l = this.buffer.getLong();
80
            return new Timestamp(l);
81
        }
82

    
83
        @Override
84
        public String getString(int n) {
85
            this.buffer.position(this.type.getFieldOffset(n));
86
            short sz = this.buffer.getShort();
87
            char[] chars = new char[sz];
88
            for (int i = 0; i < sz; i++) {
89
                chars[i] = this.buffer.getChar(i);
90
            }
91
            return new String(chars);
92
        }
93

    
94
        @Override
95
        public byte[] getBytes(int n) {
96
            this.buffer.position(this.type.getFieldOffset(n));
97
            int sz = this.buffer.getInt();
98
            byte[] bytes = new byte[sz];
99
            for (int i = 0; i < sz; i++) {
100
                bytes[i] = this.buffer.get(i);
101
            }
102
            return bytes;
103
        }
104

    
105
        @Override
106
        public int getByte(int n) {
107
            this.buffer.position(this.type.getFieldOffset(n));
108
            return this.buffer.get();
109
        }
110

    
111
        @Override
112
        public int getShort(int n) {
113
            this.buffer.position(this.type.getFieldOffset(n));
114
            return this.buffer.getShort();
115
        }
116

    
117
        @Override
118
        public void setByte(int n, byte v) {
119
            this.buffer.position(this.type.getFieldOffset(n));
120
            this.buffer.put(v);
121
        }
122

    
123
        @Override
124
        public void setShort(int n, short v) {
125
            this.buffer.position(this.type.getFieldOffset(n));
126
            this.buffer.putShort(v);
127
        }
128

    
129
        @Override
130
        public void setInt(int n, int v) {
131
            this.buffer.position(this.type.getFieldOffset(n));
132
            this.buffer.putInt(v);
133
        }
134

    
135
        @Override
136
        public void setLong(int n, long v) {
137
            this.buffer.position(this.type.getFieldOffset(n));
138
            this.buffer.putLong(v);
139
        }
140

    
141
        @Override
142
        public void setDouble(int n, double v) {
143
            this.buffer.position(this.type.getFieldOffset(n));
144
            this.buffer.putDouble(v);
145
        }
146

    
147
        @Override
148
        public void setTimestamp(int n, Timestamp v) {
149
            this.buffer.position(this.type.getFieldOffset(n));
150
            this.buffer.putLong(v.getTime());
151
        }
152

    
153
        @Override
154
        public void setString(int n, String v) {
155
            this.buffer.position(this.type.getFieldOffset(n));
156
            this.buffer.putShort((short) v.length());
157
            for (int i = 0; i < v.length(); i++) {
158
                this.buffer.putChar(v.charAt(i));
159
            }
160
        }
161

    
162
        @Override
163
        public void setBytes(int n, byte[] v) {
164
            this.buffer.position(this.type.getFieldOffset(n));
165
            this.buffer.putInt(v.length);
166
            for (int i = 0; i < v.length; i++) {
167
                this.buffer.put(v[i]);
168
            }
169
        }
170
    }
171

    
172
    
173
    public static class RecordTypeImpl implements RecordType {
174

    
175
        private static class FieldType {
176
            public byte type;
177
            public int size;
178
            public int offset;
179
        }
180
        
181
        private final List<FieldType> fields;
182
        private int size;
183
        
184
        public RecordTypeImpl() {
185
            this.fields = new ArrayList<>();
186
            this.size = -1;
187
        }
188

    
189
        public void add(int type, int size) {
190
            FieldType field = new FieldType();
191
            field.size = size;
192
            field.type = (byte) type;
193
            this.size = -1;
194
            field.offset = this.getSize();
195
            this.fields.add(field);
196
            this.size = -1;
197
            this.size = this.getSize();
198
        }
199
        
200
        @Override
201
        public int getSize() {
202
            if( this.size<0 ) {
203
                int sz = 0;
204
                for (FieldType field : fields) {
205
                    switch(field.type) {
206
                        case TYPE_BYTE:
207
                            sz += 1;
208
                            break;
209
                        case TYPE_SHORT:
210
                            sz += 2;
211
                            break;
212
                        case TYPE_INTEGER:
213
                            sz += 4;
214
                            break;
215
                        case TYPE_TIMESTAMP:
216
                        case TYPE_LONG:
217
                            sz += 8;
218
                            break;
219
                        case TYPE_DOUBLE:
220
                            sz += 8;
221
                            break;
222
                        case TYPE_STRING:
223
                            sz += 2 + (field.size*2);
224
                            break;
225
                        case TYPE_BYTES:
226
                            sz += 4 + field.size;
227
                            break;
228
                    }
229
                }
230
                this.size = sz;
231
            }
232
            return this.size;
233
        }
234

    
235
        @Override
236
        public int getFieldCount() {
237
            return this.fields.size();
238
        }
239

    
240
        @Override
241
        public int getFieldType(int n) {
242
            return this.fields.get(n).type;
243
        }
244

    
245
        @Override
246
        public int getFieldSize(int n) {
247
            return this.fields.get(n).size;
248
        }
249

    
250
        @Override
251
        public int getFieldOffset(int n) {
252
            return this.fields.get(n).offset;
253
        }
254

    
255
        @Override
256
        public Record createRecord() {
257
            this.size = -1;
258
            this.size = this.getSize();
259
            return new RecordImpl(this);
260
        }
261

    
262
        @Override
263
        public byte[] toBytes() {
264
            ByteBuffer buffer = ByteBuffer.allocate(this.fields.size()*(1+4));
265
            for (FieldType field : fields) {
266
                buffer.put(field.type);
267
                buffer.putInt(field.size);
268
            }
269
            return buffer.array();
270
        }
271
        
272
        public static RecordType from(ByteBuffer bytes) {
273
            RecordTypeImpl recordType = new RecordTypeImpl();
274
            
275
            bytes.position(0);
276
            int len = bytes.limit()/(1+4);
277
            for (int i = 0; i < len; i++) {
278
                int type = bytes.get();
279
                int size = bytes.getInt();
280
                recordType.add(type, size);
281
            }
282
            return recordType;
283
        }
284
        
285
        public static RecordType from(byte[] bytes) {
286
            ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
287
            buffer.put(bytes);
288
            return from(buffer);
289
        }
290
        
291
    }
292
    
293
    public static class RecordTypeBuilderImpl implements RecordTypeBuilder {
294

    
295
        private final RecordTypeImpl recordType;
296
        
297
        public RecordTypeBuilderImpl() {
298
            this.recordType = new RecordTypeImpl();
299
        }
300
        
301
        @Override
302
        public RecordTypeBuilder addbyte() {
303
            this.recordType.add(TYPE_BYTE, 0);
304
            return this;
305
        }
306

    
307
        @Override
308
        public RecordTypeBuilder addShort() {
309
            this.recordType.add(TYPE_SHORT, 0);
310
            return this;
311
        }
312

    
313
        @Override
314
        public RecordTypeBuilder addInteger() {
315
            this.recordType.add(TYPE_INTEGER, 0);
316
            return this;
317
        }
318

    
319
        @Override
320
        public RecordTypeBuilder addLong() {
321
            this.recordType.add(TYPE_LONG, 0);
322
            return this;
323
        }
324

    
325
        @Override
326
        public RecordTypeBuilder addDouble() {
327
            this.recordType.add(TYPE_DOUBLE, 0);
328
            return this;
329
        }
330

    
331
        @Override
332
        public RecordTypeBuilder addTimestamp() {
333
            this.recordType.add(TYPE_TIMESTAMP, 0);
334
            return this;
335
        }
336

    
337
        @Override
338
        public RecordTypeBuilder addString(int size) {
339
            this.recordType.add(TYPE_STRING, size);
340
            return this;
341
        }
342

    
343
        @Override
344
        public RecordTypeBuilder addBytes(int size) {
345
            this.recordType.add(TYPE_BYTES, size);
346
            return this;
347
        }
348

    
349
        @Override
350
        public RecordType build() {
351
            return this.recordType;
352
        }
353
        
354
        public static RecordTypeBuilder recordTypeBuilder() {
355
            return new RecordTypeBuilderImpl();
356
        }
357

    
358
    }
359
    
360
    private static final int HEADER_SIZE = 1024;
361
    
362
    private RandomAccessFile raf;
363
    private MappedByteBuffer fileByteBuffer;
364
    private long sz;
365
    private int header_size;
366
    private int type_size;
367
    private RecordType recordType;
368

    
369
    public RecordsFileImpl() {
370
        
371
    }
372

    
373
    @SuppressWarnings("OverridableMethodCallInConstructor")
374
    public RecordsFileImpl(File f) throws IOException {
375
        this.open(f);
376
    }
377
    
378
    @SuppressWarnings("OverridableMethodCallInConstructor")
379
    public RecordsFileImpl(RandomAccessFile raf) throws IOException {
380
        this.open(raf);
381
    }
382
    
383
    @Override
384
    public RecordType getRecordType() {
385
        return this.recordType;
386
    }
387
    
388
    @Override
389
    public void open(File f) throws IOException {
390
        RandomAccessFile theRaf = new RandomAccessFile(f,"r");
391
        this.open(theRaf);
392
    }
393
    
394
    @Override
395
    public void open(RandomAccessFile raf) throws IOException {
396
        this.raf = raf;
397
        this.closeByteBuffer();
398
        this.raf.seek(0);
399
        this.raf.readLong(); //For future use
400
        this.raf.readLong(); //For future use
401
        this.header_size = (int) this.raf.readLong(); //Header size
402
        this.type_size = (int) this.raf.readLong(); //Record length
403
        byte[] recordTypeBytes = new byte[this.type_size];
404
        this.raf.read(recordTypeBytes);
405
        this.recordType = RecordType.from(recordTypeBytes);
406

    
407

    
408
        this.sz = (this.raf.length()-this.header_size) / this.recordType.getSize();
409
        // TODO: meter en la cabecera el numero de registros para poder escribir datos al final
410
    }
411

    
412
    @Override
413
    public void create(File f, RecordType recordType) throws IOException {
414
        RandomAccessFile theRaf = new RandomAccessFile(f,"rw");
415
        this.create(theRaf,recordType);
416
    }
417
    
418
    @Override
419
    public void create(RandomAccessFile raf, RecordType recordType) throws IOException {
420
        this.raf = raf;
421
        this.recordType = recordType;
422
        this.header_size = HEADER_SIZE;
423
        this.closeByteBuffer();
424
        this.sz = 0;
425
        byte[] recordTypeBytes = this.recordType.toBytes();
426
        this.raf.seek(0);
427
        this.raf.writeLong(0); //For future use
428
        this.raf.writeLong(0); //For future use
429
        this.raf.writeLong(this.header_size); //Header size
430
        this.raf.writeLong(recordTypeBytes.length); //Record length
431
        this.raf.write(recordTypeBytes);
432
        this.raf.setLength(this.header_size);
433
    }
434

    
435
    @Override
436
    public void close() throws IOException {
437
        this.closeByteBuffer();
438
        IOUtils.closeQuietly(this.raf);
439
        this.raf = null;
440
        this.sz = -1;
441
    }
442

    
443
    @Override
444
    public boolean isOpen() {
445
        return this.raf!=null;
446
    }
447
      
448
    public Record get(int position) {
449
        position = checkIndex(position);
450
        Record record = this.recordType.createRecord();
451
        this.getByteBuffer().position(this.header_size+(position*this.recordType.getSize()));
452
        this.getByteBuffer().get(record.getBytes());
453
        return record;
454
    }
455

    
456
    public Record get64(long position) {
457
        position = checkIndex(position);
458
        Record record = this.recordType.createRecord();
459
        this.getByteBuffer().position((int) (this.header_size+(position*this.recordType.getSize())));
460
        this.getByteBuffer().get(record.getBytes());
461
        return record;
462
    }
463

    
464
    @Override
465
    public int size() {
466
        return (int) this.sz;
467
    }
468

    
469
    @Override
470
    public long size64() {
471
        return this.sz;
472
    }
473

    
474
    public Record set(int position, Record record) {
475
        try {
476
            closeByteBuffer();
477
            position = checkIndex(position);
478
            this.raf.seek(this.header_size + (position * this.recordType.getSize()));
479
            this.raf.write(record.getBytes());
480
            return record;
481
        } catch (IOException ex) {
482
            throw new RuntimeException("Can't add record", ex);
483
        }
484
    }
485
    
486
    private int checkIndex(long index) {
487
        if (this.raf == null) {
488
            throw new IllegalStateException("Index not open");
489
        }
490
        if( index < 0 ) {
491
            index = ((int)this.sz) + index;
492
        }
493
        if( index<0 || index>=this.sz) {
494
            throw new IllegalArgumentException("Index out of range ("+index+")");
495
        }
496
        return (int) index;
497
    }
498
    
499
    private MappedByteBuffer getByteBuffer() {
500
        try {
501
            if (this.fileByteBuffer == null) {
502
                this.fileByteBuffer = this.raf.getChannel().map(
503
                        FileChannel.MapMode.READ_ONLY,
504
                        0,
505
                        this.raf.length()
506
                );
507
            }
508
            return this.fileByteBuffer;
509
        } catch (IOException ex) {
510
            throw new RuntimeException("Can't add record", ex);
511
        }
512
    }
513
    
514
    private void closeByteBuffer() {
515
        if(this.fileByteBuffer != null){
516
            this.fileByteBuffer.force();
517
            this.fileByteBuffer = null;
518
        }
519
    }
520

    
521
    public boolean add(Record record) {
522
        try {
523
            this.raf.seek(this.raf.length());
524
            this.raf.write(record.getBytes());
525
            this.closeByteBuffer();
526
            this.sz++;
527
            return true;
528
        } catch (IOException ex) {
529
            throw new RuntimeException("Can't add record", ex);
530
        }
531
    }
532
}