Project

General

Profile

Download (9.23 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.Collection;
14
import java.util.List;
15

    
16
import com.vaadin.data.fieldgroup.FieldGroup;
17
import com.vaadin.data.fieldgroup.FieldGroup.CommitEvent;
18
import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
19
import com.vaadin.data.fieldgroup.FieldGroup.CommitHandler;
20
import com.vaadin.ui.AbstractField;
21
import com.vaadin.ui.Button;
22
import com.vaadin.ui.Component;
23
import com.vaadin.ui.CustomField;
24
import com.vaadin.ui.Field;
25
import com.vaadin.ui.HasComponents;
26

    
27
import eu.etaxonomy.cdm.vaadin.event.NestedButtonStateUpdater;
28

    
29
/**
30
 * TODO implement height methods for full component size support
31
 *
32
 * Implementations need to override {@link  AbstractField#setInternalValue(TeamOrPersonBase<?> newValue)} in order to
33
 * to set the item datasource of the fieldGroup for example:
34
 * <pre>
35
 * @Override
36
   protected void setInternalValue(TeamOrPersonBase<?> newValue) {
37
     ...
38
     fieldGroup.setItemDataSource(new BeanItem<Team>((Team)newValue));
39
     ...
40
   }
41
 * </pre>
42
 *
43
 * @author a.kohlbecker
44
 * @since May 12, 2017
45
 *
46
 * IMPORTANT see also {@link CompositeStyledComponent} which has almost the same functionality.
47
 *
48
 */
49
@SuppressWarnings("serial")
50
public abstract class CompositeCustomField<T> extends CustomField<T> implements NestedFieldGroup {
51

    
52
    protected static final String READ_ONLY_CAPTION_SUFFIX = " (read only)";
53

    
54
    private List<Component> styledComponents = new ArrayList<>();
55

    
56
    private List<Component> sizedComponents = new ArrayList<>();
57

    
58
    private CommitHandler commitHandler = new CommitHandler() {
59

    
60
        @Override
61
        public void preCommit(CommitEvent commitEvent) throws CommitException {
62
            // commit the nested bean(s) first
63
            if(getFieldGroup() != null){
64
                getFieldGroup().commit();
65
            }
66
        }
67

    
68
        @Override
69
        public void postCommit(CommitEvent commitEvent) throws CommitException {
70
            // noting to do
71
        }};
72

    
73
    protected List<Component> getStyledComponents() {
74
        if(styledComponents == null){
75
            styledComponents = new ArrayList<>();
76
        }
77
        return styledComponents;
78
    }
79

    
80
    /**
81
     * Implementations preferably call this method in the constructor
82
     *
83
     * @param component
84
     * @return
85
     */
86
    protected boolean addStyledComponent(Component component){
87
        applyCurrentStyleNames(component);
88
        return styledComponents.add(component);
89
    }
90

    
91
    /**
92
     * Implementations preferably call this method in the constructor
93
     *
94
     * @param component
95
     * @return
96
     */
97
    protected boolean addStyledComponents(Component ... component){
98
        List<Component> componentList = Arrays.asList(component);
99
        componentList.forEach(c -> applyCurrentStyleNames(c));
100
        return styledComponents.addAll(componentList);
101
    }
102

    
103
    protected List<Component> getSizedComponents() {
104
        if(sizedComponents == null){
105
            sizedComponents = new ArrayList<>();
106
        }
107
        return sizedComponents;
108
    }
109

    
110
    /**
111
     * Implementations preferably call this method in the constructor
112
     *
113
     * @param component
114
     * @return
115
     */
116
    protected boolean addSizedComponent(Component component){
117
        applyCurrentSize(component);
118
        return sizedComponents.add(component);
119
    }
120

    
121
    /**
122
     * Implementations preferably call this method in the constructor
123
     *
124
     * @param component
125
     * @return
126
     */
127
    protected boolean addSizedComponents(Component ... component){
128
        List<Component> componentList = Arrays.asList(component);
129
        componentList.forEach(c -> applyCurrentSize(c));
130
        return sizedComponents.addAll(componentList);
131
    }
132

    
133
    @Override
134
    public void setWidth(String width) {
135
        super.setWidth(width);
136
        getSizedComponents().forEach(c -> {if(c != null) {c.setWidth(width);}});
137
    }
138

    
139
    @Override
140
    public void setWidth(float width, Unit unit){
141
        super.setWidth(width, unit);
142
        getSizedComponents().forEach(c -> {if(c != null) {c.setWidth(width, unit);}});
143
    }
144

    
145
    @Override
146
    public void setWidthUndefined() {
147
        super.setWidthUndefined();
148
        getSizedComponents().forEach(c -> {if(c != null) {c.setWidthUndefined();}});
149
    }
150

    
151
    @Override
152
    public void setStyleName(String style) {
153
        super.setStyleName(style);
154
        getStyledComponents().forEach(c -> c.setStyleName(style));
155
        addDefaultStyles();
156
    }
157

    
158
    @Override
159
    public void addStyleName(String style) {
160
        super.addStyleName(style);
161
        getStyledComponents().forEach(c -> c.addStyleName(style));
162
    }
163

    
164
    protected void applyCurrentStyleNames(Component newSubComponent){
165
        newSubComponent.setStyleName(getStyleName());
166
    }
167

    
168
    protected void applyCurrentSize(Component newSubComponent){
169
        newSubComponent.setWidth(this.getWidth(), this.getWidthUnits());
170
    }
171

    
172
    /**
173
     * Implementations can may apply default styles to components added to <code>StyledComponents</code>
174
     * to prevent these styles from being overwritten when setStyleName() id called on the composite field.
175
     */
176
    protected abstract void addDefaultStyles();
177

    
178
    /**
179
     * Implementations return the local fieldGroup
180
     *
181
     * @return
182
     */
183
    @Override
184
    public abstract FieldGroup getFieldGroup();
185

    
186
    /**
187
     * @return true if all fields having the value <code>null</code>
188
     */
189
    @SuppressWarnings("rawtypes")
190
    public boolean hasNullContent() {
191
        Collection<Field> nullValueCheckIgnore = nullValueCheckIgnoreFields();
192
        return getFieldGroup().getFields().stream()
193
                .filter(
194
                        f -> !nullValueCheckIgnore.contains(f)
195
                )
196
                //.peek( f -> System.out.println("###> " + f.getCaption() + ": " + f.getValue()))
197
                .allMatch(
198
                        f -> {
199
                            if(f instanceof CompositeCustomField){
200
                                return ((CompositeCustomField)f).hasNullContent();
201
                            } else {
202
                                if(f.getValue() == null) {
203
                                    return true;
204
                                } else {
205
                                    return false;
206
                                }
207
                            }
208
                        }
209
                );
210
    }
211

    
212
    /**
213
     * @return
214
     */
215
    protected List<Field> nullValueCheckIgnoreFields() {
216
        // TODO Auto-generated method stub
217
        return new ArrayList<Field>(0);
218
    }
219

    
220
    @Override
221
    public void registerParentFieldGroup(FieldGroup parent) {
222
        parent.addCommitHandler(commitHandler);
223
    }
224

    
225
    @Override
226
    public void unregisterParentFieldGroup(FieldGroup parent) {
227
        parent.removeCommitHandler(commitHandler);
228
    }
229

    
230
    /**
231
     *
232
     * @param readOnly
233
     *            the readonly state
234
     * @param component
235
     *            the component to process on
236
     * @param ignore
237
     *            can be <code>null</code>
238
     */
239
    protected void setDeepReadOnly(boolean readOnly, Component component, Collection<Component> ignore) {
240

    
241
        if(ignore != null && ignore.contains(component)){
242
            return;
243
        }
244

    
245
        applyReadOnlyState(component, readOnly);
246
        if(HasComponents.class.isAssignableFrom(component.getClass())){
247
            for(Component nestedComponent : ((HasComponents)component)){
248
                setDeepReadOnly(readOnly, nestedComponent, ignore);
249
            }
250
        }
251
    }
252

    
253
    /**
254
     * Sets the readonly state for the component but treats Buttons differently.
255
     * For nested Buttons the readonly state is projected to enabled state to
256
     * make them inactive. Finally NestedButtonStateUpdaters are triggered to
257
     * allow to disable buttons accordingly to user permissions.
258
     *
259
     * @param readOnly
260
     * @param component
261
     */
262
    protected void applyReadOnlyState(Component component, boolean readOnly) {
263
        component.setReadOnly(readOnly);
264
        if(Button.class.isAssignableFrom(component.getClass())){
265
            component.setEnabled(!readOnly);
266
        }
267
        triggerNestedButtonStateUpdaters();
268
    }
269

    
270
    /**
271
     *
272
     */
273
    protected void triggerNestedButtonStateUpdaters() {
274
        for(Object l : getListeners(AbstractField.ValueChangeEvent.class)){
275
           if(NestedButtonStateUpdater.class.isAssignableFrom(l.getClass())){
276
               // trigger a fake ValueChangeEvent to let the ToOneRelatedEntityButtonUpdater fix the states
277
               // of nested buttons
278
               ((NestedButtonStateUpdater)l).valueChange(new AbstractField.ValueChangeEvent(this));
279
           }
280
        }
281

    
282
    }
283

    
284
    @Override
285
    public String toString(){
286
        return this.getClass().getSimpleName() + ": " +
287
                ( getValue() != null ? getValue() : "null");
288
    }
289

    
290
    protected void updateCaptionReadonlyNotice(boolean readOnly) {
291
        if(readOnly){
292
            setCaption(getCaption() + READ_ONLY_CAPTION_SUFFIX);
293
        } else {
294
            setCaption(getCaption().replace(READ_ONLY_CAPTION_SUFFIX, ""));
295
        }
296
    }
297

    
298
}
(1-1/17)