Statistics
| Revision:

gvsig-projects-pool / org.gvsig.online / trunk / org.gvsig.online / org.gvsig.online.lib / org.gvsig.online.lib.impl / src / main / java / org / gvsig / online / lib / impl / workspace / OnlineWorkspaceImpl.java @ 9512

History | View | Annotate | Download (158 KB)

1
/*
2
 * To change this license header, choose License Headers in Project Properties.
3
 * To change this template file, choose Tools | Templates
4
 * and open the template in the editor.
5
 */
6
package org.gvsig.online.lib.impl.workspace;
7

    
8
import java.io.File;
9
import java.io.FileInputStream;
10
import java.sql.Timestamp;
11
import java.time.LocalDateTime;
12
import java.time.ZoneOffset;
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.Collections;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.Iterator;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22
import java.util.logging.Level;
23
import javax.json.JsonArray;
24
import javax.json.JsonObject;
25
import javax.json.JsonValue;
26
import org.apache.commons.io.IOUtils;
27
import org.apache.commons.lang3.ArrayUtils;
28
import org.apache.commons.lang3.BooleanUtils;
29
import org.apache.commons.lang3.StringUtils;
30
import org.apache.commons.lang3.mutable.MutableLong;
31
import org.apache.commons.lang3.mutable.MutableObject;
32
import org.cresques.cts.IProjection;
33
import org.gvsig.expressionevaluator.Expression;
34
import org.gvsig.expressionevaluator.ExpressionBuilder;
35
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
36
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
37
import org.gvsig.expressionevaluator.ExpressionUtils;
38
import org.gvsig.expressionevaluator.GeometryExpressionBuilder;
39
import org.gvsig.fmap.dal.DALLocator;
40
import org.gvsig.fmap.dal.DataManager;
41
import org.gvsig.fmap.dal.DataServerExplorer;
42
import org.gvsig.fmap.dal.DataServerExplorerParameters;
43
import org.gvsig.fmap.dal.DataStoreParameters;
44
import org.gvsig.fmap.dal.DataTransaction;
45
import org.gvsig.fmap.dal.DatabaseWorkspaceManager;
46
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_CONFIGURATION_NAME;
47
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_CONFIGURATION_VALUE;
48
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_RESOURCES_NAME;
49
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_RESOURCES_RESOURCE;
50
import org.gvsig.fmap.dal.OpenDataStoreParameters;
51
import org.gvsig.fmap.dal.exception.DataException;
52
import org.gvsig.fmap.dal.feature.DisposableFeatureSetIterable;
53
import org.gvsig.fmap.dal.feature.EditableFeature;
54
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
55
import org.gvsig.fmap.dal.feature.EditableFeatureType;
56
import org.gvsig.fmap.dal.feature.Feature;
57
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
58
import org.gvsig.fmap.dal.feature.FeatureQuery;
59
import org.gvsig.fmap.dal.feature.FeatureReference;
60
import org.gvsig.fmap.dal.feature.FeatureSet;
61
import org.gvsig.fmap.dal.feature.FeatureStore;
62
import static org.gvsig.fmap.dal.feature.FeatureStore.MODE_FULLEDIT;
63
import static org.gvsig.fmap.dal.feature.FeatureStore.MODE_PASS_THROUGH;
64
import org.gvsig.fmap.dal.feature.FeatureType;
65
import org.gvsig.fmap.dal.feature.OpenFeatureStoreParameters;
66
import org.gvsig.fmap.dal.store.h2.H2SpatialUtils;
67
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
68
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
69
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
70
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
71
import org.gvsig.fmap.geom.Geometry;
72
import org.gvsig.fmap.geom.GeometryException;
73
import org.gvsig.fmap.geom.GeometryLocator;
74
import org.gvsig.fmap.geom.GeometryManager;
75
import org.gvsig.fmap.geom.primitive.Envelope;
76
import org.gvsig.json.Json;
77
import org.gvsig.json.JsonObjectBuilder;
78
import org.gvsig.online.lib.api.OnlineCodeGenerator;
79
import org.gvsig.online.lib.api.OnlineDownloader;
80
import org.gvsig.online.lib.api.OnlineLayer;
81
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANCELLED_BY_USER;
82
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_ADD_CHANGE;
83
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_INSERT_CHANGE;
84
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_OPEN_CHANGES;
85
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_OPEN_ENTITIES;
86
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_REMOVE_CHANGES;
87
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_REMOVE_ENTITY;
88
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_RETRIEVE_ENTITIES;
89
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_REVERT;
90
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_UPDATE;
91
import static org.gvsig.online.lib.api.OnlineManager.ERR_CANT_UPDATE_CLEAN;
92
import static org.gvsig.online.lib.api.OnlineManager.ERR_ENTITY_NOT_EXISTS;
93
import static org.gvsig.online.lib.api.OnlineManager.ERR_ENTITY_NOT_HAS_FEATURECODE;
94
import static org.gvsig.online.lib.api.OnlineManager.ERR_INVALID_ENTITY;
95
import static org.gvsig.online.lib.api.OnlineManager.ERR_NO_ERROR;
96
import static org.gvsig.online.lib.api.OnlineManager.ERR_OFFLINE;
97
import static org.gvsig.online.lib.api.OnlineManager.ERR_OK;
98
import static org.gvsig.online.lib.api.OnlineManager.ERR_STORE_NOT_IN_WORKINGCOPY;
99
import static org.gvsig.online.lib.api.OnlineManager.ERR_SYNCHRONIZE;
100
import static org.gvsig.online.lib.api.OnlineManager.ERR_UPDATE_NEED_MERGE;
101
import static org.gvsig.online.lib.api.OnlineManager.FEATURECODE_FIELD_NAME;
102
import static org.gvsig.online.lib.api.OnlineManager.FEATUREVERSION_FIELD_NAME;
103
import static org.gvsig.online.lib.api.OnlineManager.OP_DELETE;
104
import static org.gvsig.online.lib.api.OnlineManager.OP_IGNORE;
105
import static org.gvsig.online.lib.api.OnlineManager.OP_INSERT;
106
import static org.gvsig.online.lib.api.OnlineManager.OP_UNKNOWN;
107
import static org.gvsig.online.lib.api.OnlineManager.OP_UPDATE;
108
import static org.gvsig.online.lib.api.OnlineManager.STATE_CONFLICT;
109
import static org.gvsig.online.lib.api.OnlineManager.STATE_CORRUPT;
110
import static org.gvsig.online.lib.api.OnlineManager.STATE_DISCONNECTED;
111
import static org.gvsig.online.lib.api.OnlineManager.STATE_LOCAL_MODIFIED;
112
import static org.gvsig.online.lib.api.OnlineManager.STATE_LOCAL_NEW;
113
import static org.gvsig.online.lib.api.OnlineManager.STATE_LOCAL_OUTDATED;
114
import static org.gvsig.online.lib.api.OnlineManager.STATE_LOCAL_OUTDATED_AND_MODIFIED;
115
import static org.gvsig.online.lib.api.OnlineManager.STATE_LOCAL_UNMODIFIED;
116
import static org.gvsig.online.lib.api.OnlineManager.STATE_REMOTE_NEW;
117
import static org.gvsig.online.lib.api.OnlineManager.STATE_UNKNOWN;
118
import static org.gvsig.online.lib.api.OnlineManager.TAG_ONLINE_DATAMODEL;
119
import static org.gvsig.online.lib.api.OnlineManager.TAG_ONLINE_FIELDFORLABEL;
120
import static org.gvsig.online.lib.api.OnlineManager.TAG_ONLINE_LABEL;
121
import org.gvsig.online.lib.api.OnlineProject;
122
import org.gvsig.online.lib.api.OnlineRuntimeException;
123
import org.gvsig.online.lib.api.OnlineSite;
124
import org.gvsig.online.lib.api.OnlineUserIdentificationRequester;
125
import static org.gvsig.online.lib.api.OnlineUserIdentificationRequester.CHANGE_USER;
126
import org.gvsig.online.lib.api.workingcopy.OnlineEntity;
127
import org.gvsig.online.lib.api.workingcopy.OnlineRemoteChange;
128
import org.gvsig.online.lib.api.workingcopy.OnlineWorkingcopy;
129
import org.gvsig.online.lib.api.workingcopy.OnlineWorkingcopyChange;
130
import org.gvsig.online.lib.api.workingcopy.WorkingArea;
131
import static org.gvsig.online.lib.impl.OnlineManagerImpl.INTERNAL_WORKSPACE_TABLES;
132
import org.gvsig.online.lib.impl.OnlineProjectImpl;
133
import org.gvsig.online.lib.impl.OnlineSiteImpl;
134
import org.gvsig.online.lib.impl.OnlineUtils;
135
import org.gvsig.online.lib.impl.TilesCalculator;
136
import org.gvsig.online.lib.impl.workspace.tables.EntitiesTable;
137
import org.gvsig.online.lib.impl.workspace.tables.EntitiesTable.EntityRow;
138
import org.gvsig.online.lib.impl.workspace.tables.RemoteChangesTable;
139
import org.gvsig.online.lib.impl.workspace.tables.RemoteChangesTable.RemoteChangeRow;
140
import org.gvsig.online.lib.impl.workspace.tables.VarsTable;
141
import org.gvsig.online.lib.impl.workspace.tables.WorkspaceChangesTable;
142
import org.gvsig.online.lib.impl.workspace.tables.WorkspaceChangesTable.WorkspaceChangeRow;
143
import org.gvsig.timesupport.DataTypes;
144
import org.gvsig.tools.ToolsLocator;
145
import org.gvsig.tools.dataTypes.DataTypeUtils;
146
import org.gvsig.tools.dispose.DisposableIterator;
147
import org.gvsig.tools.dispose.DisposeUtils;
148
import org.gvsig.tools.dispose.impl.AbstractDisposable;
149
import static org.gvsig.tools.dynform.spi.DynFormSPIManager.TAG_DYNFORM_READONLY;
150
import org.gvsig.tools.dynobject.Tags;
151
import org.gvsig.tools.i18n.I18nManager;
152
import org.gvsig.tools.logger.FilteredLogger;
153
import org.gvsig.tools.observer.Notification;
154
import org.gvsig.tools.observer.Observable;
155
import org.gvsig.tools.observer.Observer;
156
import org.gvsig.tools.patch.PatchGenerator;
157
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
158
import org.gvsig.tools.swing.api.ChangeListenerHelper;
159
import org.gvsig.tools.swing.api.ToolsSwingLocator;
160
import org.gvsig.tools.task.SimpleTaskStatus;
161
import org.gvsig.tools.task.UserCancelTaskException;
162
import org.gvsig.tools.util.ChainedIterator;
163
import org.gvsig.tools.util.GetItemWithSize64;
164
import org.gvsig.tools.util.HasAFile;
165
import org.gvsig.tools.util.Rewind;
166
import org.slf4j.Logger;
167
import org.slf4j.LoggerFactory;
168
import org.gvsig.online.lib.api.workingcopy.OnlineChanges;
169

    
170
/**
171
 *
172
 * @author jjdelcerro
173
 */
174
@SuppressWarnings("UseSpecificCatch")
175
public class OnlineWorkspaceImpl extends AbstractDisposable implements OnlineWorkingcopy {
176

    
177
    private static final Logger LOGGER = LoggerFactory.getLogger(OnlineWorkspaceImpl.class);
178

    
179
    public static final String CONFIG_WORKSPACE_CODE_NAME = "WORKSPACE_CODE";
180
    public static final String CONFIG_WORKSPACE_CODE_CHANGE_ON_FIRST_USE = "CHANGE_ON_FIRST_USE";
181
    public static final String CONFIG_WORKSPACE_LABEL_NAME = "WORKSPACE_LABEL";
182
    public static final String CONFIG_PROJECT_NAME = "PROJECT";
183
    public static final String CONFIG_USER_NAME = "USER";
184
    public static final String CONFIG_AUTHENTICATIONTOKEN_NAME = "AUTHENTICATIONTOKEN";
185
//    public static final String CONFIG_REPOSITORY_ENTITY_CACHE_TIME_NAME = "REPOSITORY_ENTITY_CACHE_TIME";
186
    public static final String CONFIG_OFFLINE = "OFFLINE";
187
    public static final String CONFIG_ENTITY_LABEL_TEMPLATE = "ENTITY_LABEL_TEMPLATE";
188
    public static final String CONFIG_CURRENT_WORKINGAREA = "CURRENT_WORKINGAREA";
189
//    public static final String CONFIG_USE_SAFE_MODE = "USE_SAFE_MODE";
190

    
191
    private final Map<String, FeatureStore> storesCache;
192
    private Map<String, EntityRow> workspaceEntitiesByName;
193
    private Map<String, EntityRow> workspaceEntitiesByCode;
194
    private Set<FeatureStore> storeIgnoreChanges;
195
    private JDBCServerExplorer wsexplorer;
196
    private String code;
197
    private final OnlineProjectImpl project;
198
    private final String label;
199
    private final FilteredLogger logger;
200
    private final OnlineCodeGenerator codeGenerator;
201
    private final ChangeListenerHelper workspaceEntitiesChangeListeners;
202

    
203
    private Map<Integer/*projectid*/, String/*usercode*/> currentUserCodes;
204
    private Map<Integer/*projectid*/, String/*token*/> authenticationTokens;
205

    
206
    private OnlineUserIdentificationRequester userIdentificationRequester;
207
//    private long repositoryEntitiesCacheTime = 30000;
208
    private boolean offline;
209
    private String entityLabelTemplate;
210
    private boolean disposed = false;
211
    private Observer changeUserObserver;
212
    private WorkingArea currentWorkingArea;
213

    
214
    public OnlineWorkspaceImpl(JDBCServerExplorer wsexplorer, OnlineCodeGenerator codeGenerator, OnlineProject project, String label) {
215
        // Usado en la inicializacion del workspace
216
        super();
217
        this.logger = new FilteredLogger(LOGGER, "OnlineWorkspace", 10000L);
218
        this.codeGenerator = codeGenerator;
219
        this.storesCache = new HashMap<>();
220
        this.code = createUniqueCode();
221
        this.wsexplorer = wsexplorer;
222
        DisposeUtils.bind(wsexplorer);
223
        this.project = (OnlineProjectImpl) project;
224
        DisposeUtils.bind(this.project);
225
        this.label = label;
226
        this.workspaceEntitiesChangeListeners = ToolsSwingLocator.getToolsSwingManager().createChangeListenerHelper();
227
        this.currentUserCodes = null;
228
        this.authenticationTokens = null;
229
        this.userIdentificationRequester = null;
230
        this.offline = false;
231
        this.entityLabelTemplate = "${label}";
232
        this.changeUserObserver = createChangeUserObserver();
233
    }
234

    
235
    public OnlineWorkspaceImpl(JDBCServerExplorer wsexplorer, OnlineCodeGenerator codeGenerator, String newLabel) {
236
        // Usado al abrir un workspace ya existente
237
        // Si newLabel no es null cambia la label del workspace.
238
        super();
239
        this.logger = new FilteredLogger(LOGGER, "VCSGisWorkspace", 10000L);
240
        this.codeGenerator = codeGenerator;
241
        this.storesCache = new HashMap<>();
242
        this.wsexplorer = wsexplorer;
243
        DisposeUtils.bind(wsexplorer);
244
        this.workspaceEntitiesChangeListeners = ToolsSwingLocator.getToolsSwingManager().createChangeListenerHelper();
245
        this.userIdentificationRequester = null;
246

    
247
        VarsTable varsTable = new VarsTable();
248
        if( StringUtils.isNotBlank(newLabel) ) {
249
            varsTable.set(this, CONFIG_WORKSPACE_LABEL_NAME, newLabel);
250
        }
251
        
252
        Map<String,String> vars = varsTable.getVars(this, 
253
                CONFIG_PROJECT_NAME,
254
                CONFIG_WORKSPACE_CODE_NAME,
255
                CONFIG_USER_NAME,
256
                CONFIG_AUTHENTICATIONTOKEN_NAME, 
257
//                CONFIG_REPOSITORY_ENTITY_CACHE_TIME_NAME,
258
                CONFIG_WORKSPACE_LABEL_NAME,
259
                CONFIG_OFFLINE,
260
//                CONFIG_USE_SAFE_MODE,
261
                CONFIG_ENTITY_LABEL_TEMPLATE,
262
                CONFIG_CURRENT_WORKINGAREA
263
        );
264
        this.code = vars.get(CONFIG_WORKSPACE_CODE_NAME);
265
        if (this.code == null) {
266
            throw new RuntimeException("Can't retrieve code from workspace '" + this.getMessageLabel() + "'");
267
        }
268
        if( this.code.equalsIgnoreCase(CONFIG_WORKSPACE_CODE_CHANGE_ON_FIRST_USE) ) {
269
            this.code = this.createUniqueCode();
270
            varsTable.set(this, CONFIG_WORKSPACE_CODE_NAME, this.code);
271
        }
272
        this.offline = DataTypeUtils.toBoolean(vars.get(CONFIG_OFFLINE), false);
273
        this.entityLabelTemplate = vars.getOrDefault(CONFIG_ENTITY_LABEL_TEMPLATE,"${label}");
274
//        this.repositoryEntitiesCacheTime = NumberUtils.toLong(vars.get(CONFIG_REPOSITORY_ENTITY_CACHE_TIME_NAME),30000);
275
        this.reloadWorkspaceEntities();
276
        this.project = (OnlineProjectImpl) Json.toObject(vars.get(CONFIG_PROJECT_NAME));
277
        if (this.project == null) {
278
            throw new RuntimeException("Can't retrieve repository from workspace '" + this.getMessageLabel() + "'");
279
        }
280
        this.currentWorkingArea = (WorkingAreaImpl) Json.toObject(vars.get(CONFIG_CURRENT_WORKINGAREA));
281
        this.setCurrentUserCode(this.project, vars.get(CONFIG_USER_NAME));
282
        this.setAuthenticationToken(this.project, vars.get(CONFIG_AUTHENTICATIONTOKEN_NAME));
283
        this.label = vars.get(CONFIG_WORKSPACE_LABEL_NAME);
284
        this.changeUserObserver = createChangeUserObserver();
285
        
286
//        this.refreshCodeGeneratorSequence();
287
        
288
//        LOGGER.debug("===: CREATE WORKSPACE "+ hexId(this)+ " (open code='"+this.code+"', label='"+this.label+"')");
289
    }
290

    
291
    private org.gvsig.tools.observer.Observer createChangeUserObserver() {
292
        return (Observable observable, Object notification) -> {
293
            if(notification instanceof Notification) {
294
                Notification n = (Notification)notification;
295
                if(n.isOfType(CHANGE_USER)){
296
                    String userCode = (String) n.getValue();
297
                    setCurrentUserCode(project, userCode);
298
                    VarsTable varsTable = new VarsTable();
299
                    varsTable.set(this,"USER", userCode);
300
                }
301
            }
302
        };
303
    }
304
    
305
    @Override
306
    public String getCode() {
307
        return this.code;
308
    }
309

    
310
    @Override
311
    public String getLabel() {
312
        if (this.disposed) {
313
            return "BROKEN (" + this.label + ")";
314
        }
315
        return this.label;
316
    }
317

    
318
    @Override
319
    public final String createUniqueCode() {
320
        return this.codeGenerator.generateCodeString();
321
    }
322

    
323
    public final long createUniqueCodeLong(String entityName) {
324
        if(!this.codeGenerator.isInitialized(entityName)){
325
            this.refreshCodeGeneratorSequence(entityName);
326
        }
327
        return this.codeGenerator.generateCodelong(entityName);
328
        
329
    }
330

    
331
    @Override
332
    public String getErrorMessage(int errcode) {
333
        return OnlineUtils.getErrorMessage(errcode);
334
    }
335

    
336
    @Override
337
    public JDBCServerExplorer getExplorer() {
338
        return this.wsexplorer;
339
    }
340

    
341
    @Override
342
    public JDBCServerExplorerParameters getExplorerParameters() {
343
        return this.wsexplorer.getParameters();
344
    }
345

    
346
    private String getMessageLabel() {
347
        try {
348
            return this.wsexplorer.getParameters().getUrl();
349
        } catch (Exception ex) {
350
            return "unknown";
351
        }
352
    }
353

    
354
    private String getProviderName() {
355
        return this.wsexplorer.getProviderName();
356
    }
357

    
358
    @Override
359
    public FeatureType getFeatureType(String tableName) {
360
        EntityRow entity = null;
361
        if (!INTERNAL_WORKSPACE_TABLES.contains(tableName)) {
362
            entity = this.getWorkspaceEntityByName(tableName);
363
            if (entity == null) {
364
                return null;
365
            }
366
            return entity.getFeatureType();
367
        }
368
        FeatureType ft = null;
369
        try {
370
            FeatureStore store = this.storesCache.get(tableName);
371
            if (store != null) {
372
                ft = store.getDefaultFeatureType();
373
            } else {
374
                DataManager dataManager = DALLocator.getDataManager();
375
                JDBCStoreParameters params = this.wsexplorer.get(tableName);
376
                store = (FeatureStore) dataManager.openStore(
377
                        getProviderName(),
378
                        params
379
                );
380
                this.storesCache.put(tableName, store);
381
                ft = store.getDefaultFeatureType();
382
            }
383
            return ft;
384
        } catch (Exception ex) {
385
            String msg = "can't open store from '" + this.getMessageLabel() + "'.";
386
            throw new RuntimeException(msg, ex);
387
        }
388
    }
389

    
390
    @Override
391
    public FeatureStore getFeatureStore(String tableName) {
392
        EntityRow entity = null;
393
        if (!INTERNAL_WORKSPACE_TABLES.contains(tableName)) {
394
            entity = this.getWorkspaceEntityByName(tableName);
395
            if (entity == null) {
396
                return null;
397
            }
398
            return getFeatureStore(entity);
399
        }
400
        FeatureStore store = this.storesCache.get(tableName);
401
        if (store != null) {
402
            DisposeUtils.bind(store);
403
            return store;
404
        }
405
        DataManager dataManager = DALLocator.getDataManager();
406
        try {
407
            JDBCStoreParameters params = this.wsexplorer.get(tableName);
408
            store = (FeatureStore) dataManager.openStore(
409
                    getProviderName(),
410
                    params,
411
                    true
412
            );
413
            this.storesCache.put(tableName, store);
414
            DisposeUtils.bind(store);
415
            return store;
416
        } catch (Exception ex) {
417
            LOGGER.trace("can't open store from '" + this.getMessageLabel() + "'.", ex);
418
            return null;
419
        }
420
    }
421

    
422
    private FeatureStore getFeatureStore(OnlineEntity entity) {
423
        FeatureStore store = this.storesCache.get(entity.getEntityName());
424
        if (store != null) {
425
            DisposeUtils.bind(store);
426
            return store;
427
        }
428
        DataManager dataManager = DALLocator.getDataManager();
429
        try {
430
            JDBCStoreParameters params = this.wsexplorer.get(entity.getEntityName());
431
            store = (FeatureStore) dataManager.openStore(
432
                    getProviderName(),
433
                    params
434
            );
435
            StoreProperties.set(store, this, entity.getEntityName());
436
            this.storesCache.put(entity.getEntityName(), store);
437
            DisposeUtils.bind(store);
438
            return store;
439
        } catch (Exception ex) {
440
            LOGGER.trace("can't open store from '" + this.getMessageLabel() + "'.", ex);
441
            return null;
442
        }
443
    }
444

    
445
    public FeatureStore openFeatureStore(OnlineEntity entity) {
446
        DataManager dataManager = DALLocator.getDataManager();
447
        try {
448
            JDBCStoreParameters params = this.wsexplorer.get(entity.getEntityName());
449
            FeatureStore store = (FeatureStore) dataManager.openStore(
450
                    getProviderName(),
451
                    params
452
            );
453
            return store;
454
        } catch (Exception ex) {
455
            LOGGER.trace("can't open store from '" + this.getMessageLabel() + "'.", ex);
456
            return null;
457
        }
458
    }
459

    
460
    @Override
461
    public FeatureStore openFeatureStore(String tableName, boolean ignoreDALResource) {
462
        DataManager dataManager = DALLocator.getDataManager();
463
        try {
464
            JDBCStoreParameters params = this.wsexplorer.get(tableName);
465
            FeatureStore store = (FeatureStore) dataManager.openStore(
466
                    getProviderName(),
467
                    params,
468
                    ignoreDALResource
469
            );
470

    
471
            return store;
472
        } catch (Exception ex) {
473
            LOGGER.trace("can't open store from '" + this.getMessageLabel() + "'.", ex);
474
            return null;
475
        }
476
    }
477

    
478
    @Override
479
    public void forceReloadWorkspaceEntities() {
480
        this.workspaceEntitiesByCode = null;
481
        this.workspaceEntitiesByName = null;
482
    }
483
    
484
    @Override
485
    public void reloadWorkspaceEntities() {
486
        Map<String, EntityRow> localEntities = new HashMap<>();
487
        EntitiesTable entitiesTable = new EntitiesTable();
488
        DisposableFeatureSetIterable allLocalEntities = null;
489
        try {
490
            allLocalEntities = entitiesTable.getAll(this);
491
            for (Feature lentity_f : allLocalEntities) {
492
                EntityRow entityRow = new EntityRow(this, lentity_f);
493
                localEntities.put(entityRow.getEntityName(), entityRow);
494
            }
495
        } catch (Exception ex) {
496
            throw new OnlineRuntimeException(ERR_CANT_RETRIEVE_ENTITIES, OnlineUtils.getErrorMessage(ERR_CANT_RETRIEVE_ENTITIES));
497
        } finally {
498
            DisposeUtils.dispose(allLocalEntities);
499
        }
500

    
501
        reloadWorkspaceEntities(localEntities);
502
    }
503
    
504
    public final void reloadWorkspaceEntities(Map<String,EntityRow> localEntities) {
505
        FeatureStore store = null;
506
        FeatureSet featureSet = null;
507
        try {
508
            Map<String, EntityRow> theEntitiesByName = new HashMap<>();
509
            Map<String, EntityRow> theEntitiesByCode = new HashMap<>();
510
            for (EntityRow entity : localEntities.values()) {
511
                theEntitiesByName.put(entity.getEntityName(), entity);
512
                theEntitiesByCode.put(entity.getCode(), entity);
513
            }
514
            this.workspaceEntitiesByName = theEntitiesByName;
515
            this.workspaceEntitiesByCode = theEntitiesByCode;
516
            this.workspaceEntitiesChangeListeners.fireEvent();
517

    
518
        } catch (Exception ex) {
519
            throw new OnlineRuntimeException(ERR_CANT_RETRIEVE_ENTITIES, OnlineUtils.getErrorMessage(ERR_CANT_RETRIEVE_ENTITIES));
520
        } finally {
521
            DisposeUtils.disposeQuietly(featureSet);
522
            DisposeUtils.disposeQuietly(store);
523
        }
524
    }
525
    
526
//    @Override
527
//    public int add(String name, FeatureStore source, String fieldForLabel) {
528
//        return this.add(name, source, fieldForLabel, null, null, null, null, null, true, null);
529
//    }
530
//
531
//    @Override
532
//    public int add(String name, FeatureStore source, String fieldForLabel, String category, String label) {
533
//        return this.add(name, source, fieldForLabel, category, label, null, null, null, true, null);
534
//    }
535
//
536
//    @Override
537
//    public int add(String name, FeatureStore source, String fieldForLabel, String category, String label, String model, String resources, String pkName, boolean importData, SimpleTaskStatus status) {
538
//        if (isInMyDatabase(source)) {
539
//            return addExistingTable(name, source, fieldForLabel, category, label, model, resources, pkName, status);
540
//        } else {
541
//            return addNewTable(null, name, source, fieldForLabel, category, label, model, resources, pkName, importData, status);
542
//        }
543
//
544
//    }
545
//
546
//    private int addNewTable(String editingSession, String name, FeatureStore source, String fieldForLabel, String category, String label, String model, String resources, String pkName, boolean importData, SimpleTaskStatus status) {
547
//        if (this.getEntity(name) != null) { // is in VCS
548
//            return ERR_ENTITY_ALREADY_EXISTS;
549
//        }
550
//        I18nManager i18n = ToolsLocator.getI18nManager();
551
//        if (status == null) {
552
//            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(i18n.getTranslation("_Add_layer"));
553
//            status.setAutoremove(true);
554
//            status.add();
555
//        } else {
556
//            status.push();
557
//        }
558
//
559
//        FeatureStore target = null;
560
//        FeatureSet srcFeatures = null;
561
//        int errcode = ERR_NO_ERROR;
562
//        DataTransaction transaction = null;
563
//        FeatureStore changesStore = null;
564
//        try {
565
//            transaction = DALLocator.getDataManager().createTransaction();
566
//            transaction.begin();
567
//            LOGGER.debug("===: ADD " + this.getCode() + ", '" + this.getLabel() + "', " + name);
568
//            EntityRow entity = this.getWorkspaceEntityByName(name);
569
//            if (entity != null) {
570
//                return ERR_ENTITY_ALREADY_EXISTS;
571
//            }
572
//            status.message(i18n.getTranslation("_Adding_table_layer"));
573
//            FeatureType ft = source.getDefaultFeatureTypeQuietly();
574
//            errcode = this.addEntity(ft, name, null, fieldForLabel, category, label, model, resources, pkName);
575
//            if (errcode != ERR_NO_ERROR) {
576
//                transaction.rollbackQuietly();
577
//                return errcode;
578
//            }
579
//            errcode = ERR_CANT_OPEN_STORE;
580
//            target = this.openFeatureStore(name,false);
581
//            transaction.add(target);
582
//            errcode = ERR_CANT_RETRIEVE_SOURCE_FEATURES;
583
//            status.message(i18n.getTranslation("_Preparing_source"));
584
//            srcFeatures = source.getFeatureSet();
585
//            transaction.add(srcFeatures);
586
//            errcode = ERR_CANT_INSERT_FEATURES;
587
//            target.edit(MODE_APPEND);
588
//
589
//            changesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME, false);
590
//            transaction.add(changesStore);
591
//            changesStore.edit(MODE_APPEND);
592
//
593
//            this.addStoreIgnoreChanges(target);
594
//            entity = this.getWorkspaceEntityByName(name);
595
//
596
//            if( importData ) {
597
//                status.message(null);
598
//                status.setRangeOfValues(0, srcFeatures.size64());
599
//                status.setCurValue(0);
600
//                for (Feature srcFeature : srcFeatures) {
601
//                    if( status.isCancellationRequested() ) {
602
//                        throw new UserCancelTaskException();
603
//                    }
604
//                    EditableFeature targetFeature;
605
//                    String featureCode = this.createUniqueCode();
606
//                    targetFeature = target.createNewFeature(srcFeature);
607
//                    targetFeature.set(FEATURECODE_FIELD_NAME, featureCode);
608
//                    target.insert(targetFeature);
609
//
610
//                    WorkspaceChangeRow change = new WorkspaceChangeRow(this);
611
//                    change.newCode();
612
//                    change.setEntityCode(entity.getEntityCode());
613
//                    change.setFeatureCode(featureCode);
614
//                    change.setOperation(OP_INSERT);
615
//                    change.setLabel(targetFeature.getString(entity.getFieldForLabel()));
616
//                    change.setSelected(true);
617
//                    change.setStatus(STATE_LOCAL_NEW);
618
//                    change.insert(changesStore);
619
//
620
//                    status.incrementCurrentValue();
621
//                    if (status.isCancellationRequested()) {
622
//                        transaction.rollbackQuietly();
623
//                        status.cancel();
624
//                        return ERR_CANCELLED_BY_USER;
625
//                    }
626
//                }
627
//            }
628
//            status.message(i18n.getTranslation("_Finishing"));
629
//            target.finishEditing();
630
//            transaction.commit();
631
//            this.forceReloadWorkspaceEntities();
632
//            status.terminate();
633
//            return ERR_NO_ERROR;
634
//        } catch (UserCancelTaskException ex) {
635
//            LOGGER.warn("User cancelled");
636
//            status.cancel();
637
//            DataTransaction.rollbackQuietly(transaction);
638
//            throw new UserCancelTaskException();
639
//        } catch (Exception ex) {
640
//            LOGGER.warn("Can't add features to '" + name + "' in '" + this.getMessageLabel() + "'.", ex);
641
//            status.abort();
642
//            DataTransaction.rollbackQuietly(transaction);
643
//            return errcode;
644
//        } finally {
645
//            DisposeUtils.disposeQuietly(transaction);
646
//            DisposeUtils.disposeQuietly(changesStore);
647
//            status.pop();
648
//        }
649
//    }
650
//
651
//    private int addExistingTable(String name, FeatureStore source, String fieldForLabel, String category, String label, String model, String resources, String pkName, SimpleTaskStatus status) {
652
//        if (this.getEntity(name) != null) { // is in working copy
653
//            return ERR_ENTITY_ALREADY_EXISTS;
654
//        }
655
//        DataManager dataManager = DALLocator.getDataManager();
656
//        I18nManager i18n = ToolsLocator.getI18nManager();
657
//        if (status == null) {
658
//            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(i18n.getTranslation("_Add_layer"));
659
//            status.setAutoremove(true);
660
//            status.add();
661
//        } else {
662
//            status.push();
663
//        }
664
//
665
//        FeatureStore target = null;
666
//        FeatureSet targetFeatures = null;
667
//        int errcode = ERR_NO_ERROR;
668
//        DataTransaction transaction = null;
669
//        JDBCServerExplorer explorer = null;
670
//        try {
671
//            explorer = (JDBCServerExplorer) dataManager.openServerExplorer(this.getProviderName(), this.getExplorerParameters());
672
//            transaction = dataManager.createTransaction();
673
//            transaction.begin();
674
//            transaction.add(explorer, false);
675
//            LOGGER.debug("===: ADD " + this.getCode() + ", '" + this.getLabel() + "', " + name);
676
//            EntityRow entity = this.getWorkspaceEntityByName(name);
677
//            if (entity != null) {
678
//                return ERR_ENTITY_ALREADY_EXISTS;
679
//            }
680
//            status.message(i18n.getTranslation("_Adding_table_layer"));
681
//
682
//            FeatureType featureType = source.getDefaultFeatureType();
683
//            FeatureAttributeDescriptor attr = featureType.getAttributeDescriptor(FEATURECODE_FIELD_NAME);
684
//            if (attr == null) {
685
////                explorer.execute("ALTER TABLE \""+name+"\" ADD COLUMN \""+FEATURECODE_FIELD_NAME+"\" VARCHAR(40)");
686
//                explorer.execute(
687
//                        String.format(
688
//                                OnlineUtils.getSqlTemplate(explorer.getProviderName(), "alterTableAddColumnVCSGISCODE"),
689
//                                name
690
//                        )
691
//                );
692
//            }
693
//
694
//            ResourcesStorage resourcesStorage = source.getResourcesStorage();
695
//            if (resourcesStorage != null) {
696
//                ResourcesStorage.Resource resource = resourcesStorage.getResource("dal");
697
//                if (resource != null) {
698
//                    resourcesStorage.remove(resource.getName());
699
//                }
700
//            }
701
//
702
//            target = (FeatureStore) dataManager.openStore(source.getProviderName(), source.getParameters());
703
//            
704
//            
705
//            transaction.add(target);
706
//            errcode = ERR_CANT_RETRIEVE_SOURCE_FEATURES;
707
//            status.message(i18n.getTranslation("_Preparing_source"));
708
//            targetFeatures = target.getFeatureSet();
709
//            transaction.add(targetFeatures);
710
//            errcode = ERR_CANT_INSERT_FEATURES;
711
//            target.edit(MODE_PASS_THROUGH);
712
//
713
//            FeatureStore changesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME,true);
714
//            transaction.add(changesStore);
715
//            changesStore.edit(MODE_APPEND);
716
//
717
//            FeatureStore entitiesStore = this.openFeatureStore(EntitiesTable.TABLE_NAME, true);
718
//            transaction.add(entitiesStore);
719
//            entitiesStore.edit(MODE_FULLEDIT);
720
//
721
//            this.addStoreIgnoreChanges(target);
722
////            entity = this.getWorkspaceEntityByName(name);
723
//            Tags tags = featureType.getTags();
724
//            errcode = ERR_CANT_OPEN_ENTITIES;
725
//            entity = new EntityRow(this);
726
//            entity.newCode();
727
//            entity.setEntityName(name);
728
//            entity.setFeatureIdFieldName(FEATURECODE_FIELD_NAME);
729
//            entity.setGeometryFieldName(featureType.getDefaultGeometryAttributeName());
730
//            entity.setDescription(null);
731
//            entity.setFieldForLabel(StringUtils.equalsIgnoreCase(fieldForLabel, AUTODETECT_FLAG)? tags.getString(TAG_ONLINE_FIELDFORLABEL, null) : fieldForLabel);
732
//            entity.setFeatureTypeAsJson(featureType.toJsonBuilder().toString());
733
//            entity.setState(STATE_LOCAL_NEW);
734
//            entity.setDataModels(StringUtils.equalsIgnoreCase(model, AUTODETECT_FLAG)? tags.getString(TAG_ONLINE_DATAMODEL, null) : model);
735
//            entity.setLabel(StringUtils.equalsIgnoreCase(label, AUTODETECT_FLAG)? tags.getString(TAG_ONLINE_LABEL, null) : label);
736
//            entity.insert(entitiesStore);
737
//            entitiesStore.finishEditing();
738
//
739
//            addWorkspaceEntity(entity);
740
//            
741
//            errcode = ERR_CANT_INSERT_CHANGE;
742
//
743
////            entity = this.getWorkspaceEntityByName(name);
744
//
745
////            addChange(entity, OP_ADD_ENTITY, changesStore, null, null);
746
//            WorkspaceChangeRow change = new WorkspaceChangeRow(this);
747
//            change.newCode();
748
//            change.setEntityCode(entity.getCode());
749
//            change.setFeatureCode(null);
750
//            change.setOperation(OP_ADD_ENTITY);
751
//            change.setSelected(true);
752
//            change.insert(changesStore);
753
//
754
//            status.message(null);
755
//            status.setRangeOfValues(0, targetFeatures.size64());
756
//            status.setCurValue(0);
757
//            for (Feature targetFeature : targetFeatures) {
758
//                if( status.isCancellationRequested() ) {
759
//                    throw new UserCancelTaskException();
760
//                }
761
//                EditableFeature editableFeature;
762
//                String featureCode = this.createUniqueCode();
763
//
764
//                editableFeature = targetFeature.getEditable();
765
//                editableFeature.set(FEATURECODE_FIELD_NAME, featureCode);
766
//                targetFeatures.update(editableFeature);
767
//
768
//                change = new WorkspaceChangeRow(this);
769
//                change.newCode();
770
//                change.setEntityCode(entity.getEntityCode());
771
//                change.setFeatureCode(featureCode);
772
//                change.setOperation(OP_INSERT);
773
//                change.setLabel(editableFeature.getString(entity.getFieldForLabel()));
774
//                change.setSelected(true);
775
//                change.setStatus(STATE_LOCAL_NEW);
776
//                change.insert(changesStore);
777
//
778
//                status.incrementCurrentValue();
779
//                if (status.isCancellationRequested()) {
780
//                    transaction.rollbackQuietly();
781
//                    status.cancel();
782
//                    return ERR_CANCELLED_BY_USER;
783
//                }
784
//            }
785
//
786
//            target.finishEditing();
787
//
788
//            status.message(i18n.getTranslation("_Finishing"));
789
//            transaction.commit();
790
//            this.forceReloadWorkspaceEntities();
791
//            
792
//            transaction.remove(explorer);
793
//
794
////            explorer.execute("ALTER TABLE \""+name+"\" DROP PRIMARY KEY");
795
//            explorer.execute(
796
//                    String.format(
797
//                            OnlineUtils.getSqlTemplate(explorer.getProviderName(), "alterTableDropPrimaryKey"),
798
//                            entity.getEntityName()
799
//                    )
800
//            );
801
//
802
////            explorer.execute("ALTER TABLE \""+name+"\" ALTER COLUMN \""+FEATURECODE_FIELD_NAME+"\" SET NOT NULL");
803
//            explorer.execute(
804
//                    String.format(
805
//                            OnlineUtils.getSqlTemplate(explorer.getProviderName(), "alterTableAlterColumnVCSGISCODESetNotNull"),
806
//                            entity.getEntityName()
807
//                    )
808
//            );
809
//
810
////            explorer.execute("ALTER TABLE \""+name+"\" ADD PRIMARY KEY (\""+FEATURECODE_FIELD_NAME+"\")");
811
//            explorer.execute(
812
//                    String.format(
813
//                            OnlineUtils.getSqlTemplate(explorer.getProviderName(), "alterTableAddPrimaryKeyVCSGISCODE"),
814
//                            entity.getEntityName()
815
//                    )
816
//            );
817
//
818
//            resourcesStorage = target.getResourcesStorage();
819
//            if (resourcesStorage != null) {
820
//                ResourcesStorage.Resource resource = resourcesStorage.getResource("dal");
821
//                if (resource != null) {
822
//                    resourcesStorage.remove(resource.getName());
823
//                }
824
//            }
825
//
826
//            target = (FeatureStore) dataManager.openStore(source.getProviderName(), source.getParameters());
827
//            target.edit();
828
//            EditableFeatureType ft = target.getDefaultFeatureTypeQuietly().getEditable();
829
//            EditableFeatureAttributeDescriptor geomattr = (EditableFeatureAttributeDescriptor) ft.getDefaultGeometryAttribute();
830
//            if( geomattr!=null ) {
831
//                geomattr.setIsIndexed(true);
832
//            }
833
//            EditableFeatureAttributeDescriptor eattr = ft.getEditableAttributeDescriptor(FEATURECODE_FIELD_NAME);
834
//            eattr.getTags().set(TAG_DYNFORM_READONLY, true);
835
//            target.update(ft);
836
//            target.finishEditing();
837
//
838
//            entitiesStore.edit(MODE_FULLEDIT);
839
//            entity.setFeatureTypeAsJson(ft.toJsonBuilder().toString());
840
//            entity.update(entitiesStore);
841
//            entitiesStore.finishEditing();
842
//
843
//            this.forceReloadWorkspaceEntities();
844
//            
845
//            status.terminate();
846
//            return ERR_NO_ERROR;
847
//        } catch (UserCancelTaskException ex) {
848
//            LOGGER.warn("User cancelled");
849
//            status.cancel();
850
//            DataTransaction.rollbackQuietly(transaction);
851
//            throw new UserCancelTaskException();
852
//        } catch (Exception ex) {
853
//            LOGGER.warn("Can't add features to '" + name + "' in '" + this.getMessageLabel() + "'.", ex);
854
//            status.abort();
855
//            DataTransaction.rollbackQuietly(transaction);
856
//            return errcode;
857
//        } finally {
858
//            DisposeUtils.disposeQuietly(transaction);
859
//            DisposeUtils.disposeQuietly(explorer);
860
//            status.pop();
861
//        }
862
//    }
863
//    public int addEntity(FeatureType featureType, String name, String description, String fieldForLabel) {
864
//        return this.addEntity(featureType, name, description, fieldForLabel, null, null, null, null, null);
865
//    }
866
    public int addEntity(FeatureType featureType, String name, String description, String fieldForLabel, String label, String pkName, int tileSize) {
867
        FeatureStore store = null;
868
        int err = ERR_NO_ERROR;
869
        try {
870
            LOGGER.debug("===: ADD_ENTITY " + this.getCode() + ", '" + this.getLabel() + "', " + name);
871
            EditableFeatureType ft = featureType.getCopy().getEditable();
872

    
873
            for (FeatureAttributeDescriptor attr : ft.getPrimaryKey()) {
874
                EditableFeatureAttributeDescriptor editAttr = (EditableFeatureAttributeDescriptor) attr;
875
                editAttr.setIsPrimaryKey(false);
876
                editAttr.setAllowNull(false);
877
                editAttr.setIsIndexed(true);
878
                editAttr.setAllowIndexDuplicateds(false);
879
            }
880

    
881
            EditableFeatureAttributeDescriptor attr = (EditableFeatureAttributeDescriptor) ft.getAttributeDescriptor(FEATURECODE_FIELD_NAME);
882
            if (attr == null) {
883
                err = ERR_ENTITY_NOT_HAS_FEATURECODE;
884
                attr = (EditableFeatureAttributeDescriptor) ft.add(FEATURECODE_FIELD_NAME, DataTypes.LONG)
885
//                        .setSize(ONLINECODELEN)
886
                        .setIsPrimaryKey(true)
887
                        .setIsIndexed(true)
888
                        .setAllowIndexDuplicateds(false)
889
                        .setLabel("Online Code");
890
                ft.setHasOID(false);
891
            } else {
892
                if (!attr.isPrimaryKey()) {
893
                    attr.setIsPrimaryKey(true);
894
                    ft.setHasOID(false);
895
                }
896
            }
897
            attr.getTags().set(TAG_DYNFORM_READONLY, true);
898

    
899
            EditableFeatureAttributeDescriptor geomattr = (EditableFeatureAttributeDescriptor) ft.getDefaultGeometryAttribute();
900
            if (geomattr != null) {
901
                geomattr.setIsIndexed(true);
902
            }
903

    
904
            featureType = ft.getNotEditableCopy();
905
            String defaultFieldForLabel = null;
906
            FeatureAttributeDescriptor[] pk = featureType.getPrimaryKey();
907
            if (pk == null || pk.length < 1) {
908
                defaultFieldForLabel = featureType.getAttributeName(0);
909
            } else {
910
                defaultFieldForLabel = pk[0].getName();
911
            }
912

    
913
            err = ERR_CANT_OPEN_ENTITIES;
914
            Tags tags = featureType.getTags();
915
            EntityRow entity = new EntityRow(this);
916
            entity.newCode();
917
            entity.setEntityName(name);
918
            entity.setFeatureIdFieldName(FEATURECODE_FIELD_NAME);
919
            entity.setGeometryFieldName(featureType.getDefaultGeometryAttributeName());
920
            entity.setDescription(description);
921
            entity.setFieldForLabel(StringUtils.equalsIgnoreCase(fieldForLabel, AUTODETECT_FLAG) ? tags.getString(TAG_ONLINE_FIELDFORLABEL, defaultFieldForLabel) : fieldForLabel);
922
            entity.setFeatureTypeAsJson(featureType.toJsonBuilder().toString());
923
            entity.setDataModels(tags.getString(TAG_ONLINE_DATAMODEL, null));
924
            entity.setLabel(StringUtils.equalsIgnoreCase(label, AUTODETECT_FLAG) ? tags.getString(TAG_ONLINE_LABEL, null) : label);
925
            entity.setTileSize(tileSize);
926
            if (geomattr != null) {
927
                IProjection crs = geomattr.getSRS();
928
                if (crs != null) {
929
                    entity.setCRS(crs);
930
                }
931
            }
932
            entity.setState(STATE_LOCAL_UNMODIFIED);
933
            entity.insert();
934
            addWorkspaceEntity(entity);
935

    
936
            this.forceReloadWorkspaceEntities();
937
            err = ERR_CANT_INSERT_CHANGE;
938

    
939
//            WorkspaceChangeRow change = new WorkspaceChangeRow(this);
940
//            change.newCode();
941
//            change.setEditingSession(null);
942
//            change.setEntityCode(entity.getCode());
943
//            change.setFeatureCode(0);
944
//            change.setOperation(OP_ADD_ENTITY);
945
//            change.setSelected(true);
946
//            change.insert();
947

    
948
            this.create_table(entity);
949
//            store = this.openFeatureStore(entity);
950
//            this.addStoreIgnoreChanges(store);
951
            
952
        } catch (OnlineRuntimeException ex) {
953
            LOGGER.warn("can't add entity '" + name + "'.", ex);
954
            if (store != null) {
955
                store.cancelEditingQuietly();
956
            }
957
            return ex.getErrnum();
958
        } catch (Exception ex) {
959
            LOGGER.warn("can't add entity '" + name + "'.", ex);
960
            if (store != null) {
961
                store.cancelEditingQuietly();
962
            }
963
            return err;
964
        } finally {
965
            DisposeUtils.disposeQuietly(store);
966
        }
967
        return ERR_NO_ERROR;
968
    }
969

    
970
    @Override
971
    public void create_table(String name) {
972
        EntityRow entity = this.getWorkspaceEntityByName(name);
973
        if (entity == null) {
974
            throw new IllegalArgumentException("Can't locate informacion of table '" + name + "'.");
975
        }
976
        create_table(entity);
977
    }
978

    
979
    @Override
980
    public void create_table(OnlineEntity entity) {
981
        create_table(entity, entity.getEntityName());
982
    }
983

    
984
    public void create_table(OnlineEntity entity, String tableName) {
985
        if (entity == null) {
986
            throw new IllegalArgumentException("Entity is null.");
987
        }
988
        try {
989
//            DataManager dataManager = DALLocator.getDataManager();
990
            JDBCNewStoreParameters addparams = (JDBCNewStoreParameters) this.wsexplorer.getAddParameters(tableName);
991
            addparams.setDefaultFeatureType(entity.getFeatureType());
992
            this.wsexplorer.add(getProviderName(), addparams, true);
993

    
994
        } catch (Exception ex) {
995
            throw new RuntimeException("Can't create table '" + entity.getEntityName() + "'.", ex);
996
        }
997

    
998
    }
999

    
1000
    public int addChanges(FeatureStore store, String editingSession, Iterator<Feature> insertedsFeatures, Iterator<Feature> updatedsFeatures, Iterator<FeatureReference> deletedsFeatures, Iterator<FeatureType.FeatureTypeChanged> changedsFeatureTypes) {
1001
        String entityName = StoreProperties.getEntityName(store);
1002
        if (StringUtils.isBlank(entityName)) {
1003
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1004
        }
1005
        if (this.isInStoreIgnoreChanges(store)) {
1006
            LOGGER.debug("===: ADD_CHANGE: Skip (" + store.getName() + ")");
1007
            return ERR_NO_ERROR;
1008
        }
1009
        EntityRow entity = this.getWorkspaceEntityByName(entityName);
1010
        if (entity == null) {
1011
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1012
        }
1013
        if (entity.getState() == STATE_DISCONNECTED) {
1014
            return ERR_OK;
1015
        }
1016
        if (this.isCorrupt(entity, store, 0)) {
1017
            return ERR_OK;
1018
        }
1019
//        WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
1020
        FeatureStore workspaceChangesStore = null;
1021
        int err = ERR_CANT_OPEN_CHANGES;
1022
        try {
1023
            workspaceChangesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME, false);
1024
            err = ERR_CANT_INSERT_CHANGE;
1025
            Map<Long, Integer> previousOperations = null;
1026
            if (updatedsFeatures instanceof Rewind && insertedsFeatures instanceof Rewind) {
1027
                previousOperations = calculatePreviousOperations(entity, workspaceChangesStore, insertedsFeatures, updatedsFeatures);
1028
                workspaceChangesStore.edit(FeatureStore.MODE_APPEND);
1029
            } else {
1030
                workspaceChangesStore.edit(FeatureStore.MODE_PASS_THROUGH);
1031
            }
1032
            if (insertedsFeatures != null) {
1033
                while (insertedsFeatures.hasNext()) {
1034
                    Feature f = insertedsFeatures.next();
1035
                    if (f == null) {
1036
                        continue;
1037
                    }
1038
//                    if( this.useSafeMode() ) {
1039
//                        // Estamos antes de terminar edicion, la feature f aun no se ha insertado.
1040
//                        long featurecode = f.getLong(FEATURECODE_FIELD_NAME);
1041
//                        Feature fuser = store.findFirst("\"+FEATURECODE_FIELD_NAME+\" = '"+featurecode+"'");
1042
//                        if( fuser!=null ) {
1043
//                            err = ERR_CANT_INSERT_CHANGE;
1044
//                            throw new RuntimeException("Feature already exists for vcsgiscode = '"+featurecode+"'");
1045
//                        }
1046
//                    }
1047
                    addChange(editingSession, entity, OP_INSERT, workspaceChangesStore, f, null, previousOperations);
1048
                }
1049
            }
1050
            if (updatedsFeatures != null) {
1051
                while (updatedsFeatures.hasNext()) {
1052
                    Feature f = updatedsFeatures.next();
1053
                    if (f == null) {
1054
                        continue;
1055
                    }
1056
                    Feature originalFeature = store.getOriginalFeature(f);
1057
//                    if( this.useSafeMode() ) {
1058
//                        // Estamos antes de terminar edicion, la feature f aun no se ha insertado.
1059
//                        long featurecode = f.getLong(FEATURECODE_FIELD_NAME);
1060
//                        Feature fuser = store.findFirst("\"+FEATURECODE_FIELD_NAME+\" = '"+featurecode+"'");
1061
//                        if( fuser!=null ) {
1062
//                            err = ERR_CANT_INSERT_CHANGE;
1063
//                            throw new RuntimeException("Feature already exists for vcsgiscode = '"+featurecode+"'");
1064
//                        }
1065
//                    }                    
1066
                    addChange(editingSession, entity, OP_UPDATE, workspaceChangesStore, f, originalFeature, previousOperations);
1067
                }
1068
            }
1069
            if (previousOperations != null) {
1070
                workspaceChangesStore.finishEditing();
1071
                workspaceChangesStore.edit(FeatureStore.MODE_PASS_THROUGH);
1072
            }
1073
            if (deletedsFeatures != null) {
1074
                while (deletedsFeatures.hasNext()) {
1075
                    FeatureReference fr = deletedsFeatures.next();
1076
                    if (fr == null) {
1077
                        continue;
1078
                    }
1079
                    Feature f = fr.getFeatureQuietly();
1080
                    if (f == null) {
1081
                        continue;
1082
                    }
1083
                    Feature originalFeature = store.getOriginalFeature(fr);
1084
//                    if( this.useSafeMode() ) {
1085
//                        // Estamos antes de terminar edicion, la feature f aun no se ha insertado.
1086
//                        long featurecode = f.getLong(FEATURECODE_FIELD_NAME);
1087
//                        Feature fuser = store.findFirst("\"+FEATURECODE_FIELD_NAME+\" = '"+featurecode+"'");
1088
//                        if( fuser!=null ) {
1089
//                            err = ERR_CANT_INSERT_CHANGE;
1090
//                            throw new RuntimeException("Feature already exists for vcsgiscode = '"+featurecode+"'");
1091
//                        }
1092
//                    }
1093
                    addDeleteChange(editingSession, entity, workspaceChangesStore, f.getLong(entity.getFeatureIdFieldName()), f.getString(entity.getFieldForLabel()), originalFeature);
1094
                }
1095
            }
1096
            workspaceChangesStore.finishEditing();
1097

    
1098
            if (changedsFeatureTypes != null && changedsFeatureTypes.hasNext()) {
1099
                try {
1100
                    entity.setState(STATE_DISCONNECTED);
1101
                    entity.update();
1102
                } catch (Throwable t) {
1103
                    LOGGER.warn("Can't update entity state");
1104
                }
1105
            }
1106

    
1107
            err = ERR_NO_ERROR;
1108
        } catch (Exception ex) {
1109
            LOGGER.warn("Can't add changes.", ex);
1110
            if (workspaceChangesStore != null) {
1111
                workspaceChangesStore.cancelEditingQuietly();
1112
            }
1113
        } finally {
1114
            DisposeUtils.disposeQuietly(workspaceChangesStore);
1115
        }
1116
        return err;
1117
    }
1118
    
1119
    public int addChange(String editingSession, int operation, FeatureStore userStore, Feature feature) {
1120
        String entityName = StoreProperties.getEntityName(userStore);
1121
        if (StringUtils.isBlank(entityName)) {
1122
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1123
        }
1124
        if (this.isInStoreIgnoreChanges(userStore)) {
1125
            LOGGER.debug("===: ADD_CHANGE: Skip (" + userStore.getName() + ")");
1126
            return ERR_NO_ERROR;
1127
        }
1128
        boolean isUserStoreInAppendMode = userStore.getMode()==FeatureStore.MODE_APPEND;
1129
        EntityRow entity = (EntityRow) this.getWorkspaceEntityByName(entityName);
1130
        if( entity.getState()==STATE_DISCONNECTED ) {
1131
            return ERR_OK;
1132
        }
1133
        FeatureStore changesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME,false);
1134
        try {
1135
            changesStore.edit(MODE_PASS_THROUGH);
1136
            long featureCode = feature.getLong(entity.getFeatureIdFieldName());
1137
            switch(operation) {
1138
            case OP_DELETE:
1139
                String label = feature.getString(entity.getFieldForLabel());
1140
//               if( this.useSafeMode() && !isUserStoreInAppendMode ) {
1141
//                    Feature fuser = userStore.findFirst("VCSGISCODE = '"+featureCode+"'");
1142
//                    if( fuser==null ) {
1143
//                        throw new RuntimeException("Can't get original feature for deleted vcsgiscode = '"+featureCode+"'");
1144
//                    }
1145
//                }                
1146
                return addDeleteChange(editingSession, entity, changesStore, featureCode, label, userStore.getOriginalFeature(feature));
1147
            case OP_INSERT:
1148
//               if( this.useSafeMode() && !isUserStoreInAppendMode ) {
1149
//                    Feature fuser = userStore.findFirst("VCSGISCODE = '"+featureCode+"'");
1150
//                    if( fuser!=null ) {
1151
//                        throw new RuntimeException("Already exists feature with vcsgiscode = '"+featureCode+"'");
1152
//                    }
1153
//                }                
1154
                return addChange(editingSession, entity, operation, changesStore, feature, userStore.getOriginalFeature(feature), null);
1155
            case OP_UPDATE:
1156
//               if( this.useSafeMode() && !isUserStoreInAppendMode ) {
1157
//                    Feature fuser = userStore.findFirst("VCSGISCODE = '"+featureCode+"'");
1158
//                    if( fuser==null ) {
1159
//                        throw new RuntimeException("Can't get original feature for update vcsgiscode = '"+featureCode+"'");
1160
//                    }
1161
//                }                
1162
                return addChange(editingSession, entity, operation, changesStore, feature, userStore.getOriginalFeature(feature), null);
1163
            default:
1164
                LOGGER.warn("Unsupported operation "+operation+".");
1165
                return addChange(editingSession, entity, operation, changesStore, feature, userStore.getOriginalFeature(feature), null);
1166
            }
1167
        } catch (Exception ex) {
1168
            LOGGER.warn("Can't add change (op " + OnlineUtils.getOperationLabel(operation) + ", " + entity.getEntityName() + ")", ex);
1169
            changesStore.cancelEditingQuietly();
1170
            return ERR_CANT_ADD_CHANGE;
1171
        } finally {
1172
            changesStore.finishEditingQuietly();
1173
            DisposeUtils.dispose(changesStore);
1174
        }
1175
    }
1176

    
1177

    
1178

    
1179
    private int addChange(String editingSession, EntityRow entity, int operation, FeatureStore changesStore, Feature feature, Feature oldFeature, Map<Long, Integer> previousOperations) {
1180
        if (entity.getState() == STATE_DISCONNECTED) {
1181
            return ERR_OK;
1182
        }
1183
        if (operation == OP_DELETE) {
1184
            // Por aqui no deberia pasar de normal. 
1185
            // Si esta en MODE_FULLEDIT no deberia pasar.
1186
            // En MODE_PASSTH... el otro metodo addChange deberia haberlo atrapado,
1187
            // y derivado al addDeleteChange. 
1188
            // Por si acaso llega aqui, lo derivamos al addDeleteChange.
1189
            long featureCode = feature.getLong(entity.getFeatureIdFieldName());
1190
            String label = feature.getString(entity.getFieldForLabel());
1191
            return addDeleteChange(editingSession, entity, changesStore, featureCode, label, oldFeature);
1192
        }
1193
        if (oldFeature == null) {
1194
            if (feature instanceof EditableFeature) {
1195
                oldFeature = ((EditableFeature) feature).getSource();
1196
            }
1197
        }
1198
        try {
1199
            Feature previousChange = null;
1200
            long featureCode = feature.getLong(entity.getFeatureIdFieldName());
1201
            Integer previousOperation = null;
1202
            if (previousOperations != null) {
1203
                previousOperation = previousOperations.get(featureCode);
1204
            } else {
1205
                previousChange = changesStore.findFirst(WorkspaceChangesTable.FEATUREID + " = '" + featureCode + "'"); // and " + WorkspaceChangesTable.OPERATION+ " = " + operation);
1206
                if (previousChange != null) {
1207
                    previousOperation = previousChange.getInt(WorkspaceChangesTable.OPERATION);
1208
                }
1209
            }
1210
            if (previousOperation != null) {
1211
                /*
1212
                operation - previo
1213
                INS - INS -> Error
1214
                INS - UPD -> Error
1215
                INS - DEL -> Error
1216
                UPD - INS -> no hace nada y sale con OK
1217
                UPD - UPD -> no hace nada y sale con OK
1218
                UPD - DEL -> Error
1219
                DEL - INS -> Error, aqui no deberia llegar un DEL
1220
                DEL - UPD -> Error, aqui no deberia llegar un DEL
1221
                DEL - DEL -> Error, aqui no deberia llegar un DEL
1222
                 */
1223
                switch (operation) {
1224
                    case OP_INSERT:
1225
                        LOGGER.warn("A insert change cannot be added as there is a previous change with operation " + previousOperation + " (featureCode=" + featureCode + ")");
1226
                        return ERR_CANT_ADD_CHANGE;
1227
                    case OP_UPDATE:
1228
                        switch (previousOperation) {
1229
                            case OP_INSERT:
1230
                            case OP_UPDATE:
1231
                                //TODO: Migrar a VCSGIS
1232
                                if(previousChange != null ) {
1233
                                    WorkspaceChangeRow change = new WorkspaceChangeRow(this, previousChange);
1234
                                    if(change.getStatus() == STATE_CONFLICT){
1235
                                        change.setStatus(STATE_LOCAL_MODIFIED);
1236
                                        change.update(changesStore);
1237
                                    }
1238
                                }
1239
                                return ERR_OK;
1240
                            case OP_DELETE:
1241
                                LOGGER.warn("A update change cannot be added as there is a previous change with operation delete (featureCode=" + featureCode + ")");
1242
                                return ERR_CANT_ADD_CHANGE;
1243
                            default:
1244
                                LOGGER.warn("A update change cannot be added as there is a previous change with operation " + previousOperation + " (featureCode=" + featureCode + ")");
1245
                                return ERR_OK;
1246
                        }
1247
                    case OP_DELETE:
1248
                        // Aqui no deberia llegar
1249
                        return ERR_CANT_ADD_CHANGE;
1250
                }
1251
                return ERR_OK;
1252
            }
1253

    
1254
            WorkspaceChangeRow change = new WorkspaceChangeRow(this, changesStore.createNewFeature());
1255
            change.newCode();
1256
            change.setEditingSession(editingSession);
1257
            change.setEntityCode(entity.getEntityCode());
1258
            change.setFeatureCode(featureCode);
1259
            change.setOperation(operation);
1260
            change.setLabel(feature.getString(entity.getFieldForLabel()));
1261
            if (oldFeature != null) {
1262
                change.setData(oldFeature.toJson().toString());
1263
            }
1264
            // Si cambiamos este true, no solo fallaran los test, la importacion
1265
            // del historico habria que tocarla tambien.
1266
            change.setSelected(true);
1267
            switch (operation) {
1268
                case OP_INSERT:
1269
                    change.setStatus(STATE_LOCAL_NEW);
1270
                    break;
1271
                case OP_UPDATE:
1272
                    change.setStatus(STATE_LOCAL_MODIFIED);
1273
                    break;
1274
            }
1275

    
1276
            change.insert(changesStore);
1277
            if (previousOperations != null) {
1278
                previousOperations.put(featureCode, change.getOperation());
1279
            }
1280
            updateEntityState(entity, change.getOperation());
1281

    
1282
            return ERR_OK;
1283
        } catch (Exception ex) {
1284
            LOGGER.warn("Can't add change (op " + OnlineUtils.getOperationLabel(operation) + ", " + entity.getEntityName() + ")", ex);
1285
            return ERR_CANT_ADD_CHANGE;
1286
        }
1287
    }
1288

    
1289
    public int addDeleteChanges(String editingSession, FeatureStore userStore, Expression exp) {
1290
        String entityName = StoreProperties.getEntityName(userStore);
1291
        if (StringUtils.isBlank(entityName)) {
1292
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1293
        }
1294
        if (this.isInStoreIgnoreChanges(userStore)) {
1295
            return ERR_NO_ERROR;
1296
        }
1297
        EntityRow entity = this.getWorkspaceEntityByName(entityName);
1298
        if (entity == null) {
1299
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1300
        }
1301
        if( entity.getState()==STATE_DISCONNECTED ) {
1302
            return ERR_OK;
1303
        }
1304
        FeatureStore workspaceChangesStore = null;
1305
        int err = ERR_CANT_OPEN_CHANGES;
1306
        try {
1307
            workspaceChangesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME,false);
1308
            workspaceChangesStore.edit(FeatureStore.MODE_PASS_THROUGH);
1309
            FeatureSet deletedsFeatures = userStore.getFeatureSet(exp);
1310
            err = ERR_CANT_INSERT_CHANGE;
1311
            for (Feature f : deletedsFeatures) {
1312
                addDeleteChange(editingSession, 
1313
                        entity, 
1314
                        workspaceChangesStore, 
1315
                        f.getLong(entity.getFeatureIdFieldName()), 
1316
                        f.getString(entity.getFieldForLabel()), 
1317
                        f
1318
                );
1319
            }
1320
            workspaceChangesStore.finishEditing();
1321
            err = ERR_NO_ERROR;
1322
        } catch (Exception ex) {
1323
            LOGGER.warn("Can't add  delete changes.", ex);
1324
            FeatureStore.cancelEditingQuietly(workspaceChangesStore);
1325
        } finally {
1326
            DisposeUtils.disposeQuietly(workspaceChangesStore);
1327
        }
1328
        return err;
1329
    }
1330
    
1331
    
1332
    private int addDeleteChange(String editingSession, EntityRow entity, FeatureStore changesStore, long featureCode, String label, Feature oldFeature) {
1333
        if (entity.getState() == STATE_DISCONNECTED) {
1334
            return ERR_OK;
1335
        }
1336
        try {
1337
            WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
1338
            WorkspaceChangeRow previousChange = changesTable.find(this, changesStore, entity.getEntityCode(), featureCode);
1339
            if (previousChange != null) {
1340

    
1341
                /*
1342
                DEL - INS -> Se actualiza el cambio anterior para ignorarlo, se asocia a esta sessi?n y salir con OK
1343
                DEL - UPD -> Se actualiza el cambio anterior a DELETE, se asocia a esta sessi?n y salir con OK
1344
                DEL - DEL -> No hace nada y sale con Error
1345
                 */
1346
                switch (previousChange.getOperation()) {
1347
                    case OP_INSERT:
1348
                        //Al terminar edici?n si todo va bien hay que borrar este registro
1349
                        // si falla, hay que modificarlo para devolverle la operaci?n
1350
                        previousChange.setOperation(OP_IGNORE);
1351
                        previousChange.setSelected(true);
1352
                        // Para recuperarse en caso de que falle la finalizacion de edicion
1353
                        previousChange.setPreviousOperation(OP_INSERT);
1354
                        previousChange.setEditingSession(editingSession);
1355
                        previousChange.update(changesStore);
1356
                        updateEntityState(entity, previousChange.getOperation());
1357
                        return ERR_OK;
1358
                    case OP_UPDATE:
1359
                        //Al terminar edici?n 
1360
                        // si falla, hay que modificarlo para devolverle la operaci?n
1361
                        previousChange.setOperation(OP_DELETE);
1362
                        previousChange.setSelected(true);
1363
                        // Para recuperarse en caso de que falle la finalizacion de edicion
1364
                        previousChange.setPreviousOperation(previousChange.getOperation());
1365
                        previousChange.setEditingSession(editingSession);
1366
                        previousChange.update(changesStore);
1367
                        updateEntityState(entity, previousChange.getOperation());
1368
                        return ERR_OK;
1369
                    case OP_DELETE:
1370
                        LOGGER.warn("A delete change cannot be added as there is a previous change with operation delete (featureCode=" + featureCode + ")");
1371
                        return ERR_CANT_ADD_CHANGE;
1372
                    case OP_IGNORE:
1373
                        LOGGER.warn("A delete change cannot be added as there is a previous change with operation ignore (featureCode=" + featureCode + ")");
1374
                        return ERR_CANT_ADD_CHANGE;
1375
                    default:
1376
                        LOGGER.warn("A delete change cannot be added as there is a previous change with operation " + previousChange.getOperation() + " (featureCode=" + featureCode + ")");
1377
                }
1378
            }
1379

    
1380
            WorkspaceChangeRow change = new WorkspaceChangeRow(this, changesStore.createNewFeature());
1381
            change.newCode();
1382
            change.setPreviousOperation(OP_UNKNOWN);
1383
            change.setEditingSession(editingSession);
1384
            change.setEntityCode(entity.getEntityCode());
1385
            change.setFeatureCode(featureCode);
1386
            change.setOperation(OP_DELETE);
1387
            change.setLabel(label);
1388
            if (oldFeature != null) {
1389
                change.setData(oldFeature.toJson().toString());
1390
            }
1391
            // Si cambiamos este true, no solo fallaran los test, la importacion
1392
            // del historico habria que tocarla tambien.
1393
            change.setSelected(true);
1394
            change.setStatus(STATE_LOCAL_MODIFIED);
1395
            change.insert(changesStore);
1396
            updateEntityState(entity, change.getOperation());
1397
            return ERR_OK;
1398
        } catch (Exception ex) {
1399
            LOGGER.warn("Can't add delete change (" + entity.getEntityName() + ")", ex);
1400
            return ERR_CANT_ADD_CHANGE;
1401
        }
1402
    }
1403

    
1404
    private void updateEntityState(EntityRow entity, int operation) throws DataException {
1405
        if (entity.getState() == STATE_DISCONNECTED) {
1406
            return;
1407
        }
1408
        boolean save = false;
1409
        switch (operation) {
1410
            case OP_INSERT:
1411
            case OP_DELETE:
1412
            case OP_UPDATE:
1413
            case OP_IGNORE:
1414
                switch (entity.getState()) {
1415
                    case STATE_LOCAL_MODIFIED:
1416
                    case STATE_LOCAL_NEW:
1417
                    case STATE_CONFLICT:
1418
                    case STATE_LOCAL_OUTDATED_AND_MODIFIED:
1419
                        break;
1420
                    case STATE_LOCAL_UNMODIFIED:
1421
                        entity.setState(STATE_LOCAL_MODIFIED);
1422
                        save = true;
1423
                        break;
1424
                    case STATE_LOCAL_OUTDATED:
1425
                        entity.setState(STATE_LOCAL_OUTDATED_AND_MODIFIED);
1426
                        save = true;
1427
                        break;
1428
                    case STATE_REMOTE_NEW:
1429
                    case STATE_UNKNOWN:
1430
                    default:
1431
                        //This case is impossible
1432
                        break;
1433
                }
1434
                break;
1435
//            case OP_ADD_ENTITY:
1436
//                entity.setState(STATE_LOCAL_NEW);
1437
//                save = true;
1438
//                break;
1439
        }
1440

    
1441
        if (save) {
1442
            FeatureStore entitiesStore = null;
1443
            try {
1444
                entitiesStore = this.openFeatureStore(EntitiesTable.TABLE_NAME, true);
1445
                entitiesStore.edit(FeatureStore.MODE_FULLEDIT);
1446

    
1447
                entity.update(entitiesStore);
1448
                entitiesStore.finishEditing();
1449
            } finally {
1450
                DisposeUtils.disposeQuietly(entitiesStore);
1451
            }
1452
        }
1453

    
1454
    }
1455

    
1456
    private void updateEntityState(EntityRow entity, boolean save) throws DataException {
1457
        if (entity.getState() == STATE_DISCONNECTED) {
1458
            return;
1459
        }
1460
        if (save) {
1461
            FeatureStore entitiesStore = null;
1462
            try {
1463
                entitiesStore = this.openFeatureStore(EntitiesTable.TABLE_NAME, true);
1464
                entitiesStore.edit(FeatureStore.MODE_FULLEDIT);
1465
                entity.updateState();
1466
                entity.update(entitiesStore);
1467
                entitiesStore.finishEditing();
1468
            } finally {
1469
                DisposeUtils.disposeQuietly(entitiesStore);
1470
            }
1471
        } else {
1472
            entity.updateState();
1473
        }
1474
    }
1475

    
1476
    private Map<Long, Integer> calculatePreviousOperations(EntityRow entity, FeatureStore changesStore, Iterator<Feature> insertedsFeatures, Iterator<Feature> updatedsFeatures) {
1477
        // Esta version agrupa las consultas a la base de datos de 200 en 200
1478
        // para reducir el numero de consultas y aumentar el rendimiento.
1479
        try {
1480
            Map<Long, Integer> previousOperations = new HashMap<>();
1481
            Iterator<Feature> it = new ChainedIterator<>(insertedsFeatures, updatedsFeatures);
1482
            ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
1483
            int count = 0;
1484
            while (it.hasNext()) {
1485
                Feature feature = it.next();
1486
                String featureCode = feature.getString(entity.getFeatureIdFieldName());
1487
                builder.or(
1488
                        builder.eq(
1489
                                builder.column(WorkspaceChangesTable.FEATUREID),
1490
                                builder.constant(featureCode)
1491
                        )
1492
                );
1493
                count++;
1494
                String filter = builder.build();
1495
                if (!it.hasNext() || count == 200) { //|| StringUtils.length(filter)>(1024*3) ) {
1496
                    if (filter != null) {
1497
                        FeatureSet set = changesStore.getFeatureSet(filter);
1498
                        for (Feature previousChange : set) {
1499
                            if (previousChange != null) {
1500
                                long featureCode2 = previousChange.getLong(WorkspaceChangesTable.FEATUREID);
1501
                                int previousOperation = previousChange.getInt(WorkspaceChangesTable.OPERATION);
1502
                                previousOperations.put(featureCode2, previousOperation);
1503
                            }
1504
                        }
1505
                        DisposeUtils.disposeQuietly(set);
1506
                    }
1507
                    builder = ExpressionUtils.createExpressionBuilder();
1508
                    count = 0;
1509
                }
1510
            }
1511
            ((Rewind) insertedsFeatures).rewind();
1512
            ((Rewind) updatedsFeatures).rewind();
1513
            return previousOperations;
1514
        } catch (Exception ex) {
1515
            LOGGER.warn("Can't calculate previus operations.", ex);
1516
            return null;
1517
        }
1518
    }
1519

    
1520
    public int cancelChanges(FeatureStore store, String editingSession) {
1521
        String entityName = StoreProperties.getEntityName(store);
1522
        if (StringUtils.isBlank(entityName)) {
1523
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1524
        }
1525
        if (this.isInStoreIgnoreChanges(store)) {
1526
            LOGGER.debug("===: CANCEL_CHANGES: Skip (" + store.getName() + ")");
1527
            return ERR_NO_ERROR;
1528
        }
1529
        EntityRow entity = this.getWorkspaceEntityByName(entityName);
1530
        if (entity == null) {
1531
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1532
        }
1533
        WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
1534
        changesTable.revertChangedLocalChanges(this, entity, editingSession);
1535
        changesTable.removeLocalChanges(this, entity, editingSession);
1536
        return ERR_NO_ERROR;
1537
    }
1538

    
1539
    public int acceptChanges(FeatureStore store, String editingSession) {
1540
        String entityName = StoreProperties.getEntityName(store);
1541
        if (StringUtils.isBlank(entityName)) {
1542
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1543
        }
1544
        if (this.isInStoreIgnoreChanges(store)) {
1545
            LOGGER.debug("===: CANCEL_CHANGES: Skip (" + store.getName() + ")");
1546
            return ERR_NO_ERROR;
1547
        }
1548
        EntityRow entity = this.getWorkspaceEntityByName(entityName);
1549
        if (entity == null) {
1550
            return ERR_STORE_NOT_IN_WORKINGCOPY;
1551
        }
1552
        if (entity.getState() == STATE_DISCONNECTED) {
1553
            return ERR_OK;
1554
        }
1555
        WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
1556
        changesTable.removeIgnoredLocalChanges(this, entity, editingSession);
1557
        return ERR_NO_ERROR;
1558
    }
1559

    
1560
    @Override
1561
    public OnlineChanges<OnlineWorkingcopyChange> getLocalChanges() {
1562
        return getLocalChanges(null);
1563
    }
1564

    
1565
    @Override
1566
    public OnlineChanges<OnlineWorkingcopyChange> getLocalChanges(List<OnlineEntity> entities) {
1567
        WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
1568
        ChangesImpl changes = new ChangesImpl<WorkspaceChangeRow>(changesTable.getByEntityCode(this, entities), WorkspaceChangesTable.SELECTED) {
1569
            @Override
1570
            protected WorkspaceChangeRow createChange(Feature f) {
1571
                return new WorkspaceChangeRow(OnlineWorkspaceImpl.this, f);
1572
            }
1573

    
1574
            @Override
1575
            protected void updateChange(FeatureStore store, WorkspaceChangeRow change) {
1576
                change.update(store);
1577
            }
1578

    
1579
        };
1580
        return changes;
1581
    }
1582

    
1583
    public boolean hasLocalChanges(List<OnlineEntity> entities) {
1584
        WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
1585
        boolean hasChanges = changesTable.hasLocalChangesByEntityCode(this, entities);
1586
        return hasChanges;
1587
    }
1588

    
1589
    @Override
1590
    public List<OnlineEntity> getWorkspaceEntities() {
1591
        if (disposed) {
1592
            return Collections.EMPTY_LIST;
1593
        }
1594
        List<OnlineEntity> entities = new ArrayList<>();
1595
        entities.addAll(this.getEntitiesAsEntityRow());
1596
        return entities;
1597
    }
1598

    
1599
    private Collection<EntityRow> getEntitiesAsEntityRow() {
1600
        if (this.workspaceEntitiesByName == null) {
1601
            reloadWorkspaceEntities();
1602
        }
1603
        return this.workspaceEntitiesByName.values();
1604
    }
1605

    
1606
    @Override
1607
    public EntityRow getWorkspaceEntityByName(String name) {
1608
        if (this.workspaceEntitiesByName == null) {
1609
            this.reloadWorkspaceEntities();
1610
        }
1611
        return this.workspaceEntitiesByName.get(name);
1612
    }
1613

    
1614
    @Override
1615
    public EntityRow getWorkspaceEntityByCode(String code) {
1616
        if (this.workspaceEntitiesByCode == null) {
1617
            this.reloadWorkspaceEntities();
1618
        }
1619
        return this.workspaceEntitiesByCode.get(code);
1620
    }
1621

    
1622
    private void addWorkspaceEntity(EntityRow entity) {
1623
        if (this.workspaceEntitiesByCode == null) {
1624
            this.reloadWorkspaceEntities();
1625
        }
1626
        this.workspaceEntitiesByCode.put(entity.getEntityCode(), entity);
1627
        this.workspaceEntitiesByName.put(entity.getEntityName(), entity);
1628
    }
1629

    
1630
    private boolean existsInWorkspace(String entity) {
1631
        // No hacer publico este metodo ya que es peligroso que se consulte si existe
1632
        // una entidad por nombre. Nos ha pasado ya que el nombre de entidad existe
1633
        // pero con un codigo distinto.
1634
        EntityRow lentity = this.getWorkspaceEntity(entity);
1635
        return lentity != null;
1636
    }
1637

    
1638
    @Override
1639
    public boolean existsInWorkspace(OpenFeatureStoreParameters parameters) {
1640
        if (!this.isInMyDatabase(parameters)) {
1641
            return false;
1642
        }
1643
        JDBCStoreParameters params = (JDBCStoreParameters) parameters;
1644
        return this.existsInWorkspace(params.getTable());
1645
    }
1646

    
1647
    @Override
1648
    public boolean existsInWorkspace(OnlineEntity entity) {
1649
        if (entity == null) {
1650
            return false;
1651
        }
1652
        if (this.existsInWorkspace(entity.getEntityName())) {
1653
            return true;
1654
        }
1655
        return this.existsInWorkspace(entity.getEntityCode());
1656
    }
1657

    
1658
    @Override
1659
    public EntityRow getWorkspaceEntity(String entity) {
1660
        EntityRow wsentity = this.getWorkspaceEntityByCode(entity);
1661
        if (wsentity == null) {
1662
            wsentity = this.getWorkspaceEntityByName(entity);
1663
        }
1664
        return wsentity;
1665
    }
1666

    
1667
    @Override
1668
    public OnlineSite getSite() {
1669
        return this.project.getSite();
1670
    }
1671

    
1672
    public OnlineProject getProject() {
1673
        return this.project;
1674
    }
1675

    
1676
    @Override
1677
    public List<OnlineEntity> getEntitiesOfRemoteChanges() {
1678
        List<OnlineEntity> res = new ArrayList<>();
1679
        RemoteChangesTable changesTable = new RemoteChangesTable();
1680
        DisposableFeatureSetIterable features = changesTable.getGroupedByEntity(this);
1681
        EntitiesTable entitiesTable = new EntitiesTable();
1682
        for (Feature feature : features) {
1683
            OnlineEntity entity = entitiesTable.getByEntityCode(this, feature.getString(RemoteChangesTable.COD_ENTITY));
1684
            res.add(entity);
1685
        }
1686
        DisposeUtils.disposeQuietly(features);
1687
        return res;
1688
    }
1689

    
1690
    @Override
1691
    public List<OnlineEntity> getEntitiesOfLocalChanges() {
1692
        WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
1693
        List<OnlineEntity> entities = new ArrayList<>();
1694

    
1695
        DisposableFeatureSetIterable changesGroupedByEntity = changesTable.getGroupedByEntity(this);
1696

    
1697
        for (Feature fchange : changesGroupedByEntity) {
1698
            WorkspaceChangeRow change = new WorkspaceChangeRow(this, fchange);
1699
            OnlineEntity localEntity = change.getEntity();
1700
            if (change.isSelected()) {
1701
                entities.add(localEntity);
1702
            }
1703
        }
1704
        return entities;
1705
    }
1706

    
1707
    @Override
1708
    public void addToConnectionPool() {
1709
        JDBCServerExplorerParameters params = this.getExplorerParameters();
1710
        DataManager dataManager = DALLocator.getDataManager();
1711
        dataManager.getDataServerExplorerPool().add(this.getLabel(), params);
1712
    }
1713

    
1714
    @Override
1715
    public boolean isInMyDatabase(FeatureStore store) {
1716
        if (!(store.getParameters() instanceof JDBCStoreParameters)) {
1717
            return false;
1718
        }
1719
        JDBCStoreParameters storeParams = (JDBCStoreParameters) store.getParameters();
1720
        JDBCServerExplorerParameters explorerParams = this.getExplorerParameters();
1721
        if (!StringUtils.equalsIgnoreCase(storeParams.getProviderName(), explorerParams.getProviderName())) {
1722
            return false;
1723
        }
1724
        return StringUtils.equalsIgnoreCase(storeParams.getUrl(), explorerParams.getUrl());
1725
    }
1726

    
1727
    @Override
1728
    public boolean isInMyDatabase(OpenFeatureStoreParameters parameters) {
1729
        if (!(parameters instanceof JDBCStoreParameters)) {
1730
            return false;
1731
        }
1732
        JDBCStoreParameters storeParams = (JDBCStoreParameters) parameters;
1733
        JDBCServerExplorerParameters explorerParams = this.getExplorerParameters();
1734
        if (!StringUtils.equalsIgnoreCase(storeParams.getProviderName(), explorerParams.getProviderName())) {
1735
            return false;
1736
        }
1737
        return StringUtils.equalsIgnoreCase(storeParams.getUrl(), explorerParams.getUrl());
1738
    }
1739

    
1740
    @Override
1741
    public boolean isInMyDatabase(String tableName) {
1742
        try {
1743
            if (StringUtils.isBlank(tableName)) {
1744
                return false;
1745
            }
1746
            JDBCServerExplorer explorer = this.getExplorer();
1747
            JDBCServerExplorerParameters explorerParams = explorer.getParameters();
1748
            if (explorerParams instanceof HasAFile) {
1749
                File f = ((HasAFile) explorerParams).getFile();
1750
                if (!f.exists()) {
1751
                    return false;
1752
                }
1753
            }
1754
            JDBCStoreParameters parameters = explorer.get(tableName);
1755
            if (!explorer.exists(parameters)) {
1756
                return false;
1757
            }
1758
            return true;
1759
        } catch (DataException ex) {
1760
            return false;
1761
        }
1762
    }
1763

    
1764
    @Override
1765
    public Feature getRelatedFeature(OnlineEntity entity, long featureCode) {
1766
        FeatureStore store = null;
1767
        try {
1768
            store = this.getFeatureStore(entity.getEntityName());
1769
            Feature f = store.findFirst("\"" + entity.getFeatureIdFieldName() + "\"=" + featureCode);
1770
            return f;
1771
        } catch (Exception ex) {
1772
            throw new RuntimeException("Can't retrieve feature '" + entity.getEntityName() + "/" + code + "'.", ex);
1773
        } finally {
1774
            DisposeUtils.disposeQuietly(store);
1775
        }
1776

    
1777
    }
1778

    
1779
    @Override
1780
    public Feature getRelatedFeature(OnlineRemoteChange change) {
1781
        EntityRow entity = this.getWorkspaceEntity(change.getEntityCode());
1782
        return getRelatedFeature(entity, change.getRelatedFeatureCode());
1783
    }
1784

    
1785
    @Override
1786
    public int removeEntity(OnlineEntity entity) {
1787
        if (entity == null) {
1788
            return ERR_INVALID_ENTITY;
1789
        }
1790
        int err = ERR_NO_ERROR;
1791
        DataTransaction transaction = DALLocator.getDataManager().createTransaction();
1792
        try {
1793
            transaction.begin();
1794
            EntityRow entity1 = this.getWorkspaceEntityByCode(entity.getEntityCode());
1795
            EntityRow entity2 = this.getWorkspaceEntityByName(entity.getEntityName());
1796
            if (entity1 != null && entity2 != null
1797
                    && StringUtils.equalsIgnoreCase(entity1.getEntityName(), entity2.getEntityName())
1798
                    && StringUtils.equalsIgnoreCase(entity1.getEntityCode(), entity2.getEntityCode())) {
1799
                err = doRemoveEntity(entity1, transaction);
1800
            } else if (entity1 != null) {
1801
                err = doRemoveEntity(entity1, transaction);
1802
            } else if (entity2 != null) {
1803
                err = doRemoveEntity(entity2, transaction);
1804
            } else {
1805
                err = doRemoveEntity(entity, transaction);
1806
            }
1807
            transaction.commit();
1808
            forceReloadWorkspaceEntities();
1809
            return ERR_OK;
1810
        } catch (Exception ex) {
1811
            LOGGER.warn("Can't remove entity.", ex);
1812
            transaction.rollbackQuietly();
1813
            return err;
1814
        } finally {
1815
            DisposeUtils.disposeQuietly(transaction);
1816
        }
1817
    }
1818

    
1819
    @Override
1820
    public int removeEntities(List<String> namesOrCodes) {
1821
        int err = ERR_NO_ERROR;
1822
        DataTransaction transaction = DALLocator.getDataManager().createTransaction();
1823

    
1824
        try {
1825
            transaction.begin();
1826
            for (String nameOrCode : namesOrCodes) {
1827
                EntityRow entity = (EntityRow) this.getWorkspaceEntity(nameOrCode);
1828
                if (entity == null) {
1829
                    err = ERR_ENTITY_NOT_EXISTS;
1830
                    return err;
1831
                }
1832

    
1833
                doRemoveEntity(entity, transaction);
1834
            }
1835

    
1836
            transaction.commit();
1837
            forceReloadWorkspaceEntities();
1838
            return ERR_OK;
1839
        } catch (Exception ex) {
1840
            LOGGER.warn("Can't remove entity.", ex);
1841
            transaction.rollbackQuietly();
1842
            return err;
1843
        } finally {
1844
            DisposeUtils.disposeQuietly(transaction);
1845
        }
1846

    
1847
    }
1848

    
1849
    private int doRemoveEntity(OnlineEntity entity, DataTransaction transaction) throws DataException {
1850
        int err;
1851
        LOGGER.debug("===: REMOVE ENTITY " + entity.getEntityName());
1852
        //Eliminamos todos los cambios de esta entidad
1853
        err = ERR_CANT_REMOVE_CHANGES;
1854
        FeatureStore changesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME, true);
1855
        transaction.add(changesStore);
1856
        changesStore.edit(MODE_FULLEDIT);
1857
        changesStore.delete("\"" + WorkspaceChangesTable.COD_ENTITY + "\"='" + entity.getEntityCode() + "'");
1858
        changesStore.finishEditing();
1859
        FeatureStore remoteChangesStore = this.openFeatureStore(RemoteChangesTable.TABLE_NAME, true);
1860
        transaction.add(remoteChangesStore);
1861
        remoteChangesStore.edit(MODE_FULLEDIT);
1862
        remoteChangesStore.delete("\"" + RemoteChangesTable.COD_ENTITY + "\"='" + entity.getEntityCode() + "'");
1863
        remoteChangesStore.finishEditing();
1864
        err = ERR_CANT_REMOVE_ENTITY;
1865
        //Eliminamos la entidad
1866
        FeatureStore entitiesStore = openFeatureStore(EntitiesTable.TABLE_NAME, true);
1867
        transaction.add(entitiesStore);
1868
        entitiesStore.edit(MODE_FULLEDIT);
1869
        entitiesStore.delete("\"" + EntitiesTable.COD_ENTITY + "\"='" + entity.getEntityCode() + "'");
1870
        entitiesStore.finishEditing();
1871
        err = ERR_CANT_REMOVE_ENTITY;
1872
        //Eliminamos el DALFILE
1873
        FeatureStore store = this.openFeatureStore(entity);
1874
        if (store != null) {
1875
            transaction.add(store);
1876
            ResourcesStorage resourcesStorage = store.getResourcesStorage();
1877
            if (resourcesStorage != null) {
1878
                ResourcesStorage.Resource resource = resourcesStorage.getResource("dal");
1879
                if (resource != null) {
1880
                    resourcesStorage.remove(resource.getName());
1881
                }
1882
            }
1883

    
1884
            //Eliminamos el store
1885
            transaction.add(this.wsexplorer, false);
1886
            this.wsexplorer.remove(store.getParameters());
1887
        }
1888
        return err;
1889
    }
1890

    
1891
    public boolean isInStoreIgnoreChanges(FeatureStore store) {
1892
        if (this.storeIgnoreChanges == null) {
1893
            return false;
1894
        }
1895
        return this.storeIgnoreChanges.contains(store);
1896
    }
1897

    
1898
    private void addStoreIgnoreChanges(FeatureStore store) {
1899
        if (this.storeIgnoreChanges == null) {
1900
            this.storeIgnoreChanges = new HashSet<>();
1901
        }
1902
        this.storeIgnoreChanges.add(store);
1903
    }
1904

    
1905
    private void removeStoreIgnoreChanges(FeatureStore store) {
1906
        if (store == null) {
1907
            return;
1908
        }
1909
        if (this.storeIgnoreChanges == null) {
1910
            this.storeIgnoreChanges = new HashSet<>();
1911
        }
1912
        this.storeIgnoreChanges.remove(store);
1913
    }
1914

    
1915
    public boolean isCorrupt(OnlineEntity entity, FeatureStore store, int level) {
1916
        return isCorrupt(entity, store, level, true);
1917

    
1918
    }
1919

    
1920
    public boolean isCorrupt(OnlineEntity lentity, FeatureStore store, int level, boolean saveToDisk) {
1921
        int stateMask = lentity.getState();
1922
        if ((stateMask & STATE_CORRUPT) == STATE_CORRUPT) {
1923
            return true;
1924
        }
1925
        if ((stateMask & STATE_DISCONNECTED) == STATE_DISCONNECTED) {
1926
            return false;
1927
        }
1928
        boolean corrupt = false;
1929

    
1930
        FeatureType storeft = store.getDefaultFeatureTypeQuietly();
1931
        if (storeft.get(lentity.getFeatureIdFieldName()) == null) {
1932
            corrupt = true;
1933
        }
1934
        // ... mas chequeos aqui si se nos ocurren...
1935

    
1936
        if (corrupt) {
1937
            LOGGER.warn("The table or entity '" + lentity.getEntityName() + "' is corrupt. Missing requiered field '" + lentity.getFeatureIdFieldName() + "'.");
1938
            if (lentity instanceof EntityRow) {
1939
                EntityRow rowEntity = (EntityRow) lentity;
1940
                try {
1941
                    if ((stateMask & (STATE_DISCONNECTED | STATE_CORRUPT)) != (STATE_DISCONNECTED | STATE_CORRUPT)) {
1942
                        rowEntity.setState(STATE_DISCONNECTED | STATE_CORRUPT);
1943
                        if (saveToDisk) {
1944
                            rowEntity.update();
1945
                        }
1946
                    }
1947
                } catch (Throwable t) {
1948
                    if (LOGGER.isDebugEnabled()) {
1949
                        LOGGER.debug("Can't mark as disconected the entity '" + lentity.getEntityName() + "'.", t);
1950
                    } else {
1951
                        LOGGER.warn("Can't mark as disconected the entity '" + lentity.getEntityName() + "'.");
1952
                    }
1953
                }
1954
            }
1955
        }
1956
        return corrupt;
1957
    }
1958

    
1959
    public void initialize() {
1960
        VarsTable varsTable = new VarsTable();
1961
        varsTable.set(this, CONFIG_WORKSPACE_CODE_NAME, code);
1962
        varsTable.set(this, CONFIG_WORKSPACE_LABEL_NAME, label);
1963
        varsTable.set(this, CONFIG_PROJECT_NAME, project.toJson().toString());
1964
        varsTable.set(this, CONFIG_OFFLINE, BooleanUtils.toStringTrueFalse(this.offline));
1965
        varsTable.set(this, CONFIG_ENTITY_LABEL_TEMPLATE, this.entityLabelTemplate);
1966
    }
1967

    
1968
    @Override
1969
    public void setUserIdentificationRequester(OnlineUserIdentificationRequester userIdentificationRequester) {
1970
        if(this.userIdentificationRequester != null){
1971
            this.userIdentificationRequester.deleteObserver(changeUserObserver);
1972
        }
1973
        this.userIdentificationRequester = userIdentificationRequester;
1974
        this.userIdentificationRequester.addObserver(changeUserObserver);
1975
    }
1976

    
1977
    @Override
1978
    public OnlineUserIdentificationRequester getUserIdentificationRequester() {
1979
        return this.userIdentificationRequester;
1980
    }
1981

    
1982
    @Override
1983
    public List<OnlineEntity> getEntitiesWithSelectedChanges() {
1984

    
1985
        List<OnlineEntity> entities = new ArrayList<>();
1986
        WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
1987
        DisposableFeatureSetIterable changesGroupedByEntity = null;
1988
        try {
1989

    
1990
            changesGroupedByEntity = changesTable.getGroupedByEntity(this);
1991
            for (Feature fchange : changesGroupedByEntity) {
1992
                WorkspaceChangeRow change = new WorkspaceChangeRow(this, fchange);
1993
                OnlineEntity entity = change.getEntity();
1994
                if (change.isSelected()) {
1995
                    entities.add(entity);
1996
                }
1997
            }
1998
            return entities;
1999

    
2000
        } catch (Exception ex) {
2001
            return null;
2002
        } finally {
2003
            DisposeUtils.dispose(changesGroupedByEntity);
2004
        }
2005
    }
2006

    
2007
    @Override
2008
    public List<String> getDataModels() {
2009
        List<OnlineEntity> entities = getWorkspaceEntities();
2010
        Set<String> dataModels = new HashSet();
2011
        for (OnlineEntity entity : entities) {
2012
            List<String> dataModelsEntity = entity.getDataModelsAsList();
2013
            dataModels.addAll(dataModelsEntity);
2014
        }
2015
        ArrayList<String> list = new ArrayList<>(dataModels);
2016
        Collections.sort(list);
2017
        return list;
2018
    }
2019

    
2020
    @Override
2021
    public void logout() {
2022
        VarsTable varsTable = new VarsTable();
2023
        varsTable.delete(this, "USER");
2024
        varsTable.delete(this, "AUTHENTICATIONTOKEN");
2025
        this.currentUserCodes = null;
2026
        this.authenticationTokens = null;
2027
    }
2028

    
2029
    @Override
2030
    public void registerDataModelRepository(String modelName) {
2031
//        DataManager dataManager = DALLocator.getDataManager();
2032
//        String modelResources = null;
2033
//        BaseStoresRepository modelRepository = null;
2034
//        for (OnlineEntity entity : this.getWorkspaceEntities()) {
2035
//            List<String> entityModels = entity.getDataModelsAsList();
2036
//            if( entityModels.contains(modelName) ) {
2037
//                if( modelRepository == null ) {
2038
//                    modelRepository = new BaseStoresRepository(modelName, this.getLabel(), this.getExplorerParameters());
2039
//                    modelRepository.setProperty(STORESREPOSITORY_ONLINE_MODEL, true);
2040
//                    modelRepository.setProperty(STORESREPOSITORY_ONLINE_LOCALURL, this.getExplorerParameters().getUrl());
2041
//                }
2042
//                OpenDataStoreParameters params = this.createOpenStoreParameters(entity.getEntityName());
2043
//                if( modelResources==null && StringUtils.isNotBlank(entity.getResources()) ) {
2044
//                    modelResources = entity.getResources();
2045
//                }
2046
//                modelRepository.add(entity.getEntityName(), params, entity.getLabelOrName());
2047
//            }
2048
//        }
2049
//        if( modelRepository == null ) {
2050
//            return;
2051
//        }
2052
//        StoresRepository globalRepository = dataManager.getStoresRepository();
2053
//        globalRepository.addRepository(modelRepository);
2054
//
2055
//        if (StringUtils.isNotBlank(modelResources)) {
2056
//            try {
2057
//                JDBCServerExplorer explorer = this.getExplorer();
2058
//                explorer.setCustomResources(modelName, modelResources, true);
2059
//            } catch (Throwable t) {
2060
//                LOGGER.warn("Can't register resources for model '" + modelName + "' (resources=" + modelResources + ").", t);
2061
//            }
2062
//        }
2063
//        callModel(modelName, "onConnect", null);
2064
    }
2065

    
2066
//    private Object callModel(String modelName, String functionName, Object[] args) {
2067
//        ResourcesStorage modelResourcesStorge = null;
2068
//        try {
2069
//            JDBCServerExplorer explorer = this.getExplorer();
2070
//            modelResourcesStorge = explorer.getResourcesStorage(modelName);
2071
//            Script script = ExpressionUtils.getScript(modelResourcesStorge,"modelsc");
2072
//            if( script!=null ) {
2073
//                return script.invokeFunction(functionName, args);
2074
//            }
2075
//        } catch(Throwable t) {
2076
//            LOGGER.warn("Can't call '"+functionName+"' for model '"+modelName+"'.", t);
2077
//        } finally {
2078
//            DisposeUtils.disposeQuietly(modelResourcesStorge);
2079
//        }
2080
//        return null;
2081
//    }
2082
    @Override
2083
    public boolean isOffline() {
2084
        return this.offline;
2085
    }
2086

    
2087
    @Override
2088
    public void setOffline(boolean offline) {
2089
        this.offline = offline;
2090
    }
2091

    
2092
    @Override
2093
    public String getEntityLabelTemplate() {
2094
        return this.entityLabelTemplate;
2095
    }
2096

    
2097
    @Override
2098
    public String formatEntityLabel(OnlineEntity entity) {
2099
        String s = OnlineUtils.getFormatedLabel(entity, this.entityLabelTemplate);
2100
        if (s == null) {
2101
            s = entity.getLabelOrName();
2102
        }
2103
        return s;
2104
    }
2105

    
2106
    @Override
2107
    public void setConfigValue(String name, String value) {
2108
        VarsTable varsTable = new VarsTable();
2109
        varsTable.set(this, name, value);
2110
    }
2111

    
2112
    @Override
2113
    public OnlineChanges<OnlineRemoteChange> getRemoteChanges() {
2114
        return getRemoteChangesByEntity((String) null);
2115
    }
2116

    
2117
    @Override
2118
    public OnlineChanges<OnlineRemoteChange> getRemoteChangesByEntity(String... entityNames) {
2119
        String[] entityCodes = null;
2120
        if (ArrayUtils.isNotEmpty(entityNames)) {
2121
            entityCodes = new String[entityNames.length];
2122
            for (int i = 0; i < entityNames.length; i++) {
2123
                String entityName = entityNames[i];
2124
                if (StringUtils.isBlank(entityName)) {
2125
                    entityCodes[i] = null;
2126
                } else {
2127
                    OnlineEntity entity = this.getWorkspaceEntity(entityName);
2128
                    if (entity == null) {
2129
                        return null;
2130
                    }
2131
                    entityCodes[i] = entity.getEntityCode();
2132
                }
2133
            }
2134
        }
2135
        RemoteChangesTable changesTable = new RemoteChangesTable();
2136
        GetItemWithSize64<Feature> changesByEntity = changesTable.getByEntityCode(this, entityCodes);
2137
        ChangesImpl changes = new ChangesImpl<RemoteChangeRow>(changesByEntity, RemoteChangesTable.SELECTED) {
2138
            @Override
2139
            protected RemoteChangeRow createChange(Feature f) {
2140
                return new RemoteChangeRow(OnlineWorkspaceImpl.this, f);
2141
            }
2142

    
2143
            @Override
2144
            protected void updateChange(FeatureStore store, RemoteChangeRow change) {
2145
                change.update(store);
2146
            }
2147

    
2148
        };
2149
        DisposeUtils.disposeQuietly(changesByEntity);
2150
        return changes;
2151
    }
2152

    
2153
    private Timestamp now() {
2154
        return Timestamp.from(LocalDateTime.now().toInstant(ZoneOffset.UTC));
2155
    }
2156

    
2157
    @Override
2158
    public OnlineEntity getEntity(String entityName) {
2159
        OnlineEntity entity = this.getWorkspaceEntity(entityName);
2160
        return entity;
2161
    }
2162

    
2163
    @Override
2164
    public OpenDataStoreParameters createOpenStoreParameters(String tableName) {
2165
        try {
2166
            JDBCStoreParameters params = this.wsexplorer.get(tableName);
2167
            return params;
2168
        } catch (Exception ex) {
2169
            LOGGER.trace("can't create open store parameters from '" + this.getMessageLabel() + "'.", ex);
2170
            return null;
2171
        }
2172
    }
2173

    
2174
    @Override
2175
    protected void doDispose() {
2176
        this.disposed = true;
2177
        for (Map.Entry<String, FeatureStore> entry : storesCache.entrySet()) {
2178
            FeatureStore store = entry.getValue();
2179
            if (store != null) {
2180
                DisposeUtils.disposeQuietly(store);
2181
            }
2182
        }
2183
        this.storesCache.clear();
2184
        DisposeUtils.dispose(this.wsexplorer);
2185
        this.wsexplorer = null;
2186
        this.code = null;
2187
        DisposeUtils.disposeQuietly(project);
2188
    }
2189

    
2190
    @Override
2191
    public int revert(String nameOrCode) {
2192
        return revert(nameOrCode, null);
2193
    }
2194

    
2195
    @Override
2196
    public int revert(String nameOrCode, SimpleTaskStatus status) {
2197
        if (status == null) {
2198
            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus("Commit");
2199
            status.setAutoremove(true);
2200
            status.add();
2201
        } else {
2202
            status.push();
2203
        }
2204

    
2205
        FeatureStore entitiesStore = null;
2206
        FeatureStore userStore = null;
2207

    
2208
        DataTransaction transaction = null;
2209
        try {
2210
            LOGGER.debug("===: REVERT " + this.getCode() + ", " + this.getLabel() + ", " + nameOrCode);
2211
            transaction = DALLocator.getDataManager().createTransaction();
2212
            transaction.begin();
2213

    
2214
            status.message("Reverting");
2215

    
2216
            EntityRow entity = this.getWorkspaceEntity(nameOrCode);
2217
            WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
2218
            FeatureStore changesStore = null;
2219
//            String dataCodeFieldName = entity.getFeatureIdFieldName();
2220

    
2221
            DisposableFeatureSetIterable changes = changesTable.getSelectedsByEntityCodeAsIterator(this, entity.getEntityCode());
2222
            transaction.add(changes);
2223

    
2224
            if (!changes.isEmpty()) {
2225
                userStore = this.openFeatureStore(entity);
2226
                this.addStoreIgnoreChanges(userStore);
2227
                transaction.add(userStore, userStore.getName(), true);
2228
                userStore.edit(MODE_PASS_THROUGH);
2229
                changesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME, true);
2230
                transaction.add(changesStore, WorkspaceChangesTable.TABLE_NAME, true);
2231
                changesStore.edit(MODE_PASS_THROUGH);
2232

    
2233
                status.setCurValue(0);
2234
                status.setRangeOfValues(0, changes.size64());
2235
                for (Feature feature : changes) {
2236
                    WorkspaceChangeRow change = new WorkspaceChangeRow(this, feature);
2237
                    change.revert(userStore, changesStore);
2238
                    status.incrementCurrentValue();
2239
                }
2240
                userStore.finishEditing();
2241
                changesStore.finishEditing();
2242
            }
2243

    
2244
            entitiesStore = this.openFeatureStore(EntitiesTable.TABLE_NAME, true);
2245
            transaction.add(entitiesStore, EntitiesTable.TABLE_NAME);
2246
            entitiesStore.edit(FeatureStore.MODE_FULLEDIT);
2247
            entity.updateState(transaction);
2248
            entity.update(entitiesStore);
2249
            entitiesStore.finishEditing();
2250

    
2251
            transaction.commit();
2252
            forceReloadWorkspaceEntities();
2253

    
2254
            status.message("Revert completed");
2255
            status.terminate();
2256
            return ERR_NO_ERROR;
2257
        } catch (Exception ex) {
2258
            LOGGER.warn("Can't revert changes.", ex);
2259
            status.message("Can't revert changes");
2260
            status.abort();
2261
            return ERR_CANT_REVERT;
2262
        } finally {
2263
            this.removeStoreIgnoreChanges(userStore);
2264
            DisposeUtils.disposeQuietly(transaction);
2265
            status.pop();
2266
        }
2267
    }
2268

    
2269
    @Override
2270
    public void download_config(boolean overwrite, SimpleTaskStatus status) {
2271
        OnlineLayer config = this.project.getConfigTable(status);
2272
        if( config == null ) {
2273
            return;
2274
        }
2275
        I18nManager i18n = ToolsLocator.getI18nManager();
2276
        if (status == null) {
2277
            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(i18n.getTranslation("_Download_configuration_data"));
2278
            status.setAutoremove(true);
2279
            status.add();
2280
        } else {
2281
            status.push();
2282
        }
2283
        FileInputStream fis = null;
2284
        FeatureStore store = null;
2285
        try {
2286
            status.message(i18n.getTranslation("_Download_configuration_data"));
2287
            status.setIndeterminate();
2288
            OnlineSiteImpl site = this.project.getSite();
2289
            OnlineDownloader downloader = site.getDownloader();            
2290
            File f = downloader.get(site.getURL("/api/v1/layers/" + config.getId() + "/featurelist?max=1000"));
2291
            fis = new FileInputStream(f);
2292
            JsonObject json = Json.createObject(fis);
2293
            if (json.containsKey("content")) {
2294
                json = json.getJsonObject("content");
2295
            }
2296
            int totalfeatures = json.getInt("totalfeatures", -1);
2297
            int lendata = json.getInt("lendata", -1);
2298
            JsonArray features = json.getJsonArray("features");
2299
            DataManager dataManager = DALLocator.getDataManager();
2300
            DatabaseWorkspaceManager dbworkspace = dataManager.getDatabaseWorkspace(this.wsexplorer.getParameters());
2301
            store = dbworkspace.getTable(DatabaseWorkspaceManager.TABLE_CONFIGURATION);
2302
            store.edit(FeatureStore.MODE_FULLEDIT);
2303
            status.setRangeOfValues(0, features.size());
2304
            status.setCurValue(0);
2305
            for (JsonValue feature0_json : features) {
2306
                status.incrementCurrentValue();
2307
                JsonObject feature_json = (JsonObject) feature0_json;
2308
                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
2309
                String filter = builder.eq(
2310
                        builder.column(FIELD_CONFIGURATION_NAME),
2311
                        builder.constant(feature_json.getString(FIELD_CONFIGURATION_NAME, ""))
2312
                ).toString();
2313
                Feature feature = store.findFirst(filter);
2314
                EditableFeature efeature;
2315
                if (feature == null) {
2316
                    efeature = store.createNewFeature(feature_json);
2317
                    store.insert(efeature);
2318
                } else if( overwrite ) {
2319
                    efeature = feature.getEditable();
2320
                    efeature.set(FIELD_CONFIGURATION_VALUE, feature_json.getString(FIELD_CONFIGURATION_VALUE, ""));
2321
                    store.update(efeature);
2322
                }
2323
            }
2324
            status.setIndeterminate();
2325
            status.message(i18n.getTranslation("_Writing_data"));
2326
            store.finishEditing();
2327
            status.terminate();
2328
        } catch (Exception ex) {
2329
            LOGGER.warn("", ex);
2330
            status.abort();
2331
            throw new RuntimeException("", ex);
2332
        } finally {
2333
            IOUtils.closeQuietly(fis);
2334
            FeatureStore.cancelEditingQuietly(store);
2335
            DisposeUtils.disposeQuietly(store);
2336
            status.pop();
2337
        }
2338
    }
2339

    
2340
    @Override
2341
    public void download_resources(boolean overwrite, SimpleTaskStatus status) {
2342
        OnlineLayer resources = this.project.getResourcesTable(status);
2343
        if( resources == null ) {
2344
            return;
2345
        }
2346
        I18nManager i18n = ToolsLocator.getI18nManager();
2347
        if (status == null) {
2348
            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(i18n.getTranslation("_Download_project_resources"));
2349
            status.setAutoremove(true);
2350
            status.add();
2351
        } else {
2352
            status.push();
2353
        }
2354
        FileInputStream fis = null;
2355
        FeatureStore store = null;
2356
        try {
2357
            status.message(i18n.getTranslation("_Download_project_resources"));
2358
            status.setIndeterminate();
2359
            OnlineSiteImpl site = this.project.getSite();
2360
            OnlineDownloader downloader = site.getDownloader();
2361
            File f = downloader.get(site.getURL("/api/v1/layers/" + resources.getId() + "/featurelist?max=1000"));
2362
            fis = new FileInputStream(f);
2363
            JsonObject json = Json.createObject(fis);
2364
            if (json.containsKey("content")) {
2365
                json = json.getJsonObject("content");
2366
            }
2367
            int totalfeatures = json.getInt("totalfeatures", -1);
2368
            int lendata = json.getInt("lendata", -1);
2369
            JsonArray features = json.getJsonArray("features");
2370
            DataManager dataManager = DALLocator.getDataManager();
2371
            DatabaseWorkspaceManager dbworkspace = dataManager.getDatabaseWorkspace(this.wsexplorer.getParameters());
2372
            store = dbworkspace.getTable(DatabaseWorkspaceManager.TABLE_RESOURCES);
2373
            store.edit(FeatureStore.MODE_FULLEDIT);
2374
            status.setRangeOfValues(0, features.size());
2375
            status.setCurValue(0);
2376
            for (JsonValue feature0_json : features) {
2377
                status.incrementCurrentValue();
2378
                JsonObject feature_json = (JsonObject) feature0_json;
2379
                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
2380
                String filter = builder.eq(
2381
                        builder.column(FIELD_RESOURCES_NAME),
2382
                        builder.constant(feature_json.getString(FIELD_RESOURCES_NAME, ""))
2383
                ).toString();
2384
                Feature feature = store.findFirst(filter);
2385
                EditableFeature efeature;
2386
                if (feature == null) {
2387
                    efeature = store.createNewFeature(feature_json);
2388
                    store.insert(efeature);
2389
                } else if( overwrite ) {
2390
                    efeature = feature.getEditable();
2391
                    efeature.set(FIELD_RESOURCES_RESOURCE, feature_json.getString(FIELD_RESOURCES_RESOURCE, ""));
2392
                    store.update(efeature);
2393
                }
2394
            }
2395
            status.setIndeterminate();
2396
            status.message(i18n.getTranslation("_Writing_data"));
2397
            store.finishEditing();
2398
            status.terminate();
2399
        } catch (Exception ex) {
2400
            LOGGER.warn("", ex);
2401
            status.abort();
2402
            throw new RuntimeException("", ex);
2403
        } finally {
2404
            IOUtils.closeQuietly(fis);
2405
            FeatureStore.cancelEditingQuietly(store);
2406
            DisposeUtils.disposeQuietly(store);
2407
            status.pop();
2408
        }
2409
    }
2410
    
2411
    @Override
2412
    public int download(String name, Envelope workingArea, SimpleTaskStatus status) {
2413
        I18nManager i18n = ToolsLocator.getI18nManager();
2414
        if (status == null) {
2415
            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(i18n.getTranslation("_Downloading_data"));
2416
            status.setAutoremove(true);
2417
            status.add();
2418
        } else {
2419
            status.push();
2420
        }
2421
        FeatureStore store = null;
2422
        try {
2423
            status.message(i18n.getTranslation("_Downloading_data"));
2424
            status.setIndeterminate();
2425
            TilesCalculator tiles = new TilesCalculator();
2426
            OnlineEntity entity = this.getEntity(name);
2427
            List<Envelope> envs = tiles.calculateEnvelopes(
2428
                    entity.getTileSize(),
2429
                    Collections.singletonList(workingArea.getGeometry())
2430
            );
2431
            OnlineLayer layer = this.project.getLayer((OnlineLayer t) -> StringUtils.equalsIgnoreCase(t.getName(), name), status);
2432
            store = this.openFeatureStore(entity);
2433
            this.addStoreIgnoreChanges(store);
2434
            store.edit(FeatureStore.MODE_APPEND, FeatureStore.SUBMODE_MERGE);
2435
            GeometryManager geomManager = GeometryLocator.getGeometryManager();
2436
            IProjection layerProjection = layer.getProjection();
2437
            String geometryName = store.getDefaultFeatureTypeQuietly().getDefaultGeometryAttributeName();
2438
            int nenv = 0;
2439
            for (Envelope env : envs) {
2440
                nenv++;
2441
                status.message(i18n.getTranslation("_Downloading_data_from_area")+" "+nenv+"/"+envs.size()+" ");
2442
                for (Iterator<JsonObject> it = layer.getData(env, 5000, status); it.hasNext();) {
2443
                    if( status.isCancellationRequested() ) {
2444
                        status.cancel();
2445
                        throw new UserCancelTaskException();
2446
                    }
2447
                    JsonObject feature_online_json = it.next();
2448
                    JsonObject feature_desktop_json = convertOnlineToDesktopFeature(feature_online_json, geometryName, geomManager, layerProjection);
2449
                    if(feature_desktop_json == null){
2450
                        continue;
2451
                    }
2452
                    EditableFeature feature = store.createNewFeature(feature_desktop_json);
2453
                    store.insert(feature);
2454
//                    
2455
//                    EditableFeature feature = null;
2456
//                    Geometry geom = null;
2457
//                    JsonObject properties = feature_json.getJsonObject("properties");
2458
//                    JsonObject geom_json = feature_json.getJsonObject("geometry");
2459
//                    if( geom_json!=null ) {
2460
//                        geom = geomManager.createFrom(geom_json);
2461
//                        if( geom.getProjection()==null ) {
2462
//                            geom.setProjection(layer.getProjection());
2463
//                        }
2464
//                    }
2465
//                    if( properties==null ) {
2466
//                        if( geom == null ) {
2467
//                            continue;
2468
//                        }
2469
//                        feature = store.createNewFeature();                        
2470
//                    } else {
2471
//                        feature = store.createNewFeature(properties);
2472
//                    }
2473
//                    feature.setDefaultGeometry(geom);
2474
//                    store.insert(feature); // Insert or replace
2475
                }
2476
            }
2477
            status.setIndeterminate();
2478
            status.message(i18n.getTranslation("_Writing_data"));
2479
            store.finishEditing();
2480
            status.terminate();
2481
        } catch(UserCancelTaskException ex) {
2482
            throw ex;
2483
        } catch (Exception ex) {
2484
            LOGGER.warn("", ex);
2485
            status.abort();
2486
            throw new RuntimeException("", ex);
2487
        } finally {
2488
            this.removeStoreIgnoreChanges(store);
2489
            FeatureStore.cancelEditingQuietly(store);
2490
            DisposeUtils.disposeQuietly(store);
2491
            status.pop();
2492
        }
2493
        return 0;
2494
    }
2495
    
2496
    private JsonObject convertOnlineToDesktopFeature(JsonObject online_json, String geometryName, GeometryManager geomManager, IProjection proj) {
2497
        Geometry geom = null;
2498
        JsonObject properties = online_json.getJsonObject("properties");
2499
        JsonObject geom_json = online_json.getJsonObject("geometry");
2500
        if (geom_json != null) {
2501
            try {
2502
                geom = geomManager.createFrom(geom_json);
2503
            } catch (Exception ex) {
2504
            }
2505
            if (geom != null && geom.getProjection() == null) {
2506
                geom.setProjection(proj);
2507
            }
2508
        }
2509
        if (properties == null) {
2510
            if (geom == null) {
2511
                return null;
2512
            }
2513
            JsonObjectBuilder builder = Json.createObjectBuilder();
2514
            builder.add(geometryName, geom.convertToHexWKBQuietly());
2515
            return builder.build();
2516
        }
2517
        JsonObjectBuilder builder = Json.createObjectBuilder();
2518
        if (geom == null) {
2519
            builder.addNull(geometryName);
2520
        } else {
2521
            builder.add(geometryName, geom.convertToHexWKBQuietly());
2522
        }
2523
        builder.addAll(properties);
2524
        return builder.build();
2525
    }
2526

    
2527
    public String getCurrentUserCode() {
2528
        return getCurrentUserCode(null);
2529
    }
2530
    
2531
    private String getCurrentUserCode(OnlineProject project) {
2532
        // Esto es por si acabamos cargando en un workspace 
2533
        // tablas de mas de un proyecto del mismo site.
2534
        if( this.currentUserCodes == null ) {
2535
            return null;
2536
        }
2537
        int projectid;
2538
        if( project == null ) {
2539
            projectid = this.getProject().getId();
2540
        } else {
2541
            projectid = project.getId();
2542
        }
2543
        return this.currentUserCodes.get(projectid);
2544
    }
2545
    
2546
    private void setCurrentUserCode(OnlineProject project, String userCode) {
2547
        int projectid;
2548
        if( project == null ) {
2549
            projectid = this.getProject().getId();
2550
        } else {
2551
            projectid = project.getId();
2552
        }
2553
        if( this.currentUserCodes == null ) {
2554
            this.currentUserCodes = new HashMap<>();
2555
        }
2556
        this.currentUserCodes.put(projectid, userCode);
2557
    }
2558

    
2559
    private String getAuthenticationToken(OnlineProject project) {
2560
        if( this.authenticationTokens == null ) {
2561
            return null;
2562
        }
2563
        int projectid;
2564
        if( project == null ) {
2565
            projectid = this.getProject().getId();
2566
        } else {
2567
            projectid = project.getId();
2568
        }
2569
        return this.authenticationTokens.get(projectid);
2570
    }
2571
    
2572
    private void setAuthenticationToken(OnlineProject project, String token) {
2573
        int projectid;
2574
        if( project == null ) {
2575
            projectid = this.getProject().getId();
2576
        } else {
2577
            projectid = project.getId();
2578
        }
2579
        if( this.authenticationTokens == null ) {
2580
            this.authenticationTokens = new HashMap<>();
2581
        }
2582
        this.authenticationTokens.put(projectid, token);
2583
    }
2584
   
2585
    public boolean isAResourceTable(String entityName) {
2586
        // Deberia comprobar gvsigd_resources?
2587
        return false;
2588
    }
2589

    
2590
    @Override
2591
    public boolean canUpload() {
2592
        return canUpload(null);
2593
    }
2594

    
2595
    @Override
2596
    public boolean canUpload(MutableObject<String> message) {
2597
        return canUpload(message, null);
2598
    }
2599

    
2600
    @Override
2601
    public boolean canUpload(MutableObject<String> message, List<String> entityCodes) {
2602
        if( this.isOffline() ) {
2603
            return false;
2604
        }
2605
        I18nManager i18n = ToolsLocator.getI18nManager();
2606
        if (entityCodes == null) {
2607
            LOGGER.warn("entityCodes is null");
2608
            if (message != null) {
2609
                String msg = i18n.getTranslation("_It_is_mandatory_to_indicate_the_tables_on_which_you_want_to_commit");
2610
                message.setValue(msg);
2611
            }
2612
            return false;
2613
        }
2614
        return true;
2615
//        List<String> outdatedEntityNames = new ArrayList<>();
2616
//        WorkspaceChangesTable changesTable = new WorkspaceChangesTable();
2617
//        DisposableFeatureSetIterable changesGroupedByEntity = null;
2618
//        try {
2619
//            Set<String> repositoryCodes = new HashSet<>();
2620
//            repositoryCodes.add(this.getRepository().getCode());
2621
//            for (String entityCode : entityCodes) {
2622
//                EntityRow entity = this.getWorkspaceEntity(entityCode);
2623
//                int maskStatus = entity.getState();
2624
//                if( (maskStatus & STATE_CORRUPT) == STATE_CORRUPT ) {
2625
//                    LOGGER.info("Entity '"+entity.getEntityName()+"' marked as corrupt.");
2626
//                    if (message != null) {
2627
//                        String msg = i18n.getTranslation("_Some_of_the_selected_tables_are_marked_as_corrupt")
2628
//                                + ". (" + entity.getEntityName() + ")";
2629
//                        message.setValue(msg);
2630
//                    }
2631
//                    return false;
2632
//                }
2633
//                if( (maskStatus & STATE_DISCONNECTED) == STATE_DISCONNECTED ) {
2634
//                    LOGGER.info("Entity '"+entity.getEntityName()+"' marked as disconnected.");
2635
//                    if (message != null) {
2636
//                        String msg = i18n.getTranslation("_Some_of_the_selected_tables_are_marked_as_disconnected")
2637
//                                + ". (" + entity.getEntityName() + ")";
2638
//                        message.setValue(msg);
2639
//                    }
2640
//                    return false;
2641
//                }
2642
//                if( entity.isLinkedTable() ) {
2643
//                    VCSGisRepository linkedRepo = entity.getLinkedRepository();
2644
//                    if( linkedRepo!=null ) {
2645
//                        repositoryCodes.add(linkedRepo.getCode());
2646
//                    }
2647
//                }
2648
//            }
2649
//            if( repositoryCodes.size() > 1 ) {
2650
//                LOGGER.info("Too many repositories, only one allowed in commit.");
2651
//                if (message != null) {
2652
//                    String msg = i18n.getTranslation("_Too_many_repositories_only_one_allowed");
2653
//                    message.setValue(msg);
2654
//                }
2655
//                return false;
2656
//            }
2657
//            changesGroupedByEntity = changesTable.getGroupedByEntity(this);
2658
//            for (Feature fchange : changesGroupedByEntity) {
2659
//                WorkspaceChangeRow change = new WorkspaceChangeRow(this, fchange);
2660
//                VCSGisWorkspaceEntity entity = change.getEntity();
2661
//                if(entity == null){
2662
//                    continue;
2663
//                }
2664
//                if(CollectionUtils.isEmpty(entityCodes) || entityCodes.contains(entity.getEntityCode())){
2665
//                    LOGGER.debug("===: CAN-COMMIT: add used entity = " + fchange.toJson().toString().replace('\n', ' '));
2666
//                    if (change.isSelected() && entity.isOutdated()) {
2667
//                        outdatedEntityNames.add(entity.getEntityName());
2668
//                    }
2669
//                }
2670
//                
2671
//                switch (this.getEditMode(entity)) {
2672
//                    case FeatureStore.MODE_UNKNOWN:
2673
//                        // La tabla no existe en la copia de trabajo
2674
//                        LOGGER.info("Can access to '"+entity.getEntityName()+"'.");
2675
//                        if (message != null) {
2676
//                            String msg = i18n.getTranslation("_Some_of_the_selected_tables_are_damaged")
2677
//                                    + ". ("
2678
//                                    + entity.getEntityName()
2679
//                                    + ")";
2680
//                            message.setValue(msg);
2681
//                        }
2682
//                        return false;
2683
//                    case FeatureStore.MODE_FULLEDIT:
2684
//                    case FeatureStore.MODE_APPEND:
2685
//                    case FeatureStore.MODE_PASS_THROUGH:
2686
//                        LOGGER.info("Table '"+entity.getEntityName()+"' is editing.");
2687
//                        if (message != null) {
2688
//                            String msg = i18n.getTranslation("_Selected_tables_are_editing")
2689
//                                    + ". ("
2690
//                                    + entity.getEntityName()
2691
//                                    + ")";
2692
//                            message.setValue(msg);
2693
//                        }
2694
//
2695
//                        return false;
2696
//                }
2697
//
2698
//            }
2699
//            DisposeUtils.dispose(changesGroupedByEntity);
2700
//
2701
//            if (!outdatedEntityNames.isEmpty()) {
2702
//                LOGGER.info("Somme tables in working copy are outdated ("+StringUtils.join(outdatedEntityNames, ",")+").");
2703
//                if (message != null) {
2704
//                    String msg = i18n.getTranslation("_Tables_in_working_copy_are_outdated")
2705
//                            + ". ("
2706
//                            + StringUtils.join(outdatedEntityNames, ", ")
2707
//                            + ")";
2708
//                    message.setValue(msg);
2709
//                }
2710
//                return false;
2711
//            }
2712
//            return true;
2713
//        } catch (Exception ex) {
2714
//            LOGGER.warn("You cannot check that a commit can be done", ex);
2715
//            if (message != null) {
2716
//                String msg = i18n.getTranslation("_You_cannot_check_that_a_commit_can_be_done");
2717
//                message.setValue(msg);
2718
//            }
2719
//            return false;
2720
//        } finally {
2721
//            DisposeUtils.dispose(changesGroupedByEntity);
2722
//        }
2723
    }
2724

    
2725
    @Override
2726
    public int removeRemoteChanges(String entityCode) {
2727
        return removeRemoteChanges(entityCode, null);
2728
    }
2729

    
2730
    @Override
2731
    public int removeRemoteChanges(String entityCode, SimpleTaskStatus status) {
2732
        OnlineEntity entity = this.getEntity(entityCode);
2733
        if (entity == null) {
2734
            return ERR_ENTITY_NOT_EXISTS;
2735
        }
2736
        if (status == null) {
2737
            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(
2738
                    "Update-clean " + entity.getEntityName()
2739
            );
2740
            status.setAutoremove(true);
2741
            status.add();
2742
        } else {
2743
            status.push();
2744
        }
2745
        FeatureStore remoteChangesStore = null;
2746
        FeatureStore localChangesStore = null;
2747
        FeatureStore entitiesStore = null;
2748
        try {
2749
            status.message("Deleting temporary remote changes (" + entity.getEntityName() + ")");
2750
            remoteChangesStore = openFeatureStore(RemoteChangesTable.TABLE_NAME, false);
2751
            localChangesStore = openFeatureStore(WorkspaceChangesTable.TABLE_NAME, false);
2752
            entitiesStore = openFeatureStore(EntitiesTable.TABLE_NAME, false);
2753
            removeRemoteChanges(remoteChangesStore, localChangesStore, entitiesStore, (EntityRow)entity);
2754
            status.message("Delete temporary remote changes completed");
2755
            status.terminate();
2756
            return ERR_OK;
2757
        } catch (Exception ex) {
2758
            LOGGER.warn("Can't deleting temporary remote changes of " + entity.getEntityName() + ".", ex);
2759
            status.message("Can't deleting temporary remote changes of " + entity.getEntityName() + ".");
2760
            status.abort();
2761
            return ERR_CANT_UPDATE_CLEAN;
2762
        } finally {
2763
            status.pop();
2764
            DisposeUtils.dispose(remoteChangesStore);
2765
            DisposeUtils.dispose(localChangesStore);
2766
            DisposeUtils.dispose(entitiesStore);
2767
        }
2768

    
2769
    }
2770
    
2771
    private void removeRemoteChanges(FeatureStore remoteChangesStore, FeatureStore localChangesStore, FeatureStore entitiesStore, EntityRow entity) throws DataException {
2772
        RemoteChangesTable remoteChangesTable = new RemoteChangesTable();
2773
        remoteChangesTable.delete(remoteChangesStore, entity.getEntityCode());
2774
        WorkspaceChangesTable localChangesTable = new WorkspaceChangesTable();
2775
        localChangesTable.changeState(localChangesStore, entity.getEntityCode(), STATE_CONFLICT, STATE_LOCAL_UNMODIFIED);
2776
        if(entity.getState() == STATE_CONFLICT){
2777
            entity.setState(STATE_LOCAL_MODIFIED);
2778
            boolean needFinish = false;
2779
            if(entitiesStore.getMode() == FeatureStore.MODE_QUERY){
2780
                entitiesStore.edit(MODE_PASS_THROUGH);
2781
                needFinish = true;
2782
            }
2783
            entity.update(entitiesStore);
2784
            if(needFinish){
2785
                entitiesStore.finishEditing();
2786
            }
2787
        }
2788
    }
2789

    
2790
    @Override
2791
    public boolean canUpdate(MutableObject<String> message, String tableName) {
2792
        if( this.isOffline() ) {
2793
            return false;
2794
        }
2795
        I18nManager i18n = ToolsLocator.getI18nManager();
2796
        EntityRow lentity = this.getWorkspaceEntityByName(tableName);
2797
        if (lentity == null) {
2798
            if (message != null) {
2799
                String msg = i18n.getTranslation("_Cant_update_table_XtablaX_dont_exists",new String[] {tableName});
2800
                message.setValue(msg);
2801
            }
2802
            return false;
2803
        }
2804
        switch (this.getEditMode(lentity)) {
2805
            case FeatureStore.MODE_UNKNOWN:
2806
                // La tabla no existe en la copia de trabajo
2807
                if (message != null) {
2808
                    String msg = i18n.getTranslation("_Cant_update_table_XtablaX_dont_exists",new String[] {tableName});
2809
                    message.setValue(msg);
2810
                }
2811
                return false;
2812

    
2813
            case FeatureStore.MODE_FULLEDIT:
2814
            case FeatureStore.MODE_APPEND:
2815
            case FeatureStore.MODE_PASS_THROUGH:
2816
                LOGGER.info("Table '"+lentity.getEntityName()+"' is editing.");
2817
                if (message != null) {
2818
                    String msg = i18n.getTranslation("_Cant_update_table_XtablaX_are_in_edition",new String[] {tableName});
2819
                    message.setValue(msg);
2820
                }
2821
                return false;
2822
        }
2823
        return true;
2824
    }
2825
    
2826
    private int getEditMode(OnlineEntity entity) {
2827
        FeatureStore userStore = null;
2828
        try {
2829
            userStore = this.openFeatureStore(entity);
2830
            if( userStore == null ) {
2831
                return FeatureStore.MODE_UNKNOWN;
2832
            }
2833
            return FeatureStoreObserver.getEditMode(userStore);
2834
        } finally {
2835
            DisposeUtils.dispose(userStore);
2836
        }
2837

    
2838
    }
2839
    
2840

    
2841
    @Override
2842
    public boolean updateNeedMerge(String entityName) {
2843
        if( this.isOffline() ) {
2844
            return false;
2845
        }
2846
        RemoteChangesTable remoteChangesTable = new RemoteChangesTable();
2847

    
2848
        OnlineEntity entity = this.getWorkspaceEntity(entityName);
2849

    
2850
        return remoteChangesTable.updateNeedMerge(this, entity.getEntityCode());
2851
    }
2852

    
2853
    @Override
2854
    public int update(String tableName, SimpleTaskStatus status) {
2855
        return this.update(tableName, false, status);
2856
    }
2857
    
2858
    public int update(String tableName, boolean merge, SimpleTaskStatus status) {
2859
        return update(tableName, merge, null, status);
2860
    }
2861

    
2862
    public int update(String tableName, boolean merge, MutableLong localChangesModifieds, SimpleTaskStatus status) {
2863
        if( this.isOffline() ) {
2864
            return ERR_OFFLINE;
2865
        }
2866
        if(localChangesModifieds == null){
2867
            localChangesModifieds = new MutableLong(0);
2868
        }
2869
        int errcode = ERR_NO_ERROR;
2870
        if (status == null) {
2871
            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(
2872
                    "Update " + tableName
2873
            );
2874
            status.setAutoremove(true);
2875
            status.add();
2876
        } else {
2877
            status.push();
2878
        }
2879
        LOGGER.debug("===: UPDATE " + this.getCode() + ", '" + this.getLabel() + "', " + tableName);
2880
        RemoteChangesTable remoteChangesTable = new RemoteChangesTable();
2881
        WorkspaceChangesTable localChangesTable = new WorkspaceChangesTable();
2882

    
2883
        EditableFeature ef;
2884
        DisposableFeatureSetIterable remoteChanges = null;
2885

    
2886
        FeatureStore workspaceChangesStore = null;
2887
        FeatureStore remoteChangesStore = null;
2888
        FeatureStore userStore = null;
2889
        FeatureStore entitiesStore = null;
2890

    
2891
        DataTransaction transaction = null;
2892
        try {
2893
            if( status.isCancellationRequested() ) {
2894
                throw new UserCancelTaskException();
2895
            }
2896
            transaction = DALLocator.getDataManager().createTransaction();
2897
            transaction.begin();
2898

    
2899
            entitiesStore = this.openFeatureStore(EntitiesTable.TABLE_NAME, true);
2900
            transaction.add(entitiesStore);
2901
            entitiesStore.edit(MODE_PASS_THROUGH);
2902

    
2903
            EntityRow lentity = (EntityRow) this.getWorkspaceEntityByName(tableName);
2904
            if (!merge) {
2905
                status.message("Checking the need to merge");
2906
                if (remoteChangesTable.updateNeedMerge(this, lentity.getEntityCode())) {
2907
                    status.message("Can't update without merge.");
2908
                    status.abort();
2909
                    return ERR_UPDATE_NEED_MERGE;
2910
                }
2911
            }
2912
            String dataCodeFieldName = lentity.getFeatureIdFieldName();
2913
            status.message("Searching remote changes (" + lentity.getEntityName() + ")");
2914

    
2915
            userStore = this.openFeatureStore(lentity);
2916
            if (userStore == null) {
2917
                this.create_table(lentity);
2918
                userStore = this.openFeatureStore(lentity);
2919
            }
2920
            transaction.add(userStore);
2921
            userStore.edit(MODE_PASS_THROUGH);
2922

    
2923
            workspaceChangesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME,true);
2924
            transaction.add(workspaceChangesStore);
2925
            workspaceChangesStore.edit(MODE_PASS_THROUGH);
2926

    
2927
            remoteChangesStore = this.openFeatureStore(RemoteChangesTable.TABLE_NAME,true);
2928
            transaction.add(remoteChangesStore);
2929
            remoteChangesStore.edit(MODE_PASS_THROUGH);
2930

    
2931
            remoteChanges = remoteChangesTable.getSelectedsByEntityCodeAsIterator(this, lentity.getCode());
2932
            transaction.add(remoteChanges);
2933
//            long sz = ContainerUtils.size64(remoteChanges);
2934
//            status.setRangeOfValues(0, sz);
2935
            status.setCurValue(0);
2936

    
2937
            if (!remoteChanges.isEmpty()) {
2938
                this.addStoreIgnoreChanges(userStore);
2939
                status.message("Updating table (" + lentity.getEntityName() + ")");
2940
                FeatureType type = userStore.getDefaultFeatureTypeQuietly();
2941

    
2942
                ExpressionEvaluatorManager expManager = ExpressionEvaluatorLocator.getManager();
2943
                ExpressionBuilder builder = expManager.createExpressionBuilder();
2944
                
2945
                JsonObject dataJson;
2946
                FilteredLogger log = new FilteredLogger(LOGGER, "update", 100);
2947

    
2948
                for (Feature feature : remoteChanges) {
2949
                    if( status.isCancellationRequested() ) {
2950
                        throw new UserCancelTaskException();
2951
                    }
2952
                    RemoteChangeRow remoteChangeRow = new RemoteChangeRow(this, feature);
2953
                    switch (remoteChangeRow.getOperation()) {
2954
                        case OP_INSERT:
2955
                            LOGGER.debug("===: UPDATE: insert");
2956
                            switch (remoteChangeRow.getStatus()) {
2957
                                case STATE_CONFLICT:
2958
                                    DisposableFeatureSetIterable feats = getUserFeaturesConflictWithRemoteChange(lentity, remoteChangeRow, userStore);
2959
                                    if (feats != null) {
2960
                                        for (Feature feat : feats) {
2961
                                            if (feat != null) {
2962
                                                feats.getFeatureSet().delete(feat);
2963
                                                workspaceChangesStore.delete("\"" + WorkspaceChangesTable.FEATUREID + "\" = '" + feat.getString(lentity.getFeatureIdFieldName()) + "'");
2964
                                            }
2965
                                        }
2966
                                        DisposeUtils.disposeQuietly(feats);
2967
                                    }
2968
                                //Don't break to do the default case
2969
                                default:
2970
                                    dataJson = remoteChangeRow.getRelatedFeatureDataAsJson();
2971
                                    if( dataJson==null ) {
2972
                                        log.warn("A feature (VCSGISCODE="+remoteChangeRow.getRelatedFeatureCode()+") has been retrieved with the DAT_DATA field set to null.");
2973
                                    } else {
2974
                                        ef = userStore.createNewFeature(dataJson);
2975
                                        userStore.insert(ef);
2976
                                    }
2977
                                    break;
2978
                            }
2979
                            break;
2980
                        case OP_UPDATE:
2981
                            LOGGER.debug("===: UPDATE: update");
2982
                            dataJson = remoteChangeRow.getRelatedFeatureDataAsJson();
2983
                            if( dataJson==null ) {
2984
                                log.warn("A feature (VCSGISCODE="+remoteChangeRow.getRelatedFeatureCode()+") has been retrieved with the DAT_DATA field set to null.");
2985
                                break;
2986
                            }
2987
                            builder.set(null);
2988
                            for (FeatureAttributeDescriptor attr : type.getPrimaryKey()) {
2989
                                builder.and(
2990
                                        builder.eq(
2991
                                                builder.column(attr.getName()),
2992
                                                builder.constant(
2993
                                                        Json.toObject(dataJson.get(attr.getName()))
2994
                                                )
2995
                                        )
2996
                                );
2997
                            }
2998
                            Feature f = userStore.findFirst(builder.toString());
2999
                            if (f == null) {
3000
                                WorkspaceChangeRow localChange = localChangesTable.find(this, workspaceChangesStore, lentity.getEntityCode(), remoteChangeRow.getRelatedFeatureCode());
3001
                                if(localChange != null){
3002
                                    if(localChange.getOperation() == OP_DELETE){
3003
                                        ef = userStore.createNewFeature(dataJson);
3004
                                        userStore.insert(ef);
3005
                                        break;
3006
                                    } else {
3007
                                        throw new NullPointerException("Can't update null feature (" + builder.toString() + ").");
3008
                                    }
3009
                                } else { //Si me llega un update y no tengo la feature en el userStore ni tengo borrado local, es porque deberia ser un insert
3010
                                    //NOTA: !OJO! esto es porque algunas bases de datos nos devuelven un update en lugar de un insert
3011
                                    ef = userStore.createNewFeature(dataJson);
3012
                                    userStore.insert(ef);
3013
                                    break;
3014
                                }
3015
                            }
3016
                            ef = f.getEditable();
3017
                            ef.copyFrom(dataJson);
3018
                            if( removeMissingValues(ef,dataJson) ) {
3019
                                // kill the admin
3020
                                log.warn("Missing attributes in the json (kill the admin?)");
3021
                            }
3022
                            userStore.update(ef);
3023
//                            workspaceChangesStore.delete("\"" + WorkspaceChangesTable.FEATUREID + "\"='" + remoteChangeRow.getRelatedFeatureCode()+ "'");
3024
                            break;
3025

    
3026
                        case OP_DELETE:
3027
                            LOGGER.debug("===: UPDATE: delete");
3028
                            userStore.delete("\"" + dataCodeFieldName + "\"='" + remoteChangeRow.getRelatedFeatureCode() + "'");
3029
//                            workspaceChangesStore.delete("\"" + WorkspaceChangesTable.FEATUREID + "\"='" + remoteChangeRow.getRelatedFeatureCode()+ "'");
3030
                            break;
3031
                    }
3032
                    status.incrementCurrentValue();
3033
                }
3034
            }
3035

    
3036
            if (merge) {
3037
                status.message("Searching local changes to merge (" + lentity.getEntityName() + ")");
3038
                DisposeUtils.disposeQuietly(remoteChanges);
3039
                remoteChanges = remoteChangesTable.getNotSelectedsByEntityCodeAsIterator(this, lentity.getCode());
3040
                transaction.add(remoteChanges);
3041
//                sz = ContainerUtils.size64(remoteChanges);
3042
//                status.setRangeOfValues(0, sz);
3043
                status.setCurValue(0);
3044

    
3045
                if (!remoteChanges.isEmpty()) {
3046
                    //We edit the changesStore because we will need local changes 
3047
//                    workspaceChangesStore.edit(MODE_PASS_THROUGH);
3048

    
3049
                    //We edit the destination store because we may need to update some features (in case OP_DELETE)
3050
//                    userStore.edit(MODE_PASS_THROUGH);
3051
                    this.addStoreIgnoreChanges(userStore);
3052

    
3053
                    status.message("Adding to local changes (" + lentity.getEntityName() + ")");
3054
                    for (Feature feature : remoteChanges) {
3055
                        if( status.isCancellationRequested() ) {
3056
                            throw new UserCancelTaskException();
3057
                        }
3058
                        RemoteChangeRow remoteChangeRow = new RemoteChangeRow(this, feature);
3059
                        Feature f = userStore.findFirst("\"" + lentity.getFeatureIdFieldName() + "\" = " + remoteChangeRow.getRelatedFeatureCode());
3060
                        switch (remoteChangeRow.getOperation()) {
3061
                            case OP_INSERT:
3062
                                LOGGER.debug("===: UPDATE: insert");
3063
                                if (f == null) {
3064
                                    LOGGER.debug("===: UPDATE: add delete operation to RelatedFeatureCode "+remoteChangeRow.getRelatedFeatureCode());
3065
                                    this.addDeleteChange(null, lentity, workspaceChangesStore, remoteChangeRow.getRelatedFeatureCode(), null, null);
3066
                                } else {
3067
                                    LOGGER.debug("===: UPDATE: add update operation to RelatedFeatureCode "+remoteChangeRow.getRelatedFeatureCode());
3068
                                    this.addChange(null, lentity, OP_UPDATE, workspaceChangesStore, f, feature, null);
3069
                                }
3070
                                localChangesModifieds.increment();
3071
                                break;
3072

    
3073
                            case OP_UPDATE:
3074
                                LOGGER.debug("===: UPDATE: update");
3075
                                if (f == null) {
3076
                                    LOGGER.debug("===: UPDATE: add delete operation to RelatedFeatureCode "+remoteChangeRow.getRelatedFeatureCode());
3077
                                    this.addDeleteChange(null, lentity, workspaceChangesStore, remoteChangeRow.getRelatedFeatureCode(), null, null);
3078
                                } else {
3079
                                    LOGGER.debug("===: UPDATE: add update operation to RelatedFeatureCode "+remoteChangeRow.getRelatedFeatureCode());
3080
                                    this.addChange(null, lentity, OP_UPDATE, workspaceChangesStore, f, feature,null);
3081
                                }
3082
                                localChangesModifieds.increment();
3083
                                break;
3084
                            case OP_DELETE:
3085
                                LOGGER.debug("===: UPDATE: delete");
3086
                                if (f != null) {
3087
                                    userStore.delete("\"" + dataCodeFieldName + "\"=" + remoteChangeRow.getRelatedFeatureCode());
3088
                                    ef = userStore.createNewFeature(f);
3089
                                    String localEntityName = lentity.getEntityName();
3090
                                    long newRelatedFeatureCode = this.createUniqueCodeLong(localEntityName);
3091
                                    ef.setLong(lentity.getFeatureIdFieldName(), newRelatedFeatureCode);
3092
                                    userStore.insert(ef);
3093

    
3094
                                    workspaceChangesStore.delete("\"" + WorkspaceChangesTable.FEATUREID + "\"=" + remoteChangeRow.getRelatedFeatureCode());
3095
                                    LOGGER.debug("===: UPDATE: add insert operation to RelatedFeatureCode " + newRelatedFeatureCode);
3096
                                    this.addChange(null, lentity, OP_INSERT, workspaceChangesStore, ef, null, null);
3097
                                    localChangesModifieds.increment();
3098
                                } else {
3099
                                    // Ha habido un delete remoto. Si habia tambien un
3100
                                    // delete en local del mismo elemento borramos el delete local.
3101
                                    this.removeLocalDeleteIfExists(remoteChangeRow.getRelatedFeatureCode(), workspaceChangesStore);
3102
                                    localChangesModifieds.increment();
3103
                                }
3104
                                break;
3105
                        }
3106
                        status.incrementCurrentValue();
3107
                    }
3108
                }
3109
            }
3110
            localChangesTable.removeLocalChangesRelatedToSelectedRemoteChanges(this, lentity);
3111
            remoteChangesTable.delete(remoteChangesStore, lentity.getEntityCode());
3112

    
3113
            status.message("Updating metadata tables");
3114
            if( !this.isCorrupt(lentity, userStore, 0, false) ) {
3115
                lentity.updateState(transaction);
3116
            }
3117
            lentity.update(entitiesStore);
3118
            
3119
            transaction.commit();
3120
            forceReloadWorkspaceEntities();
3121

    
3122
            status.message("Update completed");
3123
            status.terminate();
3124
            return ERR_OK;
3125
        } catch (UserCancelTaskException ex) {
3126
            LOGGER.warn("User cancelled.", ex);
3127
            status.message("User cancelled");
3128
            status.cancel();
3129
            DataTransaction.rollbackQuietly(transaction);
3130
            throw new UserCancelTaskException();
3131
        } catch (Exception ex) {
3132
            LOGGER.warn("Can't update.", ex);
3133
            status.message("Can't update");
3134
            status.abort();
3135
            return ERR_CANT_UPDATE;
3136
        } finally {
3137
            this.removeStoreIgnoreChanges(userStore);
3138
            DisposeUtils.disposeQuietly(transaction);
3139
            status.pop();
3140
        }
3141
    }
3142

    
3143
    private DisposableFeatureSetIterable getUserFeaturesConflictWithRemoteChange(OnlineEntity lentity, RemoteChangeRow remoteChange, FeatureStore store) throws DataException {
3144
        if (store == null) {
3145
            return null;
3146
        }
3147
        FeatureType featType = lentity.getFeatureType();
3148
        JsonObject data = remoteChange.getRelatedFeatureDataAsJson();
3149
        ExpressionEvaluatorManager expManager = ExpressionEvaluatorLocator.getManager();
3150
        ExpressionBuilder builder = expManager.createExpressionBuilder();
3151
        builder.set(null);
3152
        for (FeatureAttributeDescriptor attr : featType) {
3153
            if (StringUtils.equalsAnyIgnoreCase(lentity.getFeatureIdFieldName(), attr.getName())) {
3154
                continue;
3155
            }
3156
            if (attr.isIndexed() && !attr.allowIndexDuplicateds()) {
3157
                Object value = Json.toObject(data, attr.getName());
3158
                builder.or(
3159
                        builder.eq(
3160
                                builder.column(attr.getName()),
3161
                                builder.constant(value)
3162
                        )
3163
                );
3164
            }
3165
        }
3166
        ExpressionBuilder.Value value = builder.value();
3167
        if (value == null) {
3168
            return null;
3169
        }
3170
        ExpressionBuilder.BinaryOperator filter = builder.and(
3171
                builder.not(
3172
                        builder.eq(
3173
                                builder.column(lentity.getFeatureIdFieldName()),
3174
                                builder.constant(remoteChange.getRelatedFeatureCode())
3175
                        )
3176
                ),
3177
                value
3178
        );
3179

    
3180
        FeatureSet feats = store.getFeatureSet(filter.toString());
3181
        return feats.iterable();
3182
    }
3183

    
3184
    private boolean removeMissingValues(EditableFeature ef, JsonObject dataJson) {
3185
        boolean hasMissingValues = false;
3186
        try {
3187
            FeatureType ftype = ef.getType();
3188
            for (FeatureAttributeDescriptor attr : ftype) {
3189
                if( !dataJson.containsKey(attr.getName()) ) {
3190
                    if( attr.allowNull() && attr.isReadOnly() && !attr.isPrimaryKey() ) {
3191
                        ef.set(attr.getName(), null);
3192
                        hasMissingValues = true;
3193
                    }
3194
                }
3195
            }
3196
            return hasMissingValues;
3197
        } catch(Exception e) {
3198
            LOGGER.debug("Can't remove missing values",e);
3199
            // Do nothing
3200
        }
3201
        return hasMissingValues;
3202
    }
3203

    
3204
    private void removeLocalDeleteIfExists(long relatedFeatureCode, FeatureStore changesStore) {
3205
        try {
3206
            // El store esta en modo PASS-THROUGH, asi que lanzamos el delete directamente.
3207
            changesStore.delete(
3208
                "\""+WorkspaceChangesTable.FEATUREID + "\" = " + relatedFeatureCode + " AND "+
3209
                "\""+WorkspaceChangesTable.OPERATION + "\" = "+OP_DELETE
3210
            );
3211
        } catch(Exception e) {
3212
            throw new RuntimeException("Can't delete local-delete if exists", e);
3213
        }
3214
    }
3215
    
3216

    
3217
    @Override
3218
    public int merge(String tableName, MutableLong localChangesCreated, SimpleTaskStatus status) {
3219
        return this.update(tableName, true, localChangesCreated, status);
3220
    }
3221

    
3222
    @Override
3223
    public int synchronize(OnlineEntity entity, Envelope workingArea, SimpleTaskStatus status) {
3224
        I18nManager i18n = ToolsLocator.getI18nManager();
3225
        if (status == null) {
3226
            status = ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(i18n.getTranslation("_Synchronizing"));
3227
            status.setAutoremove(true);
3228
            status.add();
3229
        } else {
3230
            status.push();
3231
        }
3232

    
3233
        status.setIndeterminate();
3234
        status.message(entity.getEntityName());
3235
        
3236
        WorkspaceChangesTable wct = new WorkspaceChangesTable();
3237
        Iterator<Geometry> geoms = null;
3238
        Iterator<Geometry> geoms2 = null;
3239
        FeatureStore remoteStore = null;
3240
        FeatureStore localStore = null;
3241
        FeatureSet remoteSet = null;
3242
        DisposableIterator<Feature> remoteIt = null;
3243
        FeatureSet localSet = null;
3244
        DisposableIterator<Feature> localIt = null;
3245

    
3246
        FeatureStore localChangesStore = null;
3247
        FeatureStore remoteChangesStore = null;
3248
        FeatureStore entitiesStore = null;
3249
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
3250
        
3251
        try {
3252
            OnlineWorkspaceImpl wc = this;
3253
            DisposableFeatureSetIterable it = wct.getSelectedsByEntityCodeAsIterator(this, entity.getEntityCode());
3254
            geoms = new Iterator<Geometry>() {
3255
                @Override
3256
                public boolean hasNext() {
3257
                    return it.hasNext();
3258
                }
3259

    
3260
                @Override
3261
                public Geometry next() {
3262
                    Feature f = it.next();
3263
                    WorkspaceChangeRow row = new WorkspaceChangeRow(wc, f);
3264
                    if (row.getOperation() == OP_DELETE) {
3265
                        return null;
3266
                    }
3267
                    return row.getRelatedFeature().getDefaultGeometry();
3268
                }
3269
            };
3270

    
3271
            DisposableFeatureSetIterable it2 = wct.getSelectedsByEntityCodeAsIterator(this, entity.getEntityCode());
3272
            geoms2 = new Iterator<Geometry>() {
3273
                @Override
3274
                public boolean hasNext() {
3275
                    return it2.hasNext();
3276
                }
3277

    
3278
                @Override
3279
                public Geometry next() {
3280
                    try {
3281
                        Feature f = it2.next();
3282
                        WorkspaceChangeRow row = new WorkspaceChangeRow(wc, f);
3283
                        
3284
                        JsonObject dataAsJson = row.getDataAsJson();
3285
                        if(dataAsJson == null){
3286
                            return null;
3287
                        }
3288
                        String geom_wkb = dataAsJson.getString(entity.getGeometryFieldName());
3289
                        if(geom_wkb == null) {
3290
                            return null;
3291
                        }
3292
                        return geomManager.createFrom(geom_wkb);
3293
                    } catch (GeometryException ex) {
3294
                        LOGGER.warn("Can't get geometry of workspace change", ex);
3295
                        return null;
3296
                    }
3297
                }
3298
            };
3299

    
3300
            TilesCalculator tilesCalculator = new TilesCalculator();
3301
            List<Envelope> envs = tilesCalculator.calculateEnvelopes(
3302
                entity.getTileSize(), 
3303
                new ChainedIterator<>(
3304
                    geoms, 
3305
                    geoms2, 
3306
                    workingArea == null?Collections.EMPTY_LIST.iterator():Collections.singletonList(workingArea.getGeometry()).iterator()
3307
                )
3308
            );
3309

    
3310
            OnlineLayer layer = this.project.getLayer(
3311
                (OnlineLayer t) -> StringUtils.equals(t.getName(), entity.getEntityName()),
3312
                SimpleTaskStatus.FAKE_STATUS
3313
            );
3314
            File f = ((HasAFile) this.getExplorerParameters()).getFile();
3315
            File tmpRemote = ToolsLocator.getFoldersManager().getUniqueFile(H2SpatialUtils.removeH2FileNameExtension(f.getAbsolutePath())+"_tmp_"+entity.getEntityName());
3316
            DataManager dataManager = DALLocator.getDataManager();
3317
            DataServerExplorerParameters expParameters = DALLocator.getDataManager().createServerExplorerParameters(FeatureStore.H2SPATIAL_PROVIDER_NAME);
3318
            ((HasAFile) expParameters).setFile(tmpRemote);
3319
            DataServerExplorer explorer = dataManager.openServerExplorer(FeatureStore.H2SPATIAL_PROVIDER_NAME, expParameters);
3320
            JDBCNewStoreParameters params = (JDBCNewStoreParameters) explorer.getAddParameters(entity.getEntityName());
3321
            EditableFeatureType ft = params.getDefaultFeatureType();
3322
            ft.add(FEATURECODE_FIELD_NAME, DataTypes.INT).setIsPrimaryKey(true);
3323
            ft.add(FEATUREVERSION_FIELD_NAME, DataTypes.INT);
3324
            ft.add("feature", DataTypes.STRING, DataManager.RECOMENDED_SIZE_FOR_CLOB);
3325
            explorer.add(FeatureStore.H2SPATIAL_PROVIDER_NAME, params, true);
3326
            DataStoreParameters storeParams = explorer.get(entity.getEntityName());
3327
            remoteStore = (FeatureStore) dataManager.openStore(FeatureStore.H2SPATIAL_PROVIDER_NAME, storeParams);
3328
            remoteStore.edit(FeatureStore.MODE_APPEND, FeatureStore.SUBMODE_MERGE);
3329

    
3330
            String geometryName = entity.getFeatureType().getDefaultGeometryAttributeName();
3331
            IProjection layerProj = entity.getCRSAsProjection();
3332
            for (Envelope env : envs) {
3333
                Iterator<JsonObject> data = layer.getData(env, 1000, status);
3334
                while (data.hasNext()) {
3335
                    if(status.isCancellationRequested()){
3336
                        throw new UserCancelTaskException();
3337
                    }
3338
                    JsonObject json = data.next();
3339
                    JsonObject properties_json = json.getJsonObject("properties");
3340
                    EditableFeature feat = remoteStore.createNewFeature();
3341
                    feat.set(FEATURECODE_FIELD_NAME, properties_json.getInt(FEATURECODE_FIELD_NAME, 0));
3342
                    feat.set(FEATUREVERSION_FIELD_NAME, properties_json.getInt(FEATUREVERSION_FIELD_NAME, 0));
3343
                    feat.set("feature", convertOnlineToDesktopFeature(json, geometryName, geomManager, layerProj).toString());
3344
                    remoteStore.insert(feat);
3345
                }
3346
            }
3347
            remoteStore.finishEditing();
3348

    
3349
            localChangesStore = this.openFeatureStore(WorkspaceChangesTable.TABLE_NAME, true);
3350
            remoteChangesStore = this.openFeatureStore(RemoteChangesTable.TABLE_NAME, true);
3351
            entitiesStore = this.openFeatureStore(EntitiesTable.TABLE_NAME, true);
3352
            final FeatureStore finalLocalChangesStore = localChangesStore;
3353
            final FeatureStore finalRemoteChangesStore = remoteChangesStore;
3354

    
3355
            removeRemoteChanges(remoteChangesStore, localChangesStore, entitiesStore, (EntityRow)entity);
3356
            
3357
            remoteChangesStore.edit(FeatureStore.MODE_APPEND);
3358
            localChangesStore.edit(FeatureStore.MODE_PASS_THROUGH);
3359
            
3360
            PatchGenerator.PatchHandler<Feature> handler = new PatchGenerator.PatchHandler<Feature>() {
3361
                WorkspaceChangeRow lastLocalChange = null;
3362
                
3363
                @Override
3364
                public int compareId(Feature r, Feature l) {
3365
                    return Integer.compare(r.getInt(FEATURECODE_FIELD_NAME), l.getInt(FEATURECODE_FIELD_NAME));
3366
                }
3367

    
3368
                @Override
3369
                public boolean isNew(Feature l) {
3370
                    return l.getInt(FEATURECODE_FIELD_NAME) < 0;
3371
                }
3372

    
3373
                @Override
3374
                public boolean isLocalChange(Feature l) {
3375
                    lastLocalChange = wct.find(wc, finalLocalChangesStore, entity.getEntityCode(), l.getInt(FEATURECODE_FIELD_NAME));
3376
                    return lastLocalChange != null;
3377
                }
3378

    
3379
                @Override
3380
                public boolean isRemoteChange(Feature r, Feature l) {
3381
                    return r.getInt(FEATUREVERSION_FIELD_NAME) != l.getInt(FEATUREVERSION_FIELD_NAME);
3382
                }
3383

    
3384
                @Override
3385
                public void addRemoteChangeDelete(Feature l, boolean conflict) {
3386
                    RemoteChangeRow row = new RemoteChangeRow(wc);
3387
                    row.setData(l.toJson().toString());
3388
                    row.setEntityCode(entity.getEntityCode());
3389
                    row.setOperation(OP_DELETE);
3390
                    row.setRevisionNumber(l.getInt(FEATUREVERSION_FIELD_NAME));
3391
                    row.setSelected(true);
3392
                    row.setStatus(conflict ? STATE_CONFLICT : STATE_LOCAL_UNMODIFIED);
3393
                    row.setRelatedFeatureCode(l.getInt(FEATURECODE_FIELD_NAME));
3394
                    row.setCode(createUniqueCode());
3395
                    row.insert(finalRemoteChangesStore);
3396
                    if (conflict) {
3397
                        if (lastLocalChange == null
3398
                            || !(lastLocalChange.getEntityCode().equals(row.getEntityCode())
3399
                            && lastLocalChange.getRelatedFeatureCode() == row.getRelatedFeatureCode())) {
3400

    
3401
                            lastLocalChange = wct.find(wc, finalLocalChangesStore, entity.getEntityCode(), l.getInt(FEATURECODE_FIELD_NAME));
3402
                        }
3403
                        if(lastLocalChange != null && lastLocalChange.getStatus() != STATE_CONFLICT) {
3404
                            lastLocalChange.setStatus(STATE_CONFLICT);
3405
                            lastLocalChange.update(finalLocalChangesStore);
3406
                        }
3407
                        ((EntityRow)entity).setState(STATE_CONFLICT);
3408
                    }
3409
                }
3410

    
3411
                @Override
3412
                public void addRemoteChangeModify(Feature r, boolean conflict) {
3413
                    RemoteChangeRow row = new RemoteChangeRow(wc);
3414
                    row.setData(r.getString("feature"));
3415
                    row.setEntityCode(entity.getEntityCode());
3416
                    row.setOperation(OP_UPDATE);
3417
                    row.setRevisionNumber(r.getInt(FEATUREVERSION_FIELD_NAME));
3418
                    row.setSelected(true);
3419
                    row.setStatus(conflict ? STATE_CONFLICT : STATE_LOCAL_UNMODIFIED);
3420
                    row.setRelatedFeatureCode(r.getInt(FEATURECODE_FIELD_NAME));
3421
                    row.setCode(createUniqueCode());
3422
                    row.insert(finalRemoteChangesStore);
3423
                    if (conflict) {
3424
                        if (lastLocalChange == null
3425
                            || !(lastLocalChange.getEntityCode().equals(row.getEntityCode())
3426
                            && lastLocalChange.getRelatedFeatureCode() == r.getInt(FEATURECODE_FIELD_NAME))) {
3427

    
3428
                            lastLocalChange = wct.find(wc, finalLocalChangesStore, entity.getEntityCode(), r.getInt(FEATURECODE_FIELD_NAME));
3429
                        }
3430
                        if(lastLocalChange != null && lastLocalChange.getStatus() != STATE_CONFLICT) {
3431
                            lastLocalChange.setStatus(STATE_CONFLICT);
3432
                            lastLocalChange.update(finalLocalChangesStore);
3433
                        }
3434
                        ((EntityRow)entity).setState(STATE_CONFLICT);
3435
                    }
3436
                }
3437

    
3438
                @Override
3439
                public void addRemoteChangeInsert(Feature r) {
3440
                    RemoteChangeRow row = new RemoteChangeRow(wc);
3441
                    row.setData(r.getString("feature"));
3442
                    row.setEntityCode(entity.getEntityCode());
3443
                    row.setOperation(OP_INSERT);
3444
                    row.setRevisionNumber(r.getInt(FEATUREVERSION_FIELD_NAME));
3445
                    row.setSelected(true);
3446
                    row.setStatus(STATE_LOCAL_UNMODIFIED);
3447
                    row.setCode(createUniqueCode());
3448
                    row.insert(finalRemoteChangesStore);
3449
                }
3450
            };
3451

    
3452
            PatchGenerator<Feature> patchGenerator = PatchGenerator.create(handler);
3453

    
3454
            localStore = this.openFeatureStore(entity);
3455
            remoteSet = remoteStore.getFeatureSet((String) null, FEATURECODE_FIELD_NAME);
3456
            remoteIt = remoteSet.fastIterator();
3457
            GeometryExpressionBuilder expBuilder = (GeometryExpressionBuilder) localStore.createExpressionBuilder();
3458
            FeatureType localStoreFt = localStore.getDefaultFeatureTypeQuietly();
3459
            String geomName = localStoreFt.getDefaultGeometryAttributeName();
3460
            IProjection localStoreProj = localStoreFt.getDefaultSRS();
3461
            for (Envelope env : envs) {
3462
                expBuilder.or(
3463
                    expBuilder.ST_Intersects(
3464
                        expBuilder.column(geomName),
3465
                        expBuilder.envelope(env, localStoreProj)
3466
                    )
3467
                );
3468
            }
3469
            localSet = localStore.getFeatureSet(expBuilder.build(), FEATURECODE_FIELD_NAME);
3470
            localIt = localSet.fastIterator();
3471
            
3472
            patchGenerator.generate(remoteIt,localIt, status);
3473
            remoteChangesStore.finishEditing();
3474
            localChangesStore.finishEditing();
3475
            status.terminate();
3476
            return ERR_OK;
3477

    
3478
        } catch (UserCancelTaskException ex) {
3479
            FeatureStore.cancelEditingQuietly(remoteStore);
3480
            FeatureStore.cancelEditingQuietly(remoteChangesStore);
3481
            FeatureStore.cancelEditingQuietly(localChangesStore);
3482
            status.cancel();
3483
            return ERR_CANCELLED_BY_USER;
3484
        } catch (Exception ex) {
3485
            LOGGER.warn("Can't synchronize", ex);
3486
            FeatureStore.cancelEditingQuietly(remoteStore);
3487
            FeatureStore.cancelEditingQuietly(remoteChangesStore);
3488
            FeatureStore.cancelEditingQuietly(localChangesStore);
3489
            status.abort();
3490
            return ERR_SYNCHRONIZE;
3491
        } finally {
3492
            status.pop();
3493

    
3494
            DisposeUtils.disposeQuietly(geoms);
3495

    
3496
            DisposeUtils.disposeQuietly(remoteChangesStore);
3497
            DisposeUtils.disposeQuietly(localChangesStore);
3498

    
3499
            DisposeUtils.disposeQuietly(remoteIt);
3500
            DisposeUtils.disposeQuietly(remoteSet);
3501
            DisposeUtils.disposeQuietly(remoteStore);
3502
            
3503
            DisposeUtils.disposeQuietly(localIt);
3504
            DisposeUtils.disposeQuietly(localSet);
3505
            DisposeUtils.disposeQuietly(localStore);
3506
            
3507
            DisposeUtils.disposeQuietly(entitiesStore);
3508
            
3509
        }
3510
    }
3511

    
3512
    @Override
3513
    public WorkingArea getCurrentWorkingArea() {
3514
        return this.currentWorkingArea;
3515
    }
3516

    
3517
    @Override
3518
    public void setCurrentWorkingArea(WorkingArea workingArea) {
3519
        this.currentWorkingArea = workingArea;
3520
        VarsTable varsTable = new VarsTable();
3521
        varsTable.set(this, CONFIG_CURRENT_WORKINGAREA, workingArea.toJson().toString());
3522
    }
3523
    
3524
    private void refreshCodeGeneratorSequence(String entityName) {
3525
        OnlineEntity entity = this.getWorkspaceEntity(entityName);
3526
        long sequence = 0;
3527
        FeatureStore store = null;
3528
        try {
3529
            store = this.openFeatureStore(entity);
3530
            FeatureQuery query = store.createFeatureQuery();
3531
            query.addAggregate("MIN", entity.getFeatureIdFieldName());
3532
            Feature f = store.findFirst(query);
3533
            long l = f.getLong(entity.getFeatureIdFieldName());
3534
            if (sequence > l) {
3535
                sequence = l;
3536
            }
3537
        } catch (Exception ex) {
3538
            LOGGER.warn("Can't refresh code generator sequence");
3539
        } finally {
3540
            DisposeUtils.dispose(store);
3541
        }
3542
        this.codeGenerator.initSequence(entityName, sequence-1);
3543
    }
3544

    
3545
}
3546