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

History | View | Annotate | Download (31.2 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.math.BigDecimal;
28
import java.nio.charset.Charset;
29
import java.util.ArrayList;
30
import java.util.Iterator;
31
import java.util.List;
32
import org.apache.commons.io.FileUtils;
33
import org.apache.commons.lang3.mutable.MutableInt;
34
import org.gvsig.fmap.dal.DALLocator;
35
import org.gvsig.fmap.dal.DataManager;
36
import org.gvsig.fmap.dal.DataServerExplorer;
37
import org.gvsig.fmap.dal.DataStore;
38
import org.gvsig.fmap.dal.DataStoreNotification;
39
import org.gvsig.fmap.dal.DataTypes;
40
import org.gvsig.fmap.dal.FileHelper;
41
import org.gvsig.fmap.dal.exception.CloseException;
42
import org.gvsig.fmap.dal.exception.DataException;
43
import org.gvsig.fmap.dal.exception.FileNotFoundException;
44
import org.gvsig.fmap.dal.exception.InitializeException;
45
import org.gvsig.fmap.dal.exception.OpenException;
46
import org.gvsig.fmap.dal.exception.ReadException;
47
import org.gvsig.fmap.dal.exception.UnsupportedVersionException;
48
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
49
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
50
import org.gvsig.fmap.dal.feature.EditableFeatureType;
51
import org.gvsig.fmap.dal.feature.Feature;
52
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
53
import org.gvsig.fmap.dal.feature.FeatureQuery;
54
import org.gvsig.fmap.dal.feature.FeatureSet;
55
import org.gvsig.fmap.dal.feature.FeatureStore;
56
import org.gvsig.fmap.dal.feature.FeatureType;
57
import org.gvsig.fmap.dal.feature.FeatureType.FeatureTypeChanged;
58
import org.gvsig.fmap.dal.feature.exception.AttributeNameException;
59
import org.gvsig.fmap.dal.feature.exception.AttributeNameTooLongException;
60
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
61
import org.gvsig.fmap.dal.feature.spi.AbstractFeatureStoreProvider;
62
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
63
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
64
import org.gvsig.fmap.dal.feature.spi.FeatureSetProvider;
65
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
66
import org.gvsig.fmap.dal.resource.ResourceAction;
67
import org.gvsig.fmap.dal.resource.exception.ResourceException;
68
import org.gvsig.fmap.dal.resource.exception.ResourceExecuteException;
69
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyChangesException;
70
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyCloseException;
71
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyOpenException;
72
import org.gvsig.fmap.dal.resource.file.FileResource;
73
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
74
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
75
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorer;
76
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemServerExplorerParameters;
77
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
78
import org.gvsig.fmap.dal.store.dbf.utils.DbaseFile;
79
import org.gvsig.fmap.dal.store.dbf.utils.FieldFormatter;
80
import org.gvsig.metadata.MetadataLocator;
81
import org.gvsig.metadata.MetadataManager;
82
import org.gvsig.metadata.exceptions.MetadataException;
83
import org.gvsig.tools.dataTypes.CoercionException;
84
import org.gvsig.tools.dispose.DisposableIterator;
85
import org.gvsig.tools.dynobject.DynObject;
86
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
87
import org.gvsig.tools.exception.BaseException;
88
import org.gvsig.tools.logger.FilteredLogger;
89
import org.gvsig.tools.math.BigDecimalUtils;
90
import org.slf4j.Logger;
91
import org.slf4j.LoggerFactory;
92

    
93

    
94
public class DBFStoreProvider extends AbstractFeatureStoreProvider implements
95
        ResourceConsumer {
96

    
97
    private static final Logger LOG = LoggerFactory.getLogger(DBFStoreProvider.class);
98

    
99
    public static final int MAX_FIELD_NAME_LENGTH = DbaseFile.MAX_FIELD_NAME_LENGTH;
100

    
101
    public static final String NAME = DataStore.DBASE_PROVIDER_NAME;
102
    public static final String DESCRIPTION = "DBF file";
103

    
104
    public static final String METADATA_DEFINITION_NAME = NAME;
105
    private static final String METADATA_ENCODING = "Encoding";
106
    private static final String METADATA_CODEPAGE = "CodePage";
107

    
108
    private DbaseFile dbfFile = null;
109
    private ResourceProvider dbfResource;
110
    private long counterNewsOIDs = -1;
111
    private DBFFeatureWriter writer;
112

    
113
    private boolean loTengoEnUso;
114

    
115
    private boolean allowDuplicatedFieldNames;
116
    private FilteredLogger logger;
117
    
118
    protected EditableFeatureType featureType;
119

    
120

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

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

    
142
    protected DBFStoreProvider(DBFStoreParameters params,
143
            DataStoreProviderServices storeServices, DynObject metadata)
144
            throws InitializeException {
145
        super(params, storeServices, metadata);
146
        this.logger = new FilteredLogger(LOG, "DBFStoreProvider", -1);
147
        this.logger.setInterval(2000L);
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
    @Override
173
    public Object getDynValue(String name) throws DynFieldNotFoundException {
174
        try {
175
            this.open();
176
        } catch (OpenException e) {
177
            throw new RuntimeException(e);
178
        }
179

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

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

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

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

    
204
    @Override
205
    public String getProviderName() {
206
        return NAME;
207
    }
208

    
209
    protected DBFStoreParameters getDBFParameters() {
210
        return (DBFStoreParameters) super.getParameters();
211
    }
212

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

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

    
235
    @Override
236
    public void performChanges(Iterator deleteds, Iterator inserteds,
237
            Iterator updateds, Iterator originalFeatureTypesUpdated)
238
            throws PerformEditingException {
239

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

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

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

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

    
265
                        tmpParams.setDBFFile(tmp_file);
266

    
267
                        writer.begin(tmpParams, theStore.getDefaultFeatureType(),
268
                                set.getSize());
269

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

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

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

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

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

    
319
        this.counterNewsOIDs = -1;
320
    }
321

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

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

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

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

    
358
        if (resp.length() == 0) {
359
            return null;
360
        } else {
361
            return "(" + resp + ")";
362
        }
363
    }
364

    
365
    @Override
366
    public FeatureProvider createFeatureProvider(FeatureType providerFeatureType) throws DataException {
367
        return new DBFFeatureProvider(this, providerFeatureType, null);
368
    }
369

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

    
374
    protected void initFeatureType() throws InitializeException {
375
        this.featureType = this.getTheFeatureType();
376
        FeatureType defaultType = this.featureType.getNotEditableCopy();
377
        this.setStoreFeatureType(defaultType);
378
    }
379

    
380
    protected void setStoreFeatureType(FeatureType ftype) {
381
        FeatureStoreProviderServices store = this.getStoreServices();
382
        List<FeatureType> ftypes = new ArrayList<>();
383
        ftypes.add(ftype);
384
        store.setFeatureTypes(ftypes, ftype);
385
    }
386
        
387
    protected EditableFeatureType getTheFeatureType() throws InitializeException {
388
        try {
389
            this.open();
390
        } catch (DataException e) {
391
            throw new InitializeException(this.getProviderName(), e);
392
        }
393
        EditableFeatureType featureType = (EditableFeatureType) getResource().execute(
394
            new ResourceAction() {
395
                public Object run() throws Exception {
396
                    EditableFeatureType featureType = getStoreServices().createFeatureType(getName());
397
                    featureType.setHasOID(true);
398
                    dbfFile.getHeader().toFeatureType(
399
                            featureType,
400
                            getDBFParameters().handleDatesAsStrings()
401
                    );
402
                    return featureType;
403
                }
404
            }
405
        );
406
        return featureType;
407
    }
408
    
409
    
410
    protected void loadValue(DBFFeatureProvider featureProvider, long rowIndex,
411
            FeatureAttributeDescriptor descriptor) throws ReadException {
412

    
413
        if (descriptor.getEvaluator() != null) {
414
            // Nothing to do
415
            return;
416
        }
417

    
418
        int dbfFieldIndex = this.dbfFile.getFieldIndex(descriptor.getName());
419

    
420
        if (dbfFieldIndex < 0) {
421
            // Someone asked to load a field
422
            // which does not exist in the DBF file. This can happen
423
            // in editing process when a field has been added
424
            // in the current editing session, so we simply do nothing.
425
            // The expansion manager is expected to manage those new fields
426
            // and their values.
427
            this.logger.warn("The requested field ("+descriptor.getName()+") does not exist in the DBF file. Assumed it's a new field in editing mode.");
428
            return;
429
        }
430

    
431
        String value = null;
432
        try {
433
            value = this.dbfFile.getStringFieldValue(rowIndex, dbfFieldIndex);
434
        } catch (DataException e) {
435
            throw new ReadException(this.getName(), e);
436
        }
437
        value = value.trim();
438
        FieldFormatter formatter = new FieldFormatter();
439
        
440
        int index = descriptor.getIndex();
441
        Object defaultValue = descriptor.getDefaultValueCoerced();
442
        
443
        switch (descriptor.getType()) {
444
            case DataTypes.STRING:
445
                featureProvider.set(index, formatter.parseString(value, (String) defaultValue));
446
                break;
447

    
448
            case DataTypes.DECIMAL:
449
                BigDecimal d = formatter.parseDecimal(
450
                    value,
451
                    descriptor.getMathContext(),
452
                    descriptor.getScale(),
453
                    (BigDecimal) defaultValue
454
                );
455
                if (!BigDecimalUtils.isValid(d, descriptor.getScale(), descriptor.getPrecision())) {
456
                    DBFStoreParameters params = this.getDBFParameters();
457
                    if(params.allowInconsistenciesInDecimals() ) {
458
                        MutableInt scale = new MutableInt(descriptor.getScale());
459
                        MutableInt precision = new MutableInt(descriptor.getPrecision());
460
                        d = BigDecimalUtils.fixIfCan(d, scale, precision);
461
                        updateStoreScaleAndPrecision(featureProvider, descriptor, scale.intValue(), precision.intValue());
462
                    } else {
463
                        FeatureType storeFeatureType = featureProvider.getStoreFeatureType();
464
                        if(storeFeatureType != null){
465
                            FeatureAttributeDescriptor storeDescriptor = storeFeatureType.getAttributeDescriptor(descriptor.getName());
466
                            if (storeDescriptor instanceof EditableFeatureAttributeDescriptor && BigDecimalUtils.isValid(d, storeDescriptor.getScale(), storeDescriptor.getPrecision())) {
467
                                ((EditableFeatureAttributeDescriptor)descriptor).setScale(storeDescriptor.getScale());
468
                                ((EditableFeatureAttributeDescriptor)descriptor).setPrecision(storeDescriptor.getPrecision());
469
                            }
470
                        }
471
                    }
472
                }
473
                featureProvider.set(index,d);
474
                break;
475

    
476
            case DataTypes.DOUBLE:
477
                featureProvider.set(index, formatter.parseDouble(value, (Double) defaultValue));
478
                break;
479

    
480
            case DataTypes.FLOAT:
481
                featureProvider.set(index, formatter.parseFloat(value, (Float) defaultValue));
482
                break;
483

    
484
            case DataTypes.LONG:
485
                featureProvider.set(index, formatter.parseLong(value, (Long) defaultValue));
486
                break;
487

    
488
            case DataTypes.INT:
489
                featureProvider.set(index, formatter.parseInt(value, (Integer) defaultValue));
490
                break;
491

    
492
            case DataTypes.BYTE:
493
                featureProvider.set(index, formatter.parseByte(value, (Byte) defaultValue));
494
                break;
495

    
496
            case DataTypes.BOOLEAN:
497
                featureProvider.set(index, formatter.parseBoolean(value, (Boolean) defaultValue));
498
                break;
499

    
500
            case DataTypes.TIMESTAMP:
501
                featureProvider.set(index, formatter.parseTimestamp(value, (java.sql.Timestamp) defaultValue));
502
                break;
503
                
504
            case DataTypes.TIME:
505
                featureProvider.set(index, formatter.parseTime(value, (java.sql.Time) defaultValue));
506
                break;
507

    
508
            case DataTypes.DATE:
509
                featureProvider.set(index, formatter.parseDate(value, (java.sql.Date) defaultValue));
510
                break;
511

    
512
            default: {
513
                    Object v;
514
                    try {
515
                        v = descriptor.getDataType().coerce(value);
516
                    } catch (CoercionException ex) {
517
                        v = defaultValue;
518
                    }
519
                    featureProvider.set(index, v);
520
                }
521
                break;
522
        }
523
    }
524

    
525
    protected FeatureProvider getFeatureProviderByIndex(long index) throws DataException {
526
        return this
527
                .getFeatureProviderByIndex(index, this.getStoreServices()
528
                        .getDefaultFeatureType(), null);
529
    }
530

    
531
    @Override
532
    public long getFeatureCount() throws ReadException, OpenException,
533
            ResourceNotifyChangesException {
534
        this.open();
535
        return (long) getResource().execute(() -> (long) dbfFile.getRecordCount());
536
    }
537

    
538
    @Override
539
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType featureType)
540
            throws DataException {
541
        return new DBFSetProvider(this, query, featureType, null);
542
    }
543

    
544
    public FeatureSetProvider createSet(FeatureQuery query, FeatureType providerFeatureType, FeatureType storeFeatureType)
545
            throws DataException {
546
        return new DBFSetProvider(this, query, providerFeatureType, storeFeatureType);
547
    }
548

    
549
    public boolean canCreate() {
550
        return true;
551
    }
552

    
553
    public boolean canWriteGeometry(int geometryType, int geometrySubType) throws DataException {
554
        return false;
555
    }
556

    
557
    public void open() throws OpenException {
558
        if (this.dbfFile.isOpen()) {
559
            return;
560
        }
561
        try {
562
            getResource().execute(new ResourceAction() {
563
                public Object run() throws Exception {
564
                    openFile();
565
                    resourcesOpen();
566
                    return null;
567
                }
568
            });
569

    
570
        } catch (ResourceExecuteException e) {
571
            throw new OpenException(this.getProviderName(), e);
572
        }
573
    }
574

    
575
    protected void openFile() throws FileNotFoundException,
576
            UnsupportedVersionException, IOException, DataException {
577
        this.dbfFile.open();
578
        // necessary when editing the file
579
        this.getDBFParameters().setEffectiveEncoding(this.dbfFile.getCharsetName());
580
    }
581

    
582
    public void close() throws CloseException {
583
        if( loTengoEnUso ) {
584
            return;
585
        }
586
        if (dbfFile == null || !this.dbfFile.isOpen()) {
587
            return;
588
        }
589
        super.close();
590

    
591
        //Cerrar recurso
592
        try {
593
            getResource().execute(new ResourceAction() {
594
                public Object run() throws Exception {
595
                    closeFile();
596
                    resourcesNotifyClose();
597
                    return null;
598
                }
599
            });
600
        } catch (ResourceExecuteException  e) {
601
            throw new CloseException(this.getProviderName(), e);
602
        }
603
    }
604

    
605
    protected void closeFile() throws CloseException {
606
        this.dbfFile.close();
607
    }
608

    
609
    @Override
610
    protected void doDispose() throws BaseException {
611
        this.close();
612
        dbfFile = null;
613
        disposeResource();
614
        super.doDispose();
615
    }
616

    
617
    protected void disposeResource() {
618
        this.dbfResource.removeConsumer(this);
619
        dbfResource = null;
620
    }
621

    
622
    public boolean closeResourceRequested(ResourceProvider resource) {
623
        try {
624
            this.close();
625
        } catch (CloseException e) {
626
            return false;
627
        }
628
        return true;
629
    }
630

    
631
    public boolean allowWrite() {
632
        if(allowDuplicatedFieldNames || this.getDBFParameters().allowInconsistenciesInDecimals()){
633
            return false;
634
        }
635
        return this.dbfFile.isWritable();
636
    }
637

    
638
    public void refresh() throws OpenException {
639
        try {
640
            this.close();
641
        } catch (CloseException e) {
642
            throw new OpenException(this.getProviderName(), e);
643
        }
644
        this.open();
645
        try {
646
            this.initFeatureType();
647
        } catch (InitializeException e) {
648
            throw new OpenException(this.getProviderName(), e);
649
        }
650
    }
651

    
652
    protected FeatureProvider getFeatureProviderByIndex(long index,
653
            FeatureType providerFeatureType, FeatureType storeFeatureType) throws DataException {
654
        FeatureProvider featureProvider = this.createFeatureProvider(providerFeatureType, storeFeatureType);
655
        featureProvider.setOID(index);
656
        return featureProvider;
657
    }
658

    
659
    protected void initFeatureProviderByIndex(FeatureProvider featureProvider,
660
            long index, FeatureType featureType) throws DataException {
661
        featureProvider.setOID(index);
662
    }
663

    
664
    protected void loadFeatureProviderByIndex(FeatureProvider featureProvider)
665
            throws DataException {
666

    
667
        long index = ((Long) featureProvider.getOID());
668
        int rec_count = this.dbfFile.getRecordCount();
669

    
670
        if (index >= rec_count) {
671

    
672
            ReadException rex = new ReadException(this.getName(),
673
                    new ArrayIndexOutOfBoundsException(
674
                            "Index of requested feature ("
675
                            + index + ") is >= record count (" + rec_count + ")"));
676

    
677
            LOG.info("Error while loading feature. ", rex);
678
            throw rex;
679
        }
680

    
681
//        long t1 = System.nanoTime();
682
        ((DBFFeatureProvider)featureProvider).setBroken(false);
683
        for (FeatureAttributeDescriptor desc : featureProvider.getType()) {
684
            try {
685
                this.loadValue((DBFFeatureProvider)featureProvider, index, desc);
686
            } catch (Throwable th) {
687
                this.logger.warn("Can't load value for attribute '"+desc.getName()+"'", th);
688
                ((DBFFeatureProvider)featureProvider).setBroken(true);
689
                featureProvider.set(desc.getIndex(), null);
690
            }
691
        }
692
//        long t2 = System.nanoTime();
693
//        LOG.info("load "+index+" in :"+(t2-t1));
694
    }
695

    
696
    @Override
697
    public int getOIDType() {
698
        return DataTypes.LONG;
699
    }
700

    
701
    @Override
702
    public Object createNewOID() {
703
        if (this.counterNewsOIDs < 0) {
704
            try {
705
                this.counterNewsOIDs = this.getFeatureCount();
706
            } catch (DataException e) {
707
                LOG.warn("Can't initialice counter for news OIDs.",e);
708
            }
709

    
710
        } else {
711
            this.counterNewsOIDs++;
712
        }
713
        return counterNewsOIDs;
714
    }
715

    
716
    @Override
717
    public boolean supportsAppendMode() {
718
        return true;
719
    }
720

    
721
    @Override
722
    public void append(final FeatureProvider featureProvider)
723
            throws DataException {
724
        getResource().execute(() -> {
725
            writer.append(getStoreServices().createFeature(featureProvider));
726
            return null;
727
        });
728
    }
729

    
730
    @Override
731
    public void beginAppend() throws DataException {
732
        this.close();
733
        getResource().execute(() -> {
734
            writer.begin(getDBFParameters(),
735
                    getStoreServices().getDefaultFeatureType(),
736
                    getStoreServices().getFeatureStore().getFeatureCount());
737
            return null;
738
        });
739
    }
740

    
741
    public void endAppend() throws DataException {
742
        getResource().execute(new ResourceAction() {
743
            public Object run() throws Exception {
744
                writer.end();
745
                resourcesNotifyChanges();
746
                counterNewsOIDs = -1;
747
                return null;
748
            }
749
        });
750
    }
751

    
752
    public void resourceChanged(ResourceProvider resource) {
753
        if (this.getStoreServices()!=null){
754
            this.getStoreServices().notifyChange(
755
                    DataStoreNotification.RESOURCE_CHANGED,
756
                    resource);
757
        }
758
    }
759

    
760
    protected void resourcesNotifyChanges()
761
            throws ResourceNotifyChangesException {
762
        this.dbfResource.notifyChanges();
763
    }
764

    
765
    protected void resourcesNotifyClose() throws ResourceNotifyCloseException {
766
        this.dbfResource.notifyClose();
767
    }
768

    
769
    protected void resourcesOpen() throws ResourceNotifyOpenException {
770
        this.dbfResource.notifyOpen();
771
    }
772

    
773
    public Object getSourceId() {
774
        return this.getDBFParameters().getFile();
775
    }
776

    
777
    public String getName() {
778
        String name = this.getDBFParameters().getFile().getName();
779
        int n = name.lastIndexOf(".");
780
        if (n < 1) {
781
            return name;
782
        }
783
        return name.substring(0, n);
784
    }
785

    
786
    public String getFullName() {
787
        return this.getDBFParameters().getFile().getAbsolutePath();
788
    }
789

    
790
    protected void resourceCloseRequest() throws ResourceException {
791
        this.dbfResource.closeRequest();
792
    }
793

    
794
    public ResourceProvider getResource() {
795
        return dbfResource;
796
    }
797
    
798
    @Override
799
    public void fixFeatureTypeFromParameters() {
800
        FeatureStoreProviderServices store = this.getStoreServices();
801
        try {
802
            int sizeWithoutComputed = 0;
803
            for (FeatureAttributeDescriptor featureAttributeDescriptor : store.getDefaultFeatureType()) {
804
                if (featureAttributeDescriptor.isComputed()) {
805
                    continue;
806
                }
807
                sizeWithoutComputed +=1;
808
            }
809
            if(this.featureType.size()!=sizeWithoutComputed) {
810
                this.setStoreFeatureType(featureType.getNotEditableCopy());
811
            }
812
        } catch (DataException ex) {
813
            
814
        }
815
    }
816

    
817
    private void updateStoreScaleAndPrecision(DBFFeatureProvider featureProvider, FeatureAttributeDescriptor descriptor, int scale, int precision) {
818
        if(descriptor.getType() == DataTypes.DECIMAL){
819
            if(descriptor.getScale() == scale && descriptor.getPrecision() == precision) {
820
                return;
821
            }
822
            FeatureType ft = this.getFeatureStore().getDefaultFeatureTypeQuietly();
823
            FeatureAttributeDescriptor d = ft.getAttributeDescriptor(descriptor.getName());
824
            if(d.getScale() == scale && d.getPrecision() == precision) {
825
                ((DBFFeatureProvider)featureProvider).setProviderFeatureType(ft);
826
                return;
827
            }
828
            EditableFeatureType eft = ft.getEditable();
829
            EditableFeatureAttributeDescriptor ed = (EditableFeatureAttributeDescriptor) eft.getAttributeDescriptor(descriptor.getName());
830
            ed.setScale(scale);
831
            ed.setPrecision(precision);
832
            ft = eft.getNotEditableCopy();
833
            ((DBFFeatureProvider)featureProvider).setProviderFeatureType(ft);
834
            this.setStoreFeatureType(ft);
835
        }
836
    }
837

    
838
}