Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libFMap_dalfile / src / org / gvsig / fmap / dal / store / dbf / utils / DbaseFileWriter.java @ 33331

History | View | Annotate | Download (15.1 KB)

1
/*
2
 *    Geotools - OpenSource mapping toolkit
3
 *    (C) 2002, Centre for Computational Geography
4
 *
5
 *    This library is free software; you can redistribute it and/or
6
 *    modify it under the terms of the GNU Lesser General Public
7
 *    License as published by the Free Software Foundation;
8
 *    version 2.1 of the License.
9
 *
10
 *    This library is distributed in the hope that it will be useful,
11
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 *    Lesser General Public License for more details.
14
 *
15
 *    You should have received a copy of the GNU Lesser General Public
16
 *    License along with this library; if not, write to the Free Software
17
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 *
19
 *    This file is based on an origional contained in the GISToolkit project:
20
 *    http://gistoolkit.sourceforge.net/
21
 *
22
 */
23
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
24
 *
25
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
26
 *
27
 * This program is free software; you can redistribute it and/or
28
 * modify it under the terms of the GNU General Public License
29
 * as published by the Free Software Foundation; either version 2
30
 * of the License, or (at your option) any later version.
31
 *
32
 * This program is distributed in the hope that it will be useful,
33
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35
 * GNU General Public License for more details.
36
 *
37
 * You should have received a copy of the GNU General Public License
38
 * along with this program; if not, write to the Free Software
39
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
40
 *
41
 * For more information, contact:
42
 *
43
 *  Generalitat Valenciana
44
 *   Conselleria d'Infraestructures i Transport
45
 *   Av. Blasco Ib??ez, 50
46
 *   46010 VALENCIA
47
 *   SPAIN
48
 *
49
 *      +34 963862235
50
 *   gvsig@gva.es
51
 *      www.gvsig.gva.es
52
 *
53
 *    or
54
 *
55
 *   IVER T.I. S.A
56
 *   Salamanca 50
57
 *   46005 Valencia
58
 *   Spain
59
 *
60
 *   +34 963163400
61
 *   dac@iver.es
62
 */
63
package org.gvsig.fmap.dal.store.dbf.utils;
64

    
65
import java.io.IOException;
66
import java.nio.ByteBuffer;
67
import java.nio.MappedByteBuffer;
68
import java.nio.channels.FileChannel;
69
import java.nio.charset.Charset;
70
import java.text.FieldPosition;
71
import java.text.NumberFormat;
72
import java.util.Calendar;
73
import java.util.Date;
74
import java.util.Iterator;
75
import java.util.Locale;
76

    
77
import org.gvsig.fmap.dal.DataTypes;
78
import org.gvsig.fmap.dal.exception.CloseException;
79
import org.gvsig.fmap.dal.exception.InitializeException;
80
import org.gvsig.fmap.dal.exception.UnsupportedEncodingException;
81
import org.gvsig.fmap.dal.exception.WriteException;
82
import org.gvsig.fmap.dal.feature.Feature;
83
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
84
import org.gvsig.fmap.dal.feature.FeatureType;
85

    
86
/**
87
 * A DbaseFileReader is used to read a dbase III format file. The general use of
88
 * this class is: <CODE><PRE>
89
 * DbaseFileHeader header = ...
90
 * WritableFileChannel out = new FileOutputStream("thefile.dbf").getChannel();
91
 * DbaseFileWriter w = new DbaseFileWriter(header,out);
92
 * while ( moreRecords ) {
93
 *   w.write( getMyRecord() );
94
 * }
95
 * w.close();
96
 * </PRE></CODE> You must supply the <CODE>moreRecords</CODE> and
97
 * <CODE>getMyRecord()</CODE> logic...
98
 *
99
 * @author Ian Schneider
100
 */
101
public class DbaseFileWriter {
102

    
103
        private DbaseFileHeader header;
104
        private DbaseFileWriter.FieldFormatter formatter = new DbaseFileWriter.FieldFormatter();
105
        FileChannel channel;
106
        private ByteBuffer buffer;
107
//        private final Number NULL_NUMBER = new Integer(0);
108
        private final String NULL_STRING = "";
109
        private final String NULL_DATE = "        ";
110
        private boolean headDrity = false;
111

    
112
        // TODO: READ HEADER AND STABLIST THE RIGHT CHARSET
113
        private Charset charset = Charset.forName("ISO-8859-1");
114

    
115
        /**
116
         * Create a DbaseFileWriter using the specified header and writing to the
117
         * given channel.
118
         *
119
         * @param header
120
         *            The DbaseFileHeader to write.
121
         * @param out
122
         *            The Channel to write to.
123
         *
124
         *
125
         * @throws InitializeWriterException
126
         * @throws IOException
127
         *             If errors occur while initializing.
128
         */
129
        public DbaseFileWriter(DbaseFileHeader header, FileChannel out,
130
                        boolean isNew)
131
                        throws InitializeException {
132
                this.header = header;
133
                this.channel = out;
134
                this.headDrity = isNew;
135

    
136
                init();
137
        }
138

    
139
        private void init() throws InitializeException {
140
                try {
141
                        if (this.channel.size() < this.header.getHeaderLength()) {
142
                                this.writeHeader();
143
                        }
144
                        buffer = ByteBuffer.allocateDirect(header.getRecordLength());
145
                } catch (Exception e) {
146
                        throw new InitializeException("DBF Writer", e);
147
                }
148
        }
149

    
150
        private void write() throws WriteException {
151
                buffer.position(0);
152
                int r = buffer.remaining();
153
                try {
154
                        while ((r -= channel.write(buffer)) > 0) {
155
                                ; // do nothing
156
                        }
157
                } catch (IOException e) {
158
                        throw new WriteException("DBF Writer", e);
159
                }
160
        }
161

    
162
        private void writeHeader() throws WriteException {
163
                try {
164
                        channel.position(0);
165
                        header.writeHeader(channel);
166
                } catch (IOException e) {
167
                        throw new WriteException("DBF Writer", e);
168
                }
169
        }
170

    
171
        /**
172
         * Write a single dbase record.
173
         *
174
         * @param record
175
         *            The entries to write.
176
         * @throws UnsupportedEncodingException
177
         * @throws WriteException
178
         */
179
        public void append(Feature feature) throws WriteException,
180
                        UnsupportedEncodingException {
181
                this.fillBuffer(feature);
182
                try {
183
                        this.moveToEOF();
184
                } catch (IOException e) {
185
                        throw new WriteException("DbaseFileWriter", e);
186
                }
187
                this.header.setNumRecords(this.header.getNumRecords() + 1);
188
                write();
189

    
190
                this.headDrity = true;
191
        }
192

    
193
        private void fillBuffer(Feature feature)
194
        throws UnsupportedEncodingException {
195
                FeatureType featureType = feature.getType();
196
                try{
197
                        buffer.position(0);
198

    
199
                        // put the 'not-deleted' marker
200
                        buffer.put((byte) ' ');
201

    
202
                        Iterator iterator = featureType.iterator();
203
                        while (iterator.hasNext()) {
204
                                FeatureAttributeDescriptor fad = (FeatureAttributeDescriptor) iterator
205
                                .next();
206
                                int type = fad.getType();
207
                                if (type == DataTypes.GEOMETRY) {
208
                                        continue;
209
                                }
210
                                String fieldString = fieldString(fad, feature);
211
                                if (fieldString == null) {
212
                                        if (type == DataTypes.STRING) {
213
                                                fieldString = NULL_STRING;
214
                                        } else if (type == DataTypes.DATE) {
215
                                                fieldString = NULL_DATE;
216
                                        } else {
217
                                                fieldString = "0";
218
                                        }
219
                                }
220
                                try {
221
                                        buffer.put(fieldString.getBytes(charset.name()));
222
                                } catch (java.io.UnsupportedEncodingException e) {
223
                                        throw new UnsupportedEncodingException(e);
224
                                }
225
                        }
226
                }catch (Exception e) {
227
                        System.err.println("error");
228
                }
229
        }
230

    
231
        private void moveToEOF() throws IOException {
232
                this.moveTo(this.header.getNumRecords());
233
        }
234

    
235
        private void moveTo(long numReg) throws IOException {
236
                //                if (!(channel instanceof FileChannel)) {
237
                //                        throw new IOException(
238
                //                                                        "DbaseFileWriterNIO: channel is not a FileChannel. Cannot position properly");
239
                //                }
240

    
241
                long newPos = header.getHeaderLength() + numReg
242
                                * header.getRecordLength();
243
                if (this.channel.position() != newPos) {
244
                        this.channel.position(newPos);
245
                }
246
        }
247

    
248
        /**
249
         * Write a single dbase record. Useful to update a dbf.
250
         *
251
         * @param record
252
         *            The entries to write.
253
         * @throws WriteException
254
         * @throws UnsupportedEncodingException
255
         */
256
        public void update(Feature feature, long numReg) throws WriteException,
257
                        UnsupportedEncodingException {
258
                this.fillBuffer(feature);
259

    
260
                try {
261
                        this.moveTo(numReg);
262
                } catch (IOException e) {
263
                        throw new WriteException("DbaseFileWriter", e);
264
                }
265

    
266
                write();
267
        }
268

    
269
        private String fieldString(FeatureAttributeDescriptor attr, Feature feature) {
270
                // FIXME Habria que revisar este metodo, ya que hay caracteristicas
271
                //                 del campo que se sacan del FType y otroas de la cabecera del DBF.
272
                //                 Esto tendria que ser uniforme, de un sitio o de otro, pero no
273
                //                 mezclado.
274
                int type = attr.getType();
275
                int dbfFieldIndex = this.header.getFieldIndex(attr.getName());
276
                final int fieldLen = header.getFieldLength(dbfFieldIndex);
277
                String fieldString = "";
278
                if (DataTypes.BOOLEAN == type) {
279
                        boolean b = feature.getBoolean(attr.getIndex());
280
                        if (b) {
281
                                fieldString = "T";
282
                        } else {
283
                                fieldString = "F";
284
                        }
285
                } else if (DataTypes.BYTE == type) {
286
                        fieldString = String.valueOf(feature.getByte(attr.getIndex()));
287
                } else if (DataTypes.DATE == type) {
288
                        Date date = feature.getDate(attr.getIndex());
289
                        fieldString = formatter.getFieldString(date);
290
                } else if (DataTypes.DOUBLE == type) {
291
                        double d = feature.getDouble(attr.getIndex());
292
                        fieldString = formatter.getFieldString(fieldLen, header
293
                                        .getFieldDecimalCount(attr.getIndex()), d);
294
                } else if (DataTypes.FLOAT == type) {
295
                        float f = feature.getFloat(attr.getIndex());
296
                        fieldString = formatter.getFieldString(fieldLen, header
297
                                        .getFieldDecimalCount(attr.getIndex()), f);
298
                } else if (DataTypes.INT == type) {
299
                        int integer = feature.getInt(attr.getIndex());
300
                        fieldString = formatter.getFieldString(fieldLen, header
301
                                        .getFieldDecimalCount(attr.getIndex()), integer);
302
                } else if (DataTypes.LONG == type) {
303
                        long l = feature.getLong(attr.getIndex());
304
                        fieldString = formatter.getFieldString(fieldLen, header
305
                                        .getFieldDecimalCount(dbfFieldIndex), l);
306
                } else if (DataTypes.STRING == type) {
307
                        String s = feature.getString(attr.getIndex());
308
                        fieldString = formatter.getFieldString(fieldLen, s);
309
                }
310
                return fieldString;
311

    
312
        }
313

    
314
        //  private String fieldString(Object obj,final int col) {
315
        //    String o;
316
        //    final int fieldLen = header.getFieldLength(col);
317
        //    switch (header.getFieldType(col)) {
318
        //      case 'C':
319
        //      case 'c':
320
        //        o = formatter.getFieldString(
321
        //          fieldLen,
322
        //          (obj instanceof NullValue)? NULL_STRING : ((StringValue) obj).getValue()
323
        //        );
324
        //        break;
325
        //      case 'L':
326
        //      case 'l':
327
        //        o = (obj instanceof NullValue) ? "F" : ((BooleanValue)obj).getValue() == true ? "T" : "F";
328
        //        break;
329
        //      case 'M':
330
        //      case 'G':
331
        //        o = formatter.getFieldString(
332
        //          fieldLen,
333
        //          (obj instanceof NullValue) ? NULL_STRING : ((StringValue) obj).getValue()
334
        //        );
335
        //        break;
336
        //     /* case 'N':
337
        //      case 'n':
338
        //        // int?
339
        //        if (header.getFieldDecimalCount(col) == 0) {
340
        //
341
        //          o = formatter.getFieldString(
342
        //            fieldLen, 0, (Number) (obj == null ? NULL_NUMBER : Double.valueOf(obj.toString()))
343
        //          );
344
        //          break;
345
        //
346
        //         }
347
        //      */
348
        //      case 'N':
349
        //      case 'n':
350
        //      case 'F':
351
        //      case 'f':
352
        //            Number number = null;
353
        //            if(obj instanceof NullValue){
354
        //                    number = NULL_NUMBER;
355
        //            }else{
356
        //                    NumericValue gVal = (NumericValue) obj;
357
        //                    number = new Double(gVal.doubleValue());
358
        //            }
359
        //        o = formatter.getFieldString(fieldLen,
360
        //                        header.getFieldDecimalCount(col),
361
        //                                number);
362
        //        break;
363
        //      case 'D':
364
        //      case 'd':
365
        //          if (obj instanceof NullValue)
366
        //              o = NULL_DATE;
367
        //          else
368
        //              o = formatter.getFieldString(((DateValue)obj).getValue());
369
        //        break;
370
        //      default:
371
        //        throw new RuntimeException("Unknown type " + header.getFieldType(col));
372
        //    }
373
        //
374
        //    return o;
375
        //  }
376

    
377
        /**
378
         * Release resources associated with this writer. <B>Highly recommended</B>
379
         *
380
         * @throws CloseException
381
         * @throws IOException
382
         *             If errors occur.
383
         */
384
        public void close() throws CloseException {
385
                // IANS - GEOT 193, bogus 0x00 written. According to dbf spec, optional
386
                // eof 0x1a marker is, well, optional. Since the original code wrote a
387
                // 0x00 (which is wrong anyway) lets just do away with this :)
388
                // - produced dbf works in OpenOffice and ArcExplorer java, so it must
389
                // be okay.
390
                //    buffer.position(0);
391
                //    buffer.put((byte) 0).position(0).limit(1);
392
                //    write();
393

    
394
                if (headDrity) {
395
                        try {
396
                                this.writeHeader();
397
                        } catch (WriteException e) {
398
                                throw new CloseException("DbaseFileWriter", e);
399
                        }
400
                }
401

    
402
                try {
403
                        channel.close();
404
                } catch (IOException e) {
405
                        throw new CloseException("DBF Writer", e);
406
                }
407
                if (buffer instanceof MappedByteBuffer) {
408
                        // NIOUtilities.clean(buffer);
409
                }
410

    
411
                buffer = null;
412
                channel = null;
413
                formatter = null;
414
        }
415

    
416
        /** Utility for formatting Dbase fields. */
417
        public static class FieldFormatter {
418
                private StringBuffer buffer = new StringBuffer(255);
419
                private NumberFormat numFormat = NumberFormat
420
                                .getNumberInstance(Locale.US);
421
                private Calendar calendar = Calendar.getInstance(Locale.US);
422
                private String emtpyString;
423
                private static final int MAXCHARS = 255;
424

    
425
                public FieldFormatter() {
426
                        // Avoid grouping on number format
427
                        numFormat.setGroupingUsed(false);
428

    
429
                        // build a 255 white spaces string
430
                        StringBuffer sb = new StringBuffer(MAXCHARS);
431
                        sb.setLength(MAXCHARS);
432
                        for (int i = 0; i < MAXCHARS; i++) {
433
                                sb.setCharAt(i, ' ');
434
                        }
435

    
436
                        emtpyString = sb.toString();
437
                }
438

    
439
                public String getFieldString(int size, String s) {
440
                        buffer.replace(0, size, emtpyString);
441
                        buffer.setLength(size);
442

    
443
                        if (s != null) {
444
                                buffer.replace(0, size, s);
445
                                if (s.length() <= size) {
446
                                        for (int i = s.length(); i < size; i++) {
447
                                                buffer.append(' ');
448
                                        }
449
                                }
450
                        }
451

    
452
                        buffer.setLength(size);
453
                        return buffer.toString();
454
                }
455

    
456
                public String getFieldString(Date d) {
457

    
458
                        if (d != null) {
459
                                buffer.delete(0, buffer.length());
460

    
461
                                calendar.setTime(d);
462
                                int year = calendar.get(Calendar.YEAR);
463
                                int month = calendar.get(Calendar.MONTH) + 1; // returns 0 based month?
464
                                int day = calendar.get(Calendar.DAY_OF_MONTH);
465

    
466
                                if (year < 1000) {
467
                                        if (year >= 100) {
468
                                                buffer.append("0");
469
                                        } else if (year >= 10) {
470
                                                buffer.append("00");
471
                                        } else {
472
                                                buffer.append("000");
473
                                        }
474
                                }
475
                                buffer.append(year);
476

    
477
                                if (month < 10) {
478
                                        buffer.append("0");
479
                                }
480
                                buffer.append(month);
481

    
482
                                if (day < 10) {
483
                                        buffer.append("0");
484
                                }
485
                                buffer.append(day);
486
                        } else {
487
                                buffer.setLength(8);
488
                                buffer.replace(0, 8, emtpyString);
489
                        }
490

    
491
                        buffer.setLength(8);
492
                        return buffer.toString();
493
                }
494

    
495
                public String getFieldString(int size, int decimalPlaces, double n) {
496
                        buffer.delete(0, buffer.length());
497

    
498
                        //      if (n != null) {
499
                        numFormat.setMaximumFractionDigits(decimalPlaces);
500
                        numFormat.setMinimumFractionDigits(decimalPlaces);
501
                        numFormat.format(n, buffer, new FieldPosition(
502
                                        NumberFormat.INTEGER_FIELD));
503
                        //      }
504

    
505
                        int diff = size - buffer.length();
506
                        if (diff >= 0) {
507
                                while (diff-- > 0) {
508
                                        buffer.insert(0, ' ');
509
                                }
510
                        } else {
511
                                buffer.setLength(size);
512
                        }
513
                        return buffer.toString();
514
                }
515
        }
516

    
517
        public void setCharset(Charset charset) {
518
                this.charset = charset;
519

    
520
        }
521

    
522
}