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.dbf / src / main / java / org / gvsig / fmap / dal / store / dbf / utils / DbaseFileWriter.java @ 43978

History | View | Annotate | Download (24.2 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.store.dbf.utils;
25

    
26
import java.io.IOException;
27
import java.nio.BufferOverflowException;
28
import java.nio.ByteBuffer;
29
import java.nio.MappedByteBuffer;
30
import java.nio.channels.FileChannel;
31
import java.nio.charset.Charset;
32
import java.text.FieldPosition;
33
import java.text.NumberFormat;
34
import java.util.Arrays;
35
import java.util.Calendar;
36
import java.util.Date;
37
import java.util.Iterator;
38
import java.util.Locale;
39
import org.apache.commons.lang3.StringUtils;
40

    
41
import org.gvsig.fmap.dal.DataTypes;
42
import org.gvsig.fmap.dal.exception.CloseException;
43
import org.gvsig.fmap.dal.exception.InitializeException;
44
import org.gvsig.fmap.dal.exception.UnsupportedEncodingException;
45
import org.gvsig.fmap.dal.exception.WriteException;
46
import org.gvsig.fmap.dal.feature.Feature;
47
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
48
import org.gvsig.fmap.dal.feature.FeatureType;
49

    
50
/**
51
 * A DbaseFileReader is used to read a dbase III format file. The general use of
52
 * this class is: <CODE><PRE>
53
 * DbaseFileHeader header = ...
54
 * WritableFileChannel out = new FileOutputStream("thefile.dbf").getChannel();
55
 * DbaseFileWriter w = new DbaseFileWriter(header,out);
56
 * while ( moreRecords ) {
57
 *   w.write( getMyRecord() );
58
 * }
59
 * w.close();
60
 * </PRE></CODE> You must supply the <CODE>moreRecords</CODE> and
61
 * <CODE>getMyRecord()</CODE> logic...
62
 * 
63
 * @author Ian Schneider
64
 */
65
public class DbaseFileWriter {
66

    
67
    private DbaseFileHeader header;
68
    private DbaseFileWriter.FieldFormatter formatter =
69
        new DbaseFileWriter.FieldFormatter();
70
    FileChannel channel;
71
    private ByteBuffer buffer;
72
    private boolean headDrity = false;
73
    private ByteBuffer blank;
74
    private int blankSize;
75
    
76
    //private Charset charset = Charset.forName("ISO-8859-1");
77
    private Charset charset;
78

    
79
    /**
80
     * Create a DbaseFileWriter using the specified header and writing to the
81
     * given channel.
82
     * 
83
     * @param header
84
     *            The DbaseFileHeader to write.
85
     * @param out
86
     *            The Channel to write to.
87
     * 
88
     * 
89
     * @throws InitializeWriterException
90
     * @throws IOException
91
     *             If errors occur while initializing.
92
     */
93
    public DbaseFileWriter(DbaseFileHeader header, FileChannel out,
94
        boolean isNew) throws InitializeException {
95
        this.header = header;
96
        this.channel = out;
97
        this.headDrity = isNew;
98
        this.setCharset(Charset.forName(header.mappingEncoding(header.getCharsetName())));
99
        
100
        init();
101
    }
102

    
103
    private void init() throws InitializeException {
104
        try {
105
            if (this.channel.size() < this.header.getHeaderLength()) {
106
                this.writeHeader();
107
            }
108
            buffer = ByteBuffer.allocateDirect(header.getRecordLength());
109
        } catch (Exception e) {
110
            throw new InitializeException("DBF Writer", e);
111
        }
112
    }
113

    
114
    private void write() throws WriteException {
115
        buffer.position(0);
116
        int r = buffer.remaining();
117
        try {
118
            while ((r -= channel.write(buffer)) > 0) {
119
                ; // do nothing
120
            }
121
        } catch (IOException e) {
122
            throw new WriteException("DBF Writer", e);
123
        }
124
    }
125

    
126
    private void writeHeader() throws WriteException {
127
        try {
128
            channel.position(0);
129
            header.writeHeader(channel);
130
        } catch (IOException e) {
131
            throw new WriteException("DBF Writer", e);
132
        }
133
    }
134

    
135
    /**
136
     * Write a single dbase record.
137
     * 
138
     * @param record
139
     *            The entries to write.
140
     * @throws UnsupportedEncodingException
141
     * @throws WriteException
142
     */
143
    public void append(Feature feature) throws WriteException,
144
        UnsupportedEncodingException {
145
        this.fillBuffer(feature);
146
        try {
147
            this.moveToEOF();
148
        } catch (IOException e) {
149
            throw new WriteException("DbaseFileWriter", e);
150
        }
151
        this.header.setNumRecords(this.header.getNumRecords() + 1);
152
        write();
153

    
154
        this.headDrity = true;
155
    }
156

    
157
    private void fillBuffer(Feature feature)
158
        throws UnsupportedEncodingException, WriteException {
159
        FeatureType featureType = feature.getType();
160
        try {
161
            buffer.position(0);
162

    
163
            // put the 'not-deleted' marker
164
            buffer.put((byte) ' ');
165

    
166
            @SuppressWarnings("unchecked")
167
            Iterator<FeatureAttributeDescriptor> iterator =
168
                featureType.iterator();
169
            
170
            while (iterator.hasNext()) {
171
                FeatureAttributeDescriptor fad = iterator.next();
172
                if (fad.isComputed()) {
173
                    continue;
174
                }
175
                
176
                if (fad.getName().length() > DbaseFile.MAX_FIELD_NAME_LENGTH) {
177
                    throw new FieldNameTooLongException(
178
                        "DBF file", fad.getName());
179
                }
180
                
181
                int type = fad.getType();
182
                if (type == DataTypes.GEOMETRY) {
183
                    continue;
184
                }
185
                encodeField(fad, feature);
186
            }
187
        } catch (Exception e) {
188
            throw new WriteException("DbaseFileWriter", e);
189
        }
190
    }
191
    
192
    private void moveToEOF() throws IOException {
193
        this.moveTo(this.header.getNumRecords());
194
    }
195

    
196
    private void moveTo(long numReg) throws IOException {
197
        // if (!(channel instanceof FileChannel)) {
198
        // throw new IOException(
199
        // "DbaseFileWriterNIO: channel is not a FileChannel. Cannot position properly");
200
        // }
201

    
202
        long newPos =
203
            header.getHeaderLength() + numReg * header.getRecordLength();
204
        if (this.channel.position() != newPos) {
205
            this.channel.position(newPos);
206
        }
207
    }
208

    
209
    /**
210
     * Write a single dbase record. Useful to update a dbf.
211
     * 
212
     * @param record
213
     *            The entries to write.
214
     * @throws WriteException
215
     * @throws UnsupportedEncodingException
216
     */
217
    public void update(Feature feature, long numReg) throws WriteException,
218
        UnsupportedEncodingException {
219
        this.fillBuffer(feature);
220

    
221
        try {
222
            this.moveTo(numReg);
223
        } catch (IOException e) {
224
            throw new WriteException("DbaseFileWriter", e);
225
        }
226

    
227
        write();
228
    }
229

    
230
    private String fieldString(FeatureAttributeDescriptor attr, Feature feature) throws java.io.UnsupportedEncodingException {
231
        int type = attr.getType();
232
        int dbfFieldIndex = this.header.getFieldIndex(attr.getName());
233
        final int fieldLen = header.getFieldLength(dbfFieldIndex);
234
        String fieldString = "";
235
        if (DataTypes.BOOLEAN == type) {
236
            boolean b = feature.getBoolean(attr.getIndex());
237
            if (b) {
238
                fieldString = "T";
239
            } else {
240
                fieldString = "F";
241
            }
242
        } else
243
            if (DataTypes.BYTE == type) {
244
                fieldString = String.valueOf(feature.getByte(attr.getIndex()));
245
            } else
246
                if (DataTypes.DATE == type) {
247
                    Date date = feature.getDate(attr.getIndex());
248
                    fieldString = formatter.getFieldString(date);
249
                } else
250
                    if (DataTypes.DOUBLE == type) {
251
                        double d = feature.getDouble(attr.getIndex());
252
                        fieldString =
253
                            formatter.getFieldString(fieldLen,
254
                                header.getFieldDecimalCount(dbfFieldIndex), d);
255
                    } else
256
                        if (DataTypes.FLOAT == type) {
257
                            float f = feature.getFloat(attr.getIndex());
258
                            fieldString =
259
                                formatter.getFieldString(fieldLen,
260
                                    header.getFieldDecimalCount(dbfFieldIndex),
261
                                    f);
262
                        } else
263
                            if (DataTypes.INT == type) {
264
                                int integer = feature.getInt(attr.getIndex());
265
                                fieldString =
266
                                    formatter.getFieldString(fieldLen, header
267
                                        .getFieldDecimalCount(dbfFieldIndex),
268
                                        integer);
269
                            } else
270
                                if (DataTypes.LONG == type) {
271
                                    long l = feature.getLong(attr.getIndex());
272
                                    fieldString =
273
                                        formatter
274
                                            .getFieldString(
275
                                                fieldLen,
276
                                                header
277
                                                    .getFieldDecimalCount(dbfFieldIndex),
278
                                                l);
279
                                } else
280
                                    if (DataTypes.STRING == type) {
281
                                        String s =
282
                                            feature.getString(attr.getIndex());
283
                                        return s;
284
                                    }
285
        return fieldString;
286

    
287
    }
288

    
289
    private void encodeField(FeatureAttributeDescriptor attr, Feature feature) throws java.io.UnsupportedEncodingException, UnsupportedEncodingException {
290
        int type = attr.getType();
291
        int dbfFieldIndex = this.header.getFieldIndex(attr.getName());
292
        final int fieldLen = header.getFieldLength(dbfFieldIndex);
293
        String fieldString = "";
294
        
295
        if( DataTypes.BOOLEAN == type ) {
296
            boolean b = feature.getBoolean(attr.getIndex());
297
            if( b ) {
298
                safeEncode("T", 1, true);
299
            } else {
300
                safeEncode("F", 1, true);
301
            }
302
        
303
        } else if( DataTypes.BYTE == type ) {
304
            fieldString = String.valueOf(feature.getByte(attr.getIndex()));
305
            safeEncode(fieldString, 8, false);
306
        
307
        } else if( DataTypes.DATE == type ) {
308
            Date date = feature.getDate(attr.getIndex());
309
            fieldString = formatter.getFieldString(date);
310
            safeEncode(fieldString, 8, false);
311
        
312
        } else if( DataTypes.DOUBLE == type ) {
313
            double d = feature.getDouble(attr.getIndex());
314
            fieldString  = formatter.getFieldString(
315
                fieldLen, header.getFieldDecimalCount(dbfFieldIndex), d
316
            );
317
            safeEncode(fieldString, fieldLen, false);
318
        
319
        } else if( DataTypes.FLOAT == type ) {
320
            float f = feature.getFloat(attr.getIndex());
321
            fieldString = formatter.getFieldString(
322
                fieldLen, header.getFieldDecimalCount(dbfFieldIndex), f
323
            );
324
            safeEncode(fieldString, fieldLen, false);
325
        
326
        } else if( DataTypes.INT == type ) {
327
            int integer = feature.getInt(attr.getIndex());
328
            fieldString = formatter.getFieldString(
329
                fieldLen, header.getFieldDecimalCount(dbfFieldIndex), integer
330
            );
331
            safeEncode(fieldString, fieldLen, false);
332
        
333
        } else if( DataTypes.LONG == type ) {
334
            long l = feature.getLong(attr.getIndex());
335
            fieldString = formatter.getFieldString(
336
                fieldLen, header.getFieldDecimalCount(dbfFieldIndex),l
337
            );
338
            safeEncode(fieldString, fieldLen, false);
339
        
340
        } else if( DataTypes.STRING == type ) {
341
            String s = feature.getString(attr.getIndex());
342
            safeEncode(StringUtils.defaultIfEmpty(s, ""), fieldLen, true);
343
        
344
        } else {
345
            // Si no conocemos el tipo intentamos guardarlo como un string
346
            String s = feature.getString(attr.getIndex());
347
            safeEncode(StringUtils.defaultIfEmpty(s, ""), fieldLen, true);
348

    
349
        }
350

    
351
    }
352

    
353
    /**
354
     * Returns a safely padded (and potentially truncated) string 
355
     * 
356
     * This may truncate some record, but it is required to ensure
357
     * that the field limit is not overflowed when using 
358
     * variable-length charsets such as UTF-8.
359
     * @throws UnsupportedEncodingException 
360
     */
361
    private void safeEncode(String in, int limit, boolean rightPadding) throws UnsupportedEncodingException {
362
            try {
363
                    byte[] encodedString = in.getBytes(this.charset);
364
                    if (encodedString.length>limit) {
365
                            // too long, truncating
366
                            /*
367
                             * The block code bellow is equivalent to this simple code
368
                             * fragment:
369

370
                    if (rightPadding) {
371
                            in = in.substring(0, in.length()-1);
372
                            encodedString = in.getBytes(charset);
373
                    }
374
                    else {
375
                            in.substring(1, in.length());
376
                            encodedString = in.getBytes(charset);
377
                    }
378

379
                    However, the implemented algorithm has a much better performance
380
                    for the average and worst cases (when the input string has a lot
381
                    of multibyte characters), while keeping a good performance
382
                    for the best case (when all the characters in the input string
383
                    can be represented as single bytes using the selected charset).
384

385
                    The general strategy is to compute the deviation from the
386
                    required maximum number of bytes (limit) and the actual number
387
                    of bytes of the encoded String.
388

389
                    Then, we use this deviation to estimate the amount of characters
390
                    to truncate, based on the average factor of bytes per char in the
391
                    input string.
392

393
                    We truncate the string using this approach until the deviation
394
                    gets stable.
395

396
                    Finally, as we should be close enough to the right truncation position,
397
                    we increment/decrement the truncated string by only 1 character, to
398
                    ensure we truncate in the exact position. 
399
                             */
400
                            String str = in;
401
                            int estimatedDiff, deviation;
402
                            int deviationPrev;
403
                            double ratio;
404
                            byte[] encodedChar;
405
                            int truncatePos = 0;
406
                            deviation = encodedString.length - limit;
407
                            deviationPrev = deviation - 1;
408
                            while(Math.abs(deviation)>Math.abs(deviationPrev) && str.length()>0) {
409
                                    ratio = ((double)encodedString.length) / ((double)str.length());
410
                                    // apply the estimated diff, ensuring it is at least >= 1.0 in absolute value
411
                                    estimatedDiff = Math.max((int)(((double)deviation)/ratio), (int)(Math.signum(deviation)*1));
412
                                    // too long, truncating
413
                                    if (rightPadding) {
414
                                            truncatePos = Math.max(str.length()-estimatedDiff, 0);
415
                                            str = in.substring(0, truncatePos);
416
                                    }
417
                                    else {
418
                                            truncatePos = Math.max(truncatePos + estimatedDiff, 0);
419
                                            str = in.substring(truncatePos);                                  
420
                                    }
421
                                    encodedString = str.getBytes(charset);
422
                                    deviationPrev = deviation;
423
                                    deviation = encodedString.length - limit;
424
                            }
425
                            // now we are close enough, get the exact position for truncating
426
                            while (encodedString.length>limit) {
427
                                    // too long, truncating
428
                                    //                                      System.out.println("truncating");
429
                                    if (rightPadding) {
430
                                            str = in.substring(0, str.length()-1);
431
                                    }
432
                                    else {
433
                                            truncatePos = truncatePos + 1;
434
                                            str = in.substring(truncatePos);
435
                                    }
436
                                    encodedString = str.getBytes(charset);
437
                            }
438
                            while (encodedString.length<limit && str.length()<in.length()) {
439
                                    // Extend if necessary:
440
                                    // 1 - Get the length in bytes of the next char
441
                                    // 2 - Add the char to the substring if we are still within the limits 
442
                                    //                                      System.out.println("extending");
443
                                    if (rightPadding) {
444
                                            encodedChar = in.substring(str.length(), str.length()+1).getBytes(charset);
445
                                    }
446
                                    else {
447
                                            encodedChar = in.substring(truncatePos-1, truncatePos).getBytes(charset);
448
                                            //                                              System.out.println(encodedChar);
449
                                            //                                              System.out.println(encodedChar.length);
450
                                            //                                              System.out.println(testStrings[i].substring(truncatePos-1, truncatePos));
451
                                    }
452
                                    //                                      System.out.println(testStrings[i].substring(in.length(), in.length()+1));
453
                                    if ((encodedString.length + encodedChar.length)>limit) {
454
                                            // one more char would overflow the limit
455
                                            break;
456
                                    }
457
                                    // too short, extending
458
                                    if (rightPadding) {
459
                                            str = in.substring(0, str.length()+1);
460
                                    }
461
                                    else {
462
                                            truncatePos = truncatePos - 1;
463
                                            str = in.substring(truncatePos);
464
                                    }
465
                                    encodedString = str.getBytes(charset);
466
                            }
467
                    }
468
                    if (rightPadding) {
469
                            buffer.put(encodedString);
470
                    }
471
                    if (encodedString.length<limit) {
472
                            // too short, padding
473
                            int i = encodedString.length;
474
                            while (i<limit) {
475
                                    blank.position(0);
476
                                    buffer.put(blank);
477
                                    i=i+blankSize;
478
                            }
479
                            if (i>limit) {
480
                                    // Might happen for instance if charset is UTF16 and the
481
                                    // limit of characters in the field is an odd number
482
                                    throw new UnsupportedEncodingException(new Exception("Impossible to encode this DBF using the selected charset"));
483
                            }
484
                    }
485
                    if (!rightPadding) {
486
                            buffer.put(encodedString);
487
                    }
488
            }
489
                catch(BufferOverflowException exc) {
490
                        // Might happen for instance if charset is UTF16 and the
491
                        // limit of characters in the field is an odd number
492
                        throw new UnsupportedEncodingException(exc);
493
                }
494
    }
495
    
496
    /**
497
     * Returns a safely padded (and potentially truncated) string 
498
     * 
499
     * This may truncate some record, but it is required to ensure
500
     * that the field limit is not overflowed when using 
501
     * variable-length charsets such as UTF-8.
502
     * 
503
     * This implementation is not used but it is kept here for reference.
504
     * It is fully equivalent to the {@link #safeEncode(String, int, boolean)}
505
     * method and easier to understand, but this implementation is much
506
     * slower for any multibyte charset (such as UTF-8).
507
     *  
508
     * @throws UnsupportedEncodingException 
509
     */
510
    private void safeEncodeSlow(String in, int limit, boolean rightPadding) throws UnsupportedEncodingException {
511
            try {
512
                    byte[] encodedString = in.getBytes(this.charset);
513
                    while (encodedString.length>limit) {
514
                            // too long, truncating
515
                            if (rightPadding) {
516
                                    in = in.substring(0, in.length()-1);
517
                                    encodedString = in.getBytes(charset);
518
                            }
519
                            else {
520
                                    in.substring(1, in.length());
521
                                    encodedString = in.getBytes(charset);
522
                            }
523
                    }
524
                    if (rightPadding) {
525
                            buffer.put(encodedString);
526
                    }
527
                    if (encodedString.length<limit) {
528
                            // too short, padding
529
                            int i = encodedString.length;
530
                            while (i<limit) {
531
                                    blank.position(0);
532
                                    buffer.put(blank);
533
                                    i=i+blankSize;
534
                            }
535
                            if (i>limit) {
536
                                    throw new UnsupportedEncodingException(new Exception("Impossible to encode this DBF using the selected charset"));
537
                            }
538
                    }
539
                    if (!rightPadding) {
540
                            buffer.put(encodedString);
541
                    }
542
            }
543
            catch(BufferOverflowException exc) {
544
                    // Might happen for instance if charset is UTF16 and the
545
                    // limit of characters in the field is an odd number
546
                    throw new UnsupportedEncodingException(exc);
547
            }
548
    }
549

    
550

    
551
    /**
552
     * Release resources associated with this writer. <B>Highly recommended</B>
553
     * 
554
     * @throws CloseException
555
     * @throws IOException
556
     *             If errors occur.
557
     */
558
    public void close() throws CloseException {
559
        // IANS - GEOT 193, bogus 0x00 written. According to dbf spec, optional
560
        // eof 0x1a marker is, well, optional. Since the original code wrote a
561
        // 0x00 (which is wrong anyway) lets just do away with this :)
562
        // - produced dbf works in OpenOffice and ArcExplorer java, so it must
563
        // be okay.
564
        // buffer.position(0);
565
        // buffer.put((byte) 0).position(0).limit(1);
566
        // write();
567

    
568
        if (headDrity) {
569
            try {
570
                this.writeHeader();
571
            } catch (WriteException e) {
572
                throw new CloseException("DbaseFileWriter", e);
573
            }
574
        }
575

    
576
        try {
577
            channel.close();
578
        } catch (IOException e) {
579
            throw new CloseException("DBF Writer", e);
580
        }
581
        if (buffer instanceof MappedByteBuffer) {
582
            // NIOUtilities.clean(buffer);
583
        }
584

    
585
        buffer = null;
586
        channel = null;
587
        formatter = null;
588
    }
589

    
590
    /** Utility for formatting Dbase fields. */
591
    public static class FieldFormatter {
592

    
593
        private StringBuffer buffer = new StringBuffer(255);
594
        private NumberFormat numFormat = NumberFormat
595
            .getNumberInstance(Locale.US);
596
        private Calendar calendar = Calendar.getInstance(Locale.US);
597
        private String emtpyString;
598
        private static final int MAXCHARS = 255;
599

    
600
        public FieldFormatter() {
601
            // Avoid grouping on number format
602
            numFormat.setGroupingUsed(false);
603

    
604
            // build a 255 white spaces string
605
            StringBuffer sb = new StringBuffer(MAXCHARS);
606
            sb.setLength(MAXCHARS);
607
            for (int i = 0; i < MAXCHARS; i++) {
608
                sb.setCharAt(i, ' ');
609
            }
610

    
611
            emtpyString = sb.toString();
612
        }
613

    
614
        public String getFieldString(int size, String s) {
615
            buffer.replace(0, size, emtpyString);
616
            buffer.setLength(size);
617

    
618
            if (s != null) {
619
                buffer.replace(0, size, s);
620
                if (s.length() <= size) {
621
                    for (int i = s.length(); i < size; i++) {
622
                        buffer.append(' ');
623
                    }
624
                }
625
            }
626

    
627
            buffer.setLength(size);
628
            return buffer.toString();
629
        }
630

    
631
        public String getFieldString(Date d) {
632

    
633
            if (d != null) {
634
                buffer.delete(0, buffer.length());
635

    
636
                calendar.setTime(d);
637
                int year = calendar.get(Calendar.YEAR);
638
                int month = calendar.get(Calendar.MONTH) + 1; // returns 0 based
639
                                                              // month?
640
                int day = calendar.get(Calendar.DAY_OF_MONTH);
641

    
642
                if (year < 1000) {
643
                    if (year >= 100) {
644
                        buffer.append("0");
645
                    } else
646
                        if (year >= 10) {
647
                            buffer.append("00");
648
                        } else {
649
                            buffer.append("000");
650
                        }
651
                }
652
                buffer.append(year);
653

    
654
                if (month < 10) {
655
                    buffer.append("0");
656
                }
657
                buffer.append(month);
658

    
659
                if (day < 10) {
660
                    buffer.append("0");
661
                }
662
                buffer.append(day);
663
            } else {
664
                buffer.setLength(8);
665
                buffer.replace(0, 8, emtpyString);
666
            }
667

    
668
            buffer.setLength(8);
669
            return buffer.toString();
670
        }
671

    
672
        public String getFieldString(int size, int decimalPlaces, double n) {
673
            buffer.delete(0, buffer.length());
674

    
675
            numFormat.setMaximumFractionDigits(decimalPlaces);
676
            numFormat.setMinimumFractionDigits(decimalPlaces);
677
            numFormat.format(n, buffer, new FieldPosition(
678
                NumberFormat.INTEGER_FIELD));
679
            return buffer.toString();
680
        }
681
    }
682

    
683
    public void setCharset(Charset charset) {
684
        this.charset = charset;
685
            blank = charset.encode(" ");
686
            blankSize = blank.limit();
687
    }
688

    
689
}