Statistics
| Revision:

root / trunk / libraries / libDataSourceBaseDrivers / src / org / gvsig / data / datastores / vectorial / file / dbf / utils / DbaseFileWriter.java @ 20501

History | View | Annotate | Download (15.5 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.nio.ByteBuffer;
67
import java.nio.MappedByteBuffer;
68
import java.nio.channels.FileChannel;
69
import java.nio.channels.WritableByteChannel;
70
import java.nio.charset.Charset;
71
import java.text.FieldPosition;
72
import java.text.NumberFormat;
73
import java.util.Calendar;
74
import java.util.Date;
75
import java.util.Iterator;
76
import java.util.Locale;
77

    
78
import org.gvsig.data.exception.CloseException;
79
import org.gvsig.data.exception.InitializeWriterException;
80
import org.gvsig.data.exception.UnsupportedEncodingException;
81
import org.gvsig.data.exception.WriteException;
82
import org.gvsig.data.vectorial.DefaultAttributeDescriptor;
83
import org.gvsig.data.vectorial.IFeature;
84
import org.gvsig.data.vectorial.IFeatureAttributeDescriptor;
85
import org.gvsig.data.vectorial.IFeatureType;
86

    
87

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

    
105
  private DbaseFileHeader header;
106
  private DbaseFileWriter.FieldFormatter formatter = new DbaseFileWriter.FieldFormatter();
107
  WritableByteChannel channel;
108
  private ByteBuffer buffer;
109
  private final Number NULL_NUMBER = new Integer(0);
110
  private final String NULL_STRING = "";
111
  private final String NULL_DATE = "        ";
112

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

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

    
132
    init();
133
  }
134

    
135
  private void init() throws InitializeWriterException {
136
          try{
137
    buffer = ByteBuffer.allocateDirect(header.getRecordLength());
138
          }catch (Exception e) {
139
                throw new InitializeWriterException("DBF Writer",e);
140
        }
141
  }
142

    
143
  private void write() throws WriteException {
144
    buffer.position(0);
145
    int r = buffer.remaining();
146
    try {
147
                while ( (r -= channel.write(buffer)) > 0) {
148
                  ; // do nothing
149
                }
150
        } catch (IOException e) {
151
                throw new WriteException("DBF Writer",e);
152
        }
153
  }
154

    
155

    
156
  /** Write a single dbase record.
157
   * @param record The entries to write.
158
 * @throws UnsupportedEncodingException
159
 * @throws WriteException
160
 * @throws UnsupportedEncodingDriverException
161
   * @throws IOException If IO error occurs.
162
   * @throws DbaseFileException If the entry doesn't comply to the header.
163
   */
164
  public void write(IFeature feature) throws WriteException, UnsupportedEncodingException{
165
          IFeatureType featureType=feature.getType();
166

    
167
    buffer.position(0);
168

    
169
    // put the 'not-deleted' marker
170
    buffer.put( (byte) ' ');
171

    
172
    Iterator iterator=featureType.iterator();
173
   int i=0;
174
   int jGeom=0;
175
   while (iterator.hasNext()) {
176
           IFeatureAttributeDescriptor fad = (IFeatureAttributeDescriptor) iterator.next();
177
           String type=fad.getDataType();
178
           i=((DefaultAttributeDescriptor)fad).originalPosition();
179
//           int i=fad.ordinal();
180
           if (featureType.getDefaultGeometry()!=null && featureType.getDefaultGeometry().equals(fad.getName())){
181
                   jGeom++;
182
                   continue;
183
           }
184
           String fieldString = fieldString(type, feature, i-jGeom);
185
           if (fieldString==null){
186
                   if (type==IFeatureAttributeDescriptor.TYPE_STRING){
187
                           fieldString = NULL_STRING;
188
                   }else if (type==IFeatureAttributeDescriptor.TYPE_DATE){
189
                           fieldString = NULL_DATE;
190
                   }else{
191
                           fieldString = "0";
192
                   }
193
           }
194
           try {
195
                   buffer.put(fieldString.getBytes(charset.name()));
196
           } catch (java.io.UnsupportedEncodingException e) {
197
                   throw new UnsupportedEncodingException("DBF Writer",e);
198
           }
199
           i++;
200
   }
201
    write();
202
  }
203

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

    
218
    if (featureType.size() != header.getNumFields()) {
219
      throw new WriteException("DBF Writer",new IOException("Wrong number of fields " + featureType.size() +
220
      " expected " +  header.getNumFields()));
221
    }
222

    
223
    FileChannel fileChannel = (FileChannel) channel;
224
    long newPos = header.getHeaderLength() + numReg*header.getRecordLength();
225

    
226
    buffer.position(0);
227

    
228
    // put the 'not-deleted' marker
229
    buffer.put( (byte) ' ');
230

    
231
    Iterator iterator=featureType.iterator();
232
    while (iterator.hasNext()) {
233
            IFeatureAttributeDescriptor fad = (IFeatureAttributeDescriptor) iterator.next();
234
            int i=((DefaultAttributeDescriptor)fad).originalPosition();
235
            String type=fad.getDataType();
236
            String fieldString = fieldString(type, feature, i);
237
            try {
238
                        buffer.put(fieldString.getBytes(charset.name()));
239
                } catch (java.io.UnsupportedEncodingException e) {
240
                        throw new UnsupportedEncodingException("DBF",e);
241
                }
242
        }
243

    
244
    try {
245
                fileChannel.write(buffer, newPos);
246
        } catch (IOException e) {
247
                throw new WriteException("DBF Writer",e);
248
        }
249
  }
250

    
251
  private String fieldString(String type,IFeature feature,int i){
252
          final int fieldLen = header.getFieldLength(i);
253
          String fieldString="";
254
          if (IFeatureAttributeDescriptor.TYPE_BOOLEAN==type){
255
                  boolean b=feature.getBoolean(i);
256
                  if (b)
257
                          fieldString="T";
258
                  else
259
                          fieldString="F";
260
          }else if (IFeatureAttributeDescriptor.TYPE_BYTE==type){
261
                  fieldString=String.valueOf(feature.getByte(i));
262
          }else if (IFeatureAttributeDescriptor.TYPE_DATE==type){
263
                  Date date=feature.getDate(i);
264
                  fieldString=formatter.getFieldString(date);
265
          }else if (IFeatureAttributeDescriptor.TYPE_DOUBLE==type){
266
                  double d=feature.getDouble(i);
267
                  fieldString= formatter.getFieldString(fieldLen,
268
                                header.getFieldDecimalCount(i),
269
                                        d);
270
          }else if (IFeatureAttributeDescriptor.TYPE_FLOAT==type){
271
                  float f=feature.getFloat(i);
272
                  fieldString= formatter.getFieldString(fieldLen,
273
                                header.getFieldDecimalCount(i),
274
                                        f);
275
          }else if (IFeatureAttributeDescriptor.TYPE_INT==type){
276
                  int integer=feature.getInt(i);
277
                  fieldString= formatter.getFieldString(fieldLen,
278
                                header.getFieldDecimalCount(i),
279
                                        integer);
280
          }else if (IFeatureAttributeDescriptor.TYPE_LONG==type){
281
                  long l=feature.getLong(i);
282
                  fieldString= formatter.getFieldString(fieldLen,
283
                                header.getFieldDecimalCount(i),
284
                                        l);
285
          }else if (IFeatureAttributeDescriptor.TYPE_STRING==type){
286
                  String s=feature.getString(i);
287
                  fieldString =formatter.getFieldString(
288
                          fieldLen, s);
289
          }
290
          return fieldString;
291

    
292
  }
293

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

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

    
380
    buffer = null;
381
    channel = null;
382
    formatter = null;
383
  }
384

    
385

    
386
  /** Utility for formatting Dbase fields. */
387
  public static class FieldFormatter {
388
    private StringBuffer buffer = new StringBuffer(255);
389
    private NumberFormat numFormat = NumberFormat.getNumberInstance(Locale.US);
390
    private Calendar calendar = Calendar.getInstance(Locale.US);
391
    private String emtpyString;
392
    private static final int MAXCHARS = 255;
393

    
394
    public FieldFormatter() {
395
      // Avoid grouping on number format
396
      numFormat.setGroupingUsed(false);
397

    
398
      // build a 255 white spaces string
399
      StringBuffer sb = new StringBuffer(MAXCHARS);
400
      sb.setLength(MAXCHARS);
401
      for(int i = 0; i < MAXCHARS; i++) {
402
        sb.setCharAt(i, ' ');
403
      }
404

    
405
      emtpyString = sb.toString();
406
    }
407

    
408
    public String getFieldString(int size, String s) {
409
      buffer.replace(0, size, emtpyString);
410
      buffer.setLength(size);
411

    
412
      if(s != null) {
413
        buffer.replace(0, size, s);
414
        if(s.length() <= size) {
415
          for(int i = s.length(); i < size; i++) {
416
            buffer.append(' ');
417
          }
418
        }
419
      }
420

    
421
      buffer.setLength(size);
422
      return buffer.toString();
423
    }
424

    
425
    public String getFieldString(Date d) {
426

    
427
      if(d != null) {
428
        buffer.delete(0, buffer.length());
429

    
430
        calendar.setTime(d);
431
        int year = calendar.get(Calendar.YEAR);
432
        int month = calendar.get(Calendar.MONTH) + 1;  // returns 0 based month?
433
        int day = calendar.get(Calendar.DAY_OF_MONTH);
434

    
435
        if(year < 1000) {
436
          if(year >= 100) {
437
            buffer.append("0");
438
          } else if(year >= 10) {
439
            buffer.append("00");
440
          } else {
441
            buffer.append("000");
442
          }
443
        }
444
        buffer.append(year);
445

    
446
        if(month < 10) {
447
          buffer.append("0");
448
        }
449
        buffer.append(month);
450

    
451
        if(day < 10) {
452
          buffer.append("0");
453
        }
454
        buffer.append(day);
455
      } else {
456
        buffer.setLength(8);
457
        buffer.replace(0, 8, emtpyString);
458
      }
459

    
460
      buffer.setLength(8);
461
      return buffer.toString();
462
    }
463

    
464
    public String getFieldString(int size, int decimalPlaces, double n) {
465
      buffer.delete(0, buffer.length());
466

    
467
//      if (n != null) {
468
        numFormat.setMaximumFractionDigits(decimalPlaces);
469
        numFormat.setMinimumFractionDigits(decimalPlaces);
470
        numFormat.format(n, buffer, new FieldPosition(NumberFormat.INTEGER_FIELD));
471
//      }
472

    
473
      int diff = size - buffer.length();
474
      if (diff >= 0) {
475
          while (diff-- > 0) {
476
              buffer.insert(0, ' ');
477
          }
478
      } else {
479
          buffer.setLength(size);
480
      }
481
      return buffer.toString();
482
    }
483
  }
484

    
485

    
486
public void setCharset(Charset charset) {
487
        this.charset = charset;
488

    
489
}
490

    
491

    
492
}