Project

General

Profile

Download (22.9 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2017 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.vaadin.mvp;
10

    
11
import java.util.Map;
12
import java.util.Stack;
13

    
14
import org.apache.log4j.Logger;
15
import org.vaadin.spring.events.EventScope;
16

    
17
import com.vaadin.data.Validator.InvalidValueException;
18
import com.vaadin.data.fieldgroup.BeanFieldGroup;
19
import com.vaadin.data.fieldgroup.FieldGroup.CommitEvent;
20
import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
21
import com.vaadin.data.fieldgroup.FieldGroup.CommitHandler;
22
import com.vaadin.data.fieldgroup.FieldGroup.FieldGroupInvalidValueException;
23
import com.vaadin.server.AbstractErrorMessage.ContentMode;
24
import com.vaadin.server.ErrorMessage.ErrorLevel;
25
import com.vaadin.server.FontAwesome;
26
import com.vaadin.server.UserError;
27
import com.vaadin.shared.ui.MarginInfo;
28
import com.vaadin.ui.AbstractField;
29
import com.vaadin.ui.AbstractLayout;
30
import com.vaadin.ui.AbstractOrderedLayout;
31
import com.vaadin.ui.Alignment;
32
import com.vaadin.ui.Button;
33
import com.vaadin.ui.CheckBox;
34
import com.vaadin.ui.Component;
35
import com.vaadin.ui.CssLayout;
36
import com.vaadin.ui.Field;
37
import com.vaadin.ui.GridLayout;
38
import com.vaadin.ui.GridLayout.OutOfBoundsException;
39
import com.vaadin.ui.GridLayout.OverlapsException;
40
import com.vaadin.ui.HorizontalLayout;
41
import com.vaadin.ui.Layout;
42
import com.vaadin.ui.Notification;
43
import com.vaadin.ui.Notification.Type;
44
import com.vaadin.ui.PopupDateField;
45
import com.vaadin.ui.TextField;
46
import com.vaadin.ui.VerticalLayout;
47
import com.vaadin.ui.themes.ValoTheme;
48

    
49
import eu.etaxonomy.cdm.database.PermissionDeniedException;
50
import eu.etaxonomy.cdm.vaadin.component.TextFieldNFix;
51
import eu.etaxonomy.cdm.vaadin.event.AbstractEditorAction;
52
import eu.etaxonomy.cdm.vaadin.event.AbstractEditorAction.EditorActionContext;
53
import eu.etaxonomy.vaadin.component.NestedFieldGroup;
54
import eu.etaxonomy.vaadin.component.SwitchableTextField;
55
import eu.etaxonomy.vaadin.mvp.event.EditorDeleteEvent;
56
import eu.etaxonomy.vaadin.mvp.event.EditorPreSaveEvent;
57
import eu.etaxonomy.vaadin.mvp.event.EditorSaveEvent;
58
import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent;
59
import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent.Reason;
60

    
61
/**
62
 *
63
 * Optional with a delete button which can be enabled with {@link #withDeleteButton(boolean)}
64
 *
65
 * @author a.kohlbecker
66
 * @since Apr 5, 2017
67
 *
68
 */
69
public abstract class AbstractPopupEditor<DTO extends Object, P extends AbstractEditorPresenter<DTO, ? extends ApplicationView>>
70
    extends AbstractPopupView<P> {
71

    
72
    public static final Logger logger = Logger.getLogger(AbstractPopupEditor.class);
73

    
74
    private BeanFieldGroup<DTO> fieldGroup;
75

    
76
    private VerticalLayout mainLayout;
77

    
78
    private Layout fieldLayout;
79

    
80
    private HorizontalLayout buttonLayout;
81

    
82
    private Button save;
83

    
84
    private Button cancel;
85

    
86
    private Button delete;
87

    
88
    private CssLayout toolBar = new CssLayout();
89

    
90
    private CssLayout toolBarButtonGroup = new CssLayout();
91

    
92
    private GridLayout _gridLayoutCache;
93

    
94
    private boolean isBeanLoaded;
95

    
96
    private Stack<EditorActionContext> context = new Stack<EditorActionContext>();
97

    
98
    private boolean isContextUpdated;
99

    
100
    public AbstractPopupEditor(Layout layout, Class<DTO> dtoType) {
101

    
102
        mainLayout = new VerticalLayout();
103
        // IMPORTANT: mainLayout must be set to full size otherwise the
104
        // popup window may have problems with automatic resizing of its
105
        // content.
106
        mainLayout.setSizeFull();
107
        setCompositionRoot(mainLayout);
108

    
109
        fieldGroup = new BeanFieldGroup<>(dtoType);
110
        fieldGroup.addCommitHandler(new SaveHandler());
111

    
112
        toolBar.addStyleName(ValoTheme.WINDOW_TOP_TOOLBAR);
113
        toolBar.setWidth(100, Unit.PERCENTAGE);
114
        toolBarButtonGroup.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
115
        toolBarButtonGroup.setWidthUndefined();
116
        toolBar.addComponent(toolBarButtonGroup);
117
        toolBar.setVisible(false);
118

    
119
        fieldLayout = layout;
120
        fieldLayout.setWidthUndefined();
121
        if(fieldLayout instanceof AbstractOrderedLayout){
122
            ((AbstractOrderedLayout)fieldLayout).setSpacing(true);
123
        }
124

    
125
        buttonLayout = new HorizontalLayout();
126
        buttonLayout.setStyleName(ValoTheme.WINDOW_BOTTOM_TOOLBAR);
127
        buttonLayout.setWidth(100, Unit.PERCENTAGE);
128
        buttonLayout.setSpacing(true);
129

    
130
        save = new Button("Save", FontAwesome.SAVE);
131
        save.setStyleName(ValoTheme.BUTTON_PRIMARY);
132
        save.addClickListener(e -> save());
133

    
134
        cancel = new Button("Cancel", FontAwesome.TRASH);
135
        cancel.addClickListener(e -> cancel());
136

    
137
        delete = new Button("Delete", FontAwesome.REMOVE);
138
        delete.setStyleName(ValoTheme.BUTTON_DANGER);
139
        delete.addClickListener(e -> delete());
140
        delete.setVisible(false);
141

    
142
        buttonLayout.addComponents(delete, save, cancel);
143
        // delete is initially invisible, let save take all space
144
        buttonLayout.setExpandRatio(save, 1);
145
        buttonLayout.setComponentAlignment(delete, Alignment.TOP_RIGHT);
146
        buttonLayout.setComponentAlignment(save, Alignment.TOP_RIGHT);
147
        buttonLayout.setComponentAlignment(cancel, Alignment.TOP_RIGHT);
148

    
149
        mainLayout.addComponents(toolBar, fieldLayout, buttonLayout);
150
        mainLayout.setComponentAlignment(toolBar, Alignment.TOP_RIGHT);
151

    
152
        updateToolBarVisibility();
153
    }
154

    
155
    protected VerticalLayout getMainLayout() {
156
        return mainLayout;
157
    }
158

    
159
    protected Layout getFieldLayout() {
160
        return fieldLayout;
161
    }
162

    
163
    /**
164
     * @return
165
     */
166
    private GridLayout gridLayout() {
167
        if(_gridLayoutCache == null){
168
            if(fieldLayout instanceof GridLayout){
169
                _gridLayoutCache = (GridLayout)fieldLayout;
170
            } else {
171
                throw new RuntimeException("The fieldlayout of this editor is not a GridLayout");
172
            }
173
        }
174
        return _gridLayoutCache;
175
    }
176

    
177
    @Override
178
    public void setReadOnly(boolean readOnly) {
179
        super.setReadOnly(readOnly);
180
        save.setVisible(!readOnly);
181
        delete.setVisible(!readOnly);
182
        cancel.setCaption(readOnly ? "Close" : "Cancel");
183
    }
184

    
185
    /**
186
     * @return
187
     * @return
188
     */
189
    protected AbstractLayout getToolBar() {
190
        return toolBar;
191
    }
192

    
193
    /**
194
     * @return
195
     * @return
196
     */
197
    protected void toolBarAdd(Component c) {
198
        toolBar.addComponent(c, toolBar.getComponentIndex(toolBarButtonGroup) - 1);
199
        updateToolBarVisibility();
200
    }
201

    
202
    /**
203
     * @return
204
     * @return
205
     */
206
    protected void toolBarButtonGroupAdd(Component c) {
207
        toolBarButtonGroup.addComponent(c);
208
        updateToolBarVisibility();
209
    }
210

    
211
    /**
212
     * @return
213
     * @return
214
     */
215
    protected void toolBarButtonGroupRemove(Component c) {
216
        toolBarButtonGroup.removeComponent(c);
217
        updateToolBarVisibility();
218
    }
219

    
220
    /**
221
     *
222
     */
223
    private void updateToolBarVisibility() {
224
        boolean showToolbar = toolBarButtonGroup.getComponentCount() + toolBar.getComponentCount() > 1;
225
        toolBar.setVisible(toolBarButtonGroup.getComponentCount() + toolBar.getComponentCount() > 1);
226
        if(!showToolbar){
227
            mainLayout.setMargin(new MarginInfo(true, false, false, false));
228
        } else {
229
            mainLayout.setMargin(false);
230
        }
231

    
232
    }
233

    
234
    /**
235
     * The top tool-bar is initially invisible.
236
     *
237
     * @param visible
238
     */
239
    protected void setToolBarVisible(boolean visible){
240
        toolBar.setVisible(true);
241
    }
242

    
243

    
244

    
245
    // ------------------------ event handler ------------------------ //
246

    
247
    private class SaveHandler implements CommitHandler {
248

    
249
        private static final long serialVersionUID = 2047223089707080659L;
250

    
251
        @Override
252
        public void preCommit(CommitEvent commitEvent) throws CommitException {
253
            logger.debug("preCommit(), publishing EditorPreSaveEvent");
254
            // notify the presenter to start a transaction
255
            viewEventBus.publish(this, new EditorPreSaveEvent<DTO>(AbstractPopupEditor.this, getBean()));
256
        }
257

    
258
        @Override
259
        public void postCommit(CommitEvent commitEvent) throws CommitException {
260
            try {
261
                if(logger.isTraceEnabled()){
262
                    logger.trace("postCommit() publishing EditorSaveEvent for " + getBean().toString());
263
                }
264
                // notify the presenter to persist the bean and to commit the transaction
265
                viewEventBus.publish(this, new EditorSaveEvent<DTO>(AbstractPopupEditor.this, getBean()));
266
                if(logger.isTraceEnabled()){
267
                    logger.trace("postCommit() publishing DoneWithPopupEvent");
268
                }
269
                // notify the NavigationManagerBean to close the window and to dispose the view
270
                viewEventBus.publish(EventScope.UI, this, new DoneWithPopupEvent(AbstractPopupEditor.this, Reason.SAVE));
271
            } catch (Exception e) {
272
                logger.error(e);
273
                throw new CommitException("Failed to store data to backend", e);
274
            }
275
        }
276
    }
277

    
278
    protected void addCommitHandler(CommitHandler commitHandler) {
279
        fieldGroup.addCommitHandler(commitHandler);
280
    }
281

    
282

    
283
    /**
284
     * Cancel editing and discard all modifications.
285
     */
286
    @Override
287
    public void cancel() {
288
        fieldGroup.discard();
289
        viewEventBus.publish(EventScope.UI, this, new DoneWithPopupEvent(this, Reason.CANCEL));
290
    }
291

    
292
    /**
293
     * @return
294
     */
295
    private void delete() {
296
        viewEventBus.publish(this, new EditorDeleteEvent<DTO>(this, fieldGroup.getItemDataSource().getBean()));
297
        viewEventBus.publish(EventScope.UI, this, new DoneWithPopupEvent(this, Reason.DELETE));
298
    }
299

    
300
    /**
301
     * Save the changes made in the editor.
302
     */
303
    private void save() {
304
        try {
305
            fieldGroup.commit();
306
        } catch (CommitException e) {
307
            fieldGroup.getFields().forEach(f -> ((AbstractField<?>)f).setValidationVisible(true));
308
            if(e.getCause() != null && e.getCause() instanceof FieldGroupInvalidValueException){
309
                FieldGroupInvalidValueException invalidValueException = (FieldGroupInvalidValueException)e.getCause();
310
                updateFieldNotifications(invalidValueException.getInvalidFields());
311
                Notification.show("The entered data in " + invalidValueException.getInvalidFields().size() + " fields is incomplete or invalid.");
312
            } else if(e.getCause() != null && e.getCause().getCause() != null && e.getCause().getCause() instanceof PermissionDeniedException){
313
                PermissionDeniedException permissionDeniedException = (PermissionDeniedException)e.getCause().getCause();
314
                Notification.show("Permission denied", permissionDeniedException.getMessage(), Type.ERROR_MESSAGE);
315
            } else {
316
//                Logger.getLogger(this.getClass()).error("Error saving", e);
317
//                Notification.show("Error saving", Type.ERROR_MESSAGE);
318
                throw new RuntimeException("Error saving", e);
319
            }
320
        }
321
    }
322

    
323
    /**
324
     * @param invalidFields
325
     */
326
    private void updateFieldNotifications(Map<Field<?>, InvalidValueException> invalidFields) {
327
        for(Field<?> f : invalidFields.keySet()){
328
            if(f instanceof AbstractField){
329
                String message = invalidFields.get(f).getHtmlMessage();
330
                ((AbstractField)f).setComponentError(new UserError(message, ContentMode.HTML, ErrorLevel.ERROR));
331
            }
332
        }
333

    
334
    }
335

    
336
    // ------------------------ field adding methods ------------------------ //
337

    
338

    
339
    protected TextField addTextField(String caption, String propertyId) {
340
        return addField(new TextFieldNFix(caption), propertyId);
341
    }
342

    
343
    protected TextField addTextField(String caption, String propertyId, int column1, int row1,
344
            int column2, int row2)
345
            throws OverlapsException, OutOfBoundsException {
346
        return addField(new TextFieldNFix(caption), propertyId, column1, row1, column2, row2);
347
    }
348

    
349
    protected TextField addTextField(String caption, String propertyId, int column, int row)
350
            throws OverlapsException, OutOfBoundsException {
351
        return addField(new TextFieldNFix(caption), propertyId, column, row);
352
    }
353

    
354
    protected SwitchableTextField addSwitchableTextField(String caption, String textPropertyId, String switchPropertyId, int column1, int row1,
355
            int column2, int row2)
356
            throws OverlapsException, OutOfBoundsException {
357

    
358
        SwitchableTextField field = new SwitchableTextField(caption);
359
        field.bindTo(fieldGroup, textPropertyId, switchPropertyId);
360
        addComponent(field, column1, row1, column2, row2);
361
        return field;
362
    }
363

    
364
    protected SwitchableTextField addSwitchableTextField(String caption, String textPropertyId, String switchPropertyId, int column, int row)
365
            throws OverlapsException, OutOfBoundsException {
366

    
367
        SwitchableTextField field = new SwitchableTextField(caption);
368
        field.bindTo(fieldGroup, textPropertyId, switchPropertyId);
369
        addComponent(field, column, row);
370
        return field;
371
    }
372

    
373
    protected PopupDateField addDateField(String caption, String propertyId) {
374
        return addField(new PopupDateField(caption), propertyId);
375
    }
376

    
377
    protected CheckBox addCheckBox(String caption, String propertyId) {
378
        return addField(new CheckBox(caption), propertyId);
379
    }
380

    
381
    protected CheckBox addCheckBox(String caption, String propertyId, int column, int row){
382
        return addField(new CheckBox(caption), propertyId, column, row);
383
    }
384

    
385
    protected <T extends Field> T addField(T field, String propertyId) {
386
        fieldGroup.bind(field, propertyId);
387
        if(NestedFieldGroup.class.isAssignableFrom(field.getClass())){
388
            ((NestedFieldGroup)field).registerParentFieldGroup(fieldGroup);
389
        }
390
        addComponent(field);
391
        return field;
392
    }
393

    
394
    /**
395
     * Can only be used if the <code>fieldlayout</code> is a GridLayout.
396
     *
397
     * @param field
398
     *            the field to be added, not <code>null</code>.
399
     * @param propertyId
400
     * @param column
401
     *            the column index, starting from 0.
402
     * @param row
403
     *            the row index, starting from 0.
404
     * @throws OverlapsException
405
     *             if the new component overlaps with any of the components
406
     *             already in the grid.
407
     * @throws OutOfBoundsException
408
     *             if the cell is outside the grid area.
409
     */
410
    protected <T extends Field> T addField(T field, String propertyId, int column, int row)
411
            throws OverlapsException, OutOfBoundsException {
412
        fieldGroup.bind(field, propertyId);
413
        if(NestedFieldGroup.class.isAssignableFrom(field.getClass())){
414
            ((NestedFieldGroup)field).registerParentFieldGroup(fieldGroup);
415
        }
416
        addComponent(field, column, row);
417
        return field;
418
    }
419

    
420
    /**
421
     * Can only be used if the <code>fieldlayout</code> is a GridLayout.
422
     *
423
     * @param field
424
     * @param propertyId
425
     * @param column1
426
     * @param row1
427
     * @param column2
428
     * @param row2
429
     * @return
430
     * @throws OverlapsException
431
     * @throws OutOfBoundsException
432
     */
433
    protected <T extends Field> T addField(T field, String propertyId, int column1, int row1,
434
            int column2, int row2)
435
            throws OverlapsException, OutOfBoundsException {
436
        if(propertyId != null){
437
            fieldGroup.bind(field, propertyId);
438
            if(NestedFieldGroup.class.isAssignableFrom(field.getClass())){
439
                ((NestedFieldGroup)field).registerParentFieldGroup(fieldGroup);
440
            }
441
        }
442
        addComponent(field, column1, row1, column2, row2);
443
        return field;
444
    }
445

    
446
    protected Field<?> getField(Object propertyId){
447
        return fieldGroup.getField(propertyId);
448
    }
449

    
450
    protected void addComponent(Component component) {
451
        fieldLayout.addComponent(component);
452
        applyDefaultComponentStyles(component);
453
    }
454

    
455
    protected void bindField(Field field, String propertyId){
456
        fieldGroup.bind(field, propertyId);
457
    }
458

    
459
    /**
460
     * @param component
461
     */
462
    public void applyDefaultComponentStyles(Component component) {
463
        component.addStyleName(getDefaultComponentStyles());
464
    }
465

    
466
    protected abstract String getDefaultComponentStyles();
467

    
468
    /**
469
     * Can only be used if the <code>fieldlayout</code> is a GridLayout.
470
     * <p>
471
     * Adds the component to the grid in cells column1,row1 (NortWest corner of
472
     * the area.) End coordinates (SouthEast corner of the area) are the same as
473
     * column1,row1. The coordinates are zero-based. Component width and height
474
     * is 1.
475
     *
476
     * @param component
477
     *            the component to be added, not <code>null</code>.
478
     * @param column
479
     *            the column index, starting from 0.
480
     * @param row
481
     *            the row index, starting from 0.
482
     * @throws OverlapsException
483
     *             if the new component overlaps with any of the components
484
     *             already in the grid.
485
     * @throws OutOfBoundsException
486
     *             if the cell is outside the grid area.
487
     */
488
    public void addComponent(Component component, int column, int row)
489
            throws OverlapsException, OutOfBoundsException {
490
        applyDefaultComponentStyles(component);
491
        gridLayout().addComponent(component, column, row, column, row);
492
    }
493

    
494
    /**
495
     * Can only be used if the <code>fieldlayout</code> is a GridLayout.
496
     * <p>
497
     * Adds a component to the grid in the specified area. The area is defined
498
     * by specifying the upper left corner (column1, row1) and the lower right
499
     * corner (column2, row2) of the area. The coordinates are zero-based.
500
     * </p>
501
     *
502
     * <p>
503
     * If the area overlaps with any of the existing components already present
504
     * in the grid, the operation will fail and an {@link OverlapsException} is
505
     * thrown.
506
     * </p>
507
     *
508
     * @param component
509
     *            the component to be added, not <code>null</code>.
510
     * @param column1
511
     *            the column of the upper left corner of the area <code>c</code>
512
     *            is supposed to occupy. The leftmost column has index 0.
513
     * @param row1
514
     *            the row of the upper left corner of the area <code>c</code> is
515
     *            supposed to occupy. The topmost row has index 0.
516
     * @param column2
517
     *            the column of the lower right corner of the area
518
     *            <code>c</code> is supposed to occupy.
519
     * @param row2
520
     *            the row of the lower right corner of the area <code>c</code>
521
     *            is supposed to occupy.
522
     * @throws OverlapsException
523
     *             if the new component overlaps with any of the components
524
     *             already in the grid.
525
     * @throws OutOfBoundsException
526
     *             if the cells are outside the grid area.
527
     */
528
    public void addComponent(Component component, int column1, int row1,
529
            int column2, int row2)
530
            throws OverlapsException, OutOfBoundsException {
531
        applyDefaultComponentStyles(component);
532
        gridLayout().addComponent(component, column1, row1, column2, row2);
533
    }
534

    
535

    
536
    public void withDeleteButton(boolean withDelete){
537

    
538
        if(withDelete){
539
            buttonLayout.setExpandRatio(save, 0);
540
            buttonLayout.setExpandRatio(delete, 1);
541
        } else {
542
            buttonLayout.setExpandRatio(save, 1);
543
            buttonLayout.setExpandRatio(delete, 0);
544
        }
545
        delete.setVisible(withDelete);
546
    }
547

    
548

    
549
    // ------------------------ data binding ------------------------ //
550

    
551
    protected void bindDesign(Component component) {
552
        fieldLayout.removeAllComponents();
553
        fieldGroup.bindMemberFields(component);
554
        fieldLayout.addComponent(component);
555
    }
556

    
557

    
558
    public final void loadInEditor(Object identifier) {
559

    
560
        DTO beanToEdit = getPresenter().loadBeanById(identifier);
561
        fieldGroup.setItemDataSource(beanToEdit);
562
        afterItemDataSourceSet();
563
        isBeanLoaded = true;
564
    }
565

    
566
    /**
567
     * Passes the beanInstantiator to the presenter method {@link AbstractEditorPresenter#setBeanInstantiator(BeanInstantiator)}
568
     *
569
     * @param beanInstantiator
570
     */
571
    public final void setBeanInstantiator(BeanInstantiator<DTO> beanInstantiator) {
572
        getPresenter().setBeanInstantiator(beanInstantiator);
573
    }
574

    
575
    /**
576
     * Returns the bean contained in the itemDatasource of the fieldGroup.
577
     *
578
     * @return
579
     */
580
    public DTO getBean() {
581
        if(fieldGroup.getItemDataSource() != null){
582
            return fieldGroup.getItemDataSource().getBean();
583

    
584
        }
585
        return null;
586
    }
587

    
588
    /**
589
     * @return true once the bean has been loaded indicating that all fields have
590
     *   been setup configured so that the editor is ready for use.
591
     */
592
    public boolean isBeanLoaded() {
593
        return isBeanLoaded;
594
    }
595

    
596
    /**
597
     * This method should only be used by the presenter of this view
598
     *
599
     * @param bean
600
     */
601
    protected void updateItemDataSource(DTO bean) {
602
        fieldGroup.getItemDataSource().setBean(bean);
603
    }
604

    
605
    /**
606
     * This method is called after setting the item data source whereby the
607
     * {@link FieldGroup#configureField(Field<?> field)} method will be called.
608
     * In this method all fields are set to default states defined for the fieldGroup.
609
     * <p>
610
     * You can now implement this method if you need to modify the state or value of individual fields.
611
     */
612
    protected void afterItemDataSourceSet() {
613
    }
614

    
615
    // ------------------------ issue related temporary solutions --------------------- //
616
    /**
617
     * Publicly accessible equivalent to getPreseneter(), needed for
618
     * managing the presenter listeners.
619
     * <p>
620
     * TODO: refactor the presenter listeners management to get rid of this method
621
     *
622
     * @return
623
     * @deprecated marked deprecated to emphasize on the special character of this method
624
     *    which should only be used internally see #6673
625
     */
626
    @Deprecated
627
    public P presenter() {
628
        return getPresenter();
629
    }
630

    
631
    /**
632
     * Returns the context of editor actions for this editor.
633
     * The context submitted with {@link #setParentContext(Stack)} will be updated
634
     * to represent the current context.
635
     *
636
     * @return the context
637
     */
638
    public Stack<EditorActionContext> getEditorActionContext() {
639
        if(!isContextUpdated){
640
            if(getBean() == null){
641
                throw new RuntimeException("getContext() is only possible after the bean is loaded");
642
            }
643
            context.push(new AbstractEditorAction.EditorActionContext(getBean(), this));
644
            isContextUpdated = true;
645
        }
646
        return context;
647
    }
648

    
649
    /**
650
     * Set the context of editor actions parent to this editor
651
     *
652
     * @param context the context to set
653
     */
654
    public void setParentEditorActionContext(Stack<EditorActionContext> context) {
655
        this.context.addAll(context);
656
    }
657

    
658
}
(4-4/9)