Project

General

Profile

Download (23.6 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.component;
10

    
11
import java.util.ArrayList;
12
import java.util.Arrays;
13
import java.util.Collections;
14
import java.util.HashSet;
15
import java.util.List;
16
import java.util.Optional;
17
import java.util.Set;
18

    
19
import org.apache.logging.log4j.LogManager;
20
import org.apache.logging.log4j.Logger;
21
import org.osgi.dto.DTO;
22

    
23
import com.vaadin.data.Property;
24
import com.vaadin.data.Validator;
25
import com.vaadin.data.Validator.InvalidValueException;
26
import com.vaadin.data.fieldgroup.FieldGroup;
27
import com.vaadin.server.FontAwesome;
28
import com.vaadin.ui.AbstractField;
29
import com.vaadin.ui.Button;
30
import com.vaadin.ui.Component;
31
import com.vaadin.ui.CssLayout;
32
import com.vaadin.ui.Field;
33
import com.vaadin.ui.GridLayout;
34
import com.vaadin.ui.themes.ValoTheme;
35

    
36
import eu.etaxonomy.cdm.vaadin.component.ButtonFactory;
37
import eu.etaxonomy.vaadin.event.EditorActionType;
38
import eu.etaxonomy.vaadin.event.EntityEditorActionEvent;
39
import eu.etaxonomy.vaadin.event.EntityEditorActionListener;
40
import eu.etaxonomy.vaadin.mvp.AbstractCdmEditorPresenter;
41
import eu.etaxonomy.vaadin.permission.EditPermissionTester;
42

    
43
/**
44
 * Manages the a collection of items internally as LinkedList<V>. If the Collection to operate on is a Set a Converter must be
45
 * set. Internally used fields are used in un-buffered mode. The actual instances of the field type <code>F</code> to be used to
46
 * edit or select the entities is created by a implementation of the <code>EntityFieldInstantiator</code>,
47
 * see {@link #setEntityFieldInstantiator(EntityFieldInstantiator).
48
 *
49
 * @author a.kohlbecker
50
 * @since May 11, 2017
51
 *
52
 */
53
public class ToManyRelatedEntitiesListSelect<V extends Object, F extends AbstractField<V>>  extends CompositeCustomField<List<V>> {
54

    
55
    private static final long serialVersionUID = 4670707714503199599L;
56

    
57
    private final static Logger logger = LogManager.getLogger();
58

    
59
    protected Class<F> fieldType;
60

    
61
    protected Class<V> itemType;
62

    
63
    private FieldGroup parentFieldGroup = null;
64

    
65
    private Boolean valueInitiallyWasNull = null;
66

    
67
    protected boolean isOrderedCollection = false;
68

    
69
    /**
70
     * with a button to edit existing and to add new entities
71
     */
72
    private boolean withEditButton = false;
73

    
74
    protected boolean addEmptyRowOnInitContent = true;
75

    
76
    private EntityFieldInstantiator<F> entityFieldInstantiator;
77

    
78
    private EditPermissionTester editPermissionTester;
79

    
80
    private EntityEditorActionListener editActionListener;
81

    
82
    /**
83
     * X index of the data field in the grid
84
     */
85
    private static final int GRID_X_FIELD = 0;
86

    
87
    private static final int GRID_X_BUTTON_GROUP = 1;
88

    
89
    private int GRID_COLS = 2;
90

    
91
    private GridLayout grid = new GridLayout(GRID_COLS, 1);
92

    
93
    private boolean creatingFields;
94

    
95
    private List<Validator> fieldValidators = new ArrayList<>();
96

    
97
    public  ToManyRelatedEntitiesListSelect(Class<V> itemType, Class<F> fieldType, String caption){
98
        this.fieldType = fieldType;
99
        this.itemType = itemType;
100
        setCaption(caption);
101
    }
102

    
103
    /**
104
     * @param field
105
     * @return
106
     */
107
    protected Integer findRow(F field) {
108
        Integer row = null;
109
        for(int r = 0; r < grid.getRows(); r++){
110
            if(grid.getComponent(GRID_X_FIELD, r).equals(field)){
111
                row = r;
112
                break;
113
            }
114
        }
115
        return row;
116
    }
117

    
118
    /**
119
     *
120
     * @return an unmodifiable List of the data Fields
121
     */
122
    protected List<F> fields() {
123
        Integer row = null;
124
        List<F> fields = new ArrayList<>();
125
        for(int r = 0; r < grid.getRows(); r++){
126
            fields.add((F) grid.getComponent(GRID_X_FIELD, r));
127
        }
128
        return fields;
129
    }
130

    
131
    /**
132
     * @param field
133
     * @return
134
     */
135
    private void addRowAfter(F field) {
136

    
137
        List<V> nestedValues = getValueFromNestedFields();
138

    
139
        if(isOrderedCollection){
140

    
141
        } else {
142

    
143
        }
144

    
145
        Integer row = findRow(field);
146

    
147
        grid.insertRow(row + 1);
148

    
149
        // setting null as value for new rows
150
        // see newFieldInstance() !!!
151
        addNewRow(row + 1, null);
152
        updateValue();
153

    
154
    }
155

    
156
    /**
157
     * @param field
158
     * @return
159
     */
160
    private void removeRow(F field) {
161

    
162
        Integer row = findRow(field);
163
        grid.removeRow(row);
164
        // TODO remove from nested fields
165
        updateValue();
166
        updateComponentStates();
167
    }
168

    
169

    
170
    /**
171
     * @param field
172
     * @return
173
     */
174
    private void moveRowDown(F field) {
175

    
176
        Integer row = findRow(field);
177
        swapRows(row);
178
    }
179

    
180
    /**
181
     * @param field
182
     * @return
183
     */
184
    private void moveRowUp(F field) {
185

    
186
        Integer row = findRow(field);
187
        swapRows(row - 1);
188
    }
189

    
190
    /**
191
     * @param i
192
     */
193
    private void swapRows(int i) {
194
        if(i >= 0 && i + 1 < grid.getRows()){
195
            grid.replaceComponent(grid.getComponent(GRID_X_FIELD, i), grid.getComponent(GRID_X_FIELD, i + 1));
196
            grid.replaceComponent(grid.getComponent(GRID_X_FIELD  + 1 , i), grid.getComponent(GRID_X_FIELD + 1, i + 1));
197
            updateComponentStates();
198
            updateValue();
199
        } else {
200
            throw new RuntimeException("Cannot swap rows out of the grid bounds");
201
        }
202
    }
203

    
204
    /**
205
     * update Value is only called in turn of UI changes like adding, removing, swapping rows
206
     */
207
    private void updateValue() {
208
        List<V> nestedValues = getValueFromNestedFields();
209
        List<V> beanList = getValue();
210
        if(beanList != null){
211
            beanList.clear();
212
            beanList.addAll(nestedValues);
213
        }
214
        setInternalValue(beanList);
215
    }
216

    
217
    /**
218
     * {@inheritDoc}
219
     */
220
    @Override
221
    protected Component initContent() {
222
        grid.setColumnExpandRatio(0, 1.0f);
223
        // set internal value to null to add an empty row
224

    
225
        if(addEmptyRowOnInitContent){
226
            // add an empty row
227
            setInternalValue(null);
228
        }
229
        return grid;
230
    }
231

    
232
    /**
233
     * {@inheritDoc}
234
     */
235
    @Override
236
    public Class getType() {
237
        return List.class;
238
    }
239

    
240
    /**
241
     * {@inheritDoc}
242
     */
243
    @Override
244
    protected void setInternalValue(List<V> newValue) {
245

    
246
        super.setInternalValue(newValue);
247

    
248
        if(valueInitiallyWasNull == null){
249
            valueInitiallyWasNull = newValue == null;
250
        }
251

    
252
        if(newValue != null){
253
            // newValue is already converted, need to use the original value from the data source
254
            boolean isListType = List.class.isAssignableFrom(getPropertyDataSource().getValue().getClass());
255
            // if(valueInitiallyWasNull && isOrderedCollection != isListType){
256
            if(valueInitiallyWasNull && isOrderedCollection != isListType){
257
                // need to reset the grid in this case, so that the button groups are created correctly
258
                clearRows();
259
            }
260
            isOrderedCollection = isListType;
261
        } else {
262
            clearRows();
263
        }
264

    
265
        if(!creatingFields){
266
            createFieldsForData();
267
        }
268
    }
269

    
270
    private void clearRows() {
271
        grid.removeAllComponents();
272
        grid.setRows(1);
273
    }
274

    
275
    private void createFieldsForData(){
276

    
277
        creatingFields = true;
278
        List<V> data = getValue();
279
        if(data == null || data.isEmpty()){
280
            data = Arrays.asList((V)null);
281
        }
282
        for(int row = 0; row < data.size(); row++){
283
            boolean newRowNeeded = true;
284
            if(grid.getRows() > row){
285
                Component fieldComponent = grid.getComponent(GRID_X_FIELD, row);
286
                if(fieldComponent != null){
287
                    newRowNeeded = false;
288
                    F field = (F)fieldComponent;
289
                    if(data.get(row) != null && field.getValue() != data.get(row)){
290
                        boolean roState = field.isReadOnly();
291
                        field.setReadOnly(false);
292
                        field.setValue(data.get(row));
293
                        field.setReadOnly(roState);
294
                    }
295
                }
296
            }
297
            if(newRowNeeded){
298
                addNewRow(row, data.get(row));
299
            } else {
300
                // update the editOrCreate buttons
301
                ButtonGroup bg = (ToManyRelatedEntitiesListSelect<V, F>.ButtonGroup) grid.getComponent(GRID_X_BUTTON_GROUP, row);
302
                updateEditOrCreateButton(bg, data.get(row));
303
            }
304
        }
305
        creatingFields = false;
306
    }
307

    
308
    /**
309
     * Obtains the List of values directly from the nested fields and ignores the
310
     * value of the <code>propertyDataSource</code>. This is useful when the ToManyRelatedEntitiesListSelect
311
     * is operating on a transient field, in which case the property is considered being read only by vaadin
312
     * so that the commit is doing nothing.
313
     *
314
     * See also {@link AbstractCdmEditorPresenter#handleTransientProperties(DTO bean)}
315
     *
316
     * @return
317
     */
318
    public List<V> getValueFromNestedFields() {
319
        List<V> nestedValues = new ArrayList<>();
320
        for(F f : getNestedFields()) {
321
            logger.trace(
322
                    String.format("getValueFromNestedFields() - %s:%s",
323
                       f != null ? f.getClass().getSimpleName() : "null",
324
                       f != null && f.getValue() != null ? f.getValue() : "null"
325
            ));
326
            V value = f.getValue();
327
            if(f != null /*&& value != null*/){
328
                nestedValues.add(f.getValue());
329
            }
330
         }
331
        return nestedValues;
332
    }
333

    
334
    /**
335
     * @param row the row index, starting from 0.
336
     * @param val
337
     * @return
338
     */
339
    protected int addNewRow(int row, V val) {
340
        try {
341
            F field = newFieldInstance(val);
342
            for(Validator validator : fieldValidators) {
343
                field.addValidator(validator);
344
            }
345
            ButtonGroup buttonGroup = new ButtonGroup(field);
346
            updateEditOrCreateButton(buttonGroup, val);
347
            field.addValueChangeListener(e -> {
348
                if(!creatingFields){
349
                    updateValue();
350
                    Object value = e.getProperty().getValue();
351
                    updateEditOrCreateButton(buttonGroup, value);
352
                    fireValueChange(true);
353
                }
354
            });
355
            Property ds = getPropertyDataSource();
356
            if(ds != null){
357
                Object parentVal = ds.getValue();
358
            }
359
            addStyledComponent(field);
360

    
361
            // important! all fields must be un-buffered
362
            field.setBuffered(false);
363

    
364
            if(getNestedFields().size() == grid.getRows()){
365
                grid.setRows(grid.getRows() + 1);
366
            }
367
            grid.addComponent(field, GRID_X_FIELD, row);
368
            grid.addComponent(buttonGroup, GRID_X_BUTTON_GROUP, row);
369
            updateComponentStates();
370
            nestFieldGroup(field);
371
            row++;
372
        } catch (InstantiationException e) {
373
            // TODO Auto-generated catch block
374
            e.printStackTrace();
375
        } catch (IllegalAccessException e) {
376
            // TODO Auto-generated catch block
377
            e.printStackTrace();
378
        }
379
        return row;
380
    }
381

    
382
    /**
383
     * @param buttonGroup
384
     * @param value
385
     */
386
    public void updateEditOrCreateButton(ButtonGroup buttonGroup, Object value) {
387

    
388
        if(!withEditButton || buttonGroup == null || buttonGroup.getEditOrCreateButton() == null){
389
            return;
390
        }
391

    
392
        ButtonFactory buttonStyle;
393
        if(value == null){
394
            buttonStyle = ButtonFactory.CREATE_NEW;
395
        } else {
396
            buttonStyle = ButtonFactory.EDIT_ITEM;
397
        }
398
        buttonGroup.getEditOrCreateButton().setIcon(buttonStyle.getIcon());
399
        buttonGroup.getEditOrCreateButton().setDescription(buttonStyle.getDescription());
400
    }
401

    
402
    class ButtonGroup extends CssLayout{
403

    
404
        private Button editOrCreate;
405

    
406
        ButtonGroup (F field){
407

    
408
            Button add = ButtonFactory.ADD_ITEM.createButton();
409
            add.setDescription("Add item");
410
            add.addClickListener(e -> addRowAfter(field));
411

    
412
            if(withEditButton){
413
                editOrCreate = ButtonFactory.EDIT_ITEM.createButton();
414
                editOrCreate.addClickListener(e -> editOrCreate(field));
415
                addComponent(editOrCreate);
416
                addStyledComponents(editOrCreate);
417
            }
418

    
419
            Button remove = ButtonFactory.REMOVE_ITEM.createButton();
420
            remove.setDescription("Remove item");
421
            remove.addClickListener(e -> removeRow(field));
422

    
423

    
424
            addComponent(add);
425
            addComponent(remove);
426
            addStyledComponents(add, remove);
427
            if(isOrderedCollection){
428
                Button moveUp = new Button(FontAwesome.ARROW_UP);
429
                moveUp.setDescription("Move up");
430
                moveUp.addClickListener(e -> moveRowUp(field));
431
                Button moveDown = new Button(FontAwesome.ARROW_DOWN);
432
                moveDown.addClickListener(e -> moveRowDown(field));
433
                moveDown.setDescription("Move down");
434

    
435
                addComponents(moveUp, moveDown);
436
                addStyledComponents(moveUp, moveDown);
437
            }
438
            setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
439
        }
440

    
441
        Button getEditOrCreateButton(){
442
            return editOrCreate;
443
        }
444

    
445
    }
446

    
447
    /**
448
     * @param e
449
     * @return
450
     */
451
    private void editOrCreate(F field) {
452

    
453
        if(editActionListener == null){
454
            throw new RuntimeException("editActionListener missing");
455
        }
456

    
457
        if(field.getValue() == null){
458
            // create
459
            editActionListener.onEntityEditorActionEvent(new EntityEditorActionEvent<V>(EditorActionType.ADD, null, field));
460
        } else {
461
            // edit
462
            V value = field.getValue();
463
            editActionListener.onEntityEditorActionEvent(new EntityEditorActionEvent<V>(EditorActionType.EDIT, (Class<V>) value.getClass(), value, field));
464
        }
465
    }
466

    
467
    private void updateComponentStates(){
468

    
469
        boolean isWritable = !getState().readOnly;
470
        int fieldsCount = getNestedFields().size();
471

    
472
        for(int row = 0; row < fieldsCount; row++){
473

    
474
            boolean isFirst = row == 0;
475
            boolean isLast = row == fieldsCount - 1;
476

    
477
            F field = (F) grid.getComponent(GRID_X_FIELD, row);
478
            CssLayout buttonGroup = (CssLayout) grid.getComponent(GRID_X_FIELD + 1, row);
479

    
480
            boolean isWritableField = isWritableField(field);
481
            field.setReadOnly(!isWritableField);
482

    
483
            int addButtonIndex = 0;
484
            if(withEditButton){
485
                addButtonIndex++;
486
                // edit
487
                Button editCreateButton = ((Button)buttonGroup.getComponent(0));
488
                editCreateButton.setDescription(field.getValue() == null ? "New" : "Edit");
489
            }
490
            // add
491
            buttonGroup.getComponent(addButtonIndex).setEnabled(isWritable && (isLast || isOrderedCollection));
492
            // remove
493
            // can be always true, removing the last entry causes an new empty entry to be added.
494
            buttonGroup.getComponent(addButtonIndex + 1).setEnabled(isWritable);
495
            // up
496
            if(isOrderedCollection && buttonGroup.getComponentCount() >  addButtonIndex + 2){
497
                buttonGroup.getComponent(addButtonIndex + 2).setEnabled(isWritable && !isFirst);
498
                // down
499
                buttonGroup.getComponent(addButtonIndex + 3).setEnabled(isWritable && !isLast);
500
            }
501
        }
502
    }
503

    
504
    /**
505
     * @param isWritable
506
     * @param field
507
     * @return
508
     */
509
    public boolean isWritableField(F field) {
510
        boolean isWritable = !getState().readOnly;
511
        return isWritable && (field.getValue() == null
512
                || field.getValue() != null && testEditButtonPermission(field.getValue()));
513
    }
514

    
515
    /**
516
     * @param field
517
     * @return
518
     */
519
    protected boolean testEditButtonPermission(Object rowValue) {
520
        if(editPermissionTester != null) {
521
            return editPermissionTester.userHasEditPermission(rowValue);
522
        } else {
523
            return true;
524
        }
525
    }
526

    
527

    
528
    protected List<F> getNestedFields(){
529

    
530
        List<F> nestedFields = new ArrayList<>(grid.getRows());
531
        for(int r = 0; r < grid.getRows(); r++){
532
            F f = (F) grid.getComponent(GRID_X_FIELD, r);
533
            if(f == null){
534
                logger.debug(String.format("NULL field at %d,%d", GRID_X_FIELD, r));
535
            } else {
536
                logger.trace(String.format("field " + f.hashCode() + " at %d,%d", GRID_X_FIELD, r) + ", value: " + f.getValue());
537
                nestedFields.add(f);
538
            }
539
        }
540
        return Collections.unmodifiableList(nestedFields);
541
    }
542

    
543
    /**
544
     *
545
     * @param val
546
     * @return
547
     * @throws InstantiationException
548
     * @throws IllegalAccessException
549
     */
550
    protected F newFieldInstance(V val) throws InstantiationException, IllegalAccessException {
551

    
552
        F field;
553
        if(entityFieldInstantiator != null){
554
            field = entityFieldInstantiator.createNewInstance();
555
        } else {
556
            field = fieldType.newInstance();
557
        }
558

    
559
        field.setWidth(100, Unit.PERCENTAGE);
560
        field.setValue(val);
561

    
562
        // TODO
563
        // when passing null as value the field must take care of creating a new
564
        // instance by overriding setValue() in future we could improve this by passing a
565
        // NewInstanceFactory to this class
566
        return field;
567
    }
568

    
569
    /**
570
     * Adds the validator to the list of validators which
571
     * are applied to new fields and adds the validator to
572
     * existing fields
573
     *
574
     * @param validator
575
     */
576
    public void addFieldValidator(Validator validator){
577
        fieldValidators.add(validator);
578
        for(F field : fields()) {
579
            field.addValidator(validator);
580
        }
581
    }
582

    
583
    /**
584
     * removes the validator from the list of validators which
585
     * are applied to new fields and removes the validator from
586
     * existing fields
587
     *
588
     * @param validator
589
     */
590
    public void removeFieldValidator(Validator validator){
591
        fieldValidators.remove(validator);
592
        for(F field : fields()) {
593
            field.removeValidator(validator);
594
        }
595
    }
596

    
597
    /**
598
     * @return a unmodifialble List of the fieldValidators
599
     */
600
    public List<Validator> getFieldValidators() {
601
        return Collections.unmodifiableList(fieldValidators);
602
    }
603

    
604
    /**
605
     * Handle the data binding of the sub fields. Sub-fields can either be composite editor fields
606
     * or 'simple' fields, usually select fields.
607
     * <p>
608
     * Composite editor fields allow editing the nested bean Items and must implement the
609
     * {@link NestedFieldGroup} interface. Simple fields are only instantiated in
610
     * {@link #newFieldInstance(Object)} where the value of the field is set. No further binding is needed
611
     * for these 'simple' fields.
612
     *
613
     * @param field
614
     */
615
    protected void nestFieldGroup(F field) {
616
        if(NestedFieldGroup.class.isAssignableFrom(fieldType) && parentFieldGroup != null){
617
            ((NestedFieldGroup)field).registerParentFieldGroup(parentFieldGroup);
618
        }
619
    }
620

    
621
    /**
622
     * {@inheritDoc}
623
     * <p>
624
     * However, this class has no local fieldGroup but must delegate to the nested NestedFieldGroup
625
     * if there are any. This happens in {@link #nestFieldGroup(AbstractField)}.
626
     * <p>
627
     */
628
    @Override
629
    public Optional<FieldGroup> getFieldGroup() {
630
        return Optional.empty();
631
    }
632

    
633
    /**
634
     * This ToMany-CompositeCustomField has no own fields and this no local fieldGroup (see {@link #getFieldGroup()})
635
     * which allow changing data. Editing of the list items is delegated to
636
     * a list of sub-fields which are responsible for editing and committing the changes.
637
     * Therefore the <code>parentFieldGroup</code> is only stored in a local field so that it can
638
     * be passed to per item fields in {@link #nestFieldGroup}
639
     *
640
     * {@inheritDoc}
641
     */
642
    @Override
643
    public void registerParentFieldGroup(FieldGroup parent) {
644
        parentFieldGroup = parent;
645
    }
646

    
647
    /**
648
     * {@inheritDoc}
649
     */
650
    @Override
651
    public void commit() throws SourceException, InvalidValueException {
652

    
653
        List<F> nestedFields = getNestedFields();
654
        Set<F> emptyFields = new HashSet<>();
655
        for(F f : nestedFields){
656
            f.commit();
657
            if(f.getValue() == null){
658
                emptyFields.add(f);
659
            }
660
        }
661
        for(F deleteF : emptyFields){
662
            removeRow(deleteF);
663
        }
664
        /*
665
        List<V> list = (List<V>) getPropertyDataSource().getValue();
666

    
667
        Person p = Person.NewInstance();
668
        p.setTitleCache("Hacky", true);
669
        list.add((V) p);
670

    
671
        List<V> clonedList = new ArrayList<>(list);
672
        list.clear();
673
        for(V value : clonedList){
674
            if(value != null){
675
                list.add(value);
676
            }
677
        }
678
        //
679
         */
680
        // calling super.commit() is useless if operating on a transient property!!
681
        super.commit();
682
//        if(getValue().isEmpty() && valueInitiallyWasNull){
683
//            setPropertyDataSource(null);
684
//        }
685
    }
686

    
687
    /**
688
     * {@inheritDoc}
689
     */
690
    @Override
691
    public void setWidth(String width) {
692
        super.setWidth(width);
693
        grid.setWidth(width);
694
    }
695

    
696
    @Override
697
    public void setWidth(float width, Unit unit){
698
        super.setWidth(width, unit);
699
        if(grid != null){
700
            grid.setWidth(width, unit);
701
        }
702
    }
703

    
704
    /**
705
     * {@inheritDoc}
706
     */
707
    @Override
708
    protected void addDefaultStyles() {
709
        // no default styles
710
    }
711

    
712

    
713
    /**
714
     * with a button edit existing and to add new entities
715
     */
716
    public void withEditButton(boolean withEditButton){
717
        this.withEditButton = withEditButton;
718
        if(getPropertyDataSource() != null) {
719
            throw new RuntimeException("withEditButton must not be changed after the datasource is set.");
720
        }
721
    }
722

    
723
    /**
724
     * {@inheritDoc}
725
     */
726
    @Override
727
    public boolean hasNullContent() {
728

    
729
        for(Field f : getNestedFields()){
730
            if(f instanceof CompositeCustomField){
731
                if(!((CompositeCustomField)f).hasNullContent()){
732
                    return false;
733
                }
734
            }
735
        }
736
        return true;
737
    }
738

    
739
    /**
740
     * @return the enityFieldInstantiator
741
     */
742
    public EntityFieldInstantiator<F> getEntityFieldInstantiator() {
743
        return entityFieldInstantiator;
744
    }
745

    
746
    /**
747
     * @param enityFieldInstantiator the enityFieldInstantiator to set
748
     */
749
    public void setEntityFieldInstantiator(EntityFieldInstantiator<F> entityFieldInstantiator) {
750
        this.entityFieldInstantiator = entityFieldInstantiator;
751
    }
752

    
753
    /**
754
     * {@inheritDoc}
755
     */
756
    @Override
757
    public void setReadOnly(boolean readOnly) {
758
        super.setReadOnly(readOnly);
759
        updateComponentStates();
760
    }
761

    
762

    
763
    /**
764
     * @return the editPermissionTester
765
     */
766
    public EditPermissionTester getEditPermissionTester() {
767
        return editPermissionTester;
768
    }
769

    
770
    /**
771
     * @param editPermissionTester the editPermissionTester to set
772
     */
773
    public void setEditPermissionTester(EditPermissionTester editPermissionTester) {
774
        this.editPermissionTester = editPermissionTester;
775
    }
776

    
777
    /**
778
     * @return the editActionListener
779
     */
780
    public EntityEditorActionListener getEditActionListener() {
781
        return editActionListener;
782
    }
783

    
784
    /**
785
     * @param editActionListener the editActionListener to set
786
     */
787
    public void setEditActionListener(EntityEditorActionListener editActionListener) {
788
        this.editActionListener = editActionListener;
789
    }
790

    
791
}
(14-14/19)