Revision 44669 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
DbaseFileWriter.java | ||
---|---|---|
24 | 24 |
package org.gvsig.fmap.dal.store.dbf.utils; |
25 | 25 |
|
26 | 26 |
import java.io.IOException; |
27 |
import java.math.BigDecimal; |
|
27 | 28 |
import java.nio.Buffer; |
28 | 29 |
import java.nio.BufferOverflowException; |
29 | 30 |
import java.nio.ByteBuffer; |
30 | 31 |
import java.nio.MappedByteBuffer; |
31 | 32 |
import java.nio.channels.FileChannel; |
32 | 33 |
import java.nio.charset.Charset; |
33 |
import java.text.FieldPosition; |
|
34 |
import java.text.NumberFormat; |
|
35 |
import java.util.Arrays; |
|
36 |
import java.util.Calendar; |
|
37 | 34 |
import java.util.Date; |
38 | 35 |
import java.util.Iterator; |
39 |
import java.util.Locale; |
|
40 | 36 |
import org.apache.commons.lang3.StringUtils; |
41 | 37 |
|
42 | 38 |
import org.gvsig.fmap.dal.DataTypes; |
... | ... | |
60 | 56 |
* w.close(); |
61 | 57 |
* </PRE></CODE> You must supply the <CODE>moreRecords</CODE> and |
62 | 58 |
* <CODE>getMyRecord()</CODE> logic... |
63 |
*
|
|
59 |
* |
|
64 | 60 |
* @author Ian Schneider |
65 | 61 |
*/ |
66 | 62 |
public class DbaseFileWriter { |
67 | 63 |
|
68 |
private DbaseFileHeader header; |
|
69 |
private DbaseFileWriter.FieldFormatter formatter = |
|
70 |
new DbaseFileWriter.FieldFormatter(); |
|
71 |
FileChannel channel; |
|
64 |
private final DbaseFileHeader header; |
|
65 |
private FieldFormatter formatter = new FieldFormatter(); |
|
66 |
private FileChannel channel; |
|
72 | 67 |
private ByteBuffer buffer; |
73 | 68 |
private boolean headDrity = false; |
74 | 69 |
private ByteBuffer blank; |
75 | 70 |
private int blankSize; |
76 |
|
|
77 |
//private Charset charset = Charset.forName("ISO-8859-1"); |
|
71 |
|
|
78 | 72 |
private Charset charset; |
79 | 73 |
|
80 | 74 |
/** |
81 | 75 |
* Create a DbaseFileWriter using the specified header and writing to the |
82 | 76 |
* given channel. |
83 |
* |
|
84 |
* @param header |
|
85 |
* The DbaseFileHeader to write. |
|
86 |
* @param out |
|
87 |
* The Channel to write to. |
|
88 |
* |
|
89 |
* |
|
90 |
* @throws InitializeWriterException |
|
91 |
* @throws IOException |
|
92 |
* If errors occur while initializing. |
|
77 |
* |
|
78 |
* @param header The DbaseFileHeader to write. |
|
79 |
* @param out The Channel to write to. |
|
80 |
* @param isNew |
|
81 |
* @throws org.gvsig.fmap.dal.exception.InitializeException |
|
82 |
* |
|
83 |
* |
|
93 | 84 |
*/ |
94 | 85 |
public DbaseFileWriter(DbaseFileHeader header, FileChannel out, |
95 |
boolean isNew) throws InitializeException { |
|
86 |
boolean isNew) throws InitializeException {
|
|
96 | 87 |
this.header = header; |
97 | 88 |
this.channel = out; |
98 | 89 |
this.headDrity = isNew; |
99 | 90 |
this.setCharset(Charset.forName(header.mappingEncoding(header.getCharsetName()))); |
100 |
|
|
91 |
|
|
101 | 92 |
init(); |
102 | 93 |
} |
103 | 94 |
|
... | ... | |
113 | 104 |
} |
114 | 105 |
|
115 | 106 |
private void write() throws WriteException { |
116 |
((Buffer)buffer).position(0); |
|
107 |
((Buffer) buffer).position(0);
|
|
117 | 108 |
int r = buffer.remaining(); |
118 | 109 |
try { |
119 | 110 |
while ((r -= channel.write(buffer)) > 0) { |
120 |
; // do nothing
|
|
111 |
// do nothing |
|
121 | 112 |
} |
122 | 113 |
} catch (IOException e) { |
123 | 114 |
throw new WriteException("DBF Writer", e); |
... | ... | |
127 | 118 |
private void writeHeader() throws WriteException { |
128 | 119 |
try { |
129 | 120 |
channel.position(0); |
130 |
header.writeHeader(channel);
|
|
121 |
header.write(channel); |
|
131 | 122 |
} catch (IOException e) { |
132 | 123 |
throw new WriteException("DBF Writer", e); |
133 | 124 |
} |
... | ... | |
135 | 126 |
|
136 | 127 |
/** |
137 | 128 |
* Write a single dbase record. |
138 |
* |
|
139 |
* @param record |
|
140 |
* The entries to write. |
|
129 |
* |
|
130 |
* @param feature |
|
141 | 131 |
* @throws UnsupportedEncodingException |
142 | 132 |
* @throws WriteException |
143 | 133 |
*/ |
144 | 134 |
public void append(Feature feature) throws WriteException, |
145 |
UnsupportedEncodingException { |
|
135 |
UnsupportedEncodingException {
|
|
146 | 136 |
this.fillBuffer(feature); |
147 | 137 |
try { |
148 | 138 |
this.moveToEOF(); |
... | ... | |
156 | 146 |
} |
157 | 147 |
|
158 | 148 |
private void fillBuffer(Feature feature) |
159 |
throws UnsupportedEncodingException, WriteException { |
|
149 |
throws UnsupportedEncodingException, WriteException {
|
|
160 | 150 |
FeatureType featureType = feature.getType(); |
161 | 151 |
try { |
162 |
((Buffer)buffer).position(0); |
|
152 |
((Buffer) buffer).position(0);
|
|
163 | 153 |
|
164 | 154 |
// put the 'not-deleted' marker |
165 | 155 |
buffer.put((byte) ' '); |
166 | 156 |
|
167 | 157 |
@SuppressWarnings("unchecked") |
168 |
Iterator<FeatureAttributeDescriptor> iterator =
|
|
169 |
featureType.iterator(); |
|
170 |
|
|
158 |
Iterator<FeatureAttributeDescriptor> iterator |
|
159 |
= featureType.iterator();
|
|
160 |
|
|
171 | 161 |
while (iterator.hasNext()) { |
172 | 162 |
FeatureAttributeDescriptor fad = iterator.next(); |
173 | 163 |
if (fad.isComputed()) { |
174 | 164 |
continue; |
175 | 165 |
} |
176 |
|
|
166 |
|
|
177 | 167 |
if (fad.getName().length() > DbaseFile.MAX_FIELD_NAME_LENGTH) { |
178 | 168 |
throw new FieldNameTooLongException( |
179 |
"DBF file", fad.getName()); |
|
169 |
"DBF file", fad.getName());
|
|
180 | 170 |
} |
181 |
|
|
171 |
|
|
182 | 172 |
int type = fad.getType(); |
183 | 173 |
if (type == DataTypes.GEOMETRY) { |
184 | 174 |
continue; |
... | ... | |
189 | 179 |
throw new WriteException("DbaseFileWriter", e); |
190 | 180 |
} |
191 | 181 |
} |
192 |
|
|
182 |
|
|
193 | 183 |
private void moveToEOF() throws IOException { |
194 | 184 |
this.moveTo(this.header.getNumRecords()); |
195 | 185 |
} |
... | ... | |
200 | 190 |
// "DbaseFileWriterNIO: channel is not a FileChannel. Cannot position properly"); |
201 | 191 |
// } |
202 | 192 |
|
203 |
long newPos =
|
|
204 |
header.getHeaderLength() + numReg * header.getRecordLength(); |
|
193 |
long newPos |
|
194 |
= header.getHeaderLength() + numReg * header.getRecordLength();
|
|
205 | 195 |
if (this.channel.position() != newPos) { |
206 | 196 |
this.channel.position(newPos); |
207 | 197 |
} |
... | ... | |
209 | 199 |
|
210 | 200 |
/** |
211 | 201 |
* Write a single dbase record. Useful to update a dbf. |
212 |
*
|
|
213 |
* @param record
|
|
214 |
* The entries to write.
|
|
202 |
* |
|
203 |
* @param feature
|
|
204 |
* @param numReg
|
|
215 | 205 |
* @throws WriteException |
216 | 206 |
* @throws UnsupportedEncodingException |
217 | 207 |
*/ |
218 | 208 |
public void update(Feature feature, long numReg) throws WriteException, |
219 |
UnsupportedEncodingException { |
|
209 |
UnsupportedEncodingException {
|
|
220 | 210 |
this.fillBuffer(feature); |
221 | 211 |
|
222 | 212 |
try { |
... | ... | |
228 | 218 |
write(); |
229 | 219 |
} |
230 | 220 |
|
231 |
private String fieldString(FeatureAttributeDescriptor attr, Feature feature) throws java.io.UnsupportedEncodingException { |
|
221 |
private void encodeField(FeatureAttributeDescriptor attr, Feature feature) throws java.io.UnsupportedEncodingException, UnsupportedEncodingException { |
|
222 |
DbaseFieldDescriptor descriptor = this.header.getFieldDescription(attr.getName()); |
|
223 |
|
|
232 | 224 |
int type = attr.getType(); |
233 |
int dbfFieldIndex = this.header.getFieldIndex(attr.getName()); |
|
234 |
final int fieldLen = header.getFieldLength(dbfFieldIndex); |
|
235 |
String fieldString = ""; |
|
236 |
// |
|
237 |
// https://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm |
|
238 |
// |
|
225 |
final int fieldLen = descriptor.getSize(); |
|
226 |
String fieldString; |
|
227 |
|
|
228 |
// if( buffer.position()!=descriptor.getOffsetInRecord() ) { |
|
229 |
// throw new RuntimeException("Encoding field '"+descriptor.getName()+"' found an incorrect offset."); |
|
230 |
// } |
|
231 |
if( feature.isNull(attr.getIndex()) ) { |
|
232 |
safeEncode(" ", fieldLen, false); |
|
233 |
return; |
|
234 |
} |
|
239 | 235 |
if (DataTypes.BOOLEAN == type) { |
240 | 236 |
boolean b = feature.getBoolean(attr.getIndex()); |
241 |
if (b) { |
|
242 |
fieldString = "T"; |
|
243 |
} else { |
|
244 |
fieldString = "F"; |
|
245 |
} |
|
246 |
} else |
|
247 |
if (DataTypes.BYTE == type) { |
|
248 |
fieldString = String.valueOf(feature.getByte(attr.getIndex())); |
|
249 |
} else |
|
250 |
if (DataTypes.DATE == type) { |
|
251 |
Date date = feature.getDate(attr.getIndex()); |
|
252 |
fieldString = formatter.getFieldString(date); |
|
253 |
} else |
|
254 |
if (DataTypes.DOUBLE == type) { |
|
255 |
double d = feature.getDouble(attr.getIndex()); |
|
256 |
fieldString = |
|
257 |
formatter.getFieldString(fieldLen, |
|
258 |
header.getFieldDecimalCount(dbfFieldIndex), d); |
|
259 |
} else |
|
260 |
if (DataTypes.FLOAT == type) { |
|
261 |
float f = feature.getFloat(attr.getIndex()); |
|
262 |
fieldString = |
|
263 |
formatter.getFieldString(fieldLen, |
|
264 |
header.getFieldDecimalCount(dbfFieldIndex), |
|
265 |
f); |
|
266 |
} else |
|
267 |
if (DataTypes.INT == type) { |
|
268 |
int integer = feature.getInt(attr.getIndex()); |
|
269 |
fieldString = |
|
270 |
formatter.getFieldString(fieldLen, header |
|
271 |
.getFieldDecimalCount(dbfFieldIndex), |
|
272 |
integer); |
|
273 |
} else |
|
274 |
if (DataTypes.LONG == type) { |
|
275 |
long l = feature.getLong(attr.getIndex()); |
|
276 |
fieldString = |
|
277 |
formatter |
|
278 |
.getFieldString( |
|
279 |
fieldLen, |
|
280 |
header |
|
281 |
.getFieldDecimalCount(dbfFieldIndex), |
|
282 |
l); |
|
283 |
} else |
|
284 |
if (DataTypes.STRING == type) { |
|
285 |
String s = |
|
286 |
feature.getString(attr.getIndex()); |
|
287 |
return s; |
|
288 |
} |
|
289 |
return fieldString; |
|
237 |
safeEncode(b? "T":"F", 1, true); |
|
290 | 238 |
|
291 |
} |
|
239 |
} else if (DataTypes.TIME == type) { |
|
240 |
Date date = feature.getDate(attr.getIndex()); |
|
241 |
fieldString = formatter.formatTime(date); |
|
242 |
safeEncode(fieldString, fieldLen, false); |
|
292 | 243 |
|
293 |
private void encodeField(FeatureAttributeDescriptor attr, Feature feature) throws java.io.UnsupportedEncodingException, UnsupportedEncodingException { |
|
294 |
int type = attr.getType(); |
|
295 |
int dbfFieldIndex = this.header.getFieldIndex(attr.getName()); |
|
296 |
final int fieldLen = header.getFieldLength(dbfFieldIndex); |
|
297 |
String fieldString = ""; |
|
298 |
|
|
299 |
if( DataTypes.BOOLEAN == type ) { |
|
300 |
boolean b = feature.getBoolean(attr.getIndex()); |
|
301 |
if( b ) { |
|
302 |
safeEncode("T", 1, true); |
|
303 |
} else { |
|
304 |
safeEncode("F", 1, true); |
|
305 |
} |
|
306 |
|
|
307 |
} else if( DataTypes.BYTE == type ) { |
|
308 |
fieldString = String.valueOf(feature.getByte(attr.getIndex())); |
|
309 |
safeEncode(fieldString, 8, false); |
|
310 |
|
|
311 |
} else if( DataTypes.DATE == type ) { |
|
244 |
} else if (DataTypes.TIMESTAMP == type) { |
|
312 | 245 |
Date date = feature.getDate(attr.getIndex()); |
313 |
fieldString = formatter.getFieldString(date); |
|
314 |
safeEncode(fieldString, 8, false); |
|
315 |
|
|
316 |
} else if( DataTypes.DOUBLE == type ) { |
|
317 |
double d = feature.getDouble(attr.getIndex()); |
|
318 |
fieldString = formatter.getFieldString( |
|
319 |
fieldLen, header.getFieldDecimalCount(dbfFieldIndex), d |
|
320 |
); |
|
246 |
fieldString = formatter.formatTimestamp(date); |
|
321 | 247 |
safeEncode(fieldString, fieldLen, false); |
322 |
|
|
323 |
} else if( DataTypes.FLOAT == type ) { |
|
324 |
float f = feature.getFloat(attr.getIndex()); |
|
325 |
fieldString = formatter.getFieldString( |
|
326 |
fieldLen, header.getFieldDecimalCount(dbfFieldIndex), f |
|
327 |
); |
|
248 |
|
|
249 |
} else if (DataTypes.DATE == type) { |
|
250 |
Date date = feature.getDate(attr.getIndex()); |
|
251 |
fieldString = formatter.formatDate(date); |
|
328 | 252 |
safeEncode(fieldString, fieldLen, false); |
329 |
|
|
330 |
} else if( DataTypes.INT == type ) { |
|
331 |
int integer = feature.getInt(attr.getIndex()); |
|
332 |
fieldString = formatter.getFieldString( |
|
333 |
fieldLen, header.getFieldDecimalCount(dbfFieldIndex), integer |
|
334 |
); |
|
253 |
|
|
254 |
} else if (DataTypes.DECIMAL == type) { |
|
255 |
BigDecimal n = feature.getDecimal(attr.getIndex()); |
|
256 |
fieldString = formatter.format(n, fieldLen); |
|
335 | 257 |
safeEncode(fieldString, fieldLen, false); |
336 |
|
|
337 |
} else if( DataTypes.LONG == type ) { |
|
258 |
|
|
259 |
} else if (DataTypes.DOUBLE == type) { |
|
260 |
double n = feature.getDouble(attr.getIndex()); |
|
261 |
fieldString = formatter.format(n, fieldLen, descriptor.getScale()); |
|
262 |
safeEncode(fieldString, fieldLen, false); |
|
263 |
|
|
264 |
} else if (DataTypes.FLOAT == type) { |
|
265 |
float n = feature.getFloat(attr.getIndex()); |
|
266 |
fieldString = formatter.format(n, fieldLen, descriptor.getScale()); |
|
267 |
safeEncode(fieldString, fieldLen, false); |
|
268 |
|
|
269 |
} else if (DataTypes.LONG == type) { |
|
338 | 270 |
long l = feature.getLong(attr.getIndex()); |
339 |
fieldString = formatter.getFieldString( |
|
340 |
fieldLen, header.getFieldDecimalCount(dbfFieldIndex),l |
|
341 |
); |
|
271 |
fieldString = formatter.format(l, fieldLen, descriptor.getScale()); |
|
342 | 272 |
safeEncode(fieldString, fieldLen, false); |
343 |
|
|
344 |
} else if( DataTypes.STRING == type ) { |
|
273 |
|
|
274 |
} else if (DataTypes.INT == type) { |
|
275 |
int n = feature.getInt(attr.getIndex()); |
|
276 |
fieldString = formatter.format(n, fieldLen, descriptor.getScale()); |
|
277 |
safeEncode(fieldString, fieldLen, false); |
|
278 |
|
|
279 |
} else if (DataTypes.BYTE == type) { |
|
280 |
int n = feature.getInt(attr.getIndex()); |
|
281 |
fieldString = formatter.format(n, fieldLen); |
|
282 |
safeEncode(fieldString, fieldLen, false); |
|
283 |
|
|
284 |
} else if (DataTypes.STRING == type) { |
|
345 | 285 |
String s = feature.getString(attr.getIndex()); |
346 | 286 |
safeEncode(StringUtils.defaultIfEmpty(s, ""), fieldLen, true); |
347 |
|
|
287 |
|
|
348 | 288 |
} else { |
349 | 289 |
// Si no conocemos el tipo intentamos guardarlo como un string |
350 | 290 |
String s = feature.getString(attr.getIndex()); |
... | ... | |
355 | 295 |
} |
356 | 296 |
|
357 | 297 |
/** |
358 |
* Returns a safely padded (and potentially truncated) string |
|
359 |
* |
|
360 |
* This may truncate some record, but it is required to ensure |
|
361 |
* that the field limit is not overflowed when using |
|
362 |
* variable-length charsets such as UTF-8. |
|
363 |
* @throws UnsupportedEncodingException |
|
298 |
* Returns a safely padded (and potentially truncated) string |
|
299 |
* |
|
300 |
* This may truncate some record, but it is required to ensure that the |
|
301 |
* field limit is not overflowed when using variable-length charsets such as |
|
302 |
* UTF-8. |
|
303 |
* |
|
304 |
* @throws UnsupportedEncodingException |
|
364 | 305 |
*/ |
365 | 306 |
private void safeEncode(String in, int limit, boolean rightPadding) throws UnsupportedEncodingException { |
366 |
try {
|
|
367 |
byte[] encodedString = in.getBytes(this.charset);
|
|
368 |
if (encodedString.length>limit) {
|
|
369 |
// too long, truncating
|
|
370 |
/*
|
|
307 |
try {
|
|
308 |
byte[] encodedString = in.getBytes(this.charset);
|
|
309 |
if (encodedString.length > limit) {
|
|
310 |
// too long, truncating
|
|
311 |
/*
|
|
371 | 312 |
* The block code bellow is equivalent to this simple code |
372 | 313 |
* fragment: |
373 | 314 |
|
... | ... | |
400 | 341 |
Finally, as we should be close enough to the right truncation position, |
401 | 342 |
we increment/decrement the truncated string by only 1 character, to |
402 | 343 |
ensure we truncate in the exact position. |
403 |
*/ |
|
404 |
String str = in; |
|
405 |
int estimatedDiff, deviation; |
|
406 |
int deviationPrev; |
|
407 |
double ratio; |
|
408 |
byte[] encodedChar; |
|
409 |
int truncatePos = 0; |
|
410 |
deviation = encodedString.length - limit; |
|
411 |
deviationPrev = deviation - 1; |
|
412 |
while(Math.abs(deviation)>Math.abs(deviationPrev) && str.length()>0) { |
|
413 |
ratio = ((double)encodedString.length) / ((double)str.length()); |
|
414 |
// apply the estimated diff, ensuring it is at least >= 1.0 in absolute value |
|
415 |
estimatedDiff = Math.max((int)(((double)deviation)/ratio), (int)(Math.signum(deviation)*1)); |
|
416 |
// too long, truncating |
|
417 |
if (rightPadding) { |
|
418 |
truncatePos = Math.max(str.length()-estimatedDiff, 0); |
|
419 |
str = in.substring(0, truncatePos); |
|
420 |
} |
|
421 |
else { |
|
422 |
truncatePos = Math.max(truncatePos + estimatedDiff, 0); |
|
423 |
str = in.substring(truncatePos); |
|
424 |
} |
|
425 |
encodedString = str.getBytes(charset); |
|
426 |
deviationPrev = deviation; |
|
427 |
deviation = encodedString.length - limit; |
|
428 |
} |
|
429 |
// now we are close enough, get the exact position for truncating |
|
430 |
while (encodedString.length>limit) { |
|
431 |
// too long, truncating |
|
432 |
// System.out.println("truncating"); |
|
433 |
if (rightPadding) { |
|
434 |
str = in.substring(0, str.length()-1); |
|
435 |
} |
|
436 |
else { |
|
437 |
truncatePos = truncatePos + 1; |
|
438 |
str = in.substring(truncatePos); |
|
439 |
} |
|
440 |
encodedString = str.getBytes(charset); |
|
441 |
} |
|
442 |
while (encodedString.length<limit && str.length()<in.length()) { |
|
443 |
// Extend if necessary: |
|
444 |
// 1 - Get the length in bytes of the next char |
|
445 |
// 2 - Add the char to the substring if we are still within the limits |
|
446 |
// System.out.println("extending"); |
|
447 |
if (rightPadding) { |
|
448 |
encodedChar = in.substring(str.length(), str.length()+1).getBytes(charset); |
|
449 |
} |
|
450 |
else { |
|
451 |
encodedChar = in.substring(truncatePos-1, truncatePos).getBytes(charset); |
|
452 |
// System.out.println(encodedChar); |
|
453 |
// System.out.println(encodedChar.length); |
|
454 |
// System.out.println(testStrings[i].substring(truncatePos-1, truncatePos)); |
|
455 |
} |
|
456 |
// System.out.println(testStrings[i].substring(in.length(), in.length()+1)); |
|
457 |
if ((encodedString.length + encodedChar.length)>limit) { |
|
458 |
// one more char would overflow the limit |
|
459 |
break; |
|
460 |
} |
|
461 |
// too short, extending |
|
462 |
if (rightPadding) { |
|
463 |
str = in.substring(0, str.length()+1); |
|
464 |
} |
|
465 |
else { |
|
466 |
truncatePos = truncatePos - 1; |
|
467 |
str = in.substring(truncatePos); |
|
468 |
} |
|
469 |
encodedString = str.getBytes(charset); |
|
470 |
} |
|
471 |
} |
|
472 |
if (rightPadding) { |
|
473 |
buffer.put(encodedString); |
|
474 |
} |
|
475 |
if (encodedString.length<limit) { |
|
476 |
// too short, padding |
|
477 |
int i = encodedString.length; |
|
478 |
while (i<limit) { |
|
479 |
((Buffer)blank).position(0); |
|
480 |
buffer.put(blank); |
|
481 |
i=i+blankSize; |
|
482 |
} |
|
483 |
if (i>limit) { |
|
484 |
// Might happen for instance if charset is UTF16 and the |
|
485 |
// limit of characters in the field is an odd number |
|
486 |
throw new UnsupportedEncodingException(new Exception("Impossible to encode this DBF using the selected charset")); |
|
487 |
} |
|
488 |
} |
|
489 |
if (!rightPadding) { |
|
490 |
buffer.put(encodedString); |
|
491 |
} |
|
492 |
} |
|
493 |
catch(BufferOverflowException exc) { |
|
494 |
// Might happen for instance if charset is UTF16 and the |
|
495 |
// limit of characters in the field is an odd number |
|
496 |
throw new UnsupportedEncodingException(exc); |
|
497 |
} |
|
344 |
*/ |
|
345 |
String str = in; |
|
346 |
int estimatedDiff, deviation; |
|
347 |
int deviationPrev; |
|
348 |
double ratio; |
|
349 |
byte[] encodedChar; |
|
350 |
int truncatePos = 0; |
|
351 |
deviation = encodedString.length - limit; |
|
352 |
deviationPrev = deviation - 1; |
|
353 |
while (Math.abs(deviation) > Math.abs(deviationPrev) && str.length() > 0) { |
|
354 |
ratio = ((double) encodedString.length) / ((double) str.length()); |
|
355 |
// apply the estimated diff, ensuring it is at least >= 1.0 in absolute value |
|
356 |
estimatedDiff = Math.max((int) (((double) deviation) / ratio), (int) (Math.signum(deviation) * 1)); |
|
357 |
// too long, truncating |
|
358 |
if (rightPadding) { |
|
359 |
truncatePos = Math.max(str.length() - estimatedDiff, 0); |
|
360 |
str = in.substring(0, truncatePos); |
|
361 |
} else { |
|
362 |
truncatePos = Math.max(truncatePos + estimatedDiff, 0); |
|
363 |
str = in.substring(truncatePos); |
|
364 |
} |
|
365 |
encodedString = str.getBytes(charset); |
|
366 |
deviationPrev = deviation; |
|
367 |
deviation = encodedString.length - limit; |
|
368 |
} |
|
369 |
// now we are close enough, get the exact position for truncating |
|
370 |
while (encodedString.length > limit) { |
|
371 |
// too long, truncating |
|
372 |
// System.out.println("truncating"); |
|
373 |
if (rightPadding) { |
|
374 |
str = in.substring(0, str.length() - 1); |
|
375 |
} else { |
|
376 |
truncatePos = truncatePos + 1; |
|
377 |
str = in.substring(truncatePos); |
|
378 |
} |
|
379 |
encodedString = str.getBytes(charset); |
|
380 |
} |
|
381 |
while (encodedString.length < limit && str.length() < in.length()) { |
|
382 |
// Extend if necessary: |
|
383 |
// 1 - Get the length in bytes of the next char |
|
384 |
// 2 - Add the char to the substring if we are still within the limits |
|
385 |
// System.out.println("extending"); |
|
386 |
if (rightPadding) { |
|
387 |
encodedChar = in.substring(str.length(), str.length() + 1).getBytes(charset); |
|
388 |
} else { |
|
389 |
encodedChar = in.substring(truncatePos - 1, truncatePos).getBytes(charset); |
|
390 |
// System.out.println(encodedChar); |
|
391 |
// System.out.println(encodedChar.length); |
|
392 |
// System.out.println(testStrings[i].substring(truncatePos-1, truncatePos)); |
|
393 |
} |
|
394 |
// System.out.println(testStrings[i].substring(in.length(), in.length()+1)); |
|
395 |
if ((encodedString.length + encodedChar.length) > limit) { |
|
396 |
// one more char would overflow the limit |
|
397 |
break; |
|
398 |
} |
|
399 |
// too short, extending |
|
400 |
if (rightPadding) { |
|
401 |
str = in.substring(0, str.length() + 1); |
|
402 |
} else { |
|
403 |
truncatePos = truncatePos - 1; |
|
404 |
str = in.substring(truncatePos); |
|
405 |
} |
|
406 |
encodedString = str.getBytes(charset); |
|
407 |
} |
|
408 |
} |
|
409 |
if (rightPadding) { |
|
410 |
buffer.put(encodedString); |
|
411 |
} |
|
412 |
if (encodedString.length < limit) { |
|
413 |
// too short, padding |
|
414 |
int i = encodedString.length; |
|
415 |
while (i < limit) { |
|
416 |
((Buffer) blank).position(0); |
|
417 |
buffer.put(blank); |
|
418 |
i = i + blankSize; |
|
419 |
} |
|
420 |
if (i > limit) { |
|
421 |
// Might happen for instance if charset is UTF16 and the |
|
422 |
// limit of characters in the field is an odd number |
|
423 |
throw new UnsupportedEncodingException(new Exception("Impossible to encode this DBF using the selected charset")); |
|
424 |
} |
|
425 |
} |
|
426 |
if (!rightPadding) { |
|
427 |
buffer.put(encodedString); |
|
428 |
} |
|
429 |
} catch (BufferOverflowException exc) { |
|
430 |
// Might happen for instance if charset is UTF16 and the |
|
431 |
// limit of characters in the field is an odd number |
|
432 |
throw new UnsupportedEncodingException(exc); |
|
433 |
} |
|
498 | 434 |
} |
499 |
|
|
500 |
/** |
|
501 |
* Returns a safely padded (and potentially truncated) string |
|
502 |
* |
|
503 |
* This may truncate some record, but it is required to ensure |
|
504 |
* that the field limit is not overflowed when using |
|
505 |
* variable-length charsets such as UTF-8. |
|
506 |
* |
|
507 |
* This implementation is not used but it is kept here for reference. |
|
508 |
* It is fully equivalent to the {@link #safeEncode(String, int, boolean)} |
|
509 |
* method and easier to understand, but this implementation is much |
|
510 |
* slower for any multibyte charset (such as UTF-8). |
|
511 |
* |
|
512 |
* @throws UnsupportedEncodingException |
|
513 |
*/ |
|
514 |
private void safeEncodeSlow(String in, int limit, boolean rightPadding) throws UnsupportedEncodingException { |
|
515 |
try { |
|
516 |
byte[] encodedString = in.getBytes(this.charset); |
|
517 |
while (encodedString.length>limit) { |
|
518 |
// too long, truncating |
|
519 |
if (rightPadding) { |
|
520 |
in = in.substring(0, in.length()-1); |
|
521 |
encodedString = in.getBytes(charset); |
|
522 |
} |
|
523 |
else { |
|
524 |
in.substring(1, in.length()); |
|
525 |
encodedString = in.getBytes(charset); |
|
526 |
} |
|
527 |
} |
|
528 |
if (rightPadding) { |
|
529 |
buffer.put(encodedString); |
|
530 |
} |
|
531 |
if (encodedString.length<limit) { |
|
532 |
// too short, padding |
|
533 |
int i = encodedString.length; |
|
534 |
while (i<limit) { |
|
535 |
((Buffer)blank).position(0); |
|
536 |
buffer.put(blank); |
|
537 |
i=i+blankSize; |
|
538 |
} |
|
539 |
if (i>limit) { |
|
540 |
throw new UnsupportedEncodingException(new Exception("Impossible to encode this DBF using the selected charset")); |
|
541 |
} |
|
542 |
} |
|
543 |
if (!rightPadding) { |
|
544 |
buffer.put(encodedString); |
|
545 |
} |
|
546 |
} |
|
547 |
catch(BufferOverflowException exc) { |
|
548 |
// Might happen for instance if charset is UTF16 and the |
|
549 |
// limit of characters in the field is an odd number |
|
550 |
throw new UnsupportedEncodingException(exc); |
|
551 |
} |
|
552 |
} |
|
553 | 435 |
|
554 | 436 |
|
555 | 437 |
/** |
556 | 438 |
* Release resources associated with this writer. <B>Highly recommended</B> |
557 |
*
|
|
439 |
* |
|
558 | 440 |
* @throws CloseException |
559 |
* @throws IOException |
|
560 |
* If errors occur. |
|
561 | 441 |
*/ |
562 | 442 |
public void close() throws CloseException { |
563 | 443 |
// IANS - GEOT 193, bogus 0x00 written. According to dbf spec, optional |
... | ... | |
591 | 471 |
formatter = null; |
592 | 472 |
} |
593 | 473 |
|
594 |
/** Utility for formatting Dbase fields. */ |
|
595 |
public static class FieldFormatter { |
|
596 |
|
|
597 |
private StringBuffer buffer = new StringBuffer(255); |
|
598 |
private NumberFormat numFormat = NumberFormat |
|
599 |
.getNumberInstance(Locale.US); |
|
600 |
private Calendar calendar = Calendar.getInstance(Locale.US); |
|
601 |
private String emtpyString; |
|
602 |
private static final int MAXCHARS = 255; |
|
603 |
|
|
604 |
public FieldFormatter() { |
|
605 |
// Avoid grouping on number format |
|
606 |
numFormat.setGroupingUsed(false); |
|
607 |
|
|
608 |
// build a 255 white spaces string |
|
609 |
StringBuffer sb = new StringBuffer(MAXCHARS); |
|
610 |
sb.setLength(MAXCHARS); |
|
611 |
for (int i = 0; i < MAXCHARS; i++) { |
|
612 |
sb.setCharAt(i, ' '); |
|
613 |
} |
|
614 |
|
|
615 |
emtpyString = sb.toString(); |
|
616 |
} |
|
617 |
|
|
618 |
public String getFieldString(int size, String s) { |
|
619 |
buffer.replace(0, size, emtpyString); |
|
620 |
buffer.setLength(size); |
|
621 |
|
|
622 |
if (s != null) { |
|
623 |
buffer.replace(0, size, s); |
|
624 |
if (s.length() <= size) { |
|
625 |
for (int i = s.length(); i < size; i++) { |
|
626 |
buffer.append(' '); |
|
627 |
} |
|
628 |
} |
|
629 |
} |
|
630 |
|
|
631 |
buffer.setLength(size); |
|
632 |
return buffer.toString(); |
|
633 |
} |
|
634 |
|
|
635 |
public String getFieldString(Date d) { |
|
636 |
|
|
637 |
if (d != null) { |
|
638 |
buffer.delete(0, buffer.length()); |
|
639 |
|
|
640 |
calendar.setTime(d); |
|
641 |
int year = calendar.get(Calendar.YEAR); |
|
642 |
int month = calendar.get(Calendar.MONTH) + 1; // returns 0 based |
|
643 |
// month? |
|
644 |
int day = calendar.get(Calendar.DAY_OF_MONTH); |
|
645 |
|
|
646 |
if (year < 1000) { |
|
647 |
if (year >= 100) { |
|
648 |
buffer.append("0"); |
|
649 |
} else |
|
650 |
if (year >= 10) { |
|
651 |
buffer.append("00"); |
|
652 |
} else { |
|
653 |
buffer.append("000"); |
|
654 |
} |
|
655 |
} |
|
656 |
buffer.append(year); |
|
657 |
|
|
658 |
if (month < 10) { |
|
659 |
buffer.append("0"); |
|
660 |
} |
|
661 |
buffer.append(month); |
|
662 |
|
|
663 |
if (day < 10) { |
|
664 |
buffer.append("0"); |
|
665 |
} |
|
666 |
buffer.append(day); |
|
667 |
} else { |
|
668 |
buffer.setLength(8); |
|
669 |
buffer.replace(0, 8, emtpyString); |
|
670 |
} |
|
671 |
|
|
672 |
buffer.setLength(8); |
|
673 |
return buffer.toString(); |
|
674 |
} |
|
675 |
|
|
676 |
public String getFieldString(int size, int decimalPlaces, double n) { |
|
677 |
buffer.delete(0, buffer.length()); |
|
678 |
|
|
679 |
numFormat.setMaximumFractionDigits(decimalPlaces); |
|
680 |
numFormat.setMinimumFractionDigits(decimalPlaces); |
|
681 |
numFormat.format(n, buffer, new FieldPosition( |
|
682 |
NumberFormat.INTEGER_FIELD)); |
|
683 |
return buffer.toString(); |
|
684 |
} |
|
685 |
} |
|
686 |
|
|
687 | 474 |
public void setCharset(Charset charset) { |
688 | 475 |
this.charset = charset; |
689 |
blank = charset.encode(" ");
|
|
690 |
blankSize = blank.limit();
|
|
476 |
blank = charset.encode(" ");
|
|
477 |
blankSize = blank.limit();
|
|
691 | 478 |
} |
692 | 479 |
|
693 | 480 |
} |
Also available in: Unified diff