Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.file / org.gvsig.fmap.dal.file.dbf / src / main / java / org / gvsig / fmap / dal / store / dbf / DBFStoreProvider.java @ 43356

History | View | Annotate | Download (33.8 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.dal.store.dbf;
24

    
25
import java.io.File;
26
import java.io.IOException;
27
import java.nio.charset.Charset;
28
import java.text.DateFormat;
29
import java.text.MessageFormat;
30
import java.text.ParseException;
31
import java.text.SimpleDateFormat;
32
import java.util.ArrayList;
33
import java.util.Date;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.Locale;
37

    
38
import org.apache.commons.io.FileUtils;
39
import org.apache.commons.lang3.StringUtils;
40

    
41
import org.gvsig.fmap.dal.DALLocator;
42
import org.gvsig.fmap.dal.DataManager;
43
import org.gvsig.fmap.dal.DataServerExplorer;
44
import org.gvsig.fmap.dal.DataStoreNotification;
45
import org.gvsig.fmap.dal.DataTypes;
46
import org.gvsig.fmap.dal.FileHelper;
47
import org.gvsig.fmap.dal.exception.CloseException;
48
import org.gvsig.fmap.dal.exception.DataException;
49
import org.gvsig.fmap.dal.exception.FileNotFoundException;
50
import org.gvsig.fmap.dal.exception.InitializeException;
51
import org.gvsig.fmap.dal.exception.OpenException;
52
import org.gvsig.fmap.dal.exception.ReadException;
53
import org.gvsig.fmap.dal.exception.UnsupportedVersionException;
54
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
55
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
56
import org.gvsig.fmap.dal.feature.EditableFeatureType;
57
import org.gvsig.fmap.dal.feature.Feature;
58
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
59
import org.gvsig.fmap.dal.feature.FeatureQuery;
60
import org.gvsig.fmap.dal.feature.FeatureSet;
61
import org.gvsig.fmap.dal.feature.FeatureStore;
62
import org.gvsig.fmap.dal.feature.FeatureType;
63
import org.gvsig.fmap.dal.feature.exception.AttributeNameException;
64
import org.gvsig.fmap.dal.feature.exception.AttributeNameTooLongException;
65
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
66
import org.gvsig.fmap.dal.feature.exception.UnknownDataTypeException;
67
import org.gvsig.fmap.dal.feature.spi.AbstractFeatureStoreProvider;
68
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
69
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
70
import org.gvsig.fmap.dal.feature.spi.FeatureSetProvider;
71
import org.gvsig.fmap.dal.resource.ResourceAction;
72
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
73
import org.gvsig.fmap.dal.resource.exception.ResourceException;
74
import org.gvsig.fmap.dal.resource.exception.ResourceExecuteException;
75
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyChangesException;
76
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyCloseException;
77
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyOpenException;
78
import org.gvsig.fmap.dal.resource.file.FileResource;
79
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
80
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
81
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorer;
82
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorerParameters;
83
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
84
import org.gvsig.fmap.dal.store.dbf.utils.DbaseFile;
85
import org.gvsig.metadata.MetadataLocator;
86
import org.gvsig.metadata.MetadataManager;
87
import org.gvsig.metadata.exceptions.MetadataException;
88
import org.gvsig.tools.dispose.DisposableIterator;
89
import org.gvsig.tools.dynobject.DynObject;
90
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
91
import org.gvsig.tools.exception.BaseException;
92

    
93
import org.slf4j.Logger;
94
import org.slf4j.LoggerFactory;
95

    
96
public class DBFStoreProvider extends AbstractFeatureStoreProvider implements
97
        ResourceConsumer {
98

    
99
    private static final Logger LOG = LoggerFactory.getLogger(DBFStoreProvider.class);
100

    
101
    public static final int MAX_FIELD_NAME_LENGTH = DbaseFile.MAX_FIELD_NAME_LENGTH;
102

    
103
    public static String NAME = "DBF";
104
    public static String DESCRIPTION = "DBF file";
105
    private static final Locale ukLocale = new Locale("en", "UK");
106

    
107
    public static final String METADATA_DEFINITION_NAME = NAME;
108
    private static final String METADATA_ENCODING = "Encoding";
109
    private static final String METADATA_CODEPAGE = "CodePage";
110

    
111
    private DbaseFile dbfFile = null;
112
    private ResourceProvider dbfResource;
113
    private long counterNewsOIDs = -1;
114
    private DBFFeatureWriter writer;
115

    
116
    private static long lastLogTime = 0;
117

    
118
    private boolean loTengoEnUso;
119

    
120
    private boolean allowDuplicatedFieldNames;
121

    
122
    protected static void registerMetadataDefinition() throws MetadataException {
123
        MetadataManager manager = MetadataLocator.getMetadataManager();
124
        if (manager.getDefinition(METADATA_DEFINITION_NAME) == null) {
125
            manager.addDefinition(
126
                    METADATA_DEFINITION_NAME,
127
                    DBFStoreParameters.class.getResourceAsStream("DBFStoreMetadata.xml"),
128
                    DBFStoreParameters.class.getClassLoader()
129
            );
130
        }
131
    }
132

    
133
    public DBFStoreProvider(DBFStoreParameters params,
134
            DataStoreProviderServices storeServices)
135
            throws InitializeException {
136
        super(
137
                params,
138
                storeServices,
139
                FileHelper.newMetadataContainer(METADATA_DEFINITION_NAME)
140
        );
141
        this.init(params, storeServices);
142
    }
143

    
144
    protected DBFStoreProvider(DBFStoreParameters params,
145
            DataStoreProviderServices storeServices, DynObject metadata)
146
            throws InitializeException {
147
        super(params, storeServices, metadata);
148
        this.init(params, storeServices);
149
    }
150

    
151
    protected void init(DBFStoreParameters params,
152
            DataStoreProviderServices storeServices) throws InitializeException {
153
        if (params == null) {
154
            throw new InitializeException(new NullPointerException("params is null"));
155
        }
156
        File theFile = getDBFParameters().getDBFFile();
157
        if (theFile == null) {
158
            throw new InitializeException(new NullPointerException("dbf file is null"));
159
        }
160
        initResource(params, storeServices);
161

    
162
        Charset charset = params.getEncoding();
163
        allowDuplicatedFieldNames = params.allowDuplicatedFieldNames();
164
        this.dbfFile = new DbaseFile(theFile, charset, allowDuplicatedFieldNames);
165

    
166
        writer = new DBFFeatureWriter(this.getProviderName());
167

    
168
        this.initFeatureType();
169

    
170
    }
171

    
172
    public Object getDynValue(String name) throws DynFieldNotFoundException {
173
        try {
174
            this.open();
175
        } catch (OpenException e) {
176
            throw new RuntimeException(e);
177
        }
178

    
179
        if (METADATA_ENCODING.equalsIgnoreCase(name)) {
180
            return this.dbfFile.getOriginalCharset();
181
        } else if (METADATA_CODEPAGE.equalsIgnoreCase(name)) {
182
            return new Integer(this.dbfFile.getCodePageInt());
183
        }
184
        return super.getDynValue(name);
185
    }
186

    
187
    protected void initResource(DBFStoreParameters params,
188
            DataStoreProviderServices storeServices) throws InitializeException {
189

    
190
        File theFile = getDBFParameters().getDBFFile();
191
        dbfResource
192
                = this.createResource(FileResource.NAME,
193
                        new Object[]{theFile.getAbsolutePath()});
194
        dbfResource.addConsumer(this);
195
    }
196

    
197
    protected void initResource(ResourceProvider resource,
198
        DataStoreProviderServices storeServices){
199
        dbfResource = resource;
200
        dbfResource.addConsumer(this);
201
    }
202

    
203
    public String getProviderName() {
204
        return NAME;
205
    }
206

    
207
    protected DBFStoreParameters getDBFParameters() {
208
        return (DBFStoreParameters) super.getParameters();
209
    }
210

    
211
    public DataServerExplorer getExplorer() throws ReadException {
212
        DataManager manager = DALLocator.getDataManager();
213
        FilesystemServerExplorerParameters params;
214
        try {
215
            params = (FilesystemServerExplorerParameters) manager
216
                    .createServerExplorerParameters(FilesystemServerExplorer.NAME);
217
            params.setRoot(this.getDBFParameters().getDBFFile().getParent());
218
            return manager.openServerExplorer(FilesystemServerExplorer.NAME, params);
219
        } catch (DataException e) {
220
            throw new ReadException(this.getName(), e);
221
        } catch (ValidateDataParametersException e) {
222
            // TODO Auto-generated catch block
223
            throw new ReadException(this.getName(), e);
224
        }
225
    }
226

    
227
    protected FeatureProvider internalGetFeatureProviderByReference(
228
            FeatureReferenceProviderServices reference, FeatureType featureType)
229
            throws DataException {
230
        return this.getFeatureProviderByIndex(
231
                ((Number) reference.getOID()).longValue(), featureType);
232
    }
233

    
234
    public void performChanges(Iterator deleteds, Iterator inserteds,
235
            Iterator updateds, Iterator originalFeatureTypesUpdated)
236
            throws PerformEditingException {
237

    
238
        /*
239
         * This will throw an exception if there are new fields
240
         * with names too long
241
         */
242
        checkNewFieldsNameSize(originalFeatureTypesUpdated);
243

    
244
        try {
245
            // TODO repasar el concepto de enUso de un recurso.
246
            loTengoEnUso = true;
247
            final FeatureStore store
248
                    = this.getStoreServices().getFeatureStore();
249
            resourceCloseRequest();
250
            getResource().execute(new ResourceAction() {
251

    
252
                public Object run() throws Exception {
253
                    FeatureSet set = null;
254
                    DisposableIterator iter = null;
255
                    try {
256
                        set = store.getFeatureSet();
257
                        DBFStoreParameters tmpParams
258
                                = (DBFStoreParameters) getDBFParameters().getCopy();
259

    
260
                        File tmp_file = File.createTempFile(
261
                                "tmp_" + System.currentTimeMillis(), ".dbf");
262
                        tmp_file.deleteOnExit();
263

    
264
                        tmpParams.setDBFFile(tmp_file);
265

    
266
                        writer.begin(tmpParams, store.getDefaultFeatureType(),
267
                                set.getSize());
268

    
269
                        iter = set.fastIterator();
270
                        while (iter.hasNext()) {
271
                            Feature feature = (Feature) iter.next();
272
                            writer.append(feature);
273
                        }
274

    
275
                        writer.end();
276
                        loTengoEnUso = false;
277
                        try {
278
                            close();
279
                        } catch (CloseException e1) {
280
                            throw new PerformEditingException(getProviderName(), e1);
281
                        }
282
                        getDBFParameters().getDBFFile().delete();
283

    
284
                        File target_file = getDBFParameters().getDBFFile();
285
                        if (target_file.exists()) {
286
                            target_file.delete();
287
                        }
288
                        tmp_file.renameTo(target_file);
289

    
290
                        if (tmp_file.exists() && !target_file.exists()) {
291
                            // Renaming failed, let's simply copy.
292
                            // We assume we cannot delete it, but we
293
                            // used deleteOnExit and it's
294
                            // temporary, so no problem
295
                            LOG.info("Warning: copying tmp file instead of renaming: "
296
                                    + target_file.getName());
297
                            FileUtils.copyFile(tmp_file, target_file);
298
                        }
299

    
300
                        resourcesNotifyChanges();
301
                        initFeatureType();
302
                    } finally {
303
                        loTengoEnUso = false;
304
                        if (set != null) {
305
                            set.dispose();
306
                        }
307
                        if (iter != null) {
308
                            iter.dispose();
309
                        }
310
                    }
311
                    return null;
312
                }
313
            });
314
        } catch (ResourceExecuteException | ResourceException e) {
315
            throw new PerformEditingException(this.getProviderName(), e);
316
        }
317

    
318
        this.counterNewsOIDs = -1;
319
    }
320

    
321
    protected void checkNewFieldsNameSize(Iterator ft_upd) throws PerformEditingException {
322

    
323
        String long_fields = getNewFieldsWithNameTooLong(ft_upd);
324
        if (long_fields != null) {
325
            AttributeNameException ane = new AttributeNameTooLongException(
326
                    long_fields,
327
                    getProviderName(),
328
                    MAX_FIELD_NAME_LENGTH);
329
            throw new PerformEditingException(getProviderName(), ane);
330
        }
331
    }
332

    
333
    /**
334
     * Returns null or a string which is a comma-separated list
335
     *
336
     * @param ft_updated
337
     * @return
338
     */
339
    protected String getNewFieldsWithNameTooLong(Iterator ft_updated) {
340

    
341
        String resp = "";
342
        FeatureTypeChanged item = null;
343
        FeatureType ft = null;
344
        FeatureAttributeDescriptor[] atts = null;
345
        while (ft_updated.hasNext()) {
346
            item = (FeatureTypeChanged) ft_updated.next();
347
            ft = item.getTarget();
348
            atts = ft.getAttributeDescriptors();
349
            for (int i = 0; i < atts.length; i++) {
350
                if (atts[i].getName().length() > MAX_FIELD_NAME_LENGTH) {
351
                    if (resp.length() == 0) {
352
                        resp = atts[i].getName();
353
                    } else {
354
                        resp = resp + ", " + atts[i].getName();
355
                    }
356
                }
357
            }
358
        }
359

    
360
        if (resp.length() == 0) {
361
            return null;
362
        } else {
363
            return "(" + resp + ")";
364
        }
365
    }
366
    /*
367
     * ==================================================
368
     */
369

    
370
    public FeatureProvider createFeatureProvider(FeatureType type) throws DataException {
371
        return new DBFFeatureProvider(this, type);
372
    }
373

    
374

    
375
    /*
376
     * ===================================================
377
     */
378
    protected void initFeatureType() throws InitializeException {
379
        try {
380
            FeatureType defaultType
381
                    = this.getTheFeatureType().getNotEditableCopy();
382
            List types = new ArrayList(1);
383
            types.add(defaultType);
384
            this.getStoreServices().setFeatureTypes(types, defaultType);
385
        } catch (OpenException e) {
386
            throw new InitializeException(getResource().toString(), e);
387
        }
388
    }
389

    
390
    public class DuplicatedFieldNameException extends ReadException {
391

    
392
        /**
393
         *
394
         */
395
        private static final long serialVersionUID = -1671651605329756160L;
396
        private final static String MESSAGE_FORMAT = "Duplicated field name '%(fieldName)'.\nCheck 'Allow duplicated field names' in layer properties in add layer dialog. The layer will become read only.";
397
        private final static String MESSAGE_KEY = "_DuplicatedFieldNameException";
398

    
399
        public DuplicatedFieldNameException(String fieldName) {
400
            super(MESSAGE_FORMAT, null, MESSAGE_KEY, serialVersionUID);
401
            setValue("fieldName", fieldName);
402
        }
403
    }
404

    
405
//
406
//    private String getUniqueFieldName(String fieldName, EditableFeatureType fType) {
407
//
408
//        int index = 1;
409
//        String tempFieldName = fieldName;
410
//        while(fType.get(tempFieldName)!=null && index<1000){
411
//            index++;
412
//            String sufix = String.valueOf(index);
413
//            tempFieldName = tempFieldName.substring(0, DbaseFile.MAX_FIELD_NAME_LENGTH-sufix.length())+sufix;
414
//        }
415
//        if(index>=1000){
416
//            throw new RuntimeException("Can't fix duplicated name for field '"+fieldName+"'.");
417
//        }
418
//        return tempFieldName;
419
//    }
420

    
421
    protected EditableFeatureType getTheFeatureType()
422
            throws InitializeException, OpenException {
423
        try {
424
            this.open();
425
        } catch (DataException e) {
426
            throw new InitializeException(this.getProviderName(), e);
427
        }
428
        return (EditableFeatureType) getResource().execute(
429
                new ResourceAction() {
430

    
431
                    public Object run() throws Exception {
432
                        int fieldCount = -1;
433
                        fieldCount = dbfFile.getFieldCount();
434

    
435
                        EditableFeatureType fType = getStoreServices().createFeatureType(getName());
436

    
437
                        fType.setHasOID(true);
438
                        int precision;
439
                        for (int i = 0; i < fieldCount; i++) {
440
                            char fieldType = dbfFile.getFieldType(i);
441
                            EditableFeatureAttributeDescriptor attr;
442

    
443
                            String fieldName = dbfFile.getFieldName(i);
444
                            if(fType.get(fieldName)!=null){
445
                                throw new DuplicatedFieldNameException(fieldName);
446
                            }
447

    
448
                            if (fieldType == 'L') {
449
                                attr = fType.add(fieldName, DataTypes.BOOLEAN);
450
                                attr.setDefaultValue(new Boolean(false));
451
                                attr.setAllowNull(false);
452

    
453
                            } else if ((fieldType == 'F') || (fieldType == 'N')) {
454
                                precision = dbfFile.getFieldDecimalLength(i);
455
                                if (precision > 0) {
456
                                    attr = fType.add(fieldName,
457
                                            DataTypes.DOUBLE,
458
                                            dbfFile.getFieldLength(i));
459
                                    attr.setPrecision(precision);
460
                                    attr.setDefaultValue(new Double(0));
461

    
462
                                } else {
463
                                    int length = dbfFile.getFieldLength(i);
464
                                    int type = DataTypes.INT;
465
                                    if (length > 9) {
466
                                        type = DataTypes.LONG;
467
                                    }
468
                                    attr = fType.add(fieldName,
469
                                            type,
470
                                            length);
471
                                    attr.setDefaultValue(new Integer(0));
472
                                }
473
                                attr.setAllowNull(false);
474

    
475
                            } else if (fieldType == 'C' || getDBFParameters().handleDatesAsStrings()) {
476
                                attr = fType.add(fieldName, DataTypes.STRING);
477
                                attr.setSize(dbfFile.getFieldLength(i));
478
                                attr.setDefaultValue("");
479
                                attr.setAllowNull(false);
480

    
481
                            } else if (fieldType == 'D') {
482
                                attr = fType.add(fieldName, DataTypes.DATE);
483
                                attr.setDefaultValue(new Date(0)); // def value 1-1-1970
484
                                attr.setAllowNull(true);
485
                                String sfmt = getDBFParameters().getDateFormat();
486
                                if (!StringUtils.isBlank(sfmt)) {
487
                                    try {
488
                                        SimpleDateFormat datefmt = new SimpleDateFormat(sfmt, getDBFParameters().getLocale());
489
                                        attr.setDateFormat(datefmt);
490
                                    } catch (Exception ex) {
491
                                        LOG.warn("Invalid date format ("+sfmt+") specified in DBF parameters.",ex);
492
                                    }
493
                                }
494
                            } else {
495
                                throw new InitializeException(getProviderName(),
496
                                        new UnknownDataTypeException(
497
                                            fieldName, ""
498
                                                + fieldType, getProviderName()));
499
                            }
500
                        }
501

    
502
                        // FeatureRules rules = fType.getRules();
503
                        // rules.add(new DBFRowValidator());
504
                        return fType;
505
                    }
506

    
507
                }
508
        );
509
    }
510

    
511
    protected void loadValue(FeatureProvider featureProvider, int rowIndex,
512
            FeatureAttributeDescriptor descriptor) throws ReadException {
513

    
514
        if (descriptor.getEvaluator() != null) {
515
            // Nothing to do
516
            return;
517
        }
518

    
519
        int dbfIndex = this.dbfFile.getFieldIndex(descriptor.getName());
520

    
521
        if (dbfIndex < 0) {
522
            // Someone asked to load a field
523
            // which does not exist in the DBF file. This can happen
524
            // in editing process when a field has been added
525
            // in the current editing session, so we simply do nothing.
526
            // The expansion manager is expected to manage those new fields
527
            // and their values.
528
            long curr_time = System.currentTimeMillis();
529
            // This ensures not more than one log every 2 seconds
530
            if (curr_time - lastLogTime > 2000) {
531
                LOG.info("Warning: The requested field does not exist in the DBF file. Assumed it's a new field in editing mode.");
532
                lastLogTime = curr_time;
533
            }
534
            return;
535
        }
536

    
537
        String value = null;
538
        try {
539
            value = this.dbfFile.getStringFieldValue(rowIndex, dbfIndex);
540
        } catch (DataException e) {
541
            throw new ReadException(this.getName(), e);
542
        }
543
        value = value.trim();
544
        int fieldType = descriptor.getType();
545
        switch (fieldType) {
546
            case DataTypes.STRING:
547
                featureProvider.set(descriptor.getIndex(), value);
548
                break;
549

    
550
            case DataTypes.DOUBLE:
551
                try {
552
                    featureProvider.set(descriptor.getIndex(), new Double(value));
553
                } catch (NumberFormatException e) {
554
                    featureProvider.set(descriptor.getIndex(), null);
555
                }
556
                break;
557

    
558
            case DataTypes.INT:
559
                try {
560
                    featureProvider.set(descriptor.getIndex(), new Integer(value));
561
                } catch (NumberFormatException e) {
562
                    featureProvider.set(descriptor.getIndex(), null);
563
                }
564
                break;
565

    
566
            case DataTypes.FLOAT:
567
                try {
568
                    featureProvider.set(descriptor.getIndex(), new Float(value));
569
                } catch (NumberFormatException e) {
570
                    featureProvider.set(descriptor.getIndex(), null);
571
                }
572
                break;
573

    
574
            case DataTypes.LONG:
575
                try {
576
                    featureProvider.set(descriptor.getIndex(), new Long(value));
577
                } catch (NumberFormatException e) {
578
                    featureProvider.set(descriptor.getIndex(), null);
579
                }
580
                break;
581

    
582
            case DataTypes.BOOLEAN:
583
                if (value.equalsIgnoreCase("T")) {
584
                    featureProvider.set(descriptor.getIndex(), Boolean.TRUE);
585
                } else {
586
                    featureProvider.set(descriptor.getIndex(), Boolean.FALSE);
587

    
588
                }
589
                break;
590

    
591
            case DataTypes.BYTE:
592
                try {
593
                    featureProvider.set(descriptor.getIndex(), new Byte(value));
594
                } catch (NumberFormatException e) {
595
                    featureProvider.set(descriptor.getIndex(), null);
596
                }
597
                break;
598

    
599
            case DataTypes.DATE:
600
                if (value.equals("")) {
601
                    value = null;
602
                    return;
603
                }
604
                Date dat = null;
605
                DateFormat df = new SimpleDateFormat("yyyyMMdd");
606
                try {
607
                    dat = df.parse(value);
608
                } catch (ParseException e) {
609
                    throw new InvalidDateException(df.toString(), value, this.getProviderName(), e);
610
                }
611
                featureProvider.set(descriptor.getIndex(), dat);
612
                break;
613

    
614
            default:
615
                featureProvider
616
                        .set(descriptor.getIndex(), descriptor.getDefaultValue());
617
                break;
618
        }
619
    }
620

    
621
    private static class InvalidDateException extends ReadException {
622
        public InvalidDateException(String dateFormat, String value, String store, Throwable cause) {
623
            super(
624
                    "Can't parse date value '%(value)' with format '%(dateFormat)' in dbf '%(store)'.",
625
                    cause,
626
                    "Cant_parse_date_value_XvalueX_with_format_XdateFormatX_in_dbf_XstoreX",
627
                    0
628
            );
629
            setValue("dateFormat",dateFormat);
630
            setValue("value", value);
631
            setValue("store",store);
632
        }
633
    }
634

    
635
    /**
636
     * *
637
     * NOT supported in Alter Mode
638
     *
639
     * @param index
640
     * @return
641
     * @throws ReadException
642
     */
643
    protected FeatureProvider getFeatureProviderByIndex(long index) throws DataException {
644
        return this
645
                .getFeatureProviderByIndex(index, this.getStoreServices()
646
                        .getDefaultFeatureType());
647
    }
648

    
649
    public long getFeatureCount() throws ReadException, OpenException,
650
            ResourceNotifyChangesException {
651
        this.open();
652
        return ((Long) getResource().execute(new ResourceAction() {
653
            public Object run() throws Exception {
654
                return Long.valueOf(dbfFile.getRecordCount());
655
            }
656
        })).longValue();
657
    }
658

    
659
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType featureType)
660
            throws DataException {
661
        return new DBFSetProvider(this, query, featureType);
662
    }
663

    
664
    public boolean canCreate() {
665
        return true;
666
    }
667

    
668
    public boolean canWriteGeometry(int geometryType, int geometrySubType) throws DataException {
669
        return false;
670
    }
671

    
672
    public void open() throws OpenException {
673
        if (this.dbfFile.isOpen()) {
674
            return;
675
        }
676
        try {
677
            getResource().execute(new ResourceAction() {
678
                public Object run() throws Exception {
679
                    openFile();
680
                    resourcesOpen();
681
                    return null;
682
                }
683
            });
684

    
685
        } catch (ResourceExecuteException e) {
686
            throw new OpenException(this.getProviderName(), e);
687
        }
688
    }
689

    
690
    protected void openFile() throws FileNotFoundException,
691
            UnsupportedVersionException, IOException, DataException {
692
        this.dbfFile.open();
693
        // necessary when editing the file
694
        this.getDBFParameters().setEffectiveEncoding(this.dbfFile.getCharsetName());
695
    }
696

    
697
    public void close() throws CloseException {
698
        if( loTengoEnUso ) {
699
            return;
700
        }
701
        if (dbfFile == null || !this.dbfFile.isOpen()) {
702
            return;
703
        }
704
        super.close();
705

    
706
        //Cerrar recurso
707
        try {
708
            getResource().execute(new ResourceAction() {
709
                public Object run() throws Exception {
710
                    closeFile();
711
                    resourcesNotifyClose();
712
                    return null;
713
                }
714
            });
715
        } catch (ResourceExecuteException  e) {
716
            throw new CloseException(this.getProviderName(), e);
717
        }
718
    }
719

    
720
    protected void closeFile() throws CloseException {
721
        this.dbfFile.close();
722
    }
723

    
724
    @Override
725
    protected void doDispose() throws BaseException {
726
        this.close();
727
        dbfFile = null;
728
        disposeResource();
729
        super.doDispose();
730
    }
731

    
732
    protected void disposeResource() {
733
        this.dbfResource.removeConsumer(this);
734
        dbfResource = null;
735
    }
736

    
737
    public boolean closeResourceRequested(ResourceProvider resource) {
738
        try {
739
            this.close();
740
        } catch (CloseException e) {
741
            return false;
742
        }
743
        return true;
744
    }
745

    
746
    public boolean allowWrite() {
747
        if(allowDuplicatedFieldNames){
748
            return false;
749
        }
750
        return this.dbfFile.isWritable();
751
    }
752

    
753
    public void refresh() throws OpenException {
754
        try {
755
            this.close();
756
        } catch (CloseException e) {
757
            throw new OpenException(this.getProviderName(), e);
758
        }
759
        this.open();
760
        try {
761
            this.initFeatureType();
762
        } catch (InitializeException e) {
763
            throw new OpenException(this.getProviderName(), e);
764
        }
765
    }
766

    
767
    /**
768
     *
769
     * @param index
770
     * @param featureType
771
     * @return
772
     * @throws ReadException
773
     */
774
    protected FeatureProvider getFeatureProviderByIndex(long index,
775
            FeatureType featureType) throws DataException {
776
        FeatureProvider featureProvider = this.createFeatureProvider(featureType);
777
        featureProvider.setOID(new Long(index));
778
        return featureProvider;
779
    }
780

    
781
    protected void initFeatureProviderByIndex(FeatureProvider featureProvider,
782
            long index, FeatureType featureType) throws DataException {
783
        featureProvider.setOID(new Long(index));
784
    }
785

    
786
    /**
787
     *
788
     * @param featureProvider
789
     * @throws DataException
790
     */
791
    protected void loadFeatureProviderByIndex(FeatureProvider featureProvider)
792
            throws DataException {
793

    
794
        long index = ((Long) featureProvider.getOID()).longValue();
795
        int rec_count = this.dbfFile.getRecordCount();
796

    
797
        if (index >= rec_count) {
798

    
799
            ReadException rex = new ReadException(this.getName(),
800
                    new ArrayIndexOutOfBoundsException(
801
                            "Index of requested feature ("
802
                            + index + ") is >= record count (" + rec_count + ")"));
803

    
804
            LOG.info("Error while loading feature. ", rex);
805
            throw rex;
806
        }
807

    
808
        Iterator iterator = featureProvider.getType().iterator();
809
        while (iterator.hasNext()) {
810
            FeatureAttributeDescriptor descriptor
811
                    = (FeatureAttributeDescriptor) iterator.next();
812
            this.loadValue(featureProvider, (int) index, descriptor);
813
        }
814
    }
815

    
816
    public int getOIDType() {
817
        return DataTypes.LONG;
818
    }
819

    
820
    public Object createNewOID() {
821
        if (this.counterNewsOIDs < 0) {
822
            try {
823
                this.counterNewsOIDs = this.getFeatureCount();
824
            } catch (DataException e) {
825
                e.printStackTrace();
826
            }
827

    
828
        } else {
829
            this.counterNewsOIDs++;
830
        }
831
        return new Long(counterNewsOIDs);
832
    }
833

    
834
    public boolean supportsAppendMode() {
835
        return true;
836
    }
837

    
838
    public void append(final FeatureProvider featureProvider)
839
            throws DataException {
840
        getResource().execute(new ResourceAction() {
841
            public Object run() throws Exception {
842
                writer.append(getStoreServices().createFeature(featureProvider));
843
                return null;
844
            }
845
        });
846
    }
847

    
848
    public void beginAppend() throws DataException {
849
        this.close();
850
        getResource().execute(new ResourceAction() {
851
            public Object run() throws Exception {
852
                writer.begin(getDBFParameters(),
853
                        getStoreServices().getDefaultFeatureType(),
854
                        getStoreServices().getFeatureStore().getFeatureCount());
855
                return null;
856
            }
857
        });
858
    }
859

    
860
    public void endAppend() throws DataException {
861
        getResource().execute(new ResourceAction() {
862
            public Object run() throws Exception {
863
                writer.end();
864
                resourcesNotifyChanges();
865
                counterNewsOIDs = -1;
866
                return null;
867
            }
868
        });
869
    }
870

    
871
    /*
872
     * (non-Javadoc)
873
     *
874
     * @see
875
     * org.gvsig.fmap.dal.resource.spi.ResourceConsumer#resourceChanged(org.
876
     * gvsig.fmap.dal.resource.spi.ResourceProvider)
877
     */
878
    public void resourceChanged(ResourceProvider resource) {
879
        if (this.getStoreServices()!=null){
880
            this.getStoreServices().notifyChange(
881
                    DataStoreNotification.RESOURCE_CHANGED,
882
                    resource);
883
        }
884
    }
885

    
886
    /**
887
     *
888
     * @throws ResourceNotifyChangesException
889
     */
890
    protected void resourcesNotifyChanges()
891
            throws ResourceNotifyChangesException {
892
        this.dbfResource.notifyChanges();
893
    }
894

    
895
    /**
896
     * @throws ResourceNotifyCloseException
897
     *
898
     */
899
    protected void resourcesNotifyClose() throws ResourceNotifyCloseException {
900
        this.dbfResource.notifyClose();
901
    }
902

    
903
    /**
904
     * @throws ResourceNotifyOpenException
905
     *
906
     */
907
    protected void resourcesOpen() throws ResourceNotifyOpenException {
908
        this.dbfResource.notifyOpen();
909
    }
910

    
911
    public Object getSourceId() {
912
        return this.getDBFParameters().getFile();
913
    }
914

    
915
    public String getName() {
916
        String name = this.getDBFParameters().getFile().getName();
917
        int n = name.lastIndexOf(".");
918
        if (n < 1) {
919
            return name;
920
        }
921
        return name.substring(0, n);
922
    }
923

    
924
    public String getFullName() {
925
        return this.getDBFParameters().getFile().getAbsolutePath();
926
    }
927

    
928
    protected void resourceCloseRequest() throws ResourceException {
929
        this.dbfResource.closeRequest();
930
    }
931

    
932
    public ResourceProvider getResource() {
933
        return dbfResource;
934
    }
935

    
936
}