Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.swing / org.gvsig.fmap.dal.swing.impl / src / main / java / org / gvsig / fmap / dal / impl / DefaultEditingNotificationManager.java @ 47378

History | View | Annotate | Download (17.4 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.impl;
25

    
26
import java.awt.Dimension;
27
import java.util.Arrays;
28
import java.util.HashMap;
29
import java.util.List;
30
import java.util.Map;
31
import java.util.Objects;
32
import javax.swing.JComponent;
33
import javax.swing.SwingUtilities;
34
import org.apache.commons.lang.StringUtils;
35
import org.gvsig.expressionevaluator.ExpressionUtils;
36
import org.gvsig.featureform.swing.JFeatureForm;
37
import org.gvsig.fmap.dal.DataStore;
38
import org.gvsig.fmap.dal.EditingNotification;
39
import static org.gvsig.fmap.dal.EditingNotification.AFTER_ENTER_EDITING_STORE;
40
import static org.gvsig.fmap.dal.EditingNotification.AFTER_EXIT_EDITING_STORE;
41
import static org.gvsig.fmap.dal.EditingNotification.AFTER_INSERT_FEATURE;
42
import static org.gvsig.fmap.dal.EditingNotification.AFTER_REMOVE_FEATURE;
43
import static org.gvsig.fmap.dal.EditingNotification.AFTER_UPDATE_FEATURE;
44
import static org.gvsig.fmap.dal.EditingNotification.AFTER_UPDATE_FEATURE_TYPE;
45
import static org.gvsig.fmap.dal.EditingNotification.BEFORE_ENTER_EDITING_STORE;
46
import static org.gvsig.fmap.dal.EditingNotification.BEFORE_EXIT_EDITING_STORE;
47
import static org.gvsig.fmap.dal.EditingNotification.BEFORE_INSERT_FEATURE;
48
import static org.gvsig.fmap.dal.EditingNotification.BEFORE_REMOVE_FEATURE;
49
import static org.gvsig.fmap.dal.EditingNotification.BEFORE_UPDATE_FEATURE;
50
import static org.gvsig.fmap.dal.EditingNotification.BEFORE_UPDATE_FEATURE_TYPE;
51
import org.gvsig.fmap.dal.EditingNotificationManager;
52
import org.gvsig.fmap.dal.feature.EditableFeature;
53
import org.gvsig.fmap.dal.feature.EditableFeatureType;
54
import org.gvsig.fmap.dal.feature.Feature;
55
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
56
import org.gvsig.fmap.dal.feature.FeatureReference;
57
import org.gvsig.fmap.dal.feature.FeatureRule;
58
import org.gvsig.fmap.dal.feature.FeatureStore;
59
import org.gvsig.fmap.dal.feature.FeatureType;
60
import org.gvsig.fmap.dal.feature.exception.ValidateFeaturesException;
61
import org.gvsig.fmap.dal.swing.DALSwingLocator;
62
import org.gvsig.tools.ToolsLocator;
63
import org.gvsig.tools.dynobject.DynObjectManager;
64
import org.gvsig.tools.dynobject.Tags;
65
import org.gvsig.tools.observer.ObservableHelper;
66
import org.gvsig.tools.observer.Observer;
67
import org.gvsig.tools.observer.BaseNotification;
68
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
69
import org.gvsig.tools.script.Script;
70
import org.gvsig.tools.swing.api.ToolsSwingLocator;
71
import org.gvsig.tools.swing.api.ToolsSwingUtils;
72
import org.gvsig.tools.swing.api.windowmanager.Dialog;
73
import org.gvsig.tools.swing.api.windowmanager.WindowManager;
74
import org.gvsig.tools.swing.api.windowmanager.WindowManager_v2;
75
import org.slf4j.Logger;
76
import org.slf4j.LoggerFactory;
77

    
78
/**
79
 *
80
 * Acts as a hub to centralize editing notification events in a single manager.
81
 *
82
 * All objects that modify a store notify this manager to reported the actions
83
 * to the objects interested in knowing when start or end editing, regardless
84
 * of where the editing work is done.
85
 *
86
 */
87
@SuppressWarnings("UseSpecificCatch")
88
public class DefaultEditingNotificationManager implements EditingNotificationManager {
89

    
90
    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultEditingNotificationManager.class);
91
            
92
    private static final String TAG_FORCE_SHOW_FORM_ON_INSERT = "forceShowFormOnInsert";
93
    
94
    private ObservableHelper helper = null;
95

    
96
    private static final int SOURCE = 1;
97
    private static final int DOCUMENT = 2;
98
    private static final int AUXDATA = 3;
99
    private static final int STORE = 4;
100
    private static final int FEATURE = 5;
101
    private static final int FEATURETYPE = 6;
102
    
103
    private static List<String> cancelableNotifications = null;
104
    private static Map<String,String> event2method = null;
105

    
106

    
107

    
108
    public class DefaultEditingNotification extends BaseNotification implements EditingNotification {
109

    
110
        private boolean validateTheFeature = true;
111
        
112
        DefaultEditingNotification(String type) {
113
            super(type, 7);
114
        }
115

    
116
        @Override
117
        public Object getSource() {
118
            return this.getValue(SOURCE);
119
        }
120

    
121
        @Override
122
        public Object getDocument() {
123
            return this.getValue(DOCUMENT);
124
        }
125

    
126
        @Override
127
        public Object getAuxData() {
128
            return this.getValue(AUXDATA);
129
        }
130

    
131
        @Override
132
        public DataStore getStore() {
133
            return (DataStore) this.getValue(STORE);
134
        }
135

    
136
        @Override
137
        public FeatureStore getFeatureStore() {
138
            return (FeatureStore) this.getValue(STORE);
139
        }
140

    
141
        @Override
142
        public Feature getFeature() {
143
            return (Feature) this.getValue(FEATURE);
144
        }
145

    
146
        @Override
147
        public EditableFeatureType getFeatureType() {
148
            return (EditableFeatureType) this.getValue(FEATURETYPE);
149
        }
150

    
151
        @Override
152
        public boolean isCancelable() {
153
            if( cancelableNotifications==null ) {
154
                String nn[] = new String[] {
155
                    BEFORE_ENTER_EDITING_STORE,
156
                    BEFORE_EXIT_EDITING_STORE,
157
                    BEFORE_INSERT_FEATURE,
158
                    BEFORE_REMOVE_FEATURE,
159
                    BEFORE_UPDATE_FEATURE,
160
                    BEFORE_UPDATE_FEATURE_TYPE
161
                };
162
                cancelableNotifications = Arrays.asList(nn);
163
            }
164
            return cancelableNotifications.contains(this.getType() );
165
        }
166

    
167
        @Override
168
        public void setSkipFeatureValidation(boolean skipTheFeatureValidation) {
169
            this.validateTheFeature = !skipTheFeatureValidation;
170
        }
171

    
172
        @Override
173
        public boolean shouldValidateTheFeature() {
174
            return this.validateTheFeature;
175
        }
176
    }
177

    
178
    public DefaultEditingNotificationManager() {
179
        this.helper = new ObservableHelper();
180
    }
181

    
182
    @Override
183
    public void addObserver(Observer o) {
184
        this.helper.addObserver(o);
185
    }
186

    
187
    @Override
188
    public void deleteObserver(Observer o) {
189
        this.helper.deleteObserver(o);
190
    }
191

    
192
    @Override
193
    public void deleteObservers() {
194
        this.helper.deleteObservers();
195
    }
196

    
197
    @Override
198
    public EditingNotification notifyObservers(EditingNotification notification) {
199
        try {
200
            this.helper.notifyObservers(this, notification);
201
        } catch(Exception ex) {
202
            LOGGER.warn("Problems notifing to observers of DefaultEditingNotificationManager.",ex);
203
        }
204
        return notification;
205
    }
206

    
207
    public EditingNotification notifyObservers(Object source, String type, Object document, Object auxdata, DataStore store, Feature feature, EditableFeatureType featureType) {
208
        EditingNotification notification = new DefaultEditingNotification(type);
209
        notification.setValue(SOURCE,source);
210
        notification.setValue(DOCUMENT,document);
211
        notification.setValue(AUXDATA,auxdata);
212
        notification.setValue(STORE,store);
213
        notification.setValue(FEATURE,feature);
214
        notification.setValue(FEATURETYPE,featureType);
215
        if( store != null ) {
216
            String methodname = this.event2methodname(type);
217
            if( StringUtils.isNotBlank(methodname) ) {
218
                ResourcesStorage resources = store.getResourcesStorage();
219
                Script script = ExpressionUtils.getScript(resources, "editsc");
220
                if( script!=null ) {
221
                    try {
222
                        script.invokeFunction(methodname, new Object[] {notification} );
223
                        if( notification.isCanceled() ) {
224
                            return notification;
225
                        }
226
                    } catch (NoSuchMethodException ex) {
227
                        LOGGER.debug("No existe el metodo '"+methodname+"' en el script.");
228
                        // Do nothing
229
                    } catch (Throwable ex) {
230
                        LOGGER.warn("Can't call method '"+methodname+"'.");
231
                    }
232
                }
233
            }
234
        }        
235
        return this.notifyObservers(notification);
236
    }
237
    
238
    private String event2methodname(String type) {
239
        if( StringUtils.isBlank(type) ) {
240
            return null;
241
        }
242
        if( event2method==null ) {
243
            event2method = new HashMap<>();
244
            event2method.put(BEFORE_ENTER_EDITING_STORE,"beforeEnterEditing");
245
            event2method.put(BEFORE_EXIT_EDITING_STORE,"beforeExitEditing");
246
            event2method.put(BEFORE_INSERT_FEATURE,"beforeInsert");
247
            event2method.put(BEFORE_UPDATE_FEATURE,"beforeUpdate");
248
            event2method.put(BEFORE_REMOVE_FEATURE,"beforeRemove");
249
            event2method.put(BEFORE_UPDATE_FEATURE_TYPE,"beforeUpdateFeatureType");
250
            event2method.put(AFTER_ENTER_EDITING_STORE,"afterEnterEditing");
251
            event2method.put(AFTER_EXIT_EDITING_STORE,"afterExitEditing");
252
            event2method.put(AFTER_INSERT_FEATURE,"afterInsert");
253
            event2method.put(AFTER_UPDATE_FEATURE,"afterUpdate");
254
            event2method.put(AFTER_REMOVE_FEATURE,"afterRemove");
255
            event2method.put(AFTER_UPDATE_FEATURE_TYPE,"afterUpdateFeatureType");
256
        }
257
        return event2method.getOrDefault(type, "whenEvent");
258
    }
259

    
260
    @Override
261
    public EditingNotification notifyObservers(Object source, String type, Object document, Object auxdata, DataStore store) {
262
        return this.notifyObservers(source, type, document, auxdata, store, null, null);
263
    }
264

    
265
    @Override
266
    public EditingNotification notifyObservers(Object source, String type, Object document, Object auxdata) {
267
        return this.notifyObservers(source, type, document, auxdata, null, null, null);
268
    }
269

    
270
    @Override
271
    public EditingNotification notifyObservers(Object source, String type, Object document, DataStore store) {
272
        return this.notifyObservers(source, type, document, null, store, null, null);
273
    }
274

    
275
    @Override
276
    public EditingNotification notifyObservers(Object source, String type, Object document, DataStore store, Feature feature) {
277
        return this.notifyObservers(source, type, document, null, store, feature, null);
278
    }
279

    
280
    @Override
281
    public EditingNotification notifyObservers(Object source, String type, Object document, DataStore store, EditableFeatureType featureType) {
282
        return this.notifyObservers(source, type, document, null, store, null, featureType);
283
    }
284
    
285
    @Override
286
    public EditingNotification notifyObservers(Object source, String type, Object document, Object auxdata, DataStore store, Feature feature) {
287
        return this.notifyObservers(source, type, document, auxdata, store, feature, null);
288
    }
289

    
290
    @Override
291
    public boolean validateFeature(Feature feature) {
292
        
293
        FeatureType featureType = feature.getType();
294
        Tags tags = featureType.getTags();
295
        boolean forceShowForm = tags != null && tags.getBoolean(TAG_FORCE_SHOW_FORM_ON_INSERT, false);
296
        while( true ) {
297
            boolean validateOk = true;
298
            if( feature instanceof EditableFeature ) {
299
                // Esto deberia ser siempre cierto.
300
                try {
301
                    for (FeatureRule rule : featureType.getRules()) {
302
                        if( rule != null ) {
303
                            if( rule.checkWhen(FeatureRule.CHECK_WHEN_USER_EDIT_FEATURE) ) {
304
                                try {
305
                                    rule.validate((EditableFeature) feature, feature.getStore());
306
                                } catch (ValidateFeaturesException ex) {
307
                                    validateOk = false;
308
                                } catch (Exception ex) {
309
                                    FeatureReference ref = feature.getReference();
310
                                    LOGGER.warn("Can't run validate rule '"+rule.getName()+"' on feature '"+Objects.toString(ref)+"'.", ex);
311
                                }
312
                            }
313
                        }
314
                    } 
315
                } catch (Exception ex) {
316
                    FeatureReference ref = feature.getReference();
317
                    LOGGER.warn("Can't run validate feature '"+Objects.toString(ref)+"'.");
318
                }
319
            }
320
            if( validateOk && !forceShowForm ) {
321
                if( canWriteFeature(feature) ) {
322
                    // La feature se puede validar y no precisa de intervencion del
323
                    // usuario. Retornamos true.
324
                    return true;
325
                }
326
            }
327
            // No se habia podido validar la feature, se piden al
328
            // usuario los datos que faltan.
329
            AskRequiredAttributtes ask = new AskRequiredAttributtes();
330
            ask.showDialog(feature);
331
            if( ask.userCancel() ) {
332
                // No se habia podido validar la feature, se le han pedido al
333
                // usuario los datos que faltan y este ha pulsado en cancel,
334
                // asi que la feature no se puede validar, y retornamos
335
                // false.
336
                return false;
337
            }
338
            forceShowForm = false;
339
        }
340
    }
341
    
342
    @Override
343
    public boolean canWriteFeature(Feature feature) {
344
        FeatureType featureType = feature.getType();
345
        if( !featureType.isCheckFeaturesAtInsert() ) {
346
            return true;
347
        }
348
        FeatureAttributeDescriptor[] attributeDescriptors = featureType.getAttributeDescriptors();
349

    
350
        for (FeatureAttributeDescriptor attrDesc : attributeDescriptors) {
351
            if ( attrDesc.isAutomatic() ) {
352
                continue;
353
            }
354
            if ( (attrDesc.isPrimaryKey() || !attrDesc.allowNull())
355
                    && feature.get(attrDesc.getName()) == null ) {
356
                return false;
357
            }
358
        }
359
        return true;
360
    }
361

    
362
    private class AskRequiredAttributtes {
363

    
364
        private boolean userCancelValue = true;
365

    
366
        public boolean userCancel() {            
367
            return this.userCancelValue;
368
        }
369
        
370
        public void showDialog(final Feature feature) {
371
            if ( !SwingUtilities.isEventDispatchThread() ) {
372
                try {
373
                    SwingUtilities.invokeAndWait(() -> {
374
                        showDialog(feature);
375
                    });
376
                    return;
377
                } catch (Exception e1) {
378
                    message("Can't show form to fill need data.",e1);
379
                    this.userCancelValue = true;
380
                    return;
381
                }
382
            }
383

    
384
            try {
385
                JFeatureForm form = DALSwingLocator.getSwingManager().createJFeatureForm(feature);
386
                ToolsSwingUtils.ensureRowsCols(form.asJComponent(), 15, 80, 25, 140);
387
                WindowManager_v2 winManager = (WindowManager_v2) ToolsSwingLocator.getWindowManager();
388
                Dialog dialog = winManager.createDialog(
389
                        form.asJComponent(),
390
                        form.getDynForm().getDefinition().getLabel(),
391
                        ToolsLocator.getI18nManager().getTranslation("_Fill_the_required_fields"), 
392
                        WindowManager_v2.BUTTONS_OK_CANCEL
393
                );
394
                dialog.show(WindowManager.MODE.DIALOG);
395
                if( dialog.getAction() == WindowManager_v2.BUTTON_OK ) {
396
                    form.fetch((EditableFeature) feature);
397
                    this.userCancelValue = false;
398
                } else {
399
                    this.userCancelValue = true;
400
                }
401
                
402
            } catch (Exception ex) {
403
                message("Can't show form to fill need data.",ex);
404
                this.userCancelValue = true;
405
            }
406
        }
407
        
408
        private void message(String msg, Throwable ex) {
409
            if( ex==null ) {
410
                LOGGER.warn(msg);
411
            } else {
412
                LOGGER.warn(msg,ex);
413
            }
414
//            msg = msg + "\nSee the application log for more information.";
415
//            ApplicationManager application = ApplicationLocator.getManager();
416
//            application.message(msg,JOptionPane.WARNING_MESSAGE);
417
        }
418
    }
419
    
420
    private void setPreferredSize(JComponent c, int width, int height) {
421
        Dimension d = c.getPreferredSize();
422
        if (d.width < width) {
423
            d.width = width;
424
        }
425
        if (d.height > height) {
426
            d.height = height;
427
        }
428
        c.setPreferredSize(d);
429
    }
430

    
431
    public static final void selfRegister() {
432
         DynObjectManager dynObjectManager = ToolsLocator.getDynObjectManager();
433
         dynObjectManager.registerTag(
434
                 TAG_FORCE_SHOW_FORM_ON_INSERT, 
435
                 "String value, true/false to force show form on insert features."
436
         );
437
        
438
    }
439
    
440
    
441
}