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.shp / src / main / java / org / gvsig / fmap / dal / store / shp / SHPStoreProvider.java @ 44669

History | View | Annotate | Download (22.7 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.shp;
24

    
25
import java.io.File;
26
import java.io.IOException;
27
import java.util.ArrayList;
28
import java.util.Iterator;
29
import java.util.List;
30

    
31
import org.apache.commons.io.FileUtils;
32
import org.apache.commons.io.FilenameUtils;
33
import org.cresques.cts.IProjection;
34
import org.slf4j.Logger;
35
import org.slf4j.LoggerFactory;
36

    
37
import org.gvsig.fmap.dal.DataStore;
38
import org.gvsig.fmap.dal.DataTypes;
39
import org.gvsig.fmap.dal.FileHelper;
40
import org.gvsig.fmap.dal.exception.CloseException;
41
import org.gvsig.fmap.dal.exception.DataException;
42
import org.gvsig.fmap.dal.exception.InitializeException;
43
import org.gvsig.fmap.dal.exception.ReadException;
44
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
45
import org.gvsig.fmap.dal.feature.EditableFeatureType;
46
import org.gvsig.fmap.dal.feature.Feature;
47
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
48
import org.gvsig.fmap.dal.feature.FeatureSet;
49
import org.gvsig.fmap.dal.feature.FeatureStore;
50
import org.gvsig.fmap.dal.feature.FeatureType;
51
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
52
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
53
import org.gvsig.fmap.dal.resource.ResourceAction;
54
import org.gvsig.fmap.dal.resource.exception.ResourceException;
55
import org.gvsig.fmap.dal.resource.exception.ResourceExecuteException;
56
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyChangesException;
57
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyCloseException;
58
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyOpenException;
59
import org.gvsig.fmap.dal.resource.file.FileResource;
60
import org.gvsig.fmap.dal.resource.spi.MultiResource;
61
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
62
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
63
import org.gvsig.fmap.dal.store.dbf.DBFStoreParameters;
64
import org.gvsig.fmap.dal.store.dbf.DBFStoreProvider;
65
import org.gvsig.fmap.dal.store.shp.utils.ISHPFile;
66
import org.gvsig.fmap.dal.store.shp.utils.SHP;
67
import org.gvsig.fmap.dal.store.shp.utils.SHPFile2;
68
import org.gvsig.fmap.geom.Geometry;
69
import org.gvsig.fmap.geom.GeometryLocator;
70
import org.gvsig.fmap.geom.GeometryManager;
71
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
72
import org.gvsig.fmap.geom.exception.CreateGeometryException;
73
import org.gvsig.fmap.geom.primitive.Envelope;
74
import org.gvsig.tools.dispose.DisposableIterator;
75
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
76
import org.gvsig.tools.exception.BaseException;
77

    
78
/**
79
 *
80
 */
81
public class SHPStoreProvider extends DBFStoreProvider {
82

    
83
    private static final GeometryManager GEOM_MANAGER = GeometryLocator.getGeometryManager();
84
    private static final Logger LOGGER = LoggerFactory.getLogger(SHPStoreProvider.class);
85
    
86
    public static String NAME = DataStore.SHAPE_PROVIDER_NAME;
87
    public static String DESCRIPTION = "Shape file";
88
    private ISHPFile shpFile;
89

    
90
    private MultiResource resource;
91

    
92
    public static final String GEOMETRY_ATTIBUTE_NAME = "GEOMETRY";
93

    
94
    public static final String METADATA_DEFINITION_NAME = NAME;
95

    
96
    private SHPFeatureWriter writer = null;
97

    
98
    private boolean loTengoEnUso;
99

    
100
    /**
101
     * @param params
102
     * @param storeServices
103
     * @throws InitializeException
104
     */
105
    public SHPStoreProvider(SHPStoreParameters params, DataStoreProviderServices storeServices)
106
        throws InitializeException {
107
        super(params, storeServices, FileHelper.newMetadataContainer(METADATA_DEFINITION_NAME));
108
    }
109

    
110
    protected void init(DBFStoreParameters params, DataStoreProviderServices storeServices) throws InitializeException {
111

    
112
        this.shpFile = new SHPFile2((SHPStoreParameters) params);
113
        super.init(params, storeServices);
114
        this.shpFile.setUseNullGeometry(this.getShpParameters().getUseNullGeometry());
115
    }
116

    
117
    @Override
118
    public SHPStoreParameters getParameters() {
119
        return (SHPStoreParameters) super.getParameters();
120
    }
121

    
122
    public IProjection getProjection() {
123
        return this.getShpParameters().getCRS();
124
    }
125
    
126
    @Override
127
    public Object getDynValue(String name) throws DynFieldNotFoundException {
128
        if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
129

    
130
            return this.getShpParameters().getCRS();
131

    
132
        } else if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
133
            try {
134
                return this.shpFile.getFullExtent();
135
            } catch (ReadException e) {
136
                return null;
137
            }
138
        }
139
        return super.getDynValue(name);
140
    }
141

    
142
    protected void initResource(DBFStoreParameters params, DataStoreProviderServices storeServices)
143
        throws InitializeException {
144

    
145
        SHPStoreParameters shpParams = (SHPStoreParameters) params;
146
        resource = (MultiResource) createResource(MultiResource.TYPE_NAME, new Object[] { shpParams.getSHPFileName() });
147

    
148
        resource.addResource(FileResource.NAME, new Object[] { shpParams.getSHPFileName() }, true);
149
        resource.addResource(FileResource.NAME, new Object[] { shpParams.getSHXFileName() }, true);
150
        resource.addResource(FileResource.NAME, new Object[] { shpParams.getDBFFileName() }, true);
151

    
152
        resource.frozen();
153
        resource.addMultiResourceConsumer(this);
154
        super.initResource(resource, storeServices);
155
    }
156

    
157
    ;
158

    
159
    public ResourceProvider getResource() {
160
        return resource;
161
    }
162

    
163
    /**
164
     *
165
     * @throws ResourceNotifyChangesException
166
     */
167
    protected void resourcesNotifyChanges() throws ResourceNotifyChangesException {
168
        getResource().notifyChanges();
169
        // TODO .prj
170

    
171
    }
172

    
173
    /**
174
     * @throws ResourceNotifyCloseException
175
     *
176
     */
177
    protected void resourcesNotifyClose() throws ResourceNotifyCloseException {
178
        getResource().notifyClose();
179
        // TODO .prj
180

    
181
    }
182

    
183
    @Override
184
    protected void doDispose() throws BaseException {
185
        super.doDispose();
186
        getResource().removeConsumer(this);
187
        this.writer = null;
188
        this.shpFile = null;
189
    }
190

    
191
    protected void disposeResource() {
192
        getResource().removeConsumer(this);
193
    }
194

    
195
    /**
196
     * @throws ResourceNotifyOpenException
197
     *
198
     */
199
    protected void resourcesOpen() throws ResourceNotifyOpenException {
200
        getResource().notifyOpen();
201
    }
202

    
203
    protected static EditableFeatureAttributeDescriptor addGeometryColumn(EditableFeatureType fType) {
204

    
205
        EditableFeatureAttributeDescriptor attrTmp = null;
206
        EditableFeatureAttributeDescriptor attr = null;
207
        Iterator<?> iter = fType.iterator();
208
        while (iter.hasNext()) {
209
            attrTmp = (EditableFeatureAttributeDescriptor) iter.next();
210
            if (attrTmp.getType() == DataTypes.GEOMETRY) {
211
                if (attr != null) {
212
                    // Two geom fields not allowed
213
                    fType.remove(attrTmp.getName());
214
                } else {
215
                    attr = attrTmp;
216
                }
217
            }
218
        }
219

    
220
        if (attr == null) {
221
            String geofield = createGeometryFieldName(fType);
222
            attr = fType.add(geofield, DataTypes.GEOMETRY);
223
            attr.setDefaultValue(null);
224
        }
225

    
226
        attr.setObjectClass(Geometry.class);
227
        attr.setAllowNull(true);
228
//        fType.setDefaultGeometryAttributeName(attr.getName());
229
        return attr;
230

    
231
    }
232

    
233
    private static String createGeometryFieldName(FeatureType ft) {
234

    
235
        if (ft.getAttributeDescriptor(GEOMETRY_ATTIBUTE_NAME) == null) {
236
            return GEOMETRY_ATTIBUTE_NAME;
237
        }
238

    
239
        int i = 0;
240
        String candidate = GEOMETRY_ATTIBUTE_NAME + i;
241
        while (ft.getAttributeDescriptor(candidate) != null) {
242
            i++;
243
            candidate = GEOMETRY_ATTIBUTE_NAME + i;
244
        }
245
        return candidate;
246
    }
247

    
248
    protected EditableFeatureType getTheFeatureType() throws InitializeException{
249
        try {
250
            this.open();
251
        } catch (DataException e) {
252
            throw new InitializeException(this.getProviderName(), e);
253
        }
254
        final EditableFeatureType fType = super.getTheFeatureType();
255
        try {
256
            getResource().execute(new ResourceAction() {
257

    
258
                public Object run() throws Exception {
259
                    EditableFeatureAttributeDescriptor attr = addGeometryColumn(fType);
260

    
261
                    attr.setGeometryType(GEOM_MANAGER.getGeometryType(
262
                        shpFile.getGeometryType(),
263
                        shpFile.getGeometrySubType()
264
                    ));
265

    
266
                    IProjection srs = getShpParameters().getCRS();
267
                    attr.setSRS(srs);
268

    
269
                    return null;
270
                }
271
            });
272
            return fType;
273
        } catch (ResourceExecuteException e) {
274
            throw new InitializeException(e);
275
        }
276
    }
277

    
278
    protected SHPStoreParameters getShpParameters() {
279
        return (SHPStoreParameters) getParameters();
280
    }
281

    
282
    public String getProviderName() {
283
        return NAME;
284
    }
285

    
286
    public boolean allowWrite() {
287
        return this.shpFile.isEditable() &&
288
            super.allowWrite() &&
289
            !this.getShpParameters().getLoadCorruptGeometriesAsNull() &&
290
            !this.getShpParameters().getAllowInconsistenciesInGeometryType() &&
291
            !this.getShpParameters().getFixLinearRings();
292
    }
293

    
294
    /**
295
     *
296
     * @param index
297
     * @param featureType
298
     * @return
299
     * @throws ReadException
300
     */
301
    protected FeatureProvider getFeatureProviderByIndex(long index, FeatureType featureType) throws DataException {
302
        this.open();
303
        try {
304

    
305
            FeatureProvider featureProvider = super.getFeatureProviderByIndex(index, featureType);
306
            featureProvider.setDefaultEnvelope(this.shpFile.getBoundingBox(index));
307
            return featureProvider;
308
        } catch (DataException e) {
309
            throw e;
310
        } catch (CreateEnvelopeException e) {
311
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
312
        } catch (CreateGeometryException e) {
313
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
314
        }
315

    
316
    }
317

    
318
    protected void initFeatureProviderByIndex(FeatureProvider featureProvider, long index, FeatureType featureType)
319
        throws DataException {
320
        try {
321
            super.initFeatureProviderByIndex(featureProvider, index, featureType);
322
            featureProvider.setDefaultEnvelope(this.shpFile.getBoundingBox(index));
323
        } catch (CreateEnvelopeException e) {
324
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
325
        } catch (CreateGeometryException e) {
326
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
327
        }
328
    }
329

    
330
    /**
331
     *
332
     * @param featureProvider
333
     * @throws DataException
334
     */
335
    @Override
336
    protected void loadFeatureProviderByIndex(FeatureProvider featureProvider) throws DataException {
337

    
338
        FeatureType featureType = featureProvider.getType();
339
        long index = ((Long) featureProvider.getOID());
340
        boolean hasGeometry = false;
341
        int i = featureType.getDefaultGeometryAttributeIndex();
342
        if (i >= 0) {
343
            if (!featureProvider.isReadOnly(i)) {
344
                Geometry geom;
345
                try {
346
                    geom = this.shpFile.getGeometry(index);
347
                } catch (Exception e) {
348
                    if( this.getShpParameters().getLoadCorruptGeometriesAsNull() ) {
349
                        geom = null;
350
                    } else {
351
                        throw new ReadGeometryException(getName(), featureProvider.getOID(), e);
352
                    }
353
                }
354
                featureProvider.set(i, geom);
355
            }
356
            hasGeometry = true;
357
        }
358
        if (hasDBFAttributes(featureType, hasGeometry)) {
359
            super.loadFeatureProviderByIndex(featureProvider);
360
        }
361

    
362
    }
363

    
364
    public class ReadGeometryException extends ReadException {
365

    
366
        private final static String MESSAGE_FORMAT = "There was errors loading a geometry from '%(store)'.\nCheck 'Load corrupt geometries as null' in the shape's properties of the add layer dialog to skip corrupt geometries. The layer will become read only.";
367
        private final static String MESSAGE_KEY = "_ReadGeometryException";
368
        private static final long serialVersionUID = 2626155328734197112L;
369

    
370
        public ReadGeometryException(String store, Object oid, Throwable cause) {
371
            super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
372
            setValue("store", store);
373
            setValue("storeParameters",getParameters());
374
        }
375
    }
376

    
377
    private boolean hasDBFAttributes(FeatureType featureType, boolean hasGeometry) {
378
        FeatureAttributeDescriptor[] attributes = featureType.getAttributeDescriptors();
379
        // If there aren't any attributes, nor has any DBF attributes
380
        if (attributes == null || attributes.length == 0) {
381
            return false;
382
        }
383
        // If there is only one attribute and it is the geometry one
384
        if (attributes.length == 1 && hasGeometry) {
385
            return false;
386
        }
387
        // In any other case
388
        return true;
389
    }
390

    
391
    protected void loadValue(FeatureProvider featureProvider, long rowIndex, FeatureAttributeDescriptor descriptor)
392
        throws ReadException {
393
        if (descriptor.getType() == DataTypes.GEOMETRY) {
394
            return;
395
        } else {
396
            super.loadValue(featureProvider, rowIndex, descriptor);
397
        }
398
    }
399

    
400
    public FeatureProvider createFeatureProvider(FeatureType type) throws DataException {
401
        FeatureProvider data = new SHPFeatureProvider(this, type);
402
        return data;
403
    }
404

    
405
    protected void openFile() throws IOException, DataException {
406
        super.openFile();
407
        this.shpFile.open();
408

    
409
    }
410

    
411
    protected void closeFile() throws CloseException {
412
        super.closeFile();
413
        if (!this.shpFile.isOpen()) {
414
            return;
415
        }
416
        this.shpFile.close();
417
    }
418

    
419
    public boolean canWriteGeometry(final int geometryType, int geometrySubType) throws DataException {
420
        this.open();
421
        return ((Boolean) getResource().execute(new ResourceAction() {
422

    
423
            public Object run() throws Exception {
424
                boolean value = shpFile.canWriteGeometry(geometryType);
425
                return value ? Boolean.TRUE : Boolean.FALSE;
426
            }
427
        })).booleanValue();
428
    }
429

    
430
    @SuppressWarnings("rawtypes")
431
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds,
432
        Iterator originalFeatureTypesUpdated) throws PerformEditingException {
433
        final List<Object> originalFeatureTypesUpdatedList = new ArrayList<>();
434
        while (originalFeatureTypesUpdated.hasNext()) {
435
            originalFeatureTypesUpdatedList.add(originalFeatureTypesUpdated.next());
436
        }
437
        
438
        /*
439
         * This will throw an exception if there are new fields
440
         * with names too long
441
         */
442
        checkNewFieldsNameSize(originalFeatureTypesUpdatedList.iterator());
443

    
444
        final FeatureType fType;
445
        try {
446
            fType = this.getStoreServices().getDefaultFeatureType();
447
        } catch (DataException e) {
448
            throw new PerformEditingException(this.getProviderName(), e);
449
        }
450
        try {
451
            // TODO repasar el concepto de enUso de un recurso.
452
            loTengoEnUso = true;
453
            resourceCloseRequest();
454

    
455
            getResource().execute(new ResourceAction() {
456

    
457
                public Object run() throws Exception {
458
                    FeatureSet set = null;
459
                    DisposableIterator iter = null;
460
                    try {
461
                        set = getFeatureStore().getFeatureSet();
462
                        writer = new SHPFeatureWriter(getProviderName());
463

    
464
                        SHPStoreParameters shpParams = getShpParameters();
465
                        SHPStoreParameters tmpParams = (SHPStoreParameters) shpParams.getCopy();
466

    
467
                        File tmp_base = File.createTempFile("tmp_" + System.currentTimeMillis(), null);
468
                        String str_base = tmp_base.getCanonicalPath();
469

    
470
                        tmpParams.setDBFFile(str_base + ".dbf");
471
                        tmpParams.setSHPFile(str_base + ".shp");
472
                        tmpParams.setSHXFile(str_base + ".shx");
473

    
474
                        writer.begin(tmpParams, fType, set.getSize());
475

    
476
                        iter = set.fastIterator();
477
                        while (iter.hasNext()) {
478
                            Feature feature = (Feature) iter.next();
479
                            writer.append(feature);
480
                        }
481

    
482
                        writer.end();
483
                        loTengoEnUso = false;
484
                        close();
485

    
486

    
487
                        if (!shpParams.getDBFFile().delete()) {
488
                            LOGGER.debug("Can't delete dbf file '" + shpParams.getDBFFile() + "'.");
489
                            throw new IOException("Can't delete dbf '"
490
                                + FilenameUtils.getBaseName(shpParams.getDBFFileName())
491
                                + "' file to replace with the new dbf.\nThe new dbf is in temporary file '" + str_base
492
                                + "'");
493
                        }
494
                        if (!shpParams.getSHPFile().delete()) {
495
                            LOGGER.debug("Can't delete dbf file '" + shpParams.getSHPFile() + "'.");
496
                            throw new IOException("Can't delete shp '"
497
                                + FilenameUtils.getBaseName(shpParams.getSHPFileName())
498
                                + "' file to replace with the new shp.\nThe new shp is in temporary file '" + str_base
499
                                + "'");
500
                        }
501
                        if (!shpParams.getSHXFile().delete()) {
502
                            LOGGER.debug("Can't delete dbf file '" + shpParams.getSHXFile() + "'.");
503
                            throw new IOException("Can't delete shx '"
504
                                + FilenameUtils.getBaseName(shpParams.getSHXFileName())
505
                                + "' file to replace with the new shx.\nThe new shx is in temporary file '" + str_base
506
                                + "'");
507
                        }
508
                        if (shpParams.getCPGFile().exists() && !shpParams.getCPGFile().delete()) {
509
                            LOGGER.debug("Can't delete cpg file '" + shpParams.getCPGFile() + "'.");
510
                            throw new IOException("Can't delete cpg '"
511
                                + FilenameUtils.getBaseName(shpParams.getCPGFileName())
512
                                + "' file to replace with the new cpg.\nThe new cpg is in temporary file '" + str_base
513
                                + "'");
514
                        }
515

    
516
                        File prjFile = SHP.getPrjFile(shpParams.getSHPFile());
517
                        if (prjFile.exists()) {
518
                            if (!prjFile.delete()) {
519
                                LOGGER.debug("Can't delete prj file '" + prjFile + "'.");
520
                                throw new IOException("Can't delete shx '"
521
                                    + FilenameUtils.getBaseName(prjFile.getPath())
522
                                    + "' file to replace with the new shx.\nThe new shx is in temporary file '"
523
                                    + str_base + "'");
524
                            }
525
                        }
526
                        FileUtils.moveFile(tmpParams.getDBFFile(), shpParams.getDBFFile());
527
                        FileUtils.moveFile(tmpParams.getSHPFile(), shpParams.getSHPFile());
528
                        FileUtils.moveFile(tmpParams.getSHXFile(), shpParams.getSHXFile());
529
                        FileUtils.moveFile(tmpParams.getCPGFile(), shpParams.getCPGFile());
530

    
531
                        savePrjFile(shpParams.getFile(), tmpParams.getCRS());
532

    
533
                        resourcesNotifyChanges();
534
                        if (!originalFeatureTypesUpdatedList.isEmpty()) {
535
                             initFeatureType();
536
                        }
537
                       
538
                        return null;
539
                    } finally {
540
                        loTengoEnUso = false;
541
                        dispose(set);
542
                        dispose(iter);
543
                    }
544
                }
545
            });
546

    
547
        } catch (Exception e) {
548
            throw new PerformEditingException(this.getProviderName(), e);
549
        }
550

    
551
    }
552

    
553
    protected void resourceCloseRequest() throws ResourceException {
554
        getResource().closeRequest();
555
    }
556

    
557
    public Envelope getEnvelope() throws DataException {
558
        this.open();
559
        return (Envelope) this.getDynValue("Envelope");
560
    }
561

    
562
    public void append(final FeatureProvider featureProvider) throws DataException {
563
        getResource().execute(new ResourceAction() {
564

    
565
            public Object run() throws Exception {
566
                writer.append(getStoreServices().createFeature(featureProvider));
567
                return null;
568
            }
569
        });
570
    }
571

    
572
    public void beginAppend() throws DataException {
573
        getResource().execute(new ResourceAction() {
574

    
575
            public Object run() throws Exception {
576
                FeatureStore store = getFeatureStore();
577
                FeatureType fType = store.getDefaultFeatureType();
578

    
579
                writer = new SHPFeatureWriter(getProviderName());
580
                writer.begin(getShpParameters(), fType, store.getFeatureCount());
581
                return null;
582
            }
583
        });
584
    }
585

    
586
    public void endAppend() throws DataException {
587
        getResource().execute(new ResourceAction() {
588

    
589
            public Object run() throws Exception {
590
                writer.end();
591
                close();
592

    
593
                SHPStoreParameters shpParameters = SHPStoreProvider.this.getShpParameters();
594

    
595
                savePrjFile(shpParameters.getFile(), shpParameters.getCRS());
596

    
597
                resourcesNotifyChanges();
598
                return null;
599
            }
600
        });
601
    }
602

    
603
    public Object getSourceId() {
604
        return this.getShpParameters().getFile();
605
    }
606

    
607

    
608
}