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

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

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

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

    
77
public class SHPStoreProvider extends DBFStoreProvider {
78

    
79
    private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
80
    private static final Logger logger = LoggerFactory.getLogger(GeometryManager.class);
81
    public static String NAME = "Shape";
82
    public static String DESCRIPTION = "Shape file";
83
    private ISHPFile shpFile;
84

    
85
    private MultiResource resource;
86

    
87
    protected static final String GEOMETRY_ATTIBUTE_NAME = "GEOMETRY";
88

    
89
    public static final String METADATA_DEFINITION_NAME = NAME;
90

    
91
    private SHPFeatureWriter writer = null;
92

    
93
    public SHPStoreProvider(SHPStoreParameters params,
94
            DataStoreProviderServices storeServices)
95
            throws InitializeException {
96
        super(
97
                params,
98
                storeServices,
99
                FileHelper.newMetadataContainer(METADATA_DEFINITION_NAME)
100
        );
101
    }
102

    
103
    protected void init(DBFStoreParameters params,
104
            DataStoreProviderServices storeServices) throws InitializeException {
105

    
106
        this.shpFile = new SHPFile2((SHPStoreParameters) params);
107
        super.init(params, storeServices);
108
        this.shpFile.setUseNullGeometry(this.getShpParameters().getUseNullGeometry());
109
    }
110

    
111
    public Object getDynValue(String name) throws DynFieldNotFoundException {
112
        if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
113

    
114
            /*
115
             * String srs =  this.shpFile.getSRSParameters();
116
             if (srs != null){
117
             // This can be non null but not sure how to handle
118
             }
119
             */
120
            return this.getShpParameters().getCRS();
121

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

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

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

    
140
        resource.addResource(FileResource.NAME,
141
                new Object[]{shpParams.getSHPFileName()}, true);
142
        resource.addResource(FileResource.NAME,
143
                new Object[]{shpParams.getSHXFileName()}, true);
144
        resource.addResource(FileResource.NAME,
145
                new Object[]{shpParams.getDBFFileName()}, true);
146

    
147
        resource.addConsumer(this);
148
    }
149

    
150
    ;
151

    
152
        public ResourceProvider getResource() {
153
        return resource;
154
    }
155

    
156
    /**
157
     *
158
     * @throws ResourceNotifyChangesException
159
     */
160
    protected void resourcesNotifyChanges()
161
            throws ResourceNotifyChangesException {
162
                // super.resourcesNotifyChanges();
163
        // this.shpResource.notifyChanges();
164
        // this.shxResource.notifyChanges();
165
        getResource().notifyChanges();
166
        // TODO .prj
167

    
168
    }
169

    
170
    /**
171
     * @throws ResourceNotifyCloseException
172
     *
173
     */
174
    protected void resourcesNotifyClose() throws ResourceNotifyCloseException {
175
//                super.resourcesNotifyClose();
176
//                this.shpResource.notifyClose();
177
//                this.shxResource.notifyClose();
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
                // super.resourcesOpen();
201
        // this.shpResource.notifyOpen();
202
        // this.shxResource.notifyOpen();
203
        getResource().notifyOpen();
204
    }
205

    
206
    protected static EditableFeatureAttributeDescriptor addGeometryColumn(
207
            EditableFeatureType fType) {
208

    
209
        EditableFeatureAttributeDescriptor attrTmp = null;
210
        EditableFeatureAttributeDescriptor attr = null;
211
        Iterator iter = fType.iterator();
212
        while (iter.hasNext()) {
213
            attrTmp = (EditableFeatureAttributeDescriptor) iter.next();
214
            if (attrTmp.getType() == DataTypes.GEOMETRY) {
215
                if (attr != null) {
216
                    // Two geom fields not allowed
217
                    fType.remove(attrTmp.getName());
218
                } else {
219
                    attr = attrTmp;
220
                    // attr.setName(GEOMETRY_ATTIBUTE_NAME);
221
                }
222
            }
223
        }
224

    
225
        if (attr == null) {
226
            String geofield = createGeometryFieldName(fType);
227
            attr = fType.add(geofield, DataTypes.GEOMETRY);
228
            attr.setDefaultValue(null);
229
        }
230

    
231
        attr.setObjectClass(Geometry.class);
232
        fType.setDefaultGeometryAttributeName(attr.getName());
233
        return attr;
234

    
235
    }
236

    
237
    private static String createGeometryFieldName(FeatureType ft) {
238

    
239
        if (ft.getAttributeDescriptor(GEOMETRY_ATTIBUTE_NAME) == null) {
240
            return GEOMETRY_ATTIBUTE_NAME;
241
        }
242

    
243
        int i = 0;
244
        String candidate = GEOMETRY_ATTIBUTE_NAME + i;
245
        while (ft.getAttributeDescriptor(candidate) != null) {
246
            i++;
247
            candidate = GEOMETRY_ATTIBUTE_NAME + i;
248
        }
249
        return candidate;
250
    }
251

    
252
    protected static FeatureType removeGeometryColumn(
253
            EditableFeatureType fType) {
254
        Iterator iter = fType.iterator();
255
        FeatureAttributeDescriptor attr;
256
        while (iter.hasNext()) {
257
            attr = (FeatureAttributeDescriptor) iter.next();
258
            if (attr.getType() == DataTypes.GEOMETRY) {
259
                iter.remove();
260
            }
261
        }
262
        fType.setDefaultGeometryAttributeName(null);
263
        return fType.getNotEditableCopy();
264
    }
265

    
266
    protected EditableFeatureType getTheFeatureType()
267
            throws InitializeException, OpenException {
268
        final EditableFeatureType fType = super.getTheFeatureType();
269
        this.open();
270
                // try {
271
        // this.resourcesBegin();
272
        // } catch (DataException e) {
273
        // throw new InitializeException(this.getName(), e);
274
        // }
275
        try {
276
            getResource().execute(new ResourceAction() {
277
                public Object run() throws Exception {
278
                    EditableFeatureAttributeDescriptor attr
279
                            = addGeometryColumn(fType);
280
                    attr.setGeometryType(shpFile.getGeometryType());
281
                    attr.setGeometrySubType(shpFile.getGeometrySubType());
282

    
283
                    IProjection srs = getShpParameters().getCRS();
284
                    attr.setSRS(srs);
285

    
286
                    return null;
287
                }
288
            });
289
            return fType;
290
        } catch (ResourceExecuteException e) {
291
            throw new InitializeException(e);
292
                        // } finally {
293
            // this.resourcesEnd();
294
        }
295
    }
296

    
297
//        private String getSRSFromPrj(String srsParameters) {
298
//                // TODO identificar que SRS hay que usar, ya sea
299
//                // el que se recibe de los parametros o el que
300
//                // conicida con el que se ha encontrado en el
301
//                // prg... y si ninguna de las dos que?
302
//                return null;
303
//        }
304
    protected SHPStoreParameters getShpParameters() {
305
        return (SHPStoreParameters) getParameters();
306
    }
307

    
308
    public String getProviderName() {
309
        return NAME;
310
    }
311

    
312
    public boolean allowWrite() {
313
        return this.shpFile.isEditable();
314
    }
315

    
316
    /**
317
     *
318
     * @param index
319
     * @param featureType
320
     * @return
321
     * @throws ReadException
322
     */
323
    protected FeatureProvider getFeatureProviderByIndex(long index,
324
            FeatureType featureType) throws DataException {
325
                // this.open();
326
        // this.resourcesBegin();
327
        try {
328

    
329
            FeatureProvider featureProvider = super.getFeatureProviderByIndex(index,
330
                    featureType);
331
            featureProvider.setDefaultEnvelope(this.shpFile.getBoundingBox(index));
332
            return featureProvider;
333
        } catch (DataException e) {
334
            throw e;
335
        } catch (CreateEnvelopeException e) {
336
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
337
        } catch (CreateGeometryException e) {
338
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
339
        }
340

    
341
    }
342

    
343
    protected void initFeatureProviderByIndex(FeatureProvider featureProvider,
344
            long index, FeatureType featureType) throws DataException {
345
                // this.open();
346
        // this.resourcesBegin();
347
        try {
348
            super.initFeatureProviderByIndex(featureProvider, index, featureType);
349
            featureProvider.setDefaultEnvelope(this.shpFile.getBoundingBox(index));
350
        } catch (CreateEnvelopeException e) {
351
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
352
        } catch (CreateGeometryException e) {
353
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
354
        }
355
    }
356

    
357
    /**
358
     *
359
     * @param featureProvider
360
     * @throws DataException
361
     */
362
    protected void loadFeatureProviderByIndex(FeatureProvider featureProvider)
363
            throws DataException {
364

    
365
        FeatureType featureType = featureProvider.getType();
366
        long index = ((Long) featureProvider.getOID()).longValue();
367
        boolean hasGeometry = false;
368
        int i = featureType.getDefaultGeometryAttributeIndex();
369
        if (i >= 0) {
370
            if (!featureProvider.isReadOnly(i)) {
371
                try {
372
                    Geometry geom = this.shpFile.getGeometry(index);
373
                    featureProvider.set(i, geom);
374
                } catch (CreateGeometryException e) {
375
                    throw new ReadException(getProviderName(), e);
376
                }
377
            }
378
            hasGeometry = true;
379
        }
380
        if (hasDBFAttributes(featureType, hasGeometry)) {
381
            super.loadFeatureProviderByIndex(featureProvider);
382
        }
383

    
384
    }
385

    
386
    private boolean hasDBFAttributes(FeatureType featureType,
387
            boolean hasGeometry) {
388
        FeatureAttributeDescriptor[] attributes
389
                = featureType.getAttributeDescriptors();
390
        // If there aren't any attributes, nor has any DBF attributes
391
        if (attributes == null || attributes.length == 0) {
392
            return false;
393
        }
394
        // If there is only one attribute and it is the geometry one
395
        if (attributes.length == 1 && hasGeometry) {
396
            return false;
397
        }
398
        // In any other case
399
        return true;
400
    }
401

    
402
    protected void loadValue(FeatureProvider featureProvider, int rowIndex,
403
            FeatureAttributeDescriptor descriptor) throws ReadException {
404
        if (descriptor.getType() == DataTypes.GEOMETRY) {
405
            return;
406
        } else {
407
            super.loadValue(featureProvider, rowIndex, descriptor);
408
        }
409
    }
410

    
411
    public FeatureProvider createFeatureProvider(FeatureType type) throws DataException {
412
        FeatureProvider data = new SHPFeatureProvider(this, type);
413
        return data;
414
    }
415

    
416
    protected void openFile() throws IOException, DataException {
417
        super.openFile();
418
        this.shpFile.open();
419

    
420
    }
421

    
422
    protected void closeFile() throws CloseException {
423
        super.closeFile();
424
        if (!this.shpFile.isOpen()) {
425
            return;
426
        }
427
        this.shpFile.close();
428
    }
429

    
430
    public boolean canWriteGeometry(final int geometryType, int geometrySubType)
431
            throws DataException {
432
        this.open();
433
        return ((Boolean) getResource().execute(new ResourceAction() {
434
            public Object run() throws Exception {
435
                boolean value = shpFile.canWriteGeometry(geometryType);
436
                return value ? Boolean.TRUE : Boolean.FALSE;
437
            }
438
        })).booleanValue();
439
//                this.resourcesBegin();
440
//                try {
441
//                        return this.shpFile.canWriteGeometry(geometryType);
442
//
443
//                } finally {
444
//                        this.resourcesEnd();
445
//                }
446
    }
447

    
448
    public void performChanges(Iterator deleteds, Iterator inserteds,
449
            Iterator updateds, Iterator originalFeatureTypesUpdated)
450
            throws PerformEditingException {
451

    
452
        /*
453
         * This will throw an exception if there are new fields
454
         * with names too long
455
         */
456
        checkNewFieldsNameSize(originalFeatureTypesUpdated);
457

    
458
        final FeatureType fType;
459
        try {
460
            fType = this.getStoreServices().getDefaultFeatureType();
461
        } catch (DataException e) {
462
            throw new PerformEditingException(this.getProviderName(), e);
463
        }
464
        // TODO Comprobar el campo de geometria
465

    
466
        final EditableFeatureType dbfFtype = fType.getEditable();
467

    
468
        removeGeometryColumn(dbfFtype);
469

    
470
                // try {
471
        // this.resourcesBegin();
472
        // } catch (ResourceExecuteException e1) {
473
        // throw new PerformEditingException(this.getName(), e1);
474
        // }
475
        try {
476

    
477
            getResource().execute(new ResourceAction() {
478
                public Object run() throws Exception {
479
                    FeatureSet set = null;
480
                    DisposableIterator iter = null;
481
                    try {
482
                        set = getFeatureStore().getFeatureSet();
483
                        writer = new SHPFeatureWriter(getProviderName());
484

    
485
                        SHPStoreParameters shpParams = getShpParameters();
486
                        SHPStoreParameters tmpParams
487
                                = (SHPStoreParameters) shpParams.getCopy();
488

    
489
                        File tmp_base = File.createTempFile(
490
                                "tmp_" + System.currentTimeMillis(), null);
491
                        String str_base = tmp_base.getCanonicalPath();
492

    
493
                        tmpParams.setDBFFile(str_base + ".dbf");
494
                        tmpParams.setSHPFile(str_base + ".shp");
495
                        tmpParams.setSHXFile(str_base + ".shx");
496

    
497
                        writer.begin(tmpParams, fType, dbfFtype, set.getSize());
498

    
499
                        iter = set.fastIterator();
500
                        while (iter.hasNext()) {
501
                            Feature feature = (Feature) iter.next();
502
                            writer.append(feature);
503
                        }
504

    
505
                        writer.end();
506

    
507
                        close();
508
                        resourceCloseRequest();
509

    
510
                        if (!shpParams.getDBFFile().delete()) {
511
                            logger.debug("Can't delete dbf file '"+shpParams.getDBFFile()+"'.");
512
                            throw new IOException("Can't delete dbf '"+FilenameUtils.getBaseName(shpParams.getDBFFileName())+"' file to replace with the new dbf.\nThe new dbf is in temporary file '"+str_base+"'");
513
                        }
514
                        if (!shpParams.getSHPFile().delete()) {
515
                            logger.debug("Can't delete dbf file '"+shpParams.getSHPFile()+"'.");
516
                            throw new IOException("Can't delete shp '"+FilenameUtils.getBaseName(shpParams.getSHPFileName())+"' file to replace with the new shp.\nThe new shp is in temporary file '"+str_base+"'");
517
                        }
518
                        if (!shpParams.getSHXFile().delete()) {
519
                            logger.debug("Can't delete dbf file '"+shpParams.getSHXFile()+"'.");
520
                            throw new IOException("Can't delete shx '"+FilenameUtils.getBaseName(shpParams.getSHXFileName())+"' file to replace with the new shx.\nThe new shx is in temporary file '"+str_base+"'");
521
                        }
522
                        FileUtils.moveFile(
523
                                tmpParams.getDBFFile(),
524
                                shpParams.getDBFFile());
525
                        FileUtils.moveFile(
526
                                tmpParams.getSHPFile(),
527
                                shpParams.getSHPFile());
528
                        FileUtils.moveFile(
529
                                tmpParams.getSHXFile(),
530
                                shpParams.getSHXFile());
531

    
532
                        resourcesNotifyChanges();
533
                        initFeatureType();
534
                        return null;
535
                    } finally {
536
                        dispose(set);
537
                        dispose(iter);
538
                    }
539
                }
540
            });
541

    
542
        } catch (Exception e) {
543
            throw new PerformEditingException(this.getProviderName(), e);
544
        }
545

    
546
    }
547

    
548
    protected void resourceCloseRequest() throws ResourceException {
549
                // super.resourceCloseRequest();
550
        // this.shpResource.closeRequest();
551
        // this.shxResource.closeRequest();
552
        getResource().closeRequest();
553
    }
554

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

    
560
    public void append(final FeatureProvider featureProvider) throws DataException {
561
//                this.resourcesBegin();
562
//                try {
563

    
564
        getResource().execute(new ResourceAction() {
565
            public Object run() throws Exception {
566
                writer.append(getStoreServices().createFeature(featureProvider));
567
                return null;
568
            }
569
        });
570
//                } finally {
571
//                        this.resourcesEnd();
572
//                }
573

    
574
    }
575

    
576
    public void beginAppend() throws DataException {
577
                // this.resourcesBegin();
578
        // try {
579

    
580
        getResource().execute(new ResourceAction() {
581
            public Object run() throws Exception {
582
                FeatureStore store = getFeatureStore();
583
                FeatureType fType = store.getDefaultFeatureType();
584

    
585
                                // TODO Comprobar el campo de geometria
586
                EditableFeatureType dbfFtype = fType.getEditable();
587

    
588
                removeGeometryColumn(dbfFtype);
589
                FeatureSet set = store.getFeatureSet();
590

    
591
                writer = new SHPFeatureWriter(getProviderName());
592

    
593
                writer.begin(getShpParameters(), fType, dbfFtype, set.getSize());
594
                return null;
595
            }
596
        });
597
                // } finally {
598
        // this.resourcesEnd();
599
        // }
600

    
601
    }
602

    
603
    public void endAppend() throws DataException {
604
//                this.resourcesBegin();
605
//                try {
606
        getResource().execute(new ResourceAction() {
607
            public Object run() throws Exception {
608
                writer.end();
609
                resourcesNotifyChanges();
610
                return null;
611
            }
612
        });
613
//                } finally {
614
//                        this.resourcesEnd();
615
//                }
616

    
617
    }
618

    
619
    public Object getSourceId() {
620
        return this.getShpParameters().getFile();
621
    }
622
}