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

History | View | Annotate | Download (27.2 KB)

1 43020 jjdelcerro
package org.gvsig.fmap.dal.store.jdbc2.spi;
2
3 44297 jjdelcerro
import java.sql.Blob;
4 43020 jjdelcerro
import org.gvsig.fmap.dal.store.jdbc2.impl.ResulSetControlerBase;
5
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.OperationsFactoryBase;
6
import java.sql.Connection;
7
import java.sql.ResultSet;
8 44376 jjdelcerro
import java.util.ArrayList;
9
import java.util.List;
10
import org.apache.commons.lang3.ArrayUtils;
11 43020 jjdelcerro
import org.apache.commons.lang3.StringUtils;
12 44191 jjdelcerro
import org.apache.commons.lang3.mutable.MutableBoolean;
13
import org.gvsig.expressionevaluator.Code;
14 44376 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionBuilder;
15 44042 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType;
16
import static org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType.EWKB;
17
import static org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType.NATIVE;
18
import static org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType.WKB;
19
import static org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType.WKT;
20 44191 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
21
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
22
import org.gvsig.expressionevaluator.Function;
23 44198 jjdelcerro
import org.gvsig.expressionevaluator.SymbolTable;
24 43020 jjdelcerro
import org.gvsig.fmap.dal.DataTypes;
25 44376 jjdelcerro
import org.gvsig.fmap.dal.SQLBuilder;
26 43020 jjdelcerro
import org.gvsig.fmap.dal.exception.DataException;
27
import org.gvsig.fmap.dal.exception.InitializeException;
28
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
29 44191 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
30 43020 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureType;
31 44376 jjdelcerro
import org.gvsig.fmap.dal.feature.ForeingKey;
32 43020 jjdelcerro
33
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
34 44376 jjdelcerro
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
35 43020 jjdelcerro
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
36
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
37
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
38
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
39
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
40
import org.gvsig.fmap.dal.store.db.DBHelper;
41
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
42
import org.gvsig.fmap.dal.store.jdbc.JDBCLibrary;
43
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
44
import org.gvsig.fmap.dal.store.jdbc.JDBCResource;
45
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
46
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
47
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException;
48
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
49
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
50
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
51
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
52
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
53
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler;
54
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
55
import org.gvsig.fmap.geom.Geometry;
56
import org.gvsig.fmap.geom.GeometryLocator;
57
import org.gvsig.fmap.geom.GeometryManager;
58
import org.gvsig.tools.dispose.impl.AbstractDisposable;
59 44191 jjdelcerro
import org.gvsig.tools.evaluator.Evaluator;
60 43020 jjdelcerro
import org.gvsig.tools.exception.BaseException;
61 43377 jjdelcerro
import org.gvsig.tools.exception.NotYetImplemented;
62 44191 jjdelcerro
import org.gvsig.tools.visitor.VisitCanceledException;
63
import org.gvsig.tools.visitor.Visitor;
64 43020 jjdelcerro
import org.slf4j.Logger;
65
import org.slf4j.LoggerFactory;
66
67 44191 jjdelcerro
@SuppressWarnings("UseSpecificCatch")
68 43020 jjdelcerro
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
69
70
    private static final boolean ALLOW_AUTOMATIC_VALUES = true;
71
    private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
72
    private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
73
74 44191 jjdelcerro
    private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
75 43020 jjdelcerro
76
    private ResulSetControler resulSetControler = null;
77
78
    // Quien ha creado este helper.
79
    // Se le reenviaran las notificaciones del recurso asociado al helper.
80
    private ResourceConsumer helperClient = null;
81
82
    private GeometryManager geometryManager = null;
83
84 43035 jjdelcerro
    private JDBCConnectionParameters connectionParameters;
85 43020 jjdelcerro
86 44403 omartinez
    private JDBCStoreProvider store;
87
88 43020 jjdelcerro
    protected OperationsFactory operationsFactory = null;
89 44403 omartinez
90 43606 jjdelcerro
    protected SRSSolver srssolver;
91 44403 omartinez
92 43020 jjdelcerro
    public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
93
        this.connectionParameters = connectionParameters;
94 44403 omartinez
95 43355 jjdelcerro
        // If a particular treatment is required to convert SRS to the
96
        // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
97 43606 jjdelcerro
        this.srssolver = new SRSSolverDumb(this);
98 43020 jjdelcerro
    }
99
100
    protected void initialize(
101
            ResourceConsumer helperClient,
102
            JDBCConnectionParameters connectionParameters,
103
            JDBCStoreProvider store
104 44403 omartinez
    ) {
105 43093 jjdelcerro
        this.store = store;
106 43020 jjdelcerro
        this.helperClient = helperClient;
107
        this.connectionParameters = connectionParameters;
108 44403 omartinez
        initializeResource(connectionParameters);
109 43020 jjdelcerro
    }
110 44403 omartinez
111 43020 jjdelcerro
    protected String getResourceType() {
112
        return JDBCResource.NAME;
113
    }
114
115
    @Override
116
    public String getProviderName() {
117
        return JDBCLibrary.NAME;
118
    }
119 44403 omartinez
120 43020 jjdelcerro
    @Override
121
    public GeometrySupportType getGeometrySupportType() {
122
        // El proveedor generico de JDBC guadara las geoemtrias como WKT
123
        return GeometrySupportType.WKT;
124
    }
125
126
    @Override
127
    public boolean hasSpatialFunctions() {
128
        // Por defecto el proveedor generico de JDBC asume que la BBDD no
129
        // tiene soporte para funciones espaciales.
130
        return false;
131
    }
132
133
    @Override
134 44191 jjdelcerro
    public boolean allowNestedOperations() {
135
        return false;
136
    }
137 44403 omartinez
138 44191 jjdelcerro
    @Override
139 43020 jjdelcerro
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
140
        // Como va a guardar las geometrias en WKT, puede escribirlas todas.
141
        return true;
142
    }
143
144
    @Override
145
    public JDBCSQLBuilderBase createSQLBuilder() {
146 43687 jjdelcerro
        return new JDBCSQLBuilderBase(this);
147 43020 jjdelcerro
    }
148
149
    @Override
150
    public String getQuoteForIdentifiers() {
151
        return QUOTE_FOR_USE_IN_IDENTIFIERS;
152
    }
153
154
    @Override
155
    public boolean allowAutomaticValues() {
156
        return ALLOW_AUTOMATIC_VALUES;
157
    }
158
159
    @Override
160
    public boolean supportOffsetInSelect() {
161
        // Asumimos que la BBDD soporta OFFSET
162
        return true;
163
    }
164 44403 omartinez
165 43020 jjdelcerro
    @Override
166
    public String getQuoteForStrings() {
167
        return QUOTE_FOR_USE_IN_STRINGS;
168
    }
169 44191 jjdelcerro
170 44376 jjdelcerro
    protected boolean supportCaller(Code.Caller caller) {
171 44403 omartinez
        if (StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE")) {
172 44376 jjdelcerro
            return true;
173
        }
174
        Function function = caller.function();
175 44403 omartinez
        if (function == null) {
176 44376 jjdelcerro
            return false;
177
        }
178 44403 omartinez
        if (!function.isSQLCompatible()) {
179 44376 jjdelcerro
            return false;
180
        }
181 44403 omartinez
        if (!this.hasSpatialFunctions()) {
182
            if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
183 44376 jjdelcerro
                return false;
184
            }
185
        }
186
        return true;
187
    }
188 44403 omartinez
189 44191 jjdelcerro
    @Override
190 44198 jjdelcerro
    public boolean supportFilter(final FeatureType type, Evaluator filter) {
191 44191 jjdelcerro
        // No podemos filtrar cuando:
192
        // - Hay una subquery especificada en los parametros
193
        // - Si hay un filtro y el getSQL devuelbe null.
194
        // - Si se esta usando alguna funcion no-sql en el getSQL
195
        // - Si se estan usando funciones OGC y no soportamos funciones espaciales
196 44198 jjdelcerro
        // - Si se esta usando algun campo calculado en la expresion de filtro.
197 44191 jjdelcerro
        //
198
        // Un proveedor especifico podria sobreescribir el metodo,
199
        // para hilar mas fino al comprobar si soporta el filtro o no.
200
        //
201 44403 omartinez
202 44191 jjdelcerro
        if (this.useSubquery()) {
203
            // Se esta usando una subquery en los parametros de acceso a la
204
            // BBDD, asi que no podemos filtrar.
205
            return false;
206
        }
207
        if (filter == null) {
208
            // No hay que filtrar nada, asi que se p?ede.
209
            return true;
210
        }
211
        String sql = filter.getSQL();
212
        if (StringUtils.isEmpty(sql)) {
213
            // Hay un filtro, pero la SQL es null, con lo que no podemos
214
            // filtrar por el.
215
            return false;
216
        }
217
218
        // Ahora vamos a comprobar que las funciones que se usan son
219
        // compatibles sql, y que no se usan funciones OGC si el
220
        // proveedor dice que no soporta funciones espaciales.
221 44198 jjdelcerro
        // Tambien comprobaremos que el filtro no usa ningun campo calculado.
222 44191 jjdelcerro
        final MutableBoolean isCompatible = new MutableBoolean(true);
223
        ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getManager();
224
        Code code = manager.compile(sql);
225 44198 jjdelcerro
        SymbolTable symbolTable = manager.createSymbolTable();
226
        code.link(symbolTable);
227 44191 jjdelcerro
        try {
228
            code.accept(new Visitor() {
229
                @Override
230
                public void visit(Object code_obj) throws VisitCanceledException, BaseException {
231
                    Code code = (Code) code_obj;
232 44403 omartinez
                    switch (code.code()) {
233 44198 jjdelcerro
                        case Code.CALLER:
234
                            Code.Caller caller = (Code.Caller) code;
235 44403 omartinez
                            if (!supportCaller(caller)) {
236 44191 jjdelcerro
                                isCompatible.setValue(false);
237
                                throw new VisitCanceledException();
238
                            }
239 44198 jjdelcerro
                            break;
240 44403 omartinez
241 44198 jjdelcerro
                        case Code.IDENTIFIER:
242
                            Code.Identifier identifier = (Code.Identifier) code;
243 44403 omartinez
                            if (type != null) {
244 44198 jjdelcerro
                                FeatureAttributeDescriptor attrdesc = type.getAttributeDescriptor(identifier.name());
245 44403 omartinez
                                if (attrdesc != null) {
246
                                    if (attrdesc.isComputed()) {
247 44198 jjdelcerro
                                        isCompatible.setValue(false);
248
                                        throw new VisitCanceledException();
249
                                    }
250
                                }
251
                            }
252
                            break;
253 44191 jjdelcerro
                    }
254
                }
255
            });
256
257
        } catch (VisitCanceledException ex) {
258
            // Do nothing
259
        } catch (Exception ex) {
260
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
261
        }
262 44403 omartinez
263 44191 jjdelcerro
        return isCompatible.booleanValue();
264
    }
265 44403 omartinez
266 43020 jjdelcerro
    @Override
267 44198 jjdelcerro
    public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
268 44191 jjdelcerro
        if (this.useSubquery()) {
269
            return false;
270
        }
271
        if (order == null) {
272
            return true;
273
        }
274 44403 omartinez
        for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
275 44191 jjdelcerro
            if (member.hasEvaluator()) {
276 44403 omartinez
                if (!this.supportFilter(type, member.getEvaluator())) {
277 44191 jjdelcerro
                    return false;
278
                }
279 44403 omartinez
            }
280 44191 jjdelcerro
        }
281
        return true;
282
    }
283 44403 omartinez
284 44191 jjdelcerro
    @Override
285 43020 jjdelcerro
    public OperationsFactory getOperations() {
286 44403 omartinez
        if (this.operationsFactory == null) {
287 43020 jjdelcerro
            this.operationsFactory = new OperationsFactoryBase(this);
288
        }
289
        return operationsFactory;
290
    }
291 44403 omartinez
292 43020 jjdelcerro
    protected void initializeResource(JDBCConnectionParameters params) {
293
//        Object[] resourceParams = new Object[]{
294
//            params.getUrl(),
295
//            params.getHost(),
296
//            params.getPort(),
297
//            params.getDBName(),
298
//            params.getUser(),
299
//            params.getPassword(),
300
//            params.getJDBCDriverClassName()
301
//        };
302
//
303
//        try {
304
//            ResourceManagerProviderServices manager
305
//                    = (ResourceManagerProviderServices) DALLocator.getResourceManager();
306
//            JDBCResource resource = (JDBCResource) manager.createAddResource(
307
//                    this.getResourceType(),
308
//                    resourceParams
309
//            );
310
//            this.resource = resource;
311
//            this.resource.addConsumer(this);
312
//        } catch (InitializeException ex) {
313
//            logger.debug("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
314
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
315
//        }
316
317
    }
318
319
    @Override
320
    public String getSourceId() {
321
        return this.store.getSourceId();
322
    }
323 44403 omartinez
324 43020 jjdelcerro
    @Override
325
    public JDBCResource getResource() {
326
        return null;
327
//        return this.resource;
328
    }
329
330
    @Override
331
    public Connection getConnection() throws AccessResourceException {
332 43377 jjdelcerro
        throw new NotYetImplemented();
333 43020 jjdelcerro
    }
334
335
    @Override
336 43377 jjdelcerro
    public Connection getConnectionWritable() throws AccessResourceException {
337
        return this.getConnection();
338
    }
339
340
    @Override
341 43035 jjdelcerro
    public String getConnectionURL() {
342
        return null;
343
    }
344
345 43358 jjdelcerro
    @Override
346 43035 jjdelcerro
    public JDBCConnectionParameters getConnectionParameters() {
347
        return connectionParameters;
348
    }
349
350
    @Override
351 43020 jjdelcerro
    public void closeConnection(Connection connection) {
352 44403 omartinez
        if (connection != null) {
353
            LOGGER.debug("Clossing connection " + connection.hashCode());
354 43377 jjdelcerro
            try {
355
                connection.close();
356 44403 omartinez
            } catch (Exception ex) {
357 44191 jjdelcerro
                LOGGER.warn("Can't close connection.", ex);
358 43377 jjdelcerro
            }
359 44403 omartinez
        }
360 43377 jjdelcerro
    }
361 44191 jjdelcerro
362 44198 jjdelcerro
    @Override
363 44191 jjdelcerro
    public void closeConnectionQuietly(Connection connection) {
364 44403 omartinez
        if (connection != null) {
365
            LOGGER.debug("Clossing connection quietly " + connection.hashCode());
366 44191 jjdelcerro
            try {
367
                connection.close();
368 44403 omartinez
            } catch (Exception ex) {
369 44191 jjdelcerro
                LOGGER.warn("Can't close connection.", ex);
370
            }
371 44403 omartinez
        }
372 44191 jjdelcerro
    }
373 44403 omartinez
374 43020 jjdelcerro
    @Override
375
    protected void doDispose() throws BaseException {
376
        JDBCUtils.closeQuietly(this);
377
    }
378
379
    @Override
380
    public void close() throws Exception {
381
//        this.resource.removeConsumer(this);
382
        JDBCUtils.closeQuietly(this.resulSetControler);
383
//        this.resource = null;
384
        this.resulSetControler = null;
385
    }
386
387
    @Override
388
    public boolean closeResourceRequested(ResourceProvider resource) {
389
        return this.helperClient.closeResourceRequested(resource);
390
    }
391
392
    @Override
393
    public void resourceChanged(ResourceProvider resource) {
394
        this.helperClient.resourceChanged(resource);
395
    }
396
397
    @Override
398
    public GeometryManager getGeometryManager() {
399
        if (this.geometryManager == null) {
400
            this.geometryManager = GeometryLocator.getGeometryManager();
401
        }
402
        return this.geometryManager;
403
    }
404
405
    @Override
406
    public ResulSetControler getResulSetControler() {
407
        if (this.resulSetControler == null) {
408
            this.resulSetControler = new ResulSetControlerBase(this);
409
        }
410
        return this.resulSetControler;
411
    }
412
413
    @Override
414
    public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
415 44376 jjdelcerro
        fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
416 43020 jjdelcerro
    }
417
418
    @Override
419 44376 jjdelcerro
    public void fetchFeature(FeatureProvider feature, ResultSet rs, FeatureAttributeDescriptor[] columns, String[] extraValueNames) throws DataException {
420 43420 jjdelcerro
        Object value;
421 43020 jjdelcerro
        try {
422 44376 jjdelcerro
            int rsIndex = 1;
423
            for (FeatureAttributeDescriptor column : columns) {
424 43358 jjdelcerro
                switch (column.getType()) {
425 43020 jjdelcerro
                    case DataTypes.GEOMETRY:
426 44376 jjdelcerro
                        value = this.getGeometryFromColumn(rs, rsIndex++);
427 43020 jjdelcerro
                        break;
428
                    default:
429 44376 jjdelcerro
                        value = rs.getObject(rsIndex++);
430 44403 omartinez
                        if (value instanceof Blob) {
431
                            Blob blob = (Blob) value;
432 44297 jjdelcerro
                            value = blob.getBytes(0, (int) blob.length());
433
                            blob.free();
434
                        }
435 43020 jjdelcerro
                }
436 43358 jjdelcerro
                feature.set(column.getIndex(), value);
437 43020 jjdelcerro
            }
438 44403 omartinez
            if (ArrayUtils.isNotEmpty(extraValueNames)) {
439 44376 jjdelcerro
                feature.setExtraValueNames(extraValueNames);
440
                for (int index = 0; index < extraValueNames.length; index++) {
441
                    value = rs.getObject(rsIndex++);
442 44403 omartinez
                    if (value instanceof Blob) {
443
                        Blob blob = (Blob) value;
444 44376 jjdelcerro
                        value = blob.getBytes(0, (int) blob.length());
445
                        blob.free();
446
                    }
447
                    feature.setExtraValue(index, value);
448
                }
449
            }
450 43020 jjdelcerro
        } catch (Exception ex) {
451
            throw new JDBCCantFetchValueException(ex);
452
        }
453
    }
454
455
    @Override
456
    public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
457
        return getGeometryFromColumn(rs.get(), index);
458
    }
459
460
    @Override
461
    public Geometry getGeometryFromColumn(ResultSet rs, int index) throws DataException {
462
        try {
463
            Object value;
464
            switch (this.getGeometrySupportType()) {
465 43358 jjdelcerro
                case NATIVE:
466 43020 jjdelcerro
                case WKB:
467
                    value = rs.getBytes(index);
468
                    if (value == null) {
469
                        return null;
470
                    }
471
                    return this.getGeometryManager().createFrom((byte[]) value);
472
473
                case EWKB:
474
                    value = rs.getBytes(index);
475
                    if (value == null) {
476
                        return null;
477
                    }
478
                    return this.getGeometryManager().createFrom((byte[]) value);
479
                case WKT:
480
                default:
481
                    value = rs.getString(index);
482
                    if (value == null) {
483
                        return null;
484
                    }
485
                    return this.getGeometryManager().createFrom((String) value);
486
487
            }
488
        } catch (Exception ex) {
489
            throw new JDBCCantFetchValueException(ex);
490
        }
491
    }
492
493
    @Override
494
    public FeatureProvider createFeature(FeatureType featureType) throws DataException {
495
        return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
496
    }
497 44403 omartinez
498 43020 jjdelcerro
    @Override
499
    public boolean useSubquery() {
500 44403 omartinez
        if (this.store == null) {
501 43020 jjdelcerro
            return false;
502
        }
503
        return !StringUtils.isEmpty(this.store.getParameters().getSQL());
504 44403 omartinez
    }
505
506 43355 jjdelcerro
    @Override
507 43606 jjdelcerro
    public SRSSolver getSRSSolver() {
508
        return this.srssolver;
509
    }
510 44403 omartinez
511 43606 jjdelcerro
    @Override
512 43020 jjdelcerro
    public JDBCStoreProvider createProvider(
513 44403 omartinez
            JDBCStoreParameters parameters,
514 43020 jjdelcerro
            DataStoreProviderServices providerServices
515 44403 omartinez
    ) throws InitializeException {
516
517 44191 jjdelcerro
        JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
518 44403 omartinez
                parameters,
519
                providerServices,
520 43020 jjdelcerro
                DBHelper.newMetadataContainer(JDBCLibrary.NAME),
521
                this
522
        );
523 44191 jjdelcerro
        this.initialize(theStore, parameters, theStore);
524
        return theStore;
525 43020 jjdelcerro
    }
526
527
    @Override
528
    public JDBCServerExplorer createServerExplorer(
529 44403 omartinez
            JDBCServerExplorerParameters parameters,
530 43020 jjdelcerro
            DataServerExplorerProviderServices providerServices
531 44403 omartinez
    ) throws InitializeException {
532
533 43020 jjdelcerro
        JDBCServerExplorer explorer = new JDBCServerExplorerBase(
534 44403 omartinez
                parameters,
535
                providerServices,
536 43020 jjdelcerro
                this
537
        );
538
        this.initialize(explorer, parameters, null);
539
        return explorer;
540
    }
541
542
    @Override
543
    public JDBCNewStoreParameters createNewStoreParameters() {
544
        return new JDBCNewStoreParameters();
545
    }
546
547
    @Override
548
    public JDBCStoreParameters createOpenStoreParameters() {
549
        return new JDBCStoreParameters();
550
    }
551
552 43358 jjdelcerro
    @Override
553 43020 jjdelcerro
    public JDBCServerExplorerParameters createServerExplorerParameters() {
554
        return new JDBCServerExplorerParameters();
555
    }
556
557
    @Override
558
    public String getSourceId(JDBCStoreParameters parameters) {
559 44403 omartinez
        return parameters.getHost() + ":"
560
                + parameters.getDBName() + ":"
561
                + parameters.getSchema() + ":"
562
                + parameters.tableID();
563 43020 jjdelcerro
    }
564 44198 jjdelcerro
565
    @Override
566
    public boolean isThreadSafe() {
567
        return true;
568
    }
569 44403 omartinez
570 44376 jjdelcerro
    @Override
571
    public String[] replaceForeingValueFunction(SQLBuilder sqlbuilder, FeatureType type) {
572
        // See test SQLBuilderTest->testForeingValue()
573
        final ExpressionBuilder where = sqlbuilder.select().where();
574 44403 omartinez
        if (where == null || where.isEmpty()) {
575 44376 jjdelcerro
            return null;
576
        }
577
        final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
578
        final ExpressionBuilder expbuilder = sqlbuilder.expression();
579 44403 omartinez
580 44376 jjdelcerro
        final List<String> foreing_value_args = new ArrayList<>();
581
        final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
582
583
        // Buscamos las llamadas a la funcion "foreing_value" y nos quedamos
584
        // el argumento de esta asi como por que tendriamos que sustituirla
585
        // una vez hechos los left joins que toquen.
586
        where.accept(new ExpressionBuilder.Visitor() {
587
            @Override
588
            public void visit(ExpressionBuilder.Visitable value) {
589
                // Requiere que sea la funcion "FOREING_VALUE con un solo
590
                // argumento que sea una constante de tipo string.
591 44403 omartinez
                if (!(value instanceof ExpressionBuilder.Function)) {
592 44376 jjdelcerro
                    return;
593
                }
594
                ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
595 44403 omartinez
                if (!StringUtils.equalsIgnoreCase(function.name(), "FOREING_VALUE")) {
596 44376 jjdelcerro
                    return;
597
                }
598 44403 omartinez
                if (function.parameters().size() != 1) {
599 44376 jjdelcerro
                    return;
600 44403 omartinez
                }
601 44376 jjdelcerro
                ExpressionBuilder.Value arg = function.parameters().get(0);
602 44403 omartinez
                if (!(arg instanceof ExpressionBuilder.Constant)) {
603 44376 jjdelcerro
                    return;
604
                }
605
                Object arg_value = ((ExpressionBuilder.Constant) arg).value();
606 44403 omartinez
                if (!(arg_value instanceof CharSequence)) {
607 44376 jjdelcerro
                    return;
608
                }
609
                String foreing_value_arg = arg_value.toString();
610
                String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]");
611 44403 omartinez
                if (foreingNameParts.length != 2) {
612 44376 jjdelcerro
                    // De momento solo tratamos joins entre dos tablas.
613
                    return;
614
                }
615
                String columnNameLocal = foreingNameParts[0];
616
                String columnNameForeing = foreingNameParts[1];
617
                FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
618 44403 omartinez
                if (!attr.isForeingKey()) {
619 44376 jjdelcerro
                    // Uhm... si el argumento no referencia a un campo que es
620
                    // clave ajena no lo procesamos.
621
                    // ? Deberiamos lanzar un error ?
622
                    return;
623
                }
624
                // Nos guardaremos por que hay que reemplazar la funcion
625
                // FOREING_VALUE, y su argumento para mas tarde.
626
                ForeingKey foreingKey = attr.getForeingKey();
627
                SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
628
                        .database(table.getDatabase())
629 44403 omartinez
                        .schema(table.getSchema())
630 44376 jjdelcerro
                        .name(foreingKey.getTableName());
631
                // Reemplzaremos la funcion FOREING_VALUE, por el acceso al campo
632
                // que toca de la tabla a la que referencia la clave ajena.
633
                ExpressionBuilder.Variable function_replacement = sqlbuilder.column(foreingTable, columnNameForeing);
634
                value_replacements.add(
635 44403 omartinez
                        new ExpressionBuilder.Value[]{
636
                            function,
637
                            function_replacement
638
                        }
639 44376 jjdelcerro
                );
640
                foreing_value_args.add(foreing_value_arg);
641
            }
642
        }, null);
643 44403 omartinez
644 44376 jjdelcerro
        // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
645
        // hacer nada.
646 44403 omartinez
        if (foreing_value_args.isEmpty()) {
647 44376 jjdelcerro
            return null;
648
        }
649
650
        // Calculamos que referencias de columnas hemos de cambiar para
651
        // que no aparezcan ambiguedades al hacer el join (nombres de
652
        // columna de una y otra tabla que coincidan).
653
        // Para las columnas que puedan dar conflicto se prepara un reemplazo
654
        // de estas que tenga el nombre de tabla.
655
        for (ExpressionBuilder.Variable variable : sqlbuilder.variables()) {
656 44403 omartinez
            if (variable instanceof SQLBuilderBase.ColumnBase) {
657 44376 jjdelcerro
                continue;
658
            }
659
            for (String foreingName : foreing_value_args) {
660
                String[] foreingNameParts = foreingName.split("[.]");
661 44403 omartinez
                if (foreingNameParts.length != 2) {
662 44376 jjdelcerro
                    continue;
663
                }
664
                String columnNameLocal = foreingNameParts[0];
665
                String columnNameForeing = foreingNameParts[1];
666 44403 omartinez
                if (StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing)
667
                        || StringUtils.equalsIgnoreCase(variable.name(), columnNameLocal)) {
668
                    ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
669
                            table,
670
                            variable.name()
671
                    );
672
                    value_replacements.add(
673
                            new ExpressionBuilder.Value[]{
674
                                variable,
675
                                variable_replacement
676
                            }
677
                    );
678 44376 jjdelcerro
                }
679
            }
680
        }
681 44403 omartinez
682 44376 jjdelcerro
        // Realizamos los reemplazos calculados previamente (value_replacements).
683
        for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
684
            ExpressionBuilder.Value target = replaceValue[0];
685
            ExpressionBuilder.Value replacement = replaceValue[1];
686 44403 omartinez
            sqlbuilder.select().replace(target, replacement);
687 44376 jjdelcerro
        }
688
689
        // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
690
        // a los valores referenciados por la funcion FOREING_VALUE.
691
        // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
692
        // como columnas de la SQL para poder acceder a ellos si fuese necesario.
693
        for (String foreingName : foreing_value_args) {
694
            String[] foreingNameParts = foreingName.split("[.]");
695 44403 omartinez
            if (foreingNameParts.length != 2) {
696 44376 jjdelcerro
                continue;
697
            }
698
            String columnNameLocal = foreingNameParts[0];
699
            String columnNameForeing = foreingNameParts[1];
700
            FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
701 44403 omartinez
            if (attr.isForeingKey()) {
702 44376 jjdelcerro
                ForeingKey foreingKey = attr.getForeingKey();
703
                SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
704
                        .database(table.getDatabase())
705 44403 omartinez
                        .schema(table.getSchema())
706 44376 jjdelcerro
                        .name(foreingKey.getTableName());
707
                SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
708
                        .database(table.getDatabase())
709 44403 omartinez
                        .schema(table.getSchema())
710 44376 jjdelcerro
                        .name(table.getName());
711
712
                sqlbuilder.select().from()
713 44403 omartinez
                        .left_join(
714
                                foreingTable,
715
                                expbuilder.eq(
716
                                        sqlbuilder.column(mainTable, columnNameLocal),
717
                                        sqlbuilder.column(foreingTable, foreingKey.getCodeName())
718
                                )
719
                        );
720
                sqlbuilder.select().column().name(foreingTable, columnNameForeing);
721 44376 jjdelcerro
            }
722
        }
723
724
        return foreing_value_args.toArray(new String[foreing_value_args.size()]);
725
    }
726 43020 jjdelcerro
}