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

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

    
104
@SuppressWarnings("UseSpecificCatch")
105
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
106

    
107
  private static final boolean ALLOW_AUTOMATIC_VALUES = true;
108
  private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
109
  private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
110

    
111
  private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
112

    
113
  private ResulSetControler resulSetControler = null;
114

    
115
  // Quien ha creado este helper.
116
  // Se le reenviaran las notificaciones del recurso asociado al helper.
117
  private ResourceConsumer helperClient = null;
118

    
119
  private GeometryManager geometryManager = null;
120

    
121
  private JDBCConnectionParameters connectionParameters;
122

    
123
  private JDBCStoreProvider store;
124

    
125
  protected OperationsFactory operationsFactory = null;
126

    
127
  protected SRSSolver srssolver;
128
  
129
  protected FeatureType providerFeatureType = null;
130
  
131
  protected DataTransactionServices transaction;
132

    
133
  public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
134
        if (connectionParameters == null) {
135
            throw new IllegalArgumentException("Connection parameters can't be null.");
136
        }
137
        this.connectionParameters = connectionParameters;
138

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

    
144
  protected void initialize(
145
          ResourceConsumer helperClient,
146
          JDBCConnectionParameters connectionParameters,
147
          JDBCStoreProvider store
148
  ) {
149
    this.store = store;
150
    this.helperClient = helperClient;
151
    this.connectionParameters = connectionParameters;
152
    initializeResource(connectionParameters);
153
  }
154

    
155
  protected String getResourceType() {
156
    return JDBCResource.NAME;
157
  }
158

    
159
  @Override
160
  public String getProviderName() {
161
    return JDBCLibrary.NAME;
162
  }
163

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

    
178
    /**
179
     * @param providerFeatureType the providerFeatureType to set
180
     */
181
  @Override
182
    public void setProviderFeatureType(FeatureType providerFeatureType) {
183
        this.providerFeatureType = providerFeatureType;
184
    }
185

    
186

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

    
194
  @Override
195
  public boolean allowNestedOperations() {
196
    return false;
197
  }
198

    
199
  @Override
200
  public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
201
    // Como va a guardar las geometrias en WKT, puede escribirlas todas.
202
    return true;
203
  }
204

    
205
  @Override
206
  public JDBCSQLBuilderBase createSQLBuilder() {
207
    return new JDBCSQLBuilderBase(this);
208
  }
209

    
210
  @Override
211
  public String getQuoteForIdentifiers() {
212
    return QUOTE_FOR_USE_IN_IDENTIFIERS;
213
  }
214

    
215
  @Override
216
  public boolean allowAutomaticValues() {
217
    return ALLOW_AUTOMATIC_VALUES;
218
  }
219

    
220
  @Override
221
  public boolean supportOffsetInSelect() {
222
    // Asumimos que la BBDD soporta OFFSET
223
    return true;
224
  }
225

    
226
  @Override
227
  public String getQuoteForStrings() {
228
    return QUOTE_FOR_USE_IN_STRINGS;
229
  }
230

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

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

    
281
        if (StringUtils.isEmpty(sql)) {
282
            return false;
283
        }
284

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

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

    
350
        } catch (VisitCanceledException ex) {
351
            // Do nothing
352
        } catch (Exception ex) {
353
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
354
        }
355

    
356
        return isCompatible.booleanValue();
357
    }
358

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

    
377
  @Override
378
  public OperationsFactory getOperations() {
379
    if (this.operationsFactory == null && !isClosed()) {
380
      this.operationsFactory = new OperationsFactoryBase(this);
381
    }
382
    return operationsFactory;
383
  }
384

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

    
410
  }
411

    
412
  @Override
413
  public String getSourceId() {
414
    return this.store.getSourceId();
415
  }
416

    
417
  @Override
418
  public JDBCResource getResource() {
419
    return null;
420
//        return this.resource;
421
  }
422

    
423
  @Override
424
  public Connection getConnection() throws AccessResourceException {
425
    throw new NotYetImplemented();
426
  }
427

    
428
  @Override
429
  public Connection getConnectionWritable() throws AccessResourceException {
430
    return this.getConnection();
431
  }
432

    
433
  @Override
434
  public String getConnectionURL() {
435
    return null;
436
  }
437

    
438
  @Override
439
  public JDBCConnectionParameters getConnectionParameters() {
440
    return connectionParameters;
441
  }
442

    
443
  @Override
444
  public void closeConnection(Connection connection) {
445
      JDBCUtils.close(connection);
446
  }
447

    
448
  @Override
449
  public void closeConnectionQuietly(Connection connection) {
450
      JDBCUtils.closeQuietly(connection);
451
  }
452

    
453
  @Override
454
  protected void doDispose() throws BaseException {
455
    JDBCUtils.closeQuietly(this);
456
  }
457

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

    
475
  @Override
476
  public boolean closeResourceRequested(ResourceProvider resource) {
477
    return this.helperClient.closeResourceRequested(resource);
478
  }
479

    
480
  @Override
481
  public void resourceChanged(ResourceProvider resource) {
482
    this.helperClient.resourceChanged(resource);
483
  }
484

    
485
  @Override
486
  public GeometryManager getGeometryManager() {
487
    if (this.geometryManager == null) {
488
      this.geometryManager = GeometryLocator.getGeometryManager();
489
    }
490
    return this.geometryManager;
491
  }
492

    
493
  @Override
494
  public ResulSetControler getResulSetControler() {
495
    if (this.resulSetControler == null) {
496
      this.resulSetControler = new ResulSetControlerBase(this);
497
    }
498
    return this.resulSetControler;
499
  }
500

    
501
  @Override
502
  public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
503
    fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
504
  }
505

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

    
547
  @Override
548
  public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
549
    return getGeometryFromColumn(rs.get(), index);
550
  }
551

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

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

    
579
      }
580
    } catch (Exception ex) {
581
      throw new JDBCCantFetchValueException(ex);
582
    }
583
  }
584

    
585
  @Override
586
  public FeatureProvider createFeature(FeatureType featureType) throws DataException {
587
    return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
588
  }
589

    
590
  @Override
591
  public boolean useSubquery() {
592
    if (this.store == null) {
593
      return false;
594
    }
595
    return !StringUtils.isEmpty(this.store.getParameters().getSQL());
596
  }
597

    
598
  @Override
599
  public SRSSolver getSRSSolver() {
600
    return this.srssolver;
601
  }
602

    
603
  @Override
604
  public JDBCStoreProvider createProvider(
605
          JDBCStoreParameters parameters,
606
          DataStoreProviderServices providerServices
607
  ) throws InitializeException {
608

    
609
    JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
610
            parameters,
611
            providerServices,
612
            DBHelper.newMetadataContainer(JDBCLibrary.NAME),
613
            this
614
    );
615
    this.initialize(theStore, parameters, theStore);
616
    return theStore;
617
  }
618

    
619
  @Override
620
  public JDBCServerExplorer createServerExplorer(
621
          JDBCServerExplorerParameters parameters,
622
          DataServerExplorerProviderServices providerServices
623
  ) throws InitializeException {
624

    
625
    JDBCServerExplorer explorer = new JDBCServerExplorerBase(
626
            parameters,
627
            providerServices,
628
            this
629
    );
630
    this.initialize(explorer, parameters, null);
631
    return explorer;
632
  }
633

    
634
  @Override
635
  public JDBCNewStoreParameters createNewStoreParameters() {
636
    return new JDBCNewStoreParameters();
637
  }
638

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

    
664
  @Override
665
  public JDBCServerExplorerParameters createServerExplorerParameters() {
666
    return new JDBCServerExplorerParameters();
667
  }
668

    
669
  @Override
670
  public String getSourceId(JDBCStoreParameters parameters) {
671
    return parameters.getHost() + ":"
672
            + parameters.getDBName() + ":"
673
            + parameters.getSchema() + ":"
674
            + parameters.tableID();
675
  }
676

    
677
  @Override
678
  public boolean isThreadSafe() {
679
    return true;
680
  }
681

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

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

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

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

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

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

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

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

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

    
938
    } catch (Throwable th) {
939
      LOGGER.warn("Can't replace FOREING_VALUE function.", th);
940
      throw th;
941
    } finally {
942
      LOGGER.trace("Exit from replaceForeingValueFunction.");
943
    }
944
  }
945

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

    
960
    public String getConnectionProviderStatus() {
961
        return "";
962
    }
963
        
964
        public void expandCalculedColumns(JDBCSQLBuilderBase sqlbuilder) {
965
                ComputedAttribute computedAttributeFormater = new ComputedAttribute(sqlbuilder, sqlbuilder.formatter());
966
                List<Pair<ExpressionBuilder.Value, ExpressionBuilder.Value>> variablesToReplace = new ArrayList<>();
967
                sqlbuilder.accept(new ExpressionBuilder.Visitor() {
968
                        @Override
969
                        public void visit(ExpressionBuilder.Visitable value) {
970
                                if (computedAttributeFormater.canApply((ExpressionBuilder.Value) value)) {
971
                                        ExpressionBuilder.Variable variable = (ExpressionBuilder.Variable) value;
972
                                        ExpressionBuilder.Value replace = computedAttributeFormater.expandedValue(variable);
973
                                        variablesToReplace.add(Pair.of(variable, replace));
974
                                }
975
                        }
976
                }, null);
977
                for (Pair<ExpressionBuilder.Value, ExpressionBuilder.Value> entry : variablesToReplace) {
978
                        ExpressionBuilder.Value variable = entry.getKey();
979
                        ExpressionBuilder.Value replace = entry.getValue();
980
                        sqlbuilder.select().replace(variable, replace);
981
                }
982
        }
983

    
984
}