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

History | View | Annotate | Download (23.8 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

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

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

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

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

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

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

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

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

    
153
        this.headDrity = true;
154
    }
155

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

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

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

    
191
    private void moveTo(long numReg) throws IOException {
192
        // if (!(channel instanceof FileChannel)) {
193
        // throw new IOException(
194
        // "DbaseFileWriterNIO: channel is not a FileChannel. Cannot position properly");
195
        // }
196

    
197
        long newPos =
198
            header.getHeaderLength() + numReg * header.getRecordLength();
199
        if (this.channel.position() != newPos) {
200
            this.channel.position(newPos);
201
        }
202
    }
203

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

    
216
        try {
217
            this.moveTo(numReg);
218
        } catch (IOException e) {
219
            throw new WriteException("DbaseFileWriter", e);
220
        }
221

    
222
        write();
223
    }
224

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

    
282
    }
283

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

    
339
        }
340

    
341
    }
342

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

360
                    if (rightPadding) {
361
                            in = in.substring(0, in.length()-1);
362
                            encodedString = in.getBytes(charset);
363
                    }
364
                    else {
365
                            in.substring(1, in.length());
366
                            encodedString = in.getBytes(charset);
367
                    }
368

369
                    However, the implemented algorithm has a much better performance
370
                    for the average and worst cases (when the input string has a lot
371
                    of multibyte characters), while keeping a good performance
372
                    for the best case (when all the characters in the input string
373
                    can be represented as single bytes using the selected charset).
374

375
                    The general strategy is to compute the deviation from the
376
                    required maximum number of bytes (limit) and the actual number
377
                    of bytes of the encoded String.
378

379
                    Then, we use this deviation to estimate the amount of characters
380
                    to truncate, based on the average factor of bytes per char in the
381
                    input string.
382

383
                    We truncate the string using this approach until the deviation
384
                    gets stable.
385

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

    
540

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

    
558
        if (headDrity) {
559
            try {
560
                this.writeHeader();
561
            } catch (WriteException e) {
562
                throw new CloseException("DbaseFileWriter", e);
563
            }
564
        }
565

    
566
        try {
567
            channel.close();
568
        } catch (IOException e) {
569
            throw new CloseException("DBF Writer", e);
570
        }
571
        if (buffer instanceof MappedByteBuffer) {
572
            // NIOUtilities.clean(buffer);
573
        }
574

    
575
        buffer = null;
576
        channel = null;
577
        formatter = null;
578
    }
579

    
580
    /** Utility for formatting Dbase fields. */
581
    public static class FieldFormatter {
582

    
583
        private StringBuffer buffer = new StringBuffer(255);
584
        private NumberFormat numFormat = NumberFormat
585
            .getNumberInstance(Locale.US);
586
        private Calendar calendar = Calendar.getInstance(Locale.US);
587
        private String emtpyString;
588
        private static final int MAXCHARS = 255;
589

    
590
        public FieldFormatter() {
591
            // Avoid grouping on number format
592
            numFormat.setGroupingUsed(false);
593

    
594
            // build a 255 white spaces string
595
            StringBuffer sb = new StringBuffer(MAXCHARS);
596
            sb.setLength(MAXCHARS);
597
            for (int i = 0; i < MAXCHARS; i++) {
598
                sb.setCharAt(i, ' ');
599
            }
600

    
601
            emtpyString = sb.toString();
602
        }
603

    
604
        public String getFieldString(int size, String s) {
605
            buffer.replace(0, size, emtpyString);
606
            buffer.setLength(size);
607

    
608
            if (s != null) {
609
                buffer.replace(0, size, s);
610
                if (s.length() <= size) {
611
                    for (int i = s.length(); i < size; i++) {
612
                        buffer.append(' ');
613
                    }
614
                }
615
            }
616

    
617
            buffer.setLength(size);
618
            return buffer.toString();
619
        }
620

    
621
        public String getFieldString(Date d) {
622

    
623
            if (d != null) {
624
                buffer.delete(0, buffer.length());
625

    
626
                calendar.setTime(d);
627
                int year = calendar.get(Calendar.YEAR);
628
                int month = calendar.get(Calendar.MONTH) + 1; // returns 0 based
629
                                                              // month?
630
                int day = calendar.get(Calendar.DAY_OF_MONTH);
631

    
632
                if (year < 1000) {
633
                    if (year >= 100) {
634
                        buffer.append("0");
635
                    } else
636
                        if (year >= 10) {
637
                            buffer.append("00");
638
                        } else {
639
                            buffer.append("000");
640
                        }
641
                }
642
                buffer.append(year);
643

    
644
                if (month < 10) {
645
                    buffer.append("0");
646
                }
647
                buffer.append(month);
648

    
649
                if (day < 10) {
650
                    buffer.append("0");
651
                }
652
                buffer.append(day);
653
            } else {
654
                buffer.setLength(8);
655
                buffer.replace(0, 8, emtpyString);
656
            }
657

    
658
            buffer.setLength(8);
659
            return buffer.toString();
660
        }
661

    
662
        public String getFieldString(int size, int decimalPlaces, double n) {
663
            buffer.delete(0, buffer.length());
664

    
665
            numFormat.setMaximumFractionDigits(decimalPlaces);
666
            numFormat.setMinimumFractionDigits(decimalPlaces);
667
            numFormat.format(n, buffer, new FieldPosition(
668
                NumberFormat.INTEGER_FIELD));
669
            return buffer.toString();
670
        }
671
    }
672

    
673
    public void setCharset(Charset charset) {
674
        this.charset = charset;
675
            blank = charset.encode(" ");
676
            blankSize = blank.limit();
677
    }
678

    
679
}