root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / shp / DbaseFileHeaderNIO.java @ 1773
History | View | Annotate | Download (22.3 KB)
1 | 377 | fjp | /*
|
---|---|---|---|
2 | * Geotools - OpenSource mapping toolkit
|
||
3 | * (C) 2002, Centre for Computational Geography
|
||
4 | *
|
||
5 | * This library is free software; you can redistribute it and/or
|
||
6 | * modify it under the terms of the GNU Lesser General Public
|
||
7 | * License as published by the Free Software Foundation;
|
||
8 | * version 2.1 of the License.
|
||
9 | *
|
||
10 | * This library is distributed in the hope that it will be useful,
|
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
13 | * Lesser General Public License for more details.
|
||
14 | *
|
||
15 | * You should have received a copy of the GNU Lesser General Public
|
||
16 | * License along with this library; if not, write to the Free Software
|
||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
18 | *
|
||
19 | * This file is based on an origional contained in the GISToolkit project:
|
||
20 | * http://gistoolkit.sourceforge.net/
|
||
21 | *
|
||
22 | */
|
||
23 | 1100 | fjp | /* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
|
24 | *
|
||
25 | * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
|
||
26 | *
|
||
27 | * This program is free software; you can redistribute it and/or
|
||
28 | * modify it under the terms of the GNU General Public License
|
||
29 | * as published by the Free Software Foundation; either version 2
|
||
30 | * of the License, or (at your option) any later version.
|
||
31 | *
|
||
32 | * This program is distributed in the hope that it will be useful,
|
||
33 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
34 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
35 | * GNU General Public License for more details.
|
||
36 | *
|
||
37 | * You should have received a copy of the GNU General Public License
|
||
38 | * along with this program; if not, write to the Free Software
|
||
39 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
|
||
40 | *
|
||
41 | * For more information, contact:
|
||
42 | *
|
||
43 | * Generalitat Valenciana
|
||
44 | * Conselleria d'Infraestructures i Transport
|
||
45 | * Av. Blasco Ib??ez, 50
|
||
46 | * 46010 VALENCIA
|
||
47 | * SPAIN
|
||
48 | *
|
||
49 | * +34 963862235
|
||
50 | * gvsig@gva.es
|
||
51 | * www.gvsig.gva.es
|
||
52 | *
|
||
53 | * or
|
||
54 | *
|
||
55 | * IVER T.I. S.A
|
||
56 | * Salamanca 50
|
||
57 | * 46005 Valencia
|
||
58 | * Spain
|
||
59 | *
|
||
60 | * +34 963163400
|
||
61 | * dac@iver.es
|
||
62 | */
|
||
63 | 377 | fjp | package com.iver.cit.gvsig.fmap.drivers.shp; |
64 | |||
65 | 1132 | vcaballero | import com.hardcode.gdbms.engine.data.DriverException; |
66 | import com.hardcode.gdbms.engine.instruction.SemanticException; |
||
67 | import com.hardcode.gdbms.engine.values.Value; |
||
68 | 1160 | vcaballero | |
69 | 1132 | vcaballero | import com.iver.cit.gvsig.fmap.layers.SelectableDataSource; |
70 | 1160 | vcaballero | |
71 | 1005 | vcaballero | import com.vividsolutions.jts.geom.Geometry; |
72 | |||
73 | 377 | fjp | import java.io.EOFException; |
74 | import java.io.IOException; |
||
75 | 1005 | vcaballero | |
76 | 377 | fjp | import java.nio.ByteBuffer; |
77 | import java.nio.ByteOrder; |
||
78 | import java.nio.channels.ReadableByteChannel; |
||
79 | import java.nio.channels.WritableByteChannel; |
||
80 | 1005 | vcaballero | |
81 | 1773 | fernando | import java.sql.Types; |
82 | 377 | fjp | import java.util.Calendar; |
83 | import java.util.Date; |
||
84 | import java.util.logging.Level; |
||
85 | import java.util.logging.Logger; |
||
86 | |||
87 | |||
88 | /**
|
||
89 | * Class to represent the header of a Dbase III file. Creation date: (5/15/2001
|
||
90 | * 5:15:30 PM)
|
||
91 | */
|
||
92 | public class DbaseFileHeaderNIO { |
||
93 | 1005 | vcaballero | // Constant for the size of a record
|
94 | private static final int FILE_DESCRIPTOR_SIZE = 32; |
||
95 | 377 | fjp | |
96 | 1005 | vcaballero | // type of the file, must be 03h
|
97 | private static final byte MAGIC = 0x03; |
||
98 | private static final int MINIMUM_HEADER = 33; |
||
99 | 377 | fjp | |
100 | 1005 | vcaballero | // Date the file was last updated.
|
101 | private Date date = new Date(); |
||
102 | private int recordCnt = 0; |
||
103 | private int fieldCnt = 0; |
||
104 | private int myFileType = 0; |
||
105 | 377 | fjp | |
106 | 1005 | vcaballero | // set this to a default length of 1, which is enough for one "space"
|
107 | // character which signifies an empty record
|
||
108 | private int recordLength = 1; |
||
109 | 377 | fjp | |
110 | 1005 | vcaballero | // set this to a flagged value so if no fields are added before the write,
|
111 | // we know to adjust the headerLength to MINIMUM_HEADER
|
||
112 | private int headerLength = -1; |
||
113 | private int largestFieldSize = 0; |
||
114 | private Logger logger = Logger.getLogger("org.geotools.data.shapefile"); |
||
115 | 377 | fjp | |
116 | 1005 | vcaballero | // collection of header records.
|
117 | // lets start out with a zero-length array, just in case
|
||
118 | private DbaseField[] fields = null; // new DbaseField[0]; |
||
119 | 377 | fjp | |
120 | 1005 | vcaballero | /**
|
121 | * Lee del buffer.
|
||
122 | *
|
||
123 | * @param buffer .
|
||
124 | * @param channel .
|
||
125 | *
|
||
126 | * @throws IOException .
|
||
127 | * @throws EOFException .
|
||
128 | */
|
||
129 | private void read(ByteBuffer buffer, ReadableByteChannel channel) |
||
130 | throws IOException { |
||
131 | while (buffer.remaining() > 0) { |
||
132 | if (channel.read(buffer) == -1) { |
||
133 | throw new EOFException("Premature end of file"); |
||
134 | } |
||
135 | } |
||
136 | } |
||
137 | 377 | fjp | |
138 | 1005 | vcaballero | /**
|
139 | * Determine the most appropriate Java Class for representing the data in
|
||
140 | * the field.
|
||
141 | * <PRE>
|
||
142 | * All packages are java.lang unless otherwise specified.
|
||
143 | * C (Character) -> String
|
||
144 | * N (Numeric) -> Integer or Double (depends on field's decimal count)
|
||
145 | * F (Floating) -> Double
|
||
146 | * L (Logical) -> Boolean
|
||
147 | * D (Date) -> java.util.Date
|
||
148 | * Unknown -> String
|
||
149 | * </PRE>
|
||
150 | *
|
||
151 | * @param i The index of the field, from 0 to <CODE>getNumFields() -
|
||
152 | * 1</CODE> .
|
||
153 | *
|
||
154 | * @return A Class which closely represents the dbase field type.
|
||
155 | */
|
||
156 | public Class getFieldClass(int i) { |
||
157 | Class typeClass = null; |
||
158 | 377 | fjp | |
159 | 1005 | vcaballero | switch (fields[i].fieldType) {
|
160 | case 'C': |
||
161 | typeClass = String.class;
|
||
162 | 377 | fjp | |
163 | 1005 | vcaballero | break;
|
164 | 377 | fjp | |
165 | 1005 | vcaballero | case 'N': |
166 | 377 | fjp | |
167 | 1005 | vcaballero | if (fields[i].decimalCount == 0) { |
168 | typeClass = Integer.class;
|
||
169 | } else {
|
||
170 | typeClass = Double.class;
|
||
171 | } |
||
172 | 377 | fjp | |
173 | 1005 | vcaballero | break;
|
174 | 377 | fjp | |
175 | 1005 | vcaballero | case 'F': |
176 | typeClass = Double.class;
|
||
177 | 377 | fjp | |
178 | 1005 | vcaballero | break;
|
179 | 377 | fjp | |
180 | 1005 | vcaballero | case 'L': |
181 | typeClass = Boolean.class;
|
||
182 | 377 | fjp | |
183 | 1005 | vcaballero | break;
|
184 | 377 | fjp | |
185 | 1005 | vcaballero | case 'D': |
186 | typeClass = Date.class;
|
||
187 | 377 | fjp | |
188 | 1005 | vcaballero | break;
|
189 | 377 | fjp | |
190 | 1005 | vcaballero | default:
|
191 | typeClass = String.class;
|
||
192 | 377 | fjp | |
193 | 1005 | vcaballero | break;
|
194 | } |
||
195 | 377 | fjp | |
196 | 1005 | vcaballero | return typeClass;
|
197 | } |
||
198 | 377 | fjp | |
199 | 1005 | vcaballero | /**
|
200 | * Add a column to this DbaseFileHeader. The type is one of (C N L or D)
|
||
201 | * character, number, logical(true/false), or date. The Field length is
|
||
202 | * the total length in bytes reserved for this column. The decimal count
|
||
203 | * only applies to numbers(N), and floating point values (F), and refers
|
||
204 | * to the number of characters to reserve after the decimal point.
|
||
205 | * <B>Don't expect miracles from this...</B>
|
||
206 | * <PRE>
|
||
207 | * Field Type MaxLength
|
||
208 | * ---------- ---------
|
||
209 | * C 254
|
||
210 | * D 8
|
||
211 | * F 20
|
||
212 | * N 18
|
||
213 | * </PRE>
|
||
214 | *
|
||
215 | * @param inFieldName The name of the new field, must be less than 10
|
||
216 | * characters or it gets truncated.
|
||
217 | * @param inFieldType A character representing the dBase field, ( see above
|
||
218 | * ). Case insensitive.
|
||
219 | * @param inFieldLength The length of the field, in bytes ( see above )
|
||
220 | * @param inDecimalCount For numeric fields, the number of decimal places
|
||
221 | * to track.
|
||
222 | */
|
||
223 | public void addColumn(String inFieldName, char inFieldType, |
||
224 | int inFieldLength, int inDecimalCount) { |
||
225 | /*if (inFieldLength <=0) {
|
||
226 | throw new DbaseFileException("field length <= 0");
|
||
227 | }
|
||
228 | */
|
||
229 | if (fields == null) { |
||
230 | fields = new DbaseField[0]; |
||
231 | } |
||
232 | 377 | fjp | |
233 | 1005 | vcaballero | int tempLength = 1; // the length is used for the offset, and there is a * for deleted as the first byte |
234 | DbaseField[] tempFieldDescriptors = new DbaseField[fields.length + 1]; |
||
235 | 377 | fjp | |
236 | 1005 | vcaballero | for (int i = 0; i < fields.length; i++) { |
237 | fields[i].fieldDataAddress = tempLength; |
||
238 | tempLength = tempLength + fields[i].fieldLength; |
||
239 | tempFieldDescriptors[i] = fields[i]; |
||
240 | } |
||
241 | 377 | fjp | |
242 | 1005 | vcaballero | tempFieldDescriptors[fields.length] = new DbaseField();
|
243 | tempFieldDescriptors[fields.length].fieldLength = inFieldLength; |
||
244 | tempFieldDescriptors[fields.length].decimalCount = inDecimalCount; |
||
245 | tempFieldDescriptors[fields.length].fieldDataAddress = tempLength; |
||
246 | 377 | fjp | |
247 | 1005 | vcaballero | // set the field name
|
248 | String tempFieldName = inFieldName;
|
||
249 | 377 | fjp | |
250 | 1005 | vcaballero | if (tempFieldName == null) { |
251 | tempFieldName = "NoName";
|
||
252 | } |
||
253 | 377 | fjp | |
254 | 1005 | vcaballero | // Fix for GEOT-42, ArcExplorer will not handle field names > 10 chars
|
255 | // Sorry folks.
|
||
256 | if (tempFieldName.length() > 10) { |
||
257 | tempFieldName = tempFieldName.substring(0, 10); |
||
258 | warn("FieldName " + inFieldName +
|
||
259 | " is longer than 10 characters, truncating to " +
|
||
260 | tempFieldName); |
||
261 | } |
||
262 | 377 | fjp | |
263 | 1005 | vcaballero | tempFieldDescriptors[fields.length].fieldName = tempFieldName; |
264 | 377 | fjp | |
265 | 1005 | vcaballero | // the field type
|
266 | if ((inFieldType == 'C') || (inFieldType == 'c')) { |
||
267 | tempFieldDescriptors[fields.length].fieldType = 'C';
|
||
268 | 377 | fjp | |
269 | 1005 | vcaballero | if (inFieldLength > 254) { |
270 | warn("Field Length for " + inFieldName + " set to " + |
||
271 | inFieldLength + |
||
272 | " Which is longer than 254, not consistent with dbase III");
|
||
273 | } |
||
274 | } else if ((inFieldType == 'S') || (inFieldType == 's')) { |
||
275 | tempFieldDescriptors[fields.length].fieldType = 'C';
|
||
276 | warn("Field type for " + inFieldName +
|
||
277 | " set to S which is flat out wrong people!, I am setting this to C, in the hopes you meant character.");
|
||
278 | 377 | fjp | |
279 | 1005 | vcaballero | if (inFieldLength > 254) { |
280 | warn("Field Length for " + inFieldName + " set to " + |
||
281 | inFieldLength + |
||
282 | " Which is longer than 254, not consistent with dbase III");
|
||
283 | } |
||
284 | 377 | fjp | |
285 | 1005 | vcaballero | tempFieldDescriptors[fields.length].fieldLength = 8;
|
286 | } else if ((inFieldType == 'D') || (inFieldType == 'd')) { |
||
287 | tempFieldDescriptors[fields.length].fieldType = 'D';
|
||
288 | 377 | fjp | |
289 | 1005 | vcaballero | if (inFieldLength != 8) { |
290 | warn("Field Length for " + inFieldName + " set to " + |
||
291 | inFieldLength + " Setting to 8 digets YYYYMMDD");
|
||
292 | } |
||
293 | 377 | fjp | |
294 | 1005 | vcaballero | tempFieldDescriptors[fields.length].fieldLength = 8;
|
295 | } else if ((inFieldType == 'F') || (inFieldType == 'f')) { |
||
296 | tempFieldDescriptors[fields.length].fieldType = 'F';
|
||
297 | 377 | fjp | |
298 | 1005 | vcaballero | if (inFieldLength > 20) { |
299 | warn("Field Length for " + inFieldName + " set to " + |
||
300 | inFieldLength + |
||
301 | " Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII.");
|
||
302 | } |
||
303 | } else if ((inFieldType == 'N') || (inFieldType == 'n')) { |
||
304 | tempFieldDescriptors[fields.length].fieldType = 'N';
|
||
305 | 377 | fjp | |
306 | 1005 | vcaballero | if (inFieldLength > 18) { |
307 | warn("Field Length for " + inFieldName + " set to " + |
||
308 | inFieldLength + |
||
309 | " Preserving length, but should be set to Max of 18 for dbase III specification.");
|
||
310 | } |
||
311 | 377 | fjp | |
312 | 1005 | vcaballero | if (inDecimalCount < 0) { |
313 | warn("Field Decimal Position for " + inFieldName + " set to " + |
||
314 | inDecimalCount + |
||
315 | " Setting to 0 no decimal data will be saved.");
|
||
316 | tempFieldDescriptors[fields.length].decimalCount = 0;
|
||
317 | } |
||
318 | 377 | fjp | |
319 | 1005 | vcaballero | if (inDecimalCount > (inFieldLength - 1)) { |
320 | warn("Field Decimal Position for " + inFieldName + " set to " + |
||
321 | inDecimalCount + " Setting to " + (inFieldLength - 1) + |
||
322 | " no non decimal data will be saved.");
|
||
323 | tempFieldDescriptors[fields.length].decimalCount = inFieldLength - |
||
324 | 1;
|
||
325 | } |
||
326 | } else if ((inFieldType == 'L') || (inFieldType == 'l')) { |
||
327 | tempFieldDescriptors[fields.length].fieldType = 'L';
|
||
328 | 377 | fjp | |
329 | 1005 | vcaballero | if (inFieldLength != 1) { |
330 | warn("Field Length for " + inFieldName + " set to " + |
||
331 | inFieldLength + |
||
332 | " Setting to length of 1 for logical fields.");
|
||
333 | } |
||
334 | 377 | fjp | |
335 | 1005 | vcaballero | tempFieldDescriptors[fields.length].fieldLength = 1;
|
336 | } else {
|
||
337 | //throw new DbaseFileException("Undefined field type "+inFieldType + " For column "+inFieldName);
|
||
338 | } |
||
339 | 377 | fjp | |
340 | 1005 | vcaballero | // the length of a record
|
341 | tempLength = tempLength + |
||
342 | tempFieldDescriptors[fields.length].fieldLength; |
||
343 | 377 | fjp | |
344 | 1005 | vcaballero | // set the new fields.
|
345 | fields = tempFieldDescriptors; |
||
346 | fieldCnt = fields.length; |
||
347 | headerLength = MINIMUM_HEADER + (32 * fields.length);
|
||
348 | recordLength = tempLength; |
||
349 | } |
||
350 | 377 | fjp | |
351 | 1005 | vcaballero | /**
|
352 | * Remove a column from this DbaseFileHeader.
|
||
353 | *
|
||
354 | * @param inFieldName The name of the field, will ignore case and trim.
|
||
355 | *
|
||
356 | * @return index of the removed column, -1 if no found
|
||
357 | *
|
||
358 | * @todo This is really ugly, don't know who wrote it, but it needs
|
||
359 | * fixin...
|
||
360 | */
|
||
361 | public int removeColumn(String inFieldName) { |
||
362 | int retCol = -1; |
||
363 | int tempLength = 1; |
||
364 | DbaseField[] tempFieldDescriptors = new DbaseField[fields.length - 1]; |
||
365 | 377 | fjp | |
366 | 1005 | vcaballero | for (int i = 0, j = 0; i < fields.length; i++) { |
367 | if (!inFieldName.equalsIgnoreCase(fields[i].fieldName.trim())) {
|
||
368 | // if this is the last field and we still haven't found the
|
||
369 | // named field
|
||
370 | if ((i == j) && (i == (fields.length - 1))) { |
||
371 | System.err.println("Could not find a field named '" + |
||
372 | inFieldName + "' for removal");
|
||
373 | 377 | fjp | |
374 | 1005 | vcaballero | return retCol;
|
375 | } |
||
376 | 377 | fjp | |
377 | 1005 | vcaballero | tempFieldDescriptors[j] = fields[i]; |
378 | tempFieldDescriptors[j].fieldDataAddress = tempLength; |
||
379 | tempLength += tempFieldDescriptors[j].fieldLength; |
||
380 | 377 | fjp | |
381 | 1005 | vcaballero | // only increment j on non-matching fields
|
382 | j++; |
||
383 | } else {
|
||
384 | retCol = i; |
||
385 | } |
||
386 | } |
||
387 | 377 | fjp | |
388 | 1005 | vcaballero | // set the new fields.
|
389 | fields = tempFieldDescriptors; |
||
390 | headerLength = 33 + (32 * fields.length); |
||
391 | recordLength = tempLength; |
||
392 | 377 | fjp | |
393 | 1005 | vcaballero | return retCol;
|
394 | } |
||
395 | 377 | fjp | |
396 | 1005 | vcaballero | /**
|
397 | * DOCUMENT ME!
|
||
398 | *
|
||
399 | * @param inWarn DOCUMENT ME!
|
||
400 | *
|
||
401 | * @todo addProgessListener handling
|
||
402 | */
|
||
403 | private void warn(String inWarn) { |
||
404 | if (logger.isLoggable(Level.WARNING)) { |
||
405 | logger.warning(inWarn); |
||
406 | } |
||
407 | } |
||
408 | 377 | fjp | |
409 | 1005 | vcaballero | // Retrieve the length of the field at the given index
|
410 | 377 | fjp | |
411 | 1005 | vcaballero | /**
|
412 | * Returns the field length in bytes.
|
||
413 | *
|
||
414 | * @param inIndex The field index.
|
||
415 | *
|
||
416 | * @return The length in bytes.
|
||
417 | */
|
||
418 | public int getFieldLength(int inIndex) { |
||
419 | return fields[inIndex].fieldLength;
|
||
420 | } |
||
421 | 377 | fjp | |
422 | 1005 | vcaballero | // Retrieve the location of the decimal point within the field.
|
423 | 377 | fjp | |
424 | 1005 | vcaballero | /**
|
425 | * Get the decimal count of this field.
|
||
426 | *
|
||
427 | * @param inIndex The field index.
|
||
428 | *
|
||
429 | * @return The decimal count.
|
||
430 | */
|
||
431 | public int getFieldDecimalCount(int inIndex) { |
||
432 | return fields[inIndex].decimalCount;
|
||
433 | } |
||
434 | 377 | fjp | |
435 | 1005 | vcaballero | // Retrieve the Name of the field at the given index
|
436 | 377 | fjp | |
437 | 1005 | vcaballero | /**
|
438 | * Get the field name.
|
||
439 | *
|
||
440 | * @param inIndex The field index.
|
||
441 | *
|
||
442 | * @return The name of the field.
|
||
443 | */
|
||
444 | public String getFieldName(int inIndex) { |
||
445 | return fields[inIndex].fieldName;
|
||
446 | } |
||
447 | 377 | fjp | |
448 | 1005 | vcaballero | // Retrieve the type of field at the given index
|
449 | 377 | fjp | |
450 | 1005 | vcaballero | /**
|
451 | * Get the character class of the field.
|
||
452 | *
|
||
453 | * @param inIndex The field index.
|
||
454 | *
|
||
455 | * @return The dbase character representing this field.
|
||
456 | */
|
||
457 | public char getFieldType(int inIndex) { |
||
458 | return fields[inIndex].fieldType;
|
||
459 | } |
||
460 | 377 | fjp | |
461 | 1005 | vcaballero | /**
|
462 | * Get the date this file was last updated.
|
||
463 | *
|
||
464 | * @return The Date last modified.
|
||
465 | */
|
||
466 | public Date getLastUpdateDate() { |
||
467 | return date;
|
||
468 | } |
||
469 | 377 | fjp | |
470 | 1005 | vcaballero | /**
|
471 | * Return the number of fields in the records.
|
||
472 | *
|
||
473 | * @return The number of fields in this table.
|
||
474 | */
|
||
475 | public int getNumFields() { |
||
476 | return fields.length;
|
||
477 | } |
||
478 | 377 | fjp | |
479 | 1005 | vcaballero | /**
|
480 | * Return the number of records in the file
|
||
481 | *
|
||
482 | * @return The number of records in this table.
|
||
483 | */
|
||
484 | public int getNumRecords() { |
||
485 | return recordCnt;
|
||
486 | } |
||
487 | 377 | fjp | |
488 | 1005 | vcaballero | /**
|
489 | * Get the length of the records in bytes.
|
||
490 | *
|
||
491 | * @return The number of bytes per record.
|
||
492 | */
|
||
493 | public int getRecordLength() { |
||
494 | return recordLength;
|
||
495 | } |
||
496 | 377 | fjp | |
497 | 1005 | vcaballero | /**
|
498 | * Get the length of the header
|
||
499 | *
|
||
500 | * @return The length of the header in bytes.
|
||
501 | */
|
||
502 | public int getHeaderLength() { |
||
503 | return headerLength;
|
||
504 | } |
||
505 | 377 | fjp | |
506 | 1005 | vcaballero | /**
|
507 | * Read the header data from the DBF file.
|
||
508 | *
|
||
509 | * @param in DOCUMENT ME!
|
||
510 | *
|
||
511 | * @throws IOException DOCUMENT ME!
|
||
512 | */
|
||
513 | public void readHeader(ByteBuffer in) throws IOException { |
||
514 | // type of file.
|
||
515 | myFileType = in.get(); |
||
516 | 377 | fjp | |
517 | 1005 | vcaballero | if (myFileType != 0x03) { |
518 | throw new IOException("Unsupported DBF file Type " + |
||
519 | Integer.toHexString(myFileType));
|
||
520 | } |
||
521 | 377 | fjp | |
522 | 1005 | vcaballero | // parse the update date information.
|
523 | int tempUpdateYear = (int) in.get(); |
||
524 | int tempUpdateMonth = (int) in.get(); |
||
525 | int tempUpdateDay = (int) in.get(); |
||
526 | tempUpdateYear = tempUpdateYear + 1900;
|
||
527 | 377 | fjp | |
528 | 1005 | vcaballero | Calendar c = Calendar.getInstance(); |
529 | c.set(Calendar.YEAR, tempUpdateYear);
|
||
530 | c.set(Calendar.MONTH, tempUpdateMonth - 1); |
||
531 | c.set(Calendar.DATE, tempUpdateDay);
|
||
532 | date = c.getTime(); |
||
533 | 377 | fjp | |
534 | 1005 | vcaballero | // read the number of records.
|
535 | in.order(ByteOrder.LITTLE_ENDIAN);
|
||
536 | recordCnt = in.getInt(); |
||
537 | 377 | fjp | |
538 | 1005 | vcaballero | // read the length of the header structure.
|
539 | headerLength = in.getShort(); |
||
540 | 377 | fjp | |
541 | 1005 | vcaballero | // read the length of a record
|
542 | recordLength = in.getShort(); |
||
543 | |||
544 | 377 | fjp | in.order(ByteOrder.BIG_ENDIAN);
|
545 | |||
546 | 1005 | vcaballero | // skip the reserved bytes in the header.
|
547 | in.position(in.position() + 20);
|
||
548 | 377 | fjp | |
549 | 1005 | vcaballero | // calculate the number of Fields in the header
|
550 | fieldCnt = (headerLength - FILE_DESCRIPTOR_SIZE - 1) / FILE_DESCRIPTOR_SIZE;
|
||
551 | 377 | fjp | |
552 | 1005 | vcaballero | // read all of the header records
|
553 | fields = new DbaseField[fieldCnt];
|
||
554 | 377 | fjp | |
555 | 1005 | vcaballero | for (int i = 0; i < fieldCnt; i++) { |
556 | fields[i] = new DbaseField();
|
||
557 | 377 | fjp | |
558 | 1005 | vcaballero | // read the field name
|
559 | byte[] buffer = new byte[11]; |
||
560 | in.get(buffer); |
||
561 | fields[i].fieldName = new String(buffer); |
||
562 | 377 | fjp | |
563 | 1005 | vcaballero | // read the field type
|
564 | fields[i].fieldType = (char) in.get();
|
||
565 | 377 | fjp | |
566 | 1005 | vcaballero | // read the field data address, offset from the start of the record.
|
567 | fields[i].fieldDataAddress = in.getInt(); |
||
568 | 377 | fjp | |
569 | 1005 | vcaballero | // read the field length in bytes
|
570 | int tempLength = (int) in.get(); |
||
571 | 377 | fjp | |
572 | 1005 | vcaballero | if (tempLength < 0) { |
573 | tempLength = tempLength + 256;
|
||
574 | } |
||
575 | 377 | fjp | |
576 | 1005 | vcaballero | fields[i].fieldLength = tempLength; |
577 | 377 | fjp | |
578 | 1005 | vcaballero | // read the field decimal count in bytes
|
579 | fields[i].decimalCount = (int) in.get();
|
||
580 | 377 | fjp | |
581 | 1005 | vcaballero | // read the reserved bytes.
|
582 | in.position(in.position() + 14);
|
||
583 | } |
||
584 | 377 | fjp | |
585 | 1005 | vcaballero | // Last byte is a marker for the end of the field definitions.
|
586 | in.get(); |
||
587 | } |
||
588 | 377 | fjp | |
589 | 1005 | vcaballero | /**
|
590 | * Get the largest field size of this table.
|
||
591 | *
|
||
592 | * @return The largt field size iiin bytes.
|
||
593 | */
|
||
594 | public int getLargestFieldSize() { |
||
595 | return largestFieldSize;
|
||
596 | } |
||
597 | 377 | fjp | |
598 | 1005 | vcaballero | /**
|
599 | * Set the number of records in the file
|
||
600 | *
|
||
601 | * @param inNumRecords The number of records.
|
||
602 | */
|
||
603 | public void setNumRecords(int inNumRecords) { |
||
604 | recordCnt = inNumRecords; |
||
605 | } |
||
606 | 377 | fjp | |
607 | 1005 | vcaballero | /**
|
608 | * Write the header data to the DBF file.
|
||
609 | *
|
||
610 | * @param out A channel to write to. If you have an OutputStream you can
|
||
611 | * obtain the correct channel by using
|
||
612 | * java.nio.Channels.newChannel(OutputStream out).
|
||
613 | *
|
||
614 | * @throws IOException If errors occur.
|
||
615 | */
|
||
616 | public void writeHeader(WritableByteChannel out) throws IOException { |
||
617 | // take care of the annoying case where no records have been added...
|
||
618 | if (headerLength == -1) { |
||
619 | headerLength = MINIMUM_HEADER; |
||
620 | } |
||
621 | 377 | fjp | |
622 | 1005 | vcaballero | ByteBuffer buffer = ByteBuffer.allocateDirect(headerLength); |
623 | buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||
624 | 377 | fjp | |
625 | 1005 | vcaballero | // write the output file type.
|
626 | buffer.put((byte) MAGIC);
|
||
627 | 377 | fjp | |
628 | 1005 | vcaballero | // write the date stuff
|
629 | Calendar c = Calendar.getInstance(); |
||
630 | c.setTime(new Date()); |
||
631 | buffer.put((byte) (c.get(Calendar.YEAR) % 100)); |
||
632 | buffer.put((byte) (c.get(Calendar.MONTH) + 1)); |
||
633 | buffer.put((byte) (c.get(Calendar.DAY_OF_MONTH))); |
||
634 | 377 | fjp | |
635 | 1005 | vcaballero | // write the number of records in the datafile.
|
636 | buffer.putInt(recordCnt); |
||
637 | 377 | fjp | |
638 | 1005 | vcaballero | // write the length of the header structure.
|
639 | buffer.putShort((short) headerLength);
|
||
640 | 377 | fjp | |
641 | 1005 | vcaballero | // write the length of a record
|
642 | buffer.putShort((short) recordLength);
|
||
643 | 377 | fjp | |
644 | 1005 | vcaballero | // // write the reserved bytes in the header
|
645 | // for (int i=0; i<20; i++) out.writeByteLE(0);
|
||
646 | buffer.position(buffer.position() + 20);
|
||
647 | 377 | fjp | |
648 | 1005 | vcaballero | // write all of the header records
|
649 | int tempOffset = 0; |
||
650 | 377 | fjp | |
651 | 1005 | vcaballero | for (int i = 0; i < fields.length; i++) { |
652 | // write the field name
|
||
653 | for (int j = 0; j < 11; j++) { |
||
654 | if (fields[i].fieldName.length() > j) {
|
||
655 | buffer.put((byte) fields[i].fieldName.charAt(j));
|
||
656 | } else {
|
||
657 | buffer.put((byte) 0); |
||
658 | } |
||
659 | } |
||
660 | 377 | fjp | |
661 | 1005 | vcaballero | // write the field type
|
662 | buffer.put((byte) fields[i].fieldType);
|
||
663 | 377 | fjp | |
664 | 1005 | vcaballero | // // write the field data address, offset from the start of the record.
|
665 | buffer.putInt(tempOffset); |
||
666 | tempOffset += fields[i].fieldLength; |
||
667 | 377 | fjp | |
668 | 1005 | vcaballero | // write the length of the field.
|
669 | buffer.put((byte) fields[i].fieldLength);
|
||
670 | 377 | fjp | |
671 | 1005 | vcaballero | // write the decimal count.
|
672 | buffer.put((byte) fields[i].decimalCount);
|
||
673 | 377 | fjp | |
674 | 1005 | vcaballero | // write the reserved bytes.
|
675 | //for (in j=0; jj<14; j++) out.writeByteLE(0);
|
||
676 | buffer.position(buffer.position() + 14);
|
||
677 | } |
||
678 | 377 | fjp | |
679 | 1005 | vcaballero | // write the end of the field definitions marker
|
680 | buffer.put((byte) 0x0D); |
||
681 | 377 | fjp | |
682 | 1005 | vcaballero | buffer.position(0);
|
683 | 377 | fjp | |
684 | 1005 | vcaballero | int r = buffer.remaining();
|
685 | 377 | fjp | |
686 | 1005 | vcaballero | while ((r -= out.write(buffer)) > 0) { |
687 | ; // do nothing
|
||
688 | } |
||
689 | } |
||
690 | 377 | fjp | |
691 | 1005 | vcaballero | /**
|
692 | * Get a simple representation of this header.
|
||
693 | *
|
||
694 | * @return A String representing the state of the header.
|
||
695 | */
|
||
696 | public String toString() { |
||
697 | StringBuffer fs = new StringBuffer(); |
||
698 | 377 | fjp | |
699 | 1005 | vcaballero | for (int i = 0, ii = fields.length; i < ii; i++) { |
700 | DbaseField f = fields[i]; |
||
701 | fs.append(f.fieldName + " " + f.fieldType + " " + f.fieldLength + |
||
702 | " " + f.decimalCount + " " + f.fieldDataAddress + "\n"); |
||
703 | } |
||
704 | 377 | fjp | |
705 | 1005 | vcaballero | return "DB3 Header\n" + "Date : " + date + "\n" + "Records : " + |
706 | recordCnt + "\n" + "Fields : " + fieldCnt + "\n" + fs; |
||
707 | } |
||
708 | 377 | fjp | |
709 | 1005 | vcaballero | /**
|
710 | * Crea un DbaseFile.
|
||
711 | *
|
||
712 | * @return DbaseFileHeaderNIO
|
||
713 | *
|
||
714 | * @throws IOException .
|
||
715 | */
|
||
716 | public static DbaseFileHeaderNIO createNewDbaseHeader() |
||
717 | throws IOException { |
||
718 | DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
|
||
719 | 377 | fjp | |
720 | 1005 | vcaballero | for (int i = 0, ii = 1; i < ii; i++) { |
721 | //AttributeType type = featureType.getAttributeType(i);
|
||
722 | Class colType = Integer.class; |
||
723 | String colName = "ID"; |
||
724 | int fieldLen = 10; |
||
725 | 377 | fjp | |
726 | 1005 | vcaballero | if (fieldLen <= 0) { |
727 | fieldLen = 255;
|
||
728 | } |
||
729 | 377 | fjp | |
730 | 1005 | vcaballero | // @todo respect field length
|
731 | if ((colType == Integer.class) || (colType == Short.class) || |
||
732 | (colType == Byte.class)) {
|
||
733 | header.addColumn(colName, 'N', Math.min(fieldLen, 10), 0); |
||
734 | } else if (colType == Long.class) { |
||
735 | header.addColumn(colName, 'N', Math.min(fieldLen, 19), 0); |
||
736 | } else if ((colType == Double.class) || (colType == Float.class) || |
||
737 | (colType == Number.class)) {
|
||
738 | int l = Math.min(fieldLen, 33); |
||
739 | int d = Math.max(l - 2, 0); |
||
740 | header.addColumn(colName, 'N', l, d);
|
||
741 | } else if (java.util.Date.class.isAssignableFrom(colType)) { |
||
742 | header.addColumn(colName, 'D', fieldLen, 0); |
||
743 | } else if (colType == Boolean.class) { |
||
744 | header.addColumn(colName, 'L', 1, 0); |
||
745 | } else if (CharSequence.class.isAssignableFrom(colType)) { |
||
746 | // Possible fix for GEOT-42 : ArcExplorer doesn't like 0 length
|
||
747 | // ensure that maxLength is at least 1
|
||
748 | header.addColumn(colName, 'C', Math.min(254, fieldLen), 0); |
||
749 | } else if (Geometry.class.isAssignableFrom(colType)) { |
||
750 | continue;
|
||
751 | } else {
|
||
752 | throw new IOException("Unable to write : " + colType.getName()); |
||
753 | } |
||
754 | } |
||
755 | 377 | fjp | |
756 | 1005 | vcaballero | return header;
|
757 | } |
||
758 | 377 | fjp | |
759 | 1005 | vcaballero | /**
|
760 | * DOCUMENT ME!
|
||
761 | 1160 | vcaballero | *
|
762 | * @param sds DOCUMENT ME!
|
||
763 | *
|
||
764 | * @return DOCUMENT ME!
|
||
765 | *
|
||
766 | * @throws IOException DOCUMENT ME!
|
||
767 | 1005 | vcaballero | */
|
768 | 1160 | vcaballero | public static DbaseFileHeaderNIO createDbaseHeader(SelectableDataSource sds) |
769 | throws IOException { |
||
770 | DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
|
||
771 | 377 | fjp | |
772 | 1160 | vcaballero | try {
|
773 | for (int i = 0, ii = sds.getFieldCount(); i < ii; i++) { |
||
774 | 1658 | fjp | |
775 | 1773 | fernando | int type = sds.getFieldType(i);
|
776 | 1160 | vcaballero | String colName = sds.getFieldName(i);
|
777 | |||
778 | ///int fieldLen = ((DBFDriver)sds.getDriver()).getFieldLength(i);
|
||
779 | int fieldLen = 100; //TODO aqu? el tama?o no es correcto hay que calcularlo, ahora mismo est? puesto a pi??n. |
||
780 | int decimales = 5; |
||
781 | |||
782 | // if (fieldLen <= 0) {
|
||
783 | // fieldLen = 255;
|
||
784 | // }
|
||
785 | // @todo respect field length
|
||
786 | 1773 | fernando | if ((type == Types.DOUBLE) || |
787 | (type == Types.FLOAT) ||
|
||
788 | (type == Types.INTEGER))
|
||
789 | 1658 | fjp | |
790 | 1160 | vcaballero | header.addColumn(colName, 'N', Math.min(fieldLen, 10), |
791 | decimales); |
||
792 | 1773 | fernando | if (type == Types.DATE) |
793 | 1160 | vcaballero | header.addColumn(colName, 'D', fieldLen, 0); |
794 | 1773 | fernando | if (type == Types.BIT) |
795 | 1160 | vcaballero | header.addColumn(colName, 'L', 1, 0); |
796 | 1773 | fernando | if ((type == Types.VARCHAR) || |
797 | (type == Types.CHAR) ||
|
||
798 | (type == Types.LONGVARCHAR))
|
||
799 | 1658 | fjp | header.addColumn(colName, 'C', Math.min(254, fieldLen), |
800 | 1160 | vcaballero | 0);
|
801 | } |
||
802 | } catch (DriverException e) {
|
||
803 | e.printStackTrace(); |
||
804 | } |
||
805 | |||
806 | return header;
|
||
807 | 1132 | vcaballero | } |
808 | 1160 | vcaballero | |
809 | 1658 | fjp | |
810 | 1005 | vcaballero | /**
|
811 | * Class for holding the information assicated with a record.
|
||
812 | */
|
||
813 | class DbaseField { |
||
814 | // Field Name
|
||
815 | String fieldName;
|
||
816 | 377 | fjp | |
817 | 1005 | vcaballero | // Field Type (C N L D or M)
|
818 | char fieldType;
|
||
819 | 682 | fjp | |
820 | 1005 | vcaballero | // Field Data Address offset from the start of the record.
|
821 | int fieldDataAddress;
|
||
822 | 377 | fjp | |
823 | 1005 | vcaballero | // Length of the data in bytes
|
824 | int fieldLength;
|
||
825 | 377 | fjp | |
826 | 1005 | vcaballero | // Field decimal count in Binary, indicating where the decimal is
|
827 | int decimalCount;
|
||
828 | } |
||
829 | 377 | fjp | } |