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

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

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

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

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

    
112
  private ResulSetControler resulSetControler = null;
113

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

    
118
  private GeometryManager geometryManager = null;
119

    
120
  private JDBCConnectionParameters connectionParameters;
121

    
122
  private JDBCStoreProvider store;
123

    
124
  protected OperationsFactory operationsFactory = null;
125

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

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

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

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

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

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

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

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

    
185

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
355
        return isCompatible.booleanValue();
356
    }
357

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

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

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

    
409
  }
410

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
983
}