Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libFMap_daldb / src / org / gvsig / fmap / dal / store / postgresql / PostgreSQLStoreProvider.java @ 28136

History | View | Annotate | Download (18.4 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
*
3
* Copyright (C) 2007-2008 Infrastructures and Transports Department
4
* of the Valencian Government (CIT)
5
*
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
10
*
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
* GNU General Public License for more details.
15
*
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
* MA  02110-1301, USA.
20
*
21
*/
22

    
23
/*
24
* AUTHORS (In addition to CIT):
25
* 2009 IVER T.I   {{Task}}
26
*/
27

    
28
package org.gvsig.fmap.dal.store.postgresql;
29

    
30
import java.security.InvalidParameterException;
31
import java.sql.Connection;
32
import java.sql.PreparedStatement;
33
import java.sql.ResultSet;
34
import java.sql.SQLException;
35
import java.util.ArrayList;
36
import java.util.Iterator;
37
import java.util.List;
38
import java.util.regex.Matcher;
39
import java.util.regex.Pattern;
40

    
41
import org.cresques.cts.IProjection;
42
import org.gvsig.fmap.dal.DALLocator;
43
import org.gvsig.fmap.dal.DataManager;
44
import org.gvsig.fmap.dal.DataServerExplorer;
45
import org.gvsig.fmap.dal.DataStoreNotification;
46
import org.gvsig.fmap.dal.DataTypes;
47
import org.gvsig.fmap.dal.exception.CloseException;
48
import org.gvsig.fmap.dal.exception.DataException;
49
import org.gvsig.fmap.dal.exception.InitializeException;
50
import org.gvsig.fmap.dal.exception.OpenException;
51
import org.gvsig.fmap.dal.exception.ReadException;
52
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
53
import org.gvsig.fmap.dal.feature.EditableFeatureType;
54
import org.gvsig.fmap.dal.feature.Feature;
55
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
56
import org.gvsig.fmap.dal.feature.FeatureQuery;
57
import org.gvsig.fmap.dal.feature.FeatureStore;
58
import org.gvsig.fmap.dal.feature.FeatureType;
59
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
60
import org.gvsig.fmap.dal.feature.spi.FeatureData;
61
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
62
import org.gvsig.fmap.dal.feature.spi.FeatureSetProvider;
63
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProvider;
64
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
65
import org.gvsig.fmap.dal.resource.exception.ResourceBeginException;
66
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyCloseException;
67
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
68
import org.gvsig.fmap.dal.store.jdbc.JDBCExecuteSQLException;
69
import org.gvsig.fmap.dal.store.jdbc.JDBCSQLException;
70
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreProvider;
71
import org.gvsig.fmap.geom.Geometry;
72
import org.gvsig.fmap.geom.exception.CreateGeometryException;
73
import org.gvsig.fmap.geom.operation.towkb.ToWKB;
74
import org.gvsig.fmap.geom.primitive.Envelope;
75
import org.gvsig.tools.ToolsLocator;
76
import org.gvsig.tools.dynobject.DelegatedDynObject;
77
import org.gvsig.tools.dynobject.DynClass;
78
import org.gvsig.tools.dynobject.DynObjectManager;
79
import org.gvsig.tools.exception.BaseException;
80
import org.gvsig.tools.persistence.PersistenceException;
81
import org.gvsig.tools.persistence.PersistentState;
82
import org.postgis.PGgeometry;
83
import org.slf4j.Logger;
84
import org.slf4j.LoggerFactory;
85

    
86
public class PostgreSQLStoreProvider extends JDBCStoreProvider implements
87
                PostgreSQLHelperUser {
88

    
89
        final static private Logger logger = LoggerFactory
90
                        .getLogger(PostgreSQLStoreProvider.class);
91

    
92
        public static String NAME = "PostgreSQLStore";
93
        public static String DESCRIPTION = "PostgreSQL source";
94
        private static final String DYNCLASS_NAME = "PostgreSQLStore";
95
        private static DynClass DYNCLASS = null;
96

    
97

    
98
        protected PostgreSQLStoreParameters params;
99
        protected PostgreSQLHelper helper;
100
        protected boolean directSQLMode;
101

    
102
        private Long totalCount = null;
103
        private boolean isOpen = false;
104

    
105
        protected static void registerDynClass() {
106
                DynObjectManager dynman = ToolsLocator.getDynObjectManager();
107
                DynClass dynClass;
108
                if (DYNCLASS == null) {
109
                        dynClass = dynman.add(DYNCLASS_NAME, DESCRIPTION);
110

    
111
                        dynClass.extend(dynman.get(FeatureStore.DYNCLASS_NAME));
112
                        DYNCLASS = dynClass;
113
                }
114
        }
115

    
116
        public PostgreSQLStoreProvider() {
117
                super();
118
        }
119

    
120
        public PostgreSQLStoreProvider(PostgreSQLStoreParameters params)
121
                        throws InitializeException {
122
                super();
123
                this.init(params);
124
        }
125

    
126
        protected void init(PostgreSQLStoreParameters params)
127
                        throws InitializeException {
128
                this.params = params;
129
                this.dynObject = (DelegatedDynObject) ToolsLocator
130
                                .getDynObjectManager().createDynObject(DYNCLASS);
131

    
132
                this.dynObject.setDynValue("DefaultSRS", null);
133
                this.dynObject.setDynValue("Envelope", null);
134

    
135
                helper = new PostgreSQLHelper(this, params);
136

    
137
                if (params.getSQL() != null && (params.getSQL()).trim().length() > 0) {
138
                        directSQLMode = true;
139
                }
140
        }
141

    
142
        protected String compoundCountSelect(String filter) {
143
                if (this.directSQLMode) {
144
                        return null;
145
                }
146
                // Select
147
                StringBuilder sql = new StringBuilder();
148
                sql.append("Select count(");
149
                String[] pkFields = params.getPkFields();
150
                if (pkFields != null && pkFields.length == 1) {
151
                        sql.append(pkFields[0]);
152
                } else {
153
                        sql.append('*');
154

    
155
                }
156
                sql.append(") ");
157

    
158
                sql.append("from ");
159

    
160
                sql.append(params.tableID());
161
                sql.append(' ');
162

    
163
                appendWhere(sql, filter);
164

    
165
                return sql.toString();
166
        }
167

    
168
        private void appendWhere(StringBuilder sql, String filter) {
169
                filter = fixFilter(filter);
170
                String initialFilter = params.getInitialFilter();
171
                if ((initialFilter != null && initialFilter.length() != 0)
172
                                || (filter != null && filter.length() != 0)) {
173
                        sql.append("where (");
174

    
175
                        if (initialFilter != null && initialFilter.length() != 0
176
                                        && filter != null && filter.length() != 0) {
177
                                // initialFilter + filter
178
                                sql.append('(');
179
                                sql.append(initialFilter);
180
                                sql.append(") and (");
181
                                sql.append(filter);
182
                                sql.append(')');
183
                        } else if (initialFilter != null && initialFilter.length() != 0) {
184
                                // initialFilter only
185
                                sql.append(initialFilter);
186
                        } else {
187
                                // filter only
188
                                sql.append(filter);
189
                        }
190
                        sql.append(") ");
191
                }
192

    
193
        }
194

    
195
        private String fixFilter(String filter) {
196
                if (filter == null) {
197
                        return null;
198
                }
199

    
200
                // Transform SRS to code
201
                // GeomFromText\s*\(\s*'[^']*'\s*,\s*('[^']*')\s*\)
202
                Pattern pattern = Pattern
203
                                .compile("GeomFromText\\s*\\(\\s*'[^']*'\\s*,\\s*'([^']*)'\\s*\\)");
204
                Matcher matcher = pattern.matcher(filter);
205
                StringBuilder strb = new StringBuilder();
206
                int pos = 0;
207
                String srsCode;
208
                while (matcher.find(pos)) {
209
                        strb.append(filter.substring(pos, matcher.start(1)));
210
                        srsCode = matcher.group(1).trim();
211
                        if (srsCode.startsWith("'")) {
212
                                srsCode = srsCode.substring(1);
213
                        }
214
                        if (srsCode.endsWith("'")) {
215
                                srsCode = srsCode.substring(0, srsCode.length() - 1);
216
                        }
217
                        strb.append(helper.getSRSCode(srsCode));
218
                        strb.append(filter.substring(matcher.end(1), matcher.end()));
219
                        pos = matcher.end();
220

    
221
                }
222
                strb.append(filter.substring(pos));
223

    
224
                return strb.toString();
225
        }
226

    
227
        public String compoundSelect(String[] fields, String filter,
228
                        String order,
229
                        long limit, long offset) {
230
                StringBuilder sql = new StringBuilder();
231
                if (directSQLMode) {
232
                        if (filter != null || order != null) {
233
                                // FIXME Exception
234
                                throw new UnsupportedOperationException();
235
                        }
236
                        sql.append(params.getSQL());
237
                        sql.append(' ');
238
                } else {
239

    
240
                        // Select
241
                        sql.append("Select ");
242
                        for (int i = 0; i < fields.length - 1; i++) {
243
                                sql.append(fields[i]);
244
                                sql.append(", ");
245
                        }
246
                        sql.append(fields[fields.length - 1]);
247
                        sql.append(' ');
248
                        String[] pkFields = params.getPkFields();
249
                        if (pkFields != null
250
                                        && pkFields.length > 0
251
                                        && !(fields.length == 1 && fields[0].trim().equals("*"))) {
252
                                // checks for pk fields are in select
253
                                boolean toAdd;
254
                                for (int i = 0; i < pkFields.length; i++) {
255
                                        toAdd = true;
256
                                        for (int j = 0; j < fields.length; j++) {
257
                                                if (pkFields[i].equals(fields[j])) {
258
                                                        toAdd = false;
259
                                                        break;
260
                                                }
261
                                                if (toAdd) {
262
                                                        sql.append(", ");
263
                                                        sql.append(pkFields[i]);
264
                                                }
265
                                        }
266
                                }
267
                                sql.append(' ');
268
                        }
269

    
270

    
271
                        // table
272
                        sql.append("from ");
273
                        sql.append(params.tableID());
274
                        sql.append(' ');
275

    
276

    
277
                        // Where
278
                        appendWhere(sql, filter);
279

    
280
                        // Order
281
                        if ((params.getInitialOrder() != null && params
282
                                        .getInitialOrder().length() != 0)
283
                                        || (order != null && order.length() != 0)) {
284
                                sql.append("order by ");
285

    
286
                                if (order != null && order.length() != 0) {
287
                                        // order
288
                                        sql.append(order);
289
                                } else {
290
                                        // initial order
291
                                        sql.append(params.getInitialOrder());
292
                                }
293
                                sql.append(' ');
294
                        }
295
                }
296
                // limit
297
                if (limit >= 1) {
298
                        sql.append("limit ");
299
                        sql.append(limit);
300
                        sql.append(' ');
301
                }
302

    
303
                // offset
304
                if (offset >= 1) {
305
                        sql.append("offset ");
306
                        sql.append(offset);
307
                        sql.append(' ');
308
                }
309

    
310

    
311
                return sql.toString();
312
        }
313

    
314
        public FeatureStoreProvider initialize(FeatureStoreProviderServices store)
315
                        throws InitializeException {
316
                super.initialize(store);
317
                this.initFeatureType();
318
                return this;
319
        }
320

    
321

    
322
        protected void initFeatureType() throws InitializeException {
323

    
324
                EditableFeatureType edFType = null;
325
                try {
326
                        edFType = this.store.createFeatureType();
327

    
328
                        helper.loadFeatureType(edFType, params);
329

    
330
                } catch (DataException e) {
331
                        throw new InitializeException(this.getName(), e);
332
                }
333

    
334
                FeatureType defaultType = edFType.getNotEditableCopy();
335
                List types = new ArrayList(1);
336
                types.add(defaultType);
337
                this.store.setFeatureTypes(types, defaultType);
338
                try {
339
                        loadMetadata();
340
                } catch (DataException e) {
341
                        throw new InitializeException(e);
342
                }
343
        }
344

    
345
        public Object createNewOID() {
346
                return null;
347
        }
348

    
349

    
350
        public FeatureData getFeatureDataByReference(
351
                        FeatureReferenceProviderServices reference) throws DataException {
352
                return getFeatureDataByReference(reference, store
353
                                .getDefaultFeatureType());
354
        }
355

    
356
        public FeatureData getFeatureDataByReference(
357
                        FeatureReferenceProviderServices reference, FeatureType featureType)
358
                        throws DataException {
359
                open();
360
                resourceBegin();
361
                try {
362
                        StringBuilder filter = new StringBuilder();
363
                        List pkNames = new ArrayList();
364
                        List fields = new ArrayList();
365

    
366
                        List values = new ArrayList();
367

    
368
                        FeatureAttributeDescriptor attr;
369
                        String attrName;
370
                        Iterator iter = featureType.iterator();
371
                        while (iter.hasNext()) {
372
                                attr = (FeatureAttributeDescriptor) iter.next();
373
                                attrName = attr.getName();
374
                                fields.add(attrName);
375
                                if (attr.isPrimaryKey()) {
376
                                        pkNames.add(attrName);
377
                                        values.add(reference.getKeyValue(attrName));
378
                                }
379

    
380
                        }
381

    
382
                        int i = 0;
383
                        for (i = 0; i < pkNames.size() - 1; i++) {
384
                                filter.append(pkNames.get(i));
385
                                filter.append(" = ? AND ");
386
                        }
387
                        filter.append(pkNames.get(i));
388
                        filter.append(" = ? ");
389

    
390

    
391
                        String sql = compoundSelect(
392
                                        (String[])fields.toArray(new String[0]),
393
                                        filter.toString(), null, 1, 0);
394

    
395
                        int rsId = createResultSet(sql, values.toArray());
396
                        if (!resulsetNext(rsId)) {
397
                                // FIXME Exception
398
                                throw new RuntimeException("Reference Not found");
399
                        }
400
                        FeatureData data = createFeatureData(featureType);
401
                        try{
402
                                loadFeatureData(data, rsId);
403
                        } finally {
404
                                closeResulset(rsId);
405
                        }
406

    
407
                        return data;
408

    
409
                } finally {
410
                        resourceEnd();
411
                }
412

    
413
        }
414

    
415
        public int getFeatureReferenceOIDType() {
416
                return DataTypes.UNKNOWN;
417
        }
418

    
419
        public String getName() {
420
                return NAME;
421
        }
422

    
423
        public Iterator getChilds() {
424
                return null;
425
        }
426

    
427
        public void open() throws OpenException {
428
                helper.open();
429
        }
430

    
431

    
432
        public boolean allowWrite() {
433
                return false;
434
        }
435

    
436
        public void close() throws CloseException {
437
                helper.close();
438
        }
439

    
440
        public Object getSourceId() {
441
                return this.params.getSourceId();
442
        }
443

    
444
        protected ResultSet createNewResultSet(String sql, Object[] values)
445
                        throws DataException {
446
                this.open();
447
                Connection conn =null;
448
                PreparedStatement st=null;
449
                ResultSet rs=null;
450
                this.resourceBegin();
451
                try{
452
                        conn = this.helper.getConnection();
453
                        st = conn.prepareStatement(sql);
454

    
455
                        if (values != null) {
456
                                Object value;
457
                                for (int i = 0; i < values.length; i++) {
458
                                        value = values[i];
459
                                        if (value instanceof Geometry) {
460
                                                byte[] bytes;
461
                                                try {
462
                                                        bytes = (byte[]) ((Geometry) value)
463
                                                                        .invokeOperation(ToWKB.CODE, null);
464
                                                } catch (BaseException e) {
465
                                                        // FIXME
466
                                                        throw new InvalidParameterException();
467
                                                }
468
                                                st.setBytes(i + 1, bytes);
469
                                        }
470
                                        st.setObject(i + 1, value);
471
                                }
472

    
473
                        }
474

    
475
                        try {
476
                                rs = st.executeQuery();
477
                        } catch (SQLException e1){
478
                                try {st.close();  } catch (Exception e2) {        };
479
                                try {conn.close();} catch (Exception e2) {        };
480
                                throw new JDBCExecuteSQLException(sql,e1);
481
                        }
482
                        rs.setFetchSize(5000); // TODO add to params?
483
                        return rs;
484
                } catch (SQLException e) {
485
                        // TODO throw exception ???
486
                        try {rs.close();  } catch (Exception e1) {        };
487
                        try {st.close();  } catch (Exception e1) {        };
488
                        try {conn.close();} catch (Exception e1) {        };
489
                        throw new JDBCSQLException(e);
490
                }finally{
491
                        this.resourceEnd();
492
                }
493
        }
494

    
495
        protected long getCount(String filter) throws DataException {
496
                this.open();
497
                if (filter == null && totalCount != null) {
498
                        return totalCount.longValue();
499
                }
500
                long count = 0;
501
                String sql = compoundCountSelect(filter);
502
                resourceBegin();
503
                try {
504
                        ResultSet rs = createNewResultSet(sql, null);
505
                        try {
506
                                if (rs.next()) {
507
                                        count = rs.getLong(1);
508
                                }
509
                        } catch (SQLException e) {
510
                                throw new JDBCSQLException(e);
511
                        } finally {
512
                                closeResulset(rs);
513
                        }
514
                } finally {
515
                        resourceEnd();
516
                }
517
                if (filter == null) {
518
                        totalCount = new Long(count);
519
                }
520
                return count;
521
        }
522

    
523
        protected void resourceBegin() throws ResourceBeginException {
524
                this.helper.begin();
525

    
526
        }
527

    
528
        protected void resourceEnd() {
529
                this.helper.end();
530
        }
531

    
532
        protected boolean closeResource(ResourceProvider resource)
533
                        throws ResourceNotifyCloseException {
534
                resource.notifyClose();
535
                isOpen = false;
536
                return true;
537
        }
538

    
539

    
540
        protected void loadFeatureDataValue(FeatureData data, ResultSet rs,
541
                        FeatureAttributeDescriptor attr) throws DataException {
542
                if (attr.getDataType() == DataTypes.GEOMETRY) {
543
                        Object pgGeom = null;
544
                        try {
545
                                pgGeom = rs.getObject(attr.getIndex() + 1);
546
                                if (pgGeom == null) {
547
                                        data.set(attr.getIndex(), null);
548
                                } else {
549
                                        data.set(attr.getIndex(), this.helper
550
                                                        .getGeometry((PGgeometry) pgGeom));
551
                                }
552
                        } catch (SQLException e) {
553
                                throw new JDBCSQLException(e);
554
                        } catch (CreateGeometryException e) {
555
                                throw new ReadException(getName(), e);
556
                        }
557

    
558
                } else {
559
                        super.loadFeatureDataValue(data, rs, attr);
560
                }
561
        }
562

    
563
        public FeatureSetProvider createSet(FeatureQuery query,
564
                        FeatureType featureType) throws DataException {
565

    
566
                return new PostgreSQLSetProvider(this, query, featureType);
567
        }
568

    
569
        public void dispose() throws CloseException {
570
                this.close();
571
                this.helper.dispose();
572
                super.dispose();
573
        }
574

    
575

    
576
        public DataServerExplorer getExplorer() throws ReadException {
577
                DataManager manager = DALLocator.getDataManager();
578
                PostgreSQLServerExplorerParameters exParams;
579
                try {
580
                        exParams = (PostgreSQLServerExplorerParameters) manager
581
                                        .createServerExplorerParameters(PostgreSQLServerExplorer.NAME);
582
                        exParams.setHost(params.getHost());
583
                        exParams.setPort(params.getPort());
584
                        exParams.setDBName(params.getDBName());
585
                        exParams.setUser(params.getUser());
586
                        exParams.setPassword(params.getPassword());
587
                        exParams.setCatalog(params.getCatalog());
588
                        exParams.setSchema(params.getSchema());
589
                        exParams.setJDBCDriverClassName(params.getJDBCDriverClassName());
590
                        exParams.setUseSSL(params.getUseSSL());
591

    
592
                        return manager.createServerExplorer(exParams);
593
                } catch (DataException e) {
594
                        throw new ReadException(this.getName(), e);
595
                } catch (ValidateDataParametersException e) {
596
                        // TODO Auto-generated catch block
597
                        throw new ReadException(this.getName(), e);
598
                }
599
        }
600

    
601
        private void loadMetadata() throws DataException {
602
                IProjection srs = params.getSRS();
603

    
604
                if (srs == null) {
605
                        srs = store.getDefaultFeatureType().getDefaultSRS();
606
                }
607

    
608

    
609
                this.dynObject.setDynValue("DefaultSRS", srs);
610

    
611
                String defGeomName = this.store.getDefaultFeatureType()
612
                                .getDefaultGeometryAttributeName();
613
                Envelope env = null;
614
                if (defGeomName != null && defGeomName.length() > 0) {
615
                        env = this.helper
616
                                        .getFullEnvelopeOfField(this.params,
617
                                        defGeomName,
618
                                        this.params.getWorkingArea());
619

    
620
                }
621
                this.dynObject.setDynValue("Envelope", env);
622

    
623
        }
624

    
625
        private void clearMetadata() {
626
                this.dynObject.setDynValue("DefaultSRS", null);
627
                this.dynObject.setDynValue("Envelope", null);
628
        }
629

    
630
        public void closeDone() throws DataException {
631
                clearMetadata();
632

    
633
        }
634

    
635
        public void opendDone() throws DataException {
636
                // Nothing to do
637
        }
638

    
639
        public Envelope getEnvelope() throws DataException {
640
                this.open();
641
                return (Envelope) this.dynObject.getDynValue("Envelope");
642
        }
643

    
644
        public void resourceChanged(ResourceProvider resource) {
645
                this.store.notifyChange(DataStoreNotification.RESOURCE_CHANGED,
646
                                resource);
647
        }
648

    
649
        public boolean canWriteGeometry(int geometryType) throws DataException {
650
                return false;
651
        }
652

    
653
        public boolean allowAutomaticValues() {
654
                return true;
655
        }
656

    
657
        public void performEditing(Iterator deleteds, Iterator inserteds,
658
                        Iterator updateds, Iterator originalFeatureTypesUpdated)
659
                        throws PerformEditingException {
660
                // FIXME exception
661
                throw new UnsupportedOperationException();
662

    
663
        }
664

    
665
        // ************************************************************************************//
666

    
667

    
668
        // ************************************************************************************//
669

    
670

    
671
        public boolean supportsAppendMode() {
672
                // TODO Auto-generated method stub
673
                return false;
674
        }
675

    
676
        public PersistentState getState() throws PersistenceException {
677
                // TODO Auto-generated method stub
678
                return null;
679
        }
680

    
681
        public void loadState(PersistentState state) throws PersistenceException {
682
                // TODO Auto-generated method stub
683

    
684
        }
685

    
686
        public void setState(PersistentState state) throws PersistenceException {
687
                // TODO Auto-generated method stub
688

    
689
        }
690

    
691

    
692
        public void endAppend() throws DataException {
693
                // TODO Auto-generated method stub
694

    
695
        }
696

    
697

    
698
        public void append(Feature feature) throws DataException {
699
                // TODO Auto-generated method stub
700

    
701
        }
702

    
703
        public void beginAppend() throws DataException {
704
                // TODO Auto-generated method stub
705

    
706
        }
707

    
708
        public long getFeatureCount() throws DataException {
709
                return getCount(null);
710
        }
711

    
712
        /* (non-Javadoc)
713
         * @see org.gvsig.tools.persistence.Persistent#saveToState(org.gvsig.tools.persistence.PersistentState)
714
         */
715
        public void saveToState(PersistentState state) throws PersistenceException {
716
                // TODO Auto-generated method stub
717
                
718
        }
719

    
720
}