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 @ 44189

History | View | Annotate | Download (24.3 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
        //
236
        // https://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm
237
        //
238
        if (DataTypes.BOOLEAN == type) {
239
            boolean b = feature.getBoolean(attr.getIndex());
240
            if (b) {
241
                fieldString = "T";
242
            } else {
243
                fieldString = "F";
244
            }
245
        } else
246
            if (DataTypes.BYTE == type) {
247
                fieldString = String.valueOf(feature.getByte(attr.getIndex()));
248
            } else
249
                if (DataTypes.DATE == type) {
250
                    Date date = feature.getDate(attr.getIndex());
251
                    fieldString = formatter.getFieldString(date);
252
                } else
253
                    if (DataTypes.DOUBLE == type) {
254
                        double d = feature.getDouble(attr.getIndex());
255
                        fieldString =
256
                            formatter.getFieldString(fieldLen,
257
                                header.getFieldDecimalCount(dbfFieldIndex), d);
258
                    } else
259
                        if (DataTypes.FLOAT == type) {
260
                            float f = feature.getFloat(attr.getIndex());
261
                            fieldString =
262
                                formatter.getFieldString(fieldLen,
263
                                    header.getFieldDecimalCount(dbfFieldIndex),
264
                                    f);
265
                        } else
266
                            if (DataTypes.INT == type) {
267
                                int integer = feature.getInt(attr.getIndex());
268
                                fieldString =
269
                                    formatter.getFieldString(fieldLen, header
270
                                        .getFieldDecimalCount(dbfFieldIndex),
271
                                        integer);
272
                            } else
273
                                if (DataTypes.LONG == type) {
274
                                    long l = feature.getLong(attr.getIndex());
275
                                    fieldString =
276
                                        formatter
277
                                            .getFieldString(
278
                                                fieldLen,
279
                                                header
280
                                                    .getFieldDecimalCount(dbfFieldIndex),
281
                                                l);
282
                                } else
283
                                    if (DataTypes.STRING == type) {
284
                                        String s =
285
                                            feature.getString(attr.getIndex());
286
                                        return s;
287
                                    }
288
        return fieldString;
289

    
290
    }
291

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

    
352
        }
353

    
354
    }
355

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

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

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

388
                    The general strategy is to compute the deviation from the
389
                    required maximum number of bytes (limit) and the actual number
390
                    of bytes of the encoded String.
391

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

396
                    We truncate the string using this approach until the deviation
397
                    gets stable.
398

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

    
553

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

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

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

    
588
        buffer = null;
589
        channel = null;
590
        formatter = null;
591
    }
592

    
593
    /** Utility for formatting Dbase fields. */
594
    public static class FieldFormatter {
595

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

    
603
        public FieldFormatter() {
604
            // Avoid grouping on number format
605
            numFormat.setGroupingUsed(false);
606

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

    
614
            emtpyString = sb.toString();
615
        }
616

    
617
        public String getFieldString(int size, String s) {
618
            buffer.replace(0, size, emtpyString);
619
            buffer.setLength(size);
620

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

    
630
            buffer.setLength(size);
631
            return buffer.toString();
632
        }
633

    
634
        public String getFieldString(Date d) {
635

    
636
            if (d != null) {
637
                buffer.delete(0, buffer.length());
638

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

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

    
657
                if (month < 10) {
658
                    buffer.append("0");
659
                }
660
                buffer.append(month);
661

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

    
671
            buffer.setLength(8);
672
            return buffer.toString();
673
        }
674

    
675
        public String getFieldString(int size, int decimalPlaces, double n) {
676
            buffer.delete(0, buffer.length());
677

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

    
686
    public void setCharset(Charset charset) {
687
        this.charset = charset;
688
            blank = charset.encode(" ");
689
            blankSize = blank.limit();
690
    }
691

    
692
}