Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / shp / DbaseFileWriterNIO.java @ 29287

History | View | Annotate | Download (12.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 com.iver.cit.gvsig.fmap.drivers.shp;
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.Locale;
76

    
77
import org.apache.log4j.Logger;
78

    
79
import com.hardcode.gdbms.engine.values.BooleanValue;
80
import com.hardcode.gdbms.engine.values.DateValue;
81
import com.hardcode.gdbms.engine.values.NullValue;
82
import com.hardcode.gdbms.engine.values.NumericValue;
83
import com.hardcode.gdbms.engine.values.StringValue;
84

    
85

    
86
/** A DbaseFileReader is used to read a dbase III format file.
87
 * The general use of this class is:
88
 * <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>
97
 * You must supply the <CODE>moreRecords</CODE> and <CODE>getMyRecord()</CODE>
98
 * logic...
99
 * @author Ian Schneider
100
 */
101
public class DbaseFileWriterNIO  {
102

    
103
  private DbaseFileHeaderNIO header;
104
  private DbaseFileWriterNIO.FieldFormatter formatter = new DbaseFileWriterNIO.FieldFormatter();
105
  WritableByteChannel 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

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

    
114
  /** Create a DbaseFileWriter using the specified header and writing to the given
115
   * channel.
116
   * @param header The DbaseFileHeader to write.
117
   * @param out The Channel to write to.
118
   * @throws IOException If errors occur while initializing.
119
   */
120
  public DbaseFileWriterNIO(DbaseFileHeaderNIO header,FileChannel out) throws IOException {
121
    header.writeHeader(out);
122
    this.header = header;
123
    this.channel = out;
124

    
125
    init();
126
  }
127

    
128
  private void init() throws IOException {
129
    buffer = ByteBuffer.allocateDirect(header.getRecordLength());
130
  }
131

    
132
  private void write() throws IOException {
133
    buffer.position(0);
134
    int r = buffer.remaining();
135
    while ( (r -= channel.write(buffer)) > 0) {
136
      ; // do nothing
137
    }
138
  }
139

    
140

    
141
  /** Write a single dbase record.
142
   * @param record The entries to write.
143
   * @throws IOException If IO error occurs.
144
   * @throws DbaseFileException If the entry doesn't comply to the header.
145
   */
146
  public void write(Object[] record) throws IOException{
147
  /*
148
    if (record.length != header.getNumFields()) {
149
      throw new DbaseFileException("Wrong number of fields " + record.length +
150
      " expected " +  header.getNumFields());
151
    }
152
    */
153
    buffer.position(0);
154

    
155
    // put the 'not-deleted' marker
156
    buffer.put( (byte) ' ');
157

    
158
    for (int i = 0; i < header.getNumFields(); i++) {
159
      String fieldString = fieldString(record[i], i);
160
//      if ( header.getFieldLength(i) != fieldString.getBytes().length) {
161
//          System.out.println(i + " : " + header.getFieldName(i));
162
//      }
163
      String tmp = fieldString;
164
      while(fieldString.length()<tmp.getBytes(charset.name()).length){
165
              tmp=tmp.substring(0, tmp.length()-2);
166
      }
167
      buffer.put(tmp.getBytes(charset.name()));
168

    
169
    }
170

    
171

    
172
    write();
173
  }
174

    
175
  /** Write a single dbase record. Useful to update a dbf.
176
   * @param record The entries to write.
177
   * @throws IOException If IO error occurs.
178
   * @throws DbaseFileException If the entry doesn't comply to the header.
179
   */
180
  public void writeRecord(Object[] record, int numReg) throws IOException{
181

    
182
        if (!(channel instanceof FileChannel)) {
183
              throw new IOException("DbaseFileWriterNIO: channel is not a FileChannel. Cannot position properly");
184
        }
185

    
186
    if (record.length != header.getNumFields()) {
187
      throw new IOException("Wrong number of fields " + record.length +
188
      " expected " +  header.getNumFields());
189
    }
190

    
191
    FileChannel fileChannel = (FileChannel) channel;
192
    long newPos = header.getHeaderLength() + numReg*header.getRecordLength();
193

    
194
    buffer.position(0);
195

    
196
    // put the 'not-deleted' marker
197
    buffer.put( (byte) ' ');
198

    
199
    for (int i = 0; i < header.getNumFields(); i++) {
200
      String fieldString = fieldString(record[i], i);
201
//      if ( header.getFieldLength(i) != fieldString.getBytes().length) {
202
//          System.out.println(i + " : " + header.getFieldName(i));
203
//      }
204

    
205
      String tmp = fieldString;
206
      while(fieldString.length()<tmp.getBytes(charset.name()).length){
207
              tmp=tmp.substring(0, tmp.length()-2);
208
      }
209
      buffer.put(tmp.getBytes(charset.name()));
210

    
211
    }
212

    
213
    fileChannel.write(buffer, newPos);
214
  }
215

    
216

    
217
  private String fieldString(Object obj,final int col) {
218
    String o;
219
    final int fieldLen = header.getFieldLength(col);
220
    switch (header.getFieldType(col)) {
221
      case 'C':
222
      case 'c':
223
        o = formatter.getFieldString(
224
          fieldLen,
225
          (obj == null || obj instanceof NullValue)? NULL_STRING : ((StringValue) obj).getValue()
226
        );
227
        break;
228
      case 'L':
229
      case 'l':
230
        o = (obj == null || obj instanceof NullValue) ? "F" : ((BooleanValue)obj).getValue() == true ? "T" : "F";
231
        break;
232
      case 'M':
233
      case 'G':
234
        o = formatter.getFieldString(
235
          fieldLen,
236
          (obj == null || obj instanceof NullValue) ? NULL_STRING : ((StringValue) obj).getValue()
237
        );
238
        break;
239
     /* case 'N':
240
      case 'n':
241
        // int?
242
        if (header.getFieldDecimalCount(col) == 0) {
243

244
          o = formatter.getFieldString(
245
            fieldLen, 0, (Number) (obj == null ? NULL_NUMBER : Double.valueOf(obj.toString()))
246
          );
247
          break;
248

249
         }
250
      */
251
      case 'N':
252
      case 'n':
253
      case 'F':
254
      case 'f':
255
            Number number = null;
256
            if(obj ==null || obj instanceof NullValue){
257
                    number = NULL_NUMBER;
258
            }else{
259
                    NumericValue gVal = (NumericValue) obj;
260
                    number = new Double(gVal.doubleValue());
261
            }
262
        o = formatter.getFieldString(fieldLen,
263
                        header.getFieldDecimalCount(col),
264
                                number);
265
        break;
266
      case 'D':
267
      case 'd':
268
          if (obj == null || obj instanceof NullValue)
269
              o = NULL_DATE;
270
          else
271
              o = formatter.getFieldString(((DateValue)obj).getValue());
272
        break;
273
      default:
274
        throw new RuntimeException("Unknown type " + header.getFieldType(col));
275
    }
276

    
277
    return o;
278
  }
279

    
280
  /** Release resources associated with this writer.
281
   * <B>Highly recommended</B>
282
   * @throws IOException If errors occur.
283
   */
284
  public void close() throws IOException {
285
      // IANS - GEOT 193, bogus 0x00 written. According to dbf spec, optional
286
      // eof 0x1a marker is, well, optional. Since the original code wrote a
287
      // 0x00 (which is wrong anyway) lets just do away with this :)
288
      // - produced dbf works in OpenOffice and ArcExplorer java, so it must
289
      // be okay.
290
//    buffer.position(0);
291
//    buffer.put((byte) 0).position(0).limit(1);
292
//    write();
293
    channel.close();
294
    if (buffer instanceof MappedByteBuffer) {
295
     // NIOUtilities.clean(buffer);
296
    }
297

    
298
    buffer = null;
299
    channel = null;
300
    formatter = null;
301
  }
302

    
303

    
304
  /** Utility for formatting Dbase fields. */
305
  public static class FieldFormatter {
306
    private StringBuffer buffer = new StringBuffer(255);
307
    private NumberFormat numFormat = NumberFormat.getNumberInstance(Locale.US);
308
    private Calendar calendar = Calendar.getInstance(Locale.US);
309
    private String emtpyString;
310
    private static final int MAXCHARS = 255;
311

    
312
    public FieldFormatter() {
313
      // Avoid grouping on number format
314
      numFormat.setGroupingUsed(false);
315

    
316
      // build a 255 white spaces string
317
      StringBuffer sb = new StringBuffer(MAXCHARS);
318
      sb.setLength(MAXCHARS);
319
      for(int i = 0; i < MAXCHARS; i++) {
320
        sb.setCharAt(i, ' ');
321
      }
322

    
323
      emtpyString = sb.toString();
324
    }
325

    
326
    public String getFieldString(int size, String s) {
327
      buffer.replace(0, size, emtpyString);
328
      buffer.setLength(size);
329

    
330
      if(s != null) {
331
        buffer.replace(0, size, s);
332
        if(s.length() <= size) {
333
          for(int i = s.length(); i < size; i++) {
334
            buffer.append(' ');
335
          }
336
        }
337
      }
338

    
339
      buffer.setLength(size);
340
      return buffer.toString();
341
    }
342

    
343
    public String getFieldString(Date d) {
344

    
345
      if(d != null) {
346
        buffer.delete(0, buffer.length());
347

    
348
        calendar.setTime(d);
349
        int year = calendar.get(Calendar.YEAR);
350
        int month = calendar.get(Calendar.MONTH) + 1;  // returns 0 based month?
351
        int day = calendar.get(Calendar.DAY_OF_MONTH);
352

    
353
        if(year < 1000) {
354
          if(year >= 100) {
355
            buffer.append("0");
356
          } else if(year >= 10) {
357
            buffer.append("00");
358
          } else {
359
            buffer.append("000");
360
          }
361
        }
362
        buffer.append(year);
363

    
364
        if(month < 10) {
365
          buffer.append("0");
366
        }
367
        buffer.append(month);
368

    
369
        if(day < 10) {
370
          buffer.append("0");
371
        }
372
        buffer.append(day);
373
      } else {
374
        buffer.setLength(8);
375
        buffer.replace(0, 8, emtpyString);
376
      }
377

    
378
      buffer.setLength(8);
379
      return buffer.toString();
380
    }
381

    
382
    public String getFieldString(int size, int decimalPlaces, Number n) {
383
      buffer.delete(0, buffer.length());
384

    
385
      if (n != null) {
386
        numFormat.setMaximumFractionDigits(decimalPlaces);
387
        numFormat.setMinimumFractionDigits(decimalPlaces);
388
        numFormat.format(n, buffer, new FieldPosition(NumberFormat.INTEGER_FIELD));
389
      }
390

    
391
      int diff = size - buffer.length();
392
      if (diff >= 0) {
393
          while (diff-- > 0) {
394
              buffer.insert(0, ' ');
395
          }
396
      } else {
397
          buffer.setLength(size);
398
      }
399
      return buffer.toString();
400
    }
401
  }
402

    
403

    
404
public void setCharset(Charset charset) {
405
        if (!Charset.isSupported(charset.name())){
406
                Logger.getLogger(this.getClass()).error("Charset:  "+ charset.name() + "  not supported!");
407
        }else{
408
                this.charset = charset;
409
        }
410
}
411

    
412

    
413
}