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