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

History | View | Annotate | Download (21.9 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.Iterator;
28

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

    
36
import org.gvsig.fmap.dal.DataStore;
37
import org.gvsig.fmap.dal.DataTypes;
38
import org.gvsig.fmap.dal.FileHelper;
39
import org.gvsig.fmap.dal.exception.CloseException;
40
import org.gvsig.fmap.dal.exception.DataException;
41
import org.gvsig.fmap.dal.exception.InitializeException;
42
import org.gvsig.fmap.dal.exception.OpenException;
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 geomManager = GeometryLocator.getGeometryManager();
84
    private static final Logger logger = LoggerFactory.getLogger(SHPStoreProvider.class);
85
    public static String NAME = "Shape";
86
    public static String DESCRIPTION = "Shape file";
87
    private ISHPFile shpFile;
88

    
89
    private MultiResource resource;
90

    
91
    protected static final String GEOMETRY_ATTIBUTE_NAME = "GEOMETRY";
92

    
93
    public static final String METADATA_DEFINITION_NAME = NAME;
94

    
95
    private SHPFeatureWriter writer = null;
96

    
97
    private boolean loTengoEnUso;
98

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

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

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

    
116
    public Object getDynValue(String name) throws DynFieldNotFoundException {
117
        if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
118

    
119
            return this.getShpParameters().getCRS();
120

    
121
        } else if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
122
            try {
123
                return this.shpFile.getFullExtent();
124
            } catch (ReadException e) {
125
                return null;
126
            }
127
        }
128
        return super.getDynValue(name);
129
    }
130

    
131
    protected void initResource(DBFStoreParameters params, DataStoreProviderServices storeServices)
132
        throws InitializeException {
133

    
134
        SHPStoreParameters shpParams = (SHPStoreParameters) params;
135
        resource = (MultiResource) createResource(MultiResource.TYPE_NAME, new Object[] { shpParams.getSHPFileName() });
136

    
137
        resource.addResource(FileResource.NAME, new Object[] { shpParams.getSHPFileName() }, true);
138
        resource.addResource(FileResource.NAME, new Object[] { shpParams.getSHXFileName() }, true);
139
        resource.addResource(FileResource.NAME, new Object[] { shpParams.getDBFFileName() }, true);
140

    
141
        resource.frozen();
142
        resource.addMultiResourceConsumer(this);
143
    }
144

    
145
    ;
146

    
147
    public ResourceProvider getResource() {
148
        return resource;
149
    }
150

    
151
    /**
152
     *
153
     * @throws ResourceNotifyChangesException
154
     */
155
    protected void resourcesNotifyChanges() throws ResourceNotifyChangesException {
156
        getResource().notifyChanges();
157
        // TODO .prj
158

    
159
    }
160

    
161
    /**
162
     * @throws ResourceNotifyCloseException
163
     *
164
     */
165
    protected void resourcesNotifyClose() throws ResourceNotifyCloseException {
166
        getResource().notifyClose();
167
        // TODO .prj
168

    
169
    }
170

    
171
    @Override
172
    protected void doDispose() throws BaseException {
173
        super.doDispose();
174
        getResource().removeConsumer(this);
175
        this.writer = null;
176
        this.shpFile = null;
177
    }
178

    
179
    protected void disposeResource() {
180
        getResource().removeConsumer(this);
181
    }
182

    
183
    /**
184
     * @throws ResourceNotifyOpenException
185
     *
186
     */
187
    protected void resourcesOpen() throws ResourceNotifyOpenException {
188
        getResource().notifyOpen();
189
    }
190

    
191
    protected static EditableFeatureAttributeDescriptor addGeometryColumn(EditableFeatureType fType) {
192

    
193
        EditableFeatureAttributeDescriptor attrTmp = null;
194
        EditableFeatureAttributeDescriptor attr = null;
195
        Iterator<?> iter = fType.iterator();
196
        while (iter.hasNext()) {
197
            attrTmp = (EditableFeatureAttributeDescriptor) iter.next();
198
            if (attrTmp.getType() == DataTypes.GEOMETRY) {
199
                if (attr != null) {
200
                    // Two geom fields not allowed
201
                    fType.remove(attrTmp.getName());
202
                } else {
203
                    attr = attrTmp;
204
                }
205
            }
206
        }
207

    
208
        if (attr == null) {
209
            String geofield = createGeometryFieldName(fType);
210
            attr = fType.add(geofield, DataTypes.GEOMETRY);
211
            attr.setDefaultValue(null);
212
        }
213

    
214
        attr.setObjectClass(Geometry.class);
215
        fType.setDefaultGeometryAttributeName(attr.getName());
216
        return attr;
217

    
218
    }
219

    
220
    private static String createGeometryFieldName(FeatureType ft) {
221

    
222
        if (ft.getAttributeDescriptor(GEOMETRY_ATTIBUTE_NAME) == null) {
223
            return GEOMETRY_ATTIBUTE_NAME;
224
        }
225

    
226
        int i = 0;
227
        String candidate = GEOMETRY_ATTIBUTE_NAME + i;
228
        while (ft.getAttributeDescriptor(candidate) != null) {
229
            i++;
230
            candidate = GEOMETRY_ATTIBUTE_NAME + i;
231
        }
232
        return candidate;
233
    }
234

    
235
    protected static FeatureType removeGeometryColumn(EditableFeatureType fType) {
236
        Iterator<?> iter = fType.iterator();
237
        FeatureAttributeDescriptor attr;
238
        while (iter.hasNext()) {
239
            attr = (FeatureAttributeDescriptor) iter.next();
240
            if (attr.getType() == DataTypes.GEOMETRY) {
241
                iter.remove();
242
            }
243
        }
244
        fType.setDefaultGeometryAttributeName(null);
245
        return fType.getNotEditableCopy();
246
    }
247

    
248
    protected EditableFeatureType getTheFeatureType() throws InitializeException, OpenException {
249
        final EditableFeatureType fType = super.getTheFeatureType();
250
        this.open();
251
        try {
252
            getResource().execute(new ResourceAction() {
253

    
254
                public Object run() throws Exception {
255
                    EditableFeatureAttributeDescriptor attr = addGeometryColumn(fType);
256

    
257
                    attr.setGeometryType(geomManager.getGeometryType(shpFile.getGeometryType(),
258
                        shpFile.getGeometrySubType()));
259

    
260
                    IProjection srs = getShpParameters().getCRS();
261
                    attr.setSRS(srs);
262

    
263
                    return null;
264
                }
265
            });
266
            return fType;
267
        } catch (ResourceExecuteException e) {
268
            throw new InitializeException(e);
269
        }
270
    }
271

    
272
    protected SHPStoreParameters getShpParameters() {
273
        return (SHPStoreParameters) getParameters();
274
    }
275

    
276
    public String getProviderName() {
277
        return NAME; 
278
    }
279

    
280
    public boolean allowWrite() {
281
        return this.shpFile.isEditable();
282
    }
283

    
284
    /**
285
     *
286
     * @param index
287
     * @param featureType
288
     * @return
289
     * @throws ReadException
290
     */
291
    protected FeatureProvider getFeatureProviderByIndex(long index, FeatureType featureType) throws DataException {
292
        this.open();
293
        try {
294

    
295
            FeatureProvider featureProvider = super.getFeatureProviderByIndex(index, featureType);
296
            featureProvider.setDefaultEnvelope(this.shpFile.getBoundingBox(index));
297
            return featureProvider;
298
        } catch (DataException e) {
299
            throw e;
300
        } catch (CreateEnvelopeException e) {
301
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
302
        } catch (CreateGeometryException e) {
303
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
304
        }
305

    
306
    }
307

    
308
    protected void initFeatureProviderByIndex(FeatureProvider featureProvider, long index, FeatureType featureType)
309
        throws DataException {
310
        try {
311
            super.initFeatureProviderByIndex(featureProvider, index, featureType);
312
            featureProvider.setDefaultEnvelope(this.shpFile.getBoundingBox(index));
313
        } catch (CreateEnvelopeException e) {
314
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
315
        } catch (CreateGeometryException e) {
316
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
317
        }
318
    }
319

    
320
    /**
321
     *
322
     * @param featureProvider
323
     * @throws DataException
324
     */
325
    @Override
326
    protected void loadFeatureProviderByIndex(FeatureProvider featureProvider) throws DataException {
327

    
328
        FeatureType featureType = featureProvider.getType();
329
        long index = ((Long) featureProvider.getOID());
330
        boolean hasGeometry = false;
331
        int i = featureType.getDefaultGeometryAttributeIndex();
332
        if (i >= 0) {
333
            if (!featureProvider.isReadOnly(i)) {
334
                Geometry geom;
335
                try {
336
                    geom = this.shpFile.getGeometry(index);
337
                } catch (Exception e) {
338
                    if( this.getShpParameters().getLoadCorruptGeometriesAsNull() ) {
339
                        geom = null;
340
                    } else {
341
                        throw new ReadGeometryException(getName(), featureProvider.getOID(), e);
342
                    }
343
                }
344
                featureProvider.set(i, geom);
345
            }
346
            hasGeometry = true;
347
        }
348
        if (hasDBFAttributes(featureType, hasGeometry)) {
349
            super.loadFeatureProviderByIndex(featureProvider);
350
        }
351

    
352
    }
353
    
354
    public class ReadGeometryException extends ReadException {
355

    
356
        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.";
357
        private final static String MESSAGE_KEY = "_ReadGeometryException";
358
        private static final long serialVersionUID = 2626155328734197112L;
359

    
360
        public ReadGeometryException(String store, Object oid, Throwable cause) {
361
            super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
362
            setValue("store", store);
363
            setValue("storeParameters",getParameters());
364
        }
365
    }
366

    
367
    private boolean hasDBFAttributes(FeatureType featureType, boolean hasGeometry) {
368
        FeatureAttributeDescriptor[] attributes = featureType.getAttributeDescriptors();
369
        // If there aren't any attributes, nor has any DBF attributes
370
        if (attributes == null || attributes.length == 0) {
371
            return false;
372
        }
373
        // If there is only one attribute and it is the geometry one
374
        if (attributes.length == 1 && hasGeometry) {
375
            return false;
376
        }
377
        // In any other case
378
        return true;
379
    }
380

    
381
    protected void loadValue(FeatureProvider featureProvider, int rowIndex, FeatureAttributeDescriptor descriptor)
382
        throws ReadException {
383
        if (descriptor.getType() == DataTypes.GEOMETRY) {
384
            return;
385
        } else {
386
            super.loadValue(featureProvider, rowIndex, descriptor);
387
        }
388
    }
389

    
390
    public FeatureProvider createFeatureProvider(FeatureType type) throws DataException {
391
        FeatureProvider data = new SHPFeatureProvider(this, type);
392
        return data;
393
    }
394

    
395
    protected void openFile() throws IOException, DataException {
396
        super.openFile();
397
        this.shpFile.open();
398

    
399
    }
400

    
401
    protected void closeFile() throws CloseException {
402
        super.closeFile();
403
        if (!this.shpFile.isOpen()) {
404
            return;
405
        }
406
        this.shpFile.close();
407
    }
408

    
409
    public boolean canWriteGeometry(final int geometryType, int geometrySubType) throws DataException {
410
        this.open();
411
        return ((Boolean) getResource().execute(new ResourceAction() {
412

    
413
            public Object run() throws Exception {
414
                boolean value = shpFile.canWriteGeometry(geometryType);
415
                return value ? Boolean.TRUE : Boolean.FALSE;
416
            }
417
        })).booleanValue();
418
    }
419

    
420
    @SuppressWarnings("rawtypes")
421
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds,
422
        Iterator originalFeatureTypesUpdated) throws PerformEditingException {
423

    
424
        /*
425
         * This will throw an exception if there are new fields
426
         * with names too long
427
         */
428
        checkNewFieldsNameSize(originalFeatureTypesUpdated);
429

    
430
        final FeatureType fType;
431
        try {
432
            fType = this.getStoreServices().getDefaultFeatureType();
433
        } catch (DataException e) {
434
            throw new PerformEditingException(this.getProviderName(), e);
435
        }
436
        // TODO Comprobar el campo de geometria
437

    
438
        final EditableFeatureType dbfFtype = fType.getEditable();
439

    
440
        removeGeometryColumn(dbfFtype);
441

    
442
        try {
443
            // TODO repasar el concepto de enUso de un recurso.
444
            loTengoEnUso = true;
445
            resourceCloseRequest();
446

    
447
            getResource().execute(new ResourceAction() {
448

    
449
                public Object run() throws Exception {
450
                    FeatureSet set = null;
451
                    DisposableIterator iter = null;
452
                    try {
453
                        set = getFeatureStore().getFeatureSet();
454
                        writer = new SHPFeatureWriter(getProviderName());
455

    
456
                        SHPStoreParameters shpParams = getShpParameters();
457
                        SHPStoreParameters tmpParams = (SHPStoreParameters) shpParams.getCopy();
458

    
459
                        File tmp_base = File.createTempFile("tmp_" + System.currentTimeMillis(), null);
460
                        String str_base = tmp_base.getCanonicalPath();
461

    
462
                        tmpParams.setDBFFile(str_base + ".dbf");
463
                        tmpParams.setSHPFile(str_base + ".shp");
464
                        tmpParams.setSHXFile(str_base + ".shx");
465

    
466
                        writer.begin(tmpParams, fType, dbfFtype, set.getSize());
467

    
468
                        iter = set.fastIterator();
469
                        while (iter.hasNext()) {
470
                            Feature feature = (Feature) iter.next();
471
                            writer.append(feature);
472
                        }
473

    
474
                        writer.end();
475
                        loTengoEnUso = false;
476
                        close();
477

    
478

    
479
                        if (!shpParams.getDBFFile().delete()) {
480
                            logger.debug("Can't delete dbf file '" + shpParams.getDBFFile() + "'.");
481
                            throw new IOException("Can't delete dbf '"
482
                                + FilenameUtils.getBaseName(shpParams.getDBFFileName())
483
                                + "' file to replace with the new dbf.\nThe new dbf is in temporary file '" + str_base
484
                                + "'");
485
                        }
486
                        if (!shpParams.getSHPFile().delete()) {
487
                            logger.debug("Can't delete dbf file '" + shpParams.getSHPFile() + "'.");
488
                            throw new IOException("Can't delete shp '"
489
                                + FilenameUtils.getBaseName(shpParams.getSHPFileName())
490
                                + "' file to replace with the new shp.\nThe new shp is in temporary file '" + str_base
491
                                + "'");
492
                        }
493
                        if (!shpParams.getSHXFile().delete()) {
494
                            logger.debug("Can't delete dbf file '" + shpParams.getSHXFile() + "'.");
495
                            throw new IOException("Can't delete shx '"
496
                                + FilenameUtils.getBaseName(shpParams.getSHXFileName())
497
                                + "' file to replace with the new shx.\nThe new shx is in temporary file '" + str_base
498
                                + "'");
499
                        }
500

    
501
                        File prjFile = SHP.getPrjFile(shpParams.getSHPFile());
502
                        if (prjFile.exists()) {
503
                            if (!prjFile.delete()) {
504
                                logger.debug("Can't delete prj file '" + prjFile + "'.");
505
                                throw new IOException("Can't delete shx '"
506
                                    + FilenameUtils.getBaseName(prjFile.getPath())
507
                                    + "' file to replace with the new shx.\nThe new shx is in temporary file '"
508
                                    + str_base + "'");
509
                            }
510
                        }
511
                        FileUtils.moveFile(tmpParams.getDBFFile(), shpParams.getDBFFile());
512
                        FileUtils.moveFile(tmpParams.getSHPFile(), shpParams.getSHPFile());
513
                        FileUtils.moveFile(tmpParams.getSHXFile(), shpParams.getSHXFile());
514

    
515
                        savePrjFile(shpParams.getFile(), tmpParams.getCRS());
516

    
517
                        resourcesNotifyChanges();
518
                        initFeatureType();
519
                        return null;
520
                    } finally {
521
                        loTengoEnUso = false;
522
                        dispose(set);
523
                        dispose(iter);
524
                    }
525
                }
526
            });
527

    
528
        } catch (Exception e) {
529
            throw new PerformEditingException(this.getProviderName(), e);
530
        }
531

    
532
    }
533

    
534
    protected void resourceCloseRequest() throws ResourceException {
535
        getResource().closeRequest();
536
    }
537

    
538
    public Envelope getEnvelope() throws DataException {
539
        this.open();
540
        return (Envelope) this.getDynValue("Envelope");
541
    }
542

    
543
    public void append(final FeatureProvider featureProvider) throws DataException {
544
        getResource().execute(new ResourceAction() {
545

    
546
            public Object run() throws Exception {
547
                writer.append(getStoreServices().createFeature(featureProvider));
548
                return null;
549
            }
550
        });
551
    }
552

    
553
    public void beginAppend() throws DataException {
554
        getResource().execute(new ResourceAction() {
555

    
556
            public Object run() throws Exception {
557
                FeatureStore store = getFeatureStore();
558
                FeatureType fType = store.getDefaultFeatureType();
559

    
560
                // TODO Comprobar el campo de geometria
561
                EditableFeatureType dbfFtype = fType.getEditable();
562

    
563
                removeGeometryColumn(dbfFtype);
564
                FeatureSet set = store.getFeatureSet();
565

    
566
                writer = new SHPFeatureWriter(getProviderName());
567

    
568
                writer.begin(getShpParameters(), fType, dbfFtype, set.getSize());
569
                return null;
570
            }
571
        });
572
    }
573

    
574
    public void endAppend() throws DataException {
575
        getResource().execute(new ResourceAction() {
576

    
577
            public Object run() throws Exception {
578
                writer.end();
579
                close();
580

    
581
                SHPStoreParameters shpParameters = SHPStoreProvider.this.getShpParameters();
582

    
583
                savePrjFile(shpParameters.getFile(), shpParameters.getCRS());
584

    
585
                resourcesNotifyChanges();
586
                return null;
587
            }
588
        });
589
    }
590

    
591
    public Object getSourceId() {
592
        return this.getShpParameters().getFile();
593
    }
594

    
595

    
596
}