Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.db / org.gvsig.fmap.dal.db.jdbc / src / main / java / org / gvsig / fmap / dal / store / jdbc2 / spi / JDBCHelperBase.java @ 46050

History | View | Annotate | Download (33.9 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2020 gvSIG Association.
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 3
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
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.store.jdbc2.spi;
25

    
26
import java.io.File;
27
import java.sql.Blob;
28
import java.sql.Clob;
29
import java.sql.Connection;
30
import java.sql.ResultSet;
31
import java.util.ArrayList;
32
import java.util.HashSet;
33
import java.util.List;
34
import java.util.function.Predicate;
35
import org.apache.commons.io.IOUtils;
36
import org.apache.commons.lang3.ArrayUtils;
37
import org.apache.commons.lang3.StringUtils;
38
import org.apache.commons.lang3.mutable.MutableBoolean;
39
import org.gvsig.expressionevaluator.Code;
40
import org.gvsig.expressionevaluator.Expression;
41
import org.gvsig.expressionevaluator.ExpressionBuilder;
42
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
43
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
44
import org.gvsig.expressionevaluator.Function;
45
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType;
46
import org.gvsig.expressionevaluator.SymbolTable;
47
import static org.gvsig.fmap.dal.DataManager.FUNCTION_EXISTS;
48
import static org.gvsig.fmap.dal.DataManager.FUNCTION_FOREING_VALUE;
49
import org.gvsig.fmap.dal.DataTypes;
50
import org.gvsig.fmap.dal.SQLBuilder;
51
import org.gvsig.fmap.dal.exception.DataException;
52
import org.gvsig.fmap.dal.exception.InitializeException;
53
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
54
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
55
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
56
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
57
import org.gvsig.fmap.dal.feature.FeatureType;
58
import org.gvsig.fmap.dal.feature.ForeingKey;
59
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
60
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
61
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
62
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
63
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
64
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemStoreParameters;
65
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
66
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
67
import org.gvsig.fmap.dal.spi.DataTransactionServices;
68
import org.gvsig.fmap.dal.store.db.DBHelper;
69
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
70
import org.gvsig.fmap.dal.store.jdbc.JDBCLibrary;
71
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
72
import org.gvsig.fmap.dal.store.jdbc.JDBCResource;
73
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
74
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
75
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException;
76
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
77
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
78
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
79
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
80
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
81
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler;
82
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
83
import org.gvsig.fmap.dal.store.jdbc2.impl.ResulSetControlerBase;
84
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.OperationsFactoryBase;
85
import org.gvsig.fmap.geom.Geometry;
86
import org.gvsig.fmap.geom.GeometryLocator;
87
import org.gvsig.fmap.geom.GeometryManager;
88
import org.gvsig.tools.dispose.impl.AbstractDisposable;
89
import org.gvsig.tools.evaluator.Evaluator;
90
import org.gvsig.tools.exception.BaseException;
91
import org.gvsig.tools.exception.NotYetImplemented;
92
import org.gvsig.tools.visitor.FilteredVisitable;
93
import org.gvsig.tools.visitor.VisitCanceledException;
94
import org.gvsig.tools.visitor.Visitor;
95
import org.slf4j.Logger;
96
import org.slf4j.LoggerFactory;
97
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_$CONSTANT;
98
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_$IDENTIFIER;
99

    
100
@SuppressWarnings("UseSpecificCatch")
101
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
102

    
103
  private static final boolean ALLOW_AUTOMATIC_VALUES = true;
104
  private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
105
  private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
106

    
107
  private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
108

    
109
  private ResulSetControler resulSetControler = null;
110

    
111
  // Quien ha creado este helper.
112
  // Se le reenviaran las notificaciones del recurso asociado al helper.
113
  private ResourceConsumer helperClient = null;
114

    
115
  private GeometryManager geometryManager = null;
116

    
117
  private JDBCConnectionParameters connectionParameters;
118

    
119
  private JDBCStoreProvider store;
120

    
121
  protected OperationsFactory operationsFactory = null;
122

    
123
  protected SRSSolver srssolver;
124
  
125
  protected FeatureType providerFeatureType = null;
126
  
127
  protected DataTransactionServices transaction;
128

    
129
  public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
130
        if (connectionParameters == null) {
131
            throw new IllegalArgumentException("Connection parameters can't be null.");
132
        }
133
        this.connectionParameters = connectionParameters;
134

    
135
        // If a particular treatment is required to convert SRS to the 
136
        // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
137
        this.srssolver = new SRSSolverDumb(this);
138
    }
139

    
140
  protected void initialize(
141
          ResourceConsumer helperClient,
142
          JDBCConnectionParameters connectionParameters,
143
          JDBCStoreProvider store
144
  ) {
145
    this.store = store;
146
    this.helperClient = helperClient;
147
    this.connectionParameters = connectionParameters;
148
    initializeResource(connectionParameters);
149
  }
150

    
151
  protected String getResourceType() {
152
    return JDBCResource.NAME;
153
  }
154

    
155
  @Override
156
  public String getProviderName() {
157
    return JDBCLibrary.NAME;
158
  }
159

    
160
  @Override
161
  public GeometrySupportType getGeometrySupportType() {
162
    // El proveedor generico de JDBC guadara las geoemtrias como WKT
163
    return GeometrySupportType.WKT;
164
  }
165
  
166
    /**
167
     * @return the providerFeatureType
168
     */
169
  @Override
170
    public FeatureType getProviderFeatureType() {
171
        return providerFeatureType;
172
    }
173

    
174
    /**
175
     * @param providerFeatureType the providerFeatureType to set
176
     */
177
  @Override
178
    public void setProviderFeatureType(FeatureType providerFeatureType) {
179
        this.providerFeatureType = providerFeatureType;
180
    }
181

    
182

    
183
  @Override
184
  public boolean hasSpatialFunctions() {
185
    // Por defecto el proveedor generico de JDBC asume que la BBDD no 
186
    // tiene soporte para funciones espaciales.
187
    return false;
188
  }
189

    
190
  @Override
191
  public boolean allowNestedOperations() {
192
    return false;
193
  }
194

    
195
  @Override
196
  public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
197
    // Como va a guardar las geometrias en WKT, puede escribirlas todas.
198
    return true;
199
  }
200

    
201
  @Override
202
  public JDBCSQLBuilderBase createSQLBuilder() {
203
    return new JDBCSQLBuilderBase(this);
204
  }
205

    
206
  @Override
207
  public String getQuoteForIdentifiers() {
208
    return QUOTE_FOR_USE_IN_IDENTIFIERS;
209
  }
210

    
211
  @Override
212
  public boolean allowAutomaticValues() {
213
    return ALLOW_AUTOMATIC_VALUES;
214
  }
215

    
216
  @Override
217
  public boolean supportOffsetInSelect() {
218
    // Asumimos que la BBDD soporta OFFSET
219
    return true;
220
  }
221

    
222
  @Override
223
  public String getQuoteForStrings() {
224
    return QUOTE_FOR_USE_IN_STRINGS;
225
  }
226

    
227
  protected boolean supportCaller(Code.Callable caller) {
228
    if (StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE")) {
229
      return true;
230
    }
231
    Function function = caller.function();
232
    if (function == null) {
233
      return false;
234
    }
235
    if (!function.isSQLCompatible()) {
236
      return false;
237
    }
238
    if (!this.hasSpatialFunctions()) {
239
      if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
240
        return false;
241
      }
242
    }
243
    return true;
244
  }
245

    
246
  @Override
247
  @SuppressWarnings("Convert2Lambda")
248
  public boolean supportFilter(final FeatureType type, Evaluator filter) {
249
    if (filter == null) {
250
      // No hay que filtrar nada, asi que se p?ede.
251
      return true;
252
    }
253
    String sql = filter.getSQL();
254
    if (StringUtils.isEmpty(sql)) {
255
      // Hay un filtro, pero la SQL es null, con lo que no podemos
256
      // filtrar por el.
257
      return false;
258
    }
259
    return this.supportExpression(type, sql);
260
  }
261
  
262
    @Override
263
    @SuppressWarnings("Convert2Lambda")
264
    public boolean supportExpression(final FeatureType type, String sql) {
265
        // La expression no es valida si:
266
        // - Hay una subquery especificada en los parametros 
267
        // - Si la sql es null.
268
        // - Si se esta usando alguna funcion no-sql en el getSQL
269
        // - Si se esta invocando a metodos de un objeto
270
        // - Si se estan usando funciones OGC y no soportamos funciones espaciales
271
        // - Si se esta usando algun campo calculado en la expresion de filtro.
272
        // 
273
        // Un proveedor especifico podria sobreescribir el metodo,
274
        // para hilar mas fino al comprobar si soporta el filtro o no.
275
        //
276

    
277
        if (StringUtils.isEmpty(sql)) {
278
            return false;
279
        }
280

    
281
        if (this.useSubquery()) {
282
            // Se esta usando una subquery en los parametros de acceso a la
283
            // BBDD, asi que no podemos filtrar.
284
            return false;
285
        }
286

    
287
        // Ahora vamos a comprobar que las funciones que se usan son 
288
        // compatibles sql, y que no se usan funciones OGC si el 
289
        // proveedor dice que no soporta funciones espaciales.
290
        // Tambien comprobaremos que el filtro no usa ningun campo calculado.
291
        final MutableBoolean isCompatible = new MutableBoolean(true);
292
        ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getManager();
293
        Code code = manager.compile(sql);
294
        SymbolTable symbolTable = manager.createSymbolTable();
295
        code.link(symbolTable);
296
        try {
297
            code.accept(new Visitor() {
298
                @Override
299
                public void visit(Object code_obj) throws VisitCanceledException, BaseException {
300
                    Code code1 = (Code) code_obj;
301
                    switch (code1.code()) {
302
                        case Code.CALLABLE:
303
                            Code.Callable caller = (Code.Callable) code1;
304
                            if (!supportCaller(caller)) {
305
                                isCompatible.setValue(false);
306
                                throw new VisitCanceledException();
307
                            }
308
                            break;
309
                        case Code.METHOD:
310
                            isCompatible.setValue(false);
311
                            throw new VisitCanceledException();
312
                        case Code.IDENTIFIER:
313
                            Code.Identifier identifier = (Code.Identifier) code1;
314
                            if (type != null) {
315
                                FeatureAttributeDescriptor attrdesc = type.getAttributeDescriptor(identifier.name());
316
                                if (attrdesc != null) {
317
                                    if (attrdesc.isComputed()) {
318
                                        FeatureAttributeEmulator emulator = attrdesc.getFeatureAttributeEmulator();
319
                                        if (!(emulator instanceof FeatureAttributeEmulatorExpression)) {
320
                                            isCompatible.setValue(false);
321
                                            throw new VisitCanceledException();
322
                                        }
323
                                        Expression expr = ((FeatureAttributeEmulatorExpression) emulator).getExpression();
324
                                        if (!supportExpression(type, expr.getPhrase())) {
325
                                            isCompatible.setValue(false);
326
                                            throw new VisitCanceledException();
327
                                        }
328
                                    }
329
                                }
330
                            }
331
                            break;
332
                    }
333
                } 
334
            }, new Predicate<FilteredVisitable>() {
335
                @Override
336
                public boolean test(FilteredVisitable code0) {
337
                    if (code0 instanceof Code.Callable) {
338
                        String name = ((Code.Callable) code0).name();
339
                        return StringUtils.equalsIgnoreCase(name, FUNCTION_$CONSTANT)
340
                                || StringUtils.equalsIgnoreCase(name, FUNCTION_$IDENTIFIER);
341
                    }
342
                    return false;
343
                }
344
            });
345

    
346
        } catch (VisitCanceledException ex) {
347
            // Do nothing
348
        } catch (Exception ex) {
349
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
350
        }
351

    
352
        return isCompatible.booleanValue();
353
    }
354

    
355
  @Override
356
  public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
357
    if (this.useSubquery()) {
358
      return false;
359
    }
360
    if (order == null) {
361
      return true;
362
    }
363
    for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
364
      if (member.hasEvaluator()) {
365
        if (!this.supportFilter(type, member.getEvaluator())) {
366
          return false;
367
        }
368
      }
369
    }
370
    return true;
371
  }
372

    
373
  @Override
374
  public OperationsFactory getOperations() {
375
    if (this.operationsFactory == null && !isClosed()) {
376
      this.operationsFactory = new OperationsFactoryBase(this);
377
    }
378
    return operationsFactory;
379
  }
380

    
381
  protected void initializeResource(JDBCConnectionParameters params) {
382
//        Object[] resourceParams = new Object[]{
383
//            params.getUrl(),
384
//            params.getHost(),
385
//            params.getPort(),
386
//            params.getDBName(),
387
//            params.getUser(),
388
//            params.getPassword(),
389
//            params.getJDBCDriverClassName()
390
//        };
391
//
392
//        try {
393
//            ResourceManagerProviderServices manager
394
//                    = (ResourceManagerProviderServices) DALLocator.getResourceManager();
395
//            JDBCResource resource = (JDBCResource) manager.createAddResource(
396
//                    this.getResourceType(),
397
//                    resourceParams
398
//            );
399
//            this.resource = resource;
400
//            this.resource.addConsumer(this);
401
//        } catch (InitializeException ex) {
402
//            logger.trace("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
403
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
404
//        }
405

    
406
  }
407

    
408
  @Override
409
  public String getSourceId() {
410
    return this.store.getSourceId();
411
  }
412

    
413
  @Override
414
  public JDBCResource getResource() {
415
    return null;
416
//        return this.resource;
417
  }
418

    
419
  @Override
420
  public Connection getConnection() throws AccessResourceException {
421
    throw new NotYetImplemented();
422
  }
423

    
424
  @Override
425
  public Connection getConnectionWritable() throws AccessResourceException {
426
    return this.getConnection();
427
  }
428

    
429
  @Override
430
  public String getConnectionURL() {
431
    return null;
432
  }
433

    
434
  @Override
435
  public JDBCConnectionParameters getConnectionParameters() {
436
    return connectionParameters;
437
  }
438

    
439
  @Override
440
  public void closeConnection(Connection connection) {
441
      JDBCUtils.close(connection);
442
  }
443

    
444
  @Override
445
  public void closeConnectionQuietly(Connection connection) {
446
      JDBCUtils.closeQuietly(connection);
447
  }
448

    
449
  @Override
450
  protected void doDispose() throws BaseException {
451
    JDBCUtils.closeQuietly(this);
452
  }
453

    
454
  @Override
455
  public void close() throws Exception {
456
    JDBCUtils.closeQuietly(this.resulSetControler);
457
    this.resulSetControler = null;
458
    this.operationsFactory = null;
459
    this.srssolver = null;
460
    this.connectionParameters = null;
461
    this.helperClient = null;
462
    this.geometryManager = null;
463
    this.providerFeatureType = null;
464
    this.store = null;
465
  }
466
  
467
  public boolean isClosed() {
468
      return this.srssolver == null;
469
  }
470

    
471
  @Override
472
  public boolean closeResourceRequested(ResourceProvider resource) {
473
    return this.helperClient.closeResourceRequested(resource);
474
  }
475

    
476
  @Override
477
  public void resourceChanged(ResourceProvider resource) {
478
    this.helperClient.resourceChanged(resource);
479
  }
480

    
481
  @Override
482
  public GeometryManager getGeometryManager() {
483
    if (this.geometryManager == null) {
484
      this.geometryManager = GeometryLocator.getGeometryManager();
485
    }
486
    return this.geometryManager;
487
  }
488

    
489
  @Override
490
  public ResulSetControler getResulSetControler() {
491
    if (this.resulSetControler == null) {
492
      this.resulSetControler = new ResulSetControlerBase(this);
493
    }
494
    return this.resulSetControler;
495
  }
496

    
497
  @Override
498
  public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
499
    fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
500
  }
501

    
502
  @Override
503
  public void fetchFeature(FeatureProvider feature, ResultSet rs, FeatureAttributeDescriptor[] columns, String[] extraValueNames) throws DataException {
504
    Object value;
505
    try {
506
      int rsIndex = 1;
507
      for (FeatureAttributeDescriptor column : columns) {
508
        switch (column.getType()) {
509
          case DataTypes.GEOMETRY:
510
            value = this.getGeometryFromColumn(rs, rsIndex++);
511
            break;
512
          default:
513
            value = rs.getObject(rsIndex++);
514
            if (value instanceof Blob) {
515
              Blob blob = (Blob) value;
516
              value = blob.getBytes(1, (int) blob.length());
517
              blob.free();
518
            } else if(value instanceof Clob) {
519
              Clob clob = (Clob) value;
520
              value = new String(IOUtils.toCharArray(clob.getCharacterStream()));
521
              clob.free();
522
          }
523
        }
524
        feature.set(column.getIndex(), value);
525
      }
526
      if (ArrayUtils.isNotEmpty(extraValueNames)) {
527
        feature.setExtraValueNames(extraValueNames);
528
        for (int index = 0; index < extraValueNames.length; index++) {
529
          value = rs.getObject(rsIndex++);
530
          if (value instanceof Blob) {
531
            Blob blob = (Blob) value;
532
            value = blob.getBytes(0, (int) blob.length());
533
            blob.free();
534
          }
535
          feature.setExtraValue(index, value);
536
        }
537
      }
538
    } catch (Exception ex) {
539
      throw new JDBCCantFetchValueException(ex);
540
    }
541
  }
542

    
543
  @Override
544
  public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
545
    return getGeometryFromColumn(rs.get(), index);
546
  }
547

    
548
  @Override
549
  public Geometry getGeometryFromColumn(ResultSet rs, int index) throws DataException {
550
    try {
551
      Object value;
552
      switch (this.getGeometrySupportType()) {
553
        case NATIVE:
554
        case WKB:
555
          value = rs.getBytes(index);
556
          if (value == null) {
557
            return null;
558
          }
559
          return this.getGeometryManager().createFrom((byte[]) value);
560

    
561
        case EWKB:
562
          value = rs.getBytes(index);
563
          if (value == null) {
564
            return null;
565
          }
566
          return this.getGeometryManager().createFrom((byte[]) value);
567
        case WKT:
568
        default:
569
          value = rs.getString(index);
570
          if (value == null) {
571
            return null;
572
          }
573
          return this.getGeometryManager().createFrom((String) value);
574

    
575
      }
576
    } catch (Exception ex) {
577
      throw new JDBCCantFetchValueException(ex);
578
    }
579
  }
580

    
581
  @Override
582
  public FeatureProvider createFeature(FeatureType featureType) throws DataException {
583
    return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
584
  }
585

    
586
  @Override
587
  public boolean useSubquery() {
588
    if (this.store == null) {
589
      return false;
590
    }
591
    return !StringUtils.isEmpty(this.store.getParameters().getSQL());
592
  }
593

    
594
  @Override
595
  public SRSSolver getSRSSolver() {
596
    return this.srssolver;
597
  }
598

    
599
  @Override
600
  public JDBCStoreProvider createProvider(
601
          JDBCStoreParameters parameters,
602
          DataStoreProviderServices providerServices
603
  ) throws InitializeException {
604

    
605
    JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
606
            parameters,
607
            providerServices,
608
            DBHelper.newMetadataContainer(JDBCLibrary.NAME),
609
            this
610
    );
611
    this.initialize(theStore, parameters, theStore);
612
    return theStore;
613
  }
614

    
615
  @Override
616
  public JDBCServerExplorer createServerExplorer(
617
          JDBCServerExplorerParameters parameters,
618
          DataServerExplorerProviderServices providerServices
619
  ) throws InitializeException {
620

    
621
    JDBCServerExplorer explorer = new JDBCServerExplorerBase(
622
            parameters,
623
            providerServices,
624
            this
625
    );
626
    this.initialize(explorer, parameters, null);
627
    return explorer;
628
  }
629

    
630
  @Override
631
  public JDBCNewStoreParameters createNewStoreParameters() {
632
    return new JDBCNewStoreParameters();
633
  }
634

    
635
  @Override
636
  public JDBCStoreParameters createOpenStoreParameters() {
637
    return new JDBCStoreParameters();
638
  }
639
  
640
  @Override
641
  public JDBCStoreParameters createOpenStoreParameters(JDBCServerExplorerParameters parameters) {
642
    JDBCStoreParameters params = this.createOpenStoreParameters();
643
    params.setHost(parameters.getHost());
644
    params.setPort(parameters.getPort());
645
    params.setDBName(parameters.getDBName());
646
    params.setUser(parameters.getUser());
647
    params.setPassword(parameters.getPassword());
648
    params.setCatalog(parameters.getCatalog());
649
    params.setSchema(parameters.getSchema());
650
    params.setJDBCDriverClassName(parameters.getJDBCDriverClassName());
651
    params.setUrl(parameters.getUrl());
652
    if( parameters instanceof FilesystemStoreParameters ) {
653
        File f = ((FilesystemStoreParameters) parameters).getFile();
654
        ((FilesystemStoreParameters) params).setFile(f);
655
    }
656
    return params;
657
  }
658
  
659

    
660
  @Override
661
  public JDBCServerExplorerParameters createServerExplorerParameters() {
662
    return new JDBCServerExplorerParameters();
663
  }
664

    
665
  @Override
666
  public String getSourceId(JDBCStoreParameters parameters) {
667
    return parameters.getHost() + ":"
668
            + parameters.getDBName() + ":"
669
            + parameters.getSchema() + ":"
670
            + parameters.tableID();
671
  }
672

    
673
  @Override
674
  public boolean isThreadSafe() {
675
    return true;
676
  }
677

    
678
  /** 
679
   * This method has been overriden in Oracle provider
680
   * 
681
   * @param sqlbuilder
682
   * @param type
683
   * @param extra_column_names 
684
   */
685
  @Override
686
  public void processSpecialFunctions(
687
          SQLBuilder sqlbuilder,
688
          FeatureType type,
689
          List<String> extra_column_names) {
690
    replaceForeingValueFunction(sqlbuilder, type, extra_column_names);
691
    replaceExistsFunction(sqlbuilder, type, extra_column_names);
692
  }
693

    
694
  @SuppressWarnings("Convert2Lambda")
695
  private void replaceExistsFunction(
696
          SQLBuilder sqlbuilder,
697
          FeatureType type,
698
          final List<String> extra_column_names) {
699
    
700
    //  Si lse encuentra una construccion del tipo:
701
    //    SELECT ... FROM ... WHERE ... EXISTS(list, 'EXISTS_ID') ...
702
    //  se traslada a:
703
    //    SELECT ... EXISTS(list) AS EXISTS_ID FROM ... WHERE ... EXISTS(list) ...
704
    //  Y se a?ade el valor ESISTS_ID a las columnas extra a recuperar de la consulta.
705
    //          
706
    
707
    final SQLBuilder.SelectBuilder select = sqlbuilder.select();
708
    final ExpressionBuilder where = select.where();
709
    if (where == null || where.isEmpty() || select.has_group_by() ) {
710
      return;
711
    }
712
    final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
713
    where.accept(new ExpressionBuilder.Visitor() {
714
      @Override
715
      public void visit(ExpressionBuilder.Visitable value) {
716
        if (!(value instanceof ExpressionBuilder.Function)) {
717
          return;
718
        }
719
        ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
720
        if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_EXISTS)) {
721
          return;
722
        }
723
        if (function.parameters().size() != 2) {
724
          return;
725
        }
726
        ExpressionBuilder.Value arg0 = function.parameters().get(0);
727
        ExpressionBuilder.Value arg1 = function.parameters().get(1);
728
        if (arg1 == null) {
729
          return;
730
        }
731
        String columnName = (String) ((ExpressionBuilder.Constant) arg1).value();
732
        SQLBuilder.SelectColumnBuilder column = select.column();
733
        column.value(
734
                sqlbuilder.expression().function(FUNCTION_EXISTS, arg0)
735
        );
736
        column.as(columnName);
737

    
738
        if( extra_column_names!=null ) {
739
          extra_column_names.add(columnName);
740
        }
741
      }
742
    }, null);
743
    if (value_replacements.isEmpty()) {
744
      return;
745
    }
746
    // Realizamos los reemplazos calculados previamente (value_replacements).
747
    for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
748
      ExpressionBuilder.Value target = replaceValue[0];
749
      ExpressionBuilder.Value replacement = replaceValue[1];
750
      sqlbuilder.select().replace(target, replacement);
751
    }
752
  }
753

    
754
  @SuppressWarnings("Convert2Lambda")
755
  protected void replaceForeingValueFunction(
756
          SQLBuilder sqlbuilder,
757
          FeatureType type,
758
          List<String> extra_column_names) {
759
    try {
760
      // See test SQLBuilderTest->testForeingValue()
761
      final ExpressionBuilder where = sqlbuilder.select().where();
762
//      if (where == null || where.isEmpty()) {
763
//        return;
764
//      }
765
      final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
766
      final ExpressionBuilder expbuilder = sqlbuilder.expression();
767

    
768
      final List<String> foreing_value_args;
769
      if (extra_column_names == null || sqlbuilder.select().has_group_by()) {
770
        foreing_value_args = new ArrayList<>();
771
      } else {
772
        foreing_value_args = extra_column_names;
773
      }
774
      final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
775

    
776
      // Buscamos las llamadas a la funcion "foreing_value" y nos quedamos
777
      // el argumento de esta asi como por que tendriamos que sustituirla 
778
      // una vez hechos los left joins que toquen.
779
      sqlbuilder.select().accept(new ExpressionBuilder.Visitor() {
780
        @Override
781
        public void visit(ExpressionBuilder.Visitable value) {
782
          // Requiere que sea la funcion "FOREING_VALUE con un solo 
783
          // argumento que sea una constante de tipo string.
784
          if (!(value instanceof ExpressionBuilder.Function)) {
785
            return;
786
          }
787
          ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
788
          if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_FOREING_VALUE)) {
789
            return;
790
          }
791
          if (function.parameters().size() != 1) {
792
            return;
793
          }
794
          ExpressionBuilder.Value arg = function.parameters().get(0);
795
          if (!(arg instanceof ExpressionBuilder.Constant)) {
796
            return;
797
          }
798
          Object arg_value = ((ExpressionBuilder.Constant) arg).value();
799
          if (!(arg_value instanceof CharSequence)) {
800
            return;
801
          }
802
          String foreing_value_arg = arg_value.toString();
803
          String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]");
804
          if (foreingNameParts.length != 2) {
805
            // De momento solo tratamos joins entre dos tablas.
806
            return;
807
          }
808
          String columnNameLocal = foreingNameParts[0];
809
          String columnNameForeing = foreingNameParts[1];
810
          FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
811
          if (attr==null) {
812
              throw new RuntimeException("Cannot find in feature type attribute:"+columnNameLocal);
813
          }
814
          if (!attr.isForeingKey()) {
815
            // Uhm... si el argumento no referencia a un campo que es
816
            // clave ajena no lo procesamos. 
817
            // ? Deberiamos lanzar un error ?
818
            return;
819
          }
820
          // Nos guardaremos por que hay que reemplazar la funcion 
821
          // FOREING_VALUE, y su argumento para mas tarde.
822
          ForeingKey foreingKey = attr.getForeingKey();
823
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
824
                  .database(table.getDatabase())
825
                  .schema(table.getSchema())
826
                  .name(foreingKey.getTableName());
827
          // Reemplzaremos la funcion FOREING_VALUE, por el acceso al campo
828
          // que toca de la tabla a la que referencia la clave ajena.
829
          ExpressionBuilder.Variable function_replacement = sqlbuilder.column(foreingTable, columnNameForeing);
830
          value_replacements.add(
831
                  new ExpressionBuilder.Value[]{
832
                    function,
833
                    function_replacement
834
                  }
835
          );
836
          if (!foreing_value_args.contains(foreing_value_arg)) {
837
            foreing_value_args.add(foreing_value_arg);
838
          }
839
        }
840
      }, null);
841
      
842
      // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
843
      // hacer nada.
844
      if (foreing_value_args.isEmpty()) {
845
        return;
846
      }
847

    
848
      // Calculamos que referencias de columnas hemos de cambiar para 
849
      // que no aparezcan ambiguedades al hacer el join (nombres de
850
      // columna de una y otra tabla que coincidan).
851
      // Para las columnas que puedan dar conflicto se prepara un reemplazo 
852
      // de estas que tenga el nombre de tabla.
853
      for (ExpressionBuilder.Variable variable : sqlbuilder.variables()) {
854
        if (variable == null || variable instanceof SQLBuilderBase.ColumnBase) {
855
          continue;
856
        }
857
        for (String foreingName : foreing_value_args) {
858
          String[] foreingNameParts = foreingName.split("[.]");
859
          if (foreingNameParts.length != 2) {
860
            continue;
861
          }
862
          String columnNameLocal = foreingNameParts[0];
863
          String columnNameForeing = foreingNameParts[1];
864
          if (StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing)
865
                  || StringUtils.equalsIgnoreCase(variable.name(), columnNameLocal)) {
866
            ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
867
                    table,
868
                    variable.name()
869
            );
870
            value_replacements.add(
871
                    new ExpressionBuilder.Value[]{
872
                      variable,
873
                      variable_replacement
874
                    }
875
            );
876
          }
877
        }
878
      }
879

    
880
      // Realizamos los reemplazos calculados previamente (value_replacements).
881
      for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
882
        ExpressionBuilder.Value target = replaceValue[0];
883
        ExpressionBuilder.Value replacement = replaceValue[1];
884
        sqlbuilder.select().replace(target, replacement);
885
      }
886

    
887
        // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
888
        // a los valores referenciados por la funcion FOREING_VALUE.
889
        // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
890
        // como columnas de la SQL para poder acceder a ellos si fuese necesario.
891
        HashSet usedLeftJoins = new HashSet();
892
      for (String foreingName : foreing_value_args) {
893
        String[] foreingNameParts = foreingName.split("[.]");
894
        if (foreingNameParts.length != 2) {
895
          continue;
896
        }
897
        String columnNameLocal = foreingNameParts[0];
898
        String columnNameForeing = foreingNameParts[1];
899
        FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
900
        if (attr.isForeingKey()) {
901
          ForeingKey foreingKey = attr.getForeingKey();
902
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
903
                  .database(table.getDatabase())
904
                  .schema(table.getSchema())
905
                  .name(foreingKey.getTableName());
906
          SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
907
                  .database(table.getDatabase())
908
                  .schema(table.getSchema())
909
                  .name(table.getName());
910
          
911
          if (!usedLeftJoins.contains(foreingTable.getName())) {
912
            sqlbuilder.select().from()
913
                    .left_join(
914
                            foreingTable,
915
                            expbuilder.eq(
916
                                    sqlbuilder.column(mainTable, columnNameLocal),
917
                                    sqlbuilder.column(foreingTable, foreingKey.getCodeName())
918
                            )
919
                    );
920
            usedLeftJoins.add(foreingTable.getName());
921
          }
922
          if (!sqlbuilder.select().has_group_by()) {
923
                sqlbuilder.select().column().name(foreingTable, columnNameForeing);
924
          }
925
        }
926
      }
927
      
928
        for (SQLBuilder.SelectColumnBuilder column : sqlbuilder.select().getColumns()) {
929
            if(column.getTable()==null && column.getName()!=null) {
930
                column.name(table, column.getName());
931
            }
932
        }
933

    
934
    } catch (Throwable th) {
935
      LOGGER.warn("Can't replace FOREING_VALUE function.", th);
936
      throw th;
937
    } finally {
938
      LOGGER.trace("Exit from replaceForeingValueFunction.");
939
    }
940
  }
941

    
942
    @Override
943
    public void setTransaction(DataTransactionServices transaction) {
944
        this.transaction = transaction;
945
    }
946
    
947
    @Override
948
    public String toString() {
949
        try {
950
            return String.format("%s %x %s", this.getClass().getSimpleName(), this.hashCode(), this.connectionParameters.getUrl());
951
        } catch (Exception e) {
952
            return super.toString();
953
        }
954
    }
955

    
956
    public String getConnectionProviderStatus() {
957
        return "";
958
    }
959
   
960
}