Statistics
| Revision:

svn-gvsig-desktop / branches / Mobile_Compatible_Hito_1 / libFMap_mobile_shp_driver / src-file / org / gvsig / data / datastores / vectorial / file / dbf / utils / DbaseFileWriter.java @ 21865

History | View | Annotate | Download (14.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.data.datastores.vectorial.file.dbf.utils;
64

    
65
import java.io.IOException;
66
import java.text.FieldPosition;
67
import java.text.NumberFormat;
68
import java.util.Calendar;
69
import java.util.Date;
70
import java.util.Locale;
71

    
72
import org.gvsig.data.CloseException;
73
import org.gvsig.data.WriteException;
74
import org.gvsig.data.datastores.vectorial.InitializeWriterException;
75
import org.gvsig.data.datastores.vectorial.UnsupportedEncodingException;
76
import org.gvsig.data.vectorial.Feature;
77
import org.gvsig.data.vectorial.FeatureAttributeDescriptor;
78
import org.gvsig.data.vectorial.FeatureType;
79
import org.gvsig.datasources.common.IByteBuffer;
80
import org.gvsig.datasources.common.ICharset;
81
import org.gvsig.datasources.common.IRandomFileChannel;
82
import org.gvsig.datasources.impljme.Impl;
83

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

    
100
        private DbaseFileHeader header;
101

    
102
        private DbaseFileWriter.FieldFormatter formatter = new DbaseFileWriter.FieldFormatter();
103

    
104
        IRandomFileChannel channel;
105

    
106
        private IByteBuffer buffer;
107

    
108
        private final Number NULL_NUMBER = new Integer(0);
109

    
110
        private final String NULL_STRING = "";
111

    
112
        private final String NULL_DATE = "        ";
113

    
114
        // TODO: READ HEADER AND STABLIST THE RIGHT CHARSET
115
        private ICharset charset = Impl.getCharsetForName("ISO-8859-1");
116

    
117
        /**
118
         * Create a DbaseFileWriter using the specified header and writing to the
119
         * given channel.
120
         * 
121
         * @param header
122
         *            The DbaseFileHeader to write.
123
         * @param out
124
         *            The Channel to write to.
125
         * @throws InitializeWriterException
126
         * @throws IOException
127
         *             If errors occur while initializing.
128
         */
129
        public DbaseFileWriter(DbaseFileHeader header, IRandomFileChannel out)
130
                        throws InitializeWriterException {
131
                try {
132
                        header.writeHeader(out);
133
                } catch (IOException e) {
134
                        throw new InitializeWriterException("DBF Writer", e);
135
                }
136
                this.header = header;
137
                this.channel = out;
138

    
139
                init();
140
        }
141

    
142
        private void init() throws InitializeWriterException {
143
                try {
144
                        buffer = Impl.allocateDirect(header.getRecordLength());
145
                } catch (Exception e) {
146
                        throw new InitializeWriterException("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
        /**
163
         * Write a single dbase record.
164
         * 
165
         * @param record
166
         *            The entries to write.
167
         * @throws UnsupportedEncodingException
168
         * @throws WriteException
169
         * @throws UnsupportedEncodingDriverException
170
         * @throws IOException
171
         *             If IO error occurs.
172
         * @throws DbaseFileException
173
         *             If the entry doesn't comply to the header.
174
         */
175
        public void write(Feature feature) throws WriteException,
176
                        UnsupportedEncodingException {
177
                FeatureType featureType = feature.getType();
178

    
179
                buffer.position(0);
180

    
181
                // put the 'not-deleted' marker
182
                buffer.put((byte) ' ');
183

    
184
                for (int i = 0; i < header.getNumFields(); i++) {
185
                        String type = ((FeatureAttributeDescriptor) featureType.get(i))
186
                                        .getDataType();
187
                        String fieldString = fieldString(type, feature, i);
188
                        if (fieldString == null) {
189
                                if (type == FeatureAttributeDescriptor.TYPE_STRING) {
190
                                        fieldString = NULL_STRING;
191
                                } else if (type == FeatureAttributeDescriptor.TYPE_DATE) {
192
                                        fieldString = NULL_DATE;
193
                                } else {
194
                                        fieldString = "0";
195
                                }
196
                        }
197
                        try {
198
                                buffer.put(fieldString.getBytes(charset.name()));
199
                        } catch (java.io.UnsupportedEncodingException e) {
200
                                throw new UnsupportedEncodingException("DBF Writer", e);
201
                        }
202
                }
203
                write();
204
        }
205

    
206
        /**
207
         * Write a single dbase record. Useful to update a dbf.
208
         * 
209
         * @param record
210
         *            The entries to write.
211
         * @throws WriteException
212
         * @throws UnsupportedEncodingException
213
         * @throws
214
         * @throws IOException
215
         *             If IO error occurs.
216
         * @throws DbaseFileException
217
         *             If the entry doesn't comply to the header.
218
         */
219
        public void writeRecord(Feature feature, int numReg)
220
                        throws WriteException, UnsupportedEncodingException {
221
                FeatureType featureType = feature.getType();
222
                // if (!(channel instanceof FileChannel)) {
223
                // throw new WriteException("DBF Writer",new
224
                // IOException("DbaseFileWriterNIO: channel is not a FileChannel. Cannot
225
                // position properly"));
226
                // }
227

    
228
                if (featureType.size() != header.getNumFields()) {
229
                        throw new WriteException("DBF Writer", new IOException(
230
                                        "Wrong number of fields " + featureType.size()
231
                                                        + " expected " + header.getNumFields()));
232
                }
233

    
234
                // FileChannel fileChannel = (FileChannel) channel;
235
                long newPos = header.getHeaderLength() + numReg
236
                                * header.getRecordLength();
237

    
238
                buffer.position(0);
239

    
240
                // put the 'not-deleted' marker
241
                buffer.put((byte) ' ');
242

    
243
                for (int i = 0; i < header.getNumFields(); i++) {
244
                        FeatureAttributeDescriptor descriptor = (FeatureAttributeDescriptor) featureType
245
                                        .get(i);
246
                        String type = descriptor.getDataType();
247
                        String fieldString = fieldString(type, feature, i);
248
                        try {
249
                                buffer.put(fieldString.getBytes(charset.name()));
250
                        } catch (java.io.UnsupportedEncodingException e) {
251
                                throw new UnsupportedEncodingException("DBF", e);
252
                        }
253

    
254
                }
255

    
256
                try {
257
                        channel.write(buffer, (int) newPos);
258
                } catch (IOException e) {
259
                        throw new WriteException("DBF Writer", e);
260
                }
261
        }
262

    
263
        private String fieldString(String type, Feature feature, int i) {
264
                final int fieldLen = header.getFieldLength(i);
265
                String fieldString = "";
266
                if (FeatureAttributeDescriptor.TYPE_BOOLEAN == type) {
267
                        boolean b = feature.getBoolean(i);
268
                        if (b)
269
                                fieldString = "T";
270
                        else
271
                                fieldString = "F";
272
                } else if (FeatureAttributeDescriptor.TYPE_BYTE == type) {
273
                        fieldString = String.valueOf(feature.getByte(i));
274
                } else if (FeatureAttributeDescriptor.TYPE_DATE == type) {
275
                        Date date = feature.getDate(i);
276
                        fieldString = formatter.getFieldString(date);
277
                } else if (FeatureAttributeDescriptor.TYPE_DOUBLE == type) {
278
                        double d = feature.getDouble(i);
279
                        fieldString = formatter.getFieldString(fieldLen, header
280
                                        .getFieldDecimalCount(i), d);
281
                } else if (FeatureAttributeDescriptor.TYPE_FLOAT == type) {
282
                        float f = feature.getFloat(i);
283
                        fieldString = formatter.getFieldString(fieldLen, header
284
                                        .getFieldDecimalCount(i), f);
285
                } else if (FeatureAttributeDescriptor.TYPE_INT == type) {
286
                        int integer = feature.getInt(i);
287
                        fieldString = formatter.getFieldString(fieldLen, header
288
                                        .getFieldDecimalCount(i), integer);
289
                } else if (FeatureAttributeDescriptor.TYPE_LONG == type) {
290
                        long l = feature.getLong(i);
291
                        fieldString = formatter.getFieldString(fieldLen, header
292
                                        .getFieldDecimalCount(i), l);
293
                } else if (FeatureAttributeDescriptor.TYPE_STRING == type) {
294
                        String s = feature.getString(i);
295
                        fieldString = formatter.getFieldString(fieldLen, s);
296
                }
297
                return fieldString;
298

    
299
        }
300

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

    
366
        /**
367
         * Release resources associated with this writer. <B>Highly recommended</B>
368
         * 
369
         * @throws CloseException
370
         * @throws IOException
371
         *             If errors occur.
372
         */
373
        public void close() throws CloseException {
374
                // IANS - GEOT 193, bogus 0x00 written. According to dbf spec, optional
375
                // eof 0x1a marker is, well, optional. Since the original code wrote a
376
                // 0x00 (which is wrong anyway) lets just do away with this :)
377
                // - produced dbf works in OpenOffice and ArcExplorer java, so it must
378
                // be okay.
379
                // buffer.position(0);
380
                // buffer.put((byte) 0).position(0).limit(1);
381
                // write();
382
                try {
383
                        channel.close();
384
                } catch (IOException e) {
385
                        throw new CloseException("DBF Writer", e);
386
                }
387
                // if (buffer instanceof MappedByteBuffer) {
388
                // // NIOUtilities.clean(buffer);
389
                // }
390

    
391
                buffer = null;
392
                channel = null;
393
                formatter = null;
394
        }
395

    
396
        /** Utility for formatting Dbase fields. */
397
        public static class FieldFormatter {
398
                private StringBuffer buffer = new StringBuffer(255);
399

    
400
                private NumberFormat numFormat = NumberFormat
401
                                .getNumberInstance(Locale.US);
402

    
403
                private Calendar calendar = Calendar.getInstance(Locale.US);
404

    
405
                private String emtpyString;
406

    
407
                private static final int MAXCHARS = 255;
408

    
409
                public FieldFormatter() {
410
                        // Avoid grouping on number format
411
                        numFormat.setGroupingUsed(false);
412

    
413
                        // build a 255 white spaces string
414
                        StringBuffer sb = new StringBuffer(MAXCHARS);
415
                        sb.setLength(MAXCHARS);
416
                        for (int i = 0; i < MAXCHARS; i++) {
417
                                sb.setCharAt(i, ' ');
418
                        }
419

    
420
                        emtpyString = sb.toString();
421
                }
422

    
423
                public String getFieldString(int size, String s) {
424
                        buffer.replace(0, size, emtpyString);
425
                        buffer.setLength(size);
426

    
427
                        if (s != null) {
428
                                buffer.replace(0, size, s);
429
                                if (s.length() <= size) {
430
                                        for (int i = s.length(); i < size; i++) {
431
                                                buffer.append(' ');
432
                                        }
433
                                }
434
                        }
435

    
436
                        buffer.setLength(size);
437
                        return buffer.toString();
438
                }
439

    
440
                public String getFieldString(Date d) {
441

    
442
                        if (d != null) {
443
                                buffer.delete(0, buffer.length());
444

    
445
                                calendar.setTime(d);
446
                                int year = calendar.get(Calendar.YEAR);
447
                                int month = calendar.get(Calendar.MONTH) + 1; // returns 0
448
                                                                                                                                // based month?
449
                                int day = calendar.get(Calendar.DAY_OF_MONTH);
450

    
451
                                if (year < 1000) {
452
                                        if (year >= 100) {
453
                                                buffer.append("0");
454
                                        } else if (year >= 10) {
455
                                                buffer.append("00");
456
                                        } else {
457
                                                buffer.append("000");
458
                                        }
459
                                }
460
                                buffer.append(year);
461

    
462
                                if (month < 10) {
463
                                        buffer.append("0");
464
                                }
465
                                buffer.append(month);
466

    
467
                                if (day < 10) {
468
                                        buffer.append("0");
469
                                }
470
                                buffer.append(day);
471
                        } else {
472
                                buffer.setLength(8);
473
                                buffer.replace(0, 8, emtpyString);
474
                        }
475

    
476
                        buffer.setLength(8);
477
                        return buffer.toString();
478
                }
479

    
480
                public String getFieldString(int size, int decimalPlaces, double n) {
481
                        buffer.delete(0, buffer.length());
482

    
483
                        // if (n != null) {
484
                        numFormat.setMaximumFractionDigits(decimalPlaces);
485
                        numFormat.setMinimumFractionDigits(decimalPlaces);
486
                        numFormat.format(n, buffer, new FieldPosition(
487
                                        NumberFormat.INTEGER_FIELD));
488
                        // }
489

    
490
                        int diff = size - buffer.length();
491
                        if (diff >= 0) {
492
                                while (diff-- > 0) {
493
                                        buffer.insert(0, ' ');
494
                                }
495
                        } else {
496
                                buffer.setLength(size);
497
                        }
498
                        return buffer.toString();
499
                }
500
        }
501

    
502
        public void setCharset(ICharset charset) {
503
                this.charset = charset;
504

    
505
        }
506

    
507
}