jenkins merging release branch into master (strategy: theirs)
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / vaadin / component / WeaklyRelatedEntityCombobox.java
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.Objects;
12 import java.util.Optional;
13 import java.util.UUID;
14
15 import org.vaadin.viritin.fields.LazyComboBox.FilterableCountProvider;
16 import org.vaadin.viritin.fields.LazyComboBox.FilterablePagingProvider;
17
18 import com.vaadin.data.Property;
19 import com.vaadin.data.Validator.InvalidValueException;
20 import com.vaadin.data.fieldgroup.FieldGroup;
21 import com.vaadin.data.util.converter.Converter.ConversionException;
22 import com.vaadin.server.AbstractErrorMessage.ContentMode;
23 import com.vaadin.server.ErrorMessage.ErrorLevel;
24 import com.vaadin.server.UserError;
25 import com.vaadin.ui.Button;
26 import com.vaadin.ui.Button.ClickListener;
27 import com.vaadin.ui.Component;
28 import com.vaadin.ui.CssLayout;
29 import com.vaadin.ui.Field;
30 import com.vaadin.ui.themes.ValoTheme;
31
32 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
33 import eu.etaxonomy.cdm.model.permission.CRUD;
34 import eu.etaxonomy.cdm.service.IFilterableStringRepresentationPagingProvider;
35 import eu.etaxonomy.cdm.service.UserHelperAccess;
36 import eu.etaxonomy.cdm.vaadin.component.ButtonFactory;
37 import eu.etaxonomy.cdm.vaadin.event.NestedButtonStateUpdater;
38
39 /**
40 * By AM: Combobox (with buttons) for switching back and forth from a TextBox.
41 *
42 * @author a.kohlbecker
43 * @since May 24, 2017
44 */
45 public class WeaklyRelatedEntityCombobox<V extends IdentifiableEntity<?>>
46 extends CompositeCustomField<String>
47 implements WeaklyRelatedEntityField<V>, ReloadableSelect {
48
49 private static final long serialVersionUID = 6277565876657520311L;
50
51 public static final String PRIMARY_STYLE = "v-related-entity-combobox";
52
53 private Class<V> type;
54
55 private CssLayout container = new CssLayout();
56
57 private ReloadableLazyComboBox<String> lazySelect;
58
59 private Button addButton = ButtonFactory.CREATE_NEW.createButton();
60 private Button editButton = ButtonFactory.EDIT_ITEM.createButton();
61
62 private WeaklyRelatedEntityButtonUpdater buttonUpdater;
63
64 private IFilterableStringRepresentationPagingProvider<UUID> filterablePagingProvider;
65
66 public WeaklyRelatedEntityCombobox(String caption, Class<V> type){
67 this.type = type;
68 setCaption(caption);
69 lazySelect = new ReloadableLazyComboBox<String>(String.class);
70 addStyledComponents(lazySelect, addButton, editButton);
71 addSizedComponents(lazySelect, container);
72 buttonUpdater = new WeaklyRelatedEntityButtonUpdater(this);
73 lazySelect.addValueChangeListener(buttonUpdater);
74 lazySelect.setValidationVisible(true);
75 lazySelect.addValueChangeListener(e -> {
76 // update the itemContainer immediately so that the edit button acts on the chosen item
77 // TODO In contrast to ToOneRelatedEntityCombobox where getValue() is overwritten to call
78 // commitSelect() calling this method would most probably remove all strings which do not have a
79 // weakly related entity. Such behavior would be very unfriendly to users.
80 try {
81 lazySelect.commit();
82 } catch (InvalidValueException ie){
83 /* Ignore here */
84 }
85 });
86 }
87
88 @Override
89 public void addValueChangeListener(ValueChangeListener valueChangeListener){
90 super.addValueChangeListener(valueChangeListener);
91 lazySelect.addValueChangeListener(valueChangeListener);
92 }
93
94 @Override
95 protected Component initContent() {
96 container.addComponents(lazySelect, addButton, editButton);
97 setPrimaryStyleName(PRIMARY_STYLE);
98 addDefaultStyles();
99 return container;
100 }
101
102 @Override
103 public Class<String> getType() {
104 return String.class;
105 }
106
107 @Override
108 protected void addDefaultStyles() {
109 container.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
110 }
111
112 @Override
113 public Optional<FieldGroup> getFieldGroup() {
114 return Optional.empty();
115 }
116
117 public ReloadableLazyComboBox<String> getSelect() {
118 return lazySelect;
119 }
120
121 public void loadFrom(FilterablePagingProvider<String> filterablePagingProvider, FilterableCountProvider filterableCountProvider, int pageLength) {
122
123 this.filterablePagingProvider = (IFilterableStringRepresentationPagingProvider<UUID>) filterablePagingProvider;
124 lazySelect.loadFrom(filterablePagingProvider, filterableCountProvider, pageLength);
125 buttonUpdater.updateButtons(getValue());
126 }
127
128 /**
129 * reload the selected entity from the persistent storage
130 */
131 @Override
132 public void reload() {
133 filterablePagingProvider.clearIdCache();
134 getSelect().reload();
135 }
136
137 @Override
138 public void setAddButtonEnabled(boolean enabled) {
139 addButton.setEnabled(enabled);
140 }
141
142 @Override
143 public void addClickListenerAddEntity(ClickListener listener) {
144 addButton.addClickListener(listener);
145 }
146
147 @Override
148 public void setEditButtonEnabled(boolean enabled) {
149 editButton.setEnabled(enabled);
150 }
151
152 @Override
153 public void addClickListenerEditEntity(ClickListener listener) {
154 editButton.addClickListener(listener);
155 }
156
157 @Override
158 public void selectNewItem(String bean){
159 setValue(bean);
160 }
161
162 public UUID getIdForValue(){
163 return filterablePagingProvider.idFor(getValue());
164 }
165
166 /**
167 * sets the selection to the <code>newFieldValue</code> only if the value can
168 * be provided by the FilterablePagingProvider
169 */
170 @Override
171 public void setValue(String newFieldValue) throws com.vaadin.data.Property.ReadOnlyException, ConversionException {
172 if(!Objects.equals(newFieldValue, lazySelect.getValue())){
173 //if value changed...
174 if(contains(newFieldValue)){
175 lazySelect.setValue(newFieldValue);
176 lazySelect.markAsDirty();
177 }else if (lazySelect.getValue() != null) {
178 //if new suggested value does not exist in the list,
179 //set the value of the combobox to null, if it it not yet null
180 lazySelect.setValue(null);
181 lazySelect.markAsDirty();
182 }
183 }
184 }
185
186 @Override
187 public String getValue() {
188 return lazySelect.getValue();
189
190 }
191
192 private boolean contains(String newFieldValue) {
193 UUID id = filterablePagingProvider.idFor(newFieldValue);
194 return id != null;
195 }
196
197 @Override
198 public boolean isValueInOptions(){
199 return lazySelect.getOptions().contains(lazySelect.getValue());
200 }
201
202 @Override
203 public void setPropertyDataSource(Property newDataSource) {
204 lazySelect.setPropertyDataSource(newDataSource);
205 if(buttonUpdater != null){
206 buttonUpdater.updateButtons(lazySelect.getValue());
207 }
208 }
209
210 @Override
211 public Property getPropertyDataSource() {
212 return lazySelect.getPropertyDataSource();
213 }
214
215 @Override
216 public void setReadOnly(boolean readOnly) {
217 super.setReadOnly(readOnly);
218 setDeepReadOnly(readOnly, getContent(), null);
219 if(buttonUpdater != null){
220 buttonUpdater.updateButtons(lazySelect.getValue());
221 }
222 }
223
224 @Override
225 public void setRequired(boolean required) {
226 super.setRequired(required);
227 lazySelect.setRequired(required);
228 }
229
230 @Override
231 public void setImmediate(boolean immediate){
232 super.setImmediate(immediate);
233 lazySelect.setImmediate(immediate);
234 }
235
236 @Override
237 public void updateButtons(){
238 buttonUpdater.updateButtons(getValue());
239 }
240
241 @Override
242 public void commit() throws SourceException, InvalidValueException {
243 lazySelect.commit();
244 }
245
246 public void commitSelect() {
247 try {
248 setComponentError(null);
249 lazySelect.commit();
250 } catch (InvalidValueException ex){
251 UserError componentError = new UserError(ex.getHtmlMessage(), ContentMode.HTML, ErrorLevel.ERROR);
252 lazySelect.setComponentError(componentError);
253 }
254 }
255
256
257
258 /**
259 * {@inheritDoc}
260 * @deprecated NestedButtonStateUpdater should rather be instantiated in the RelatedEntityField instead of passing it as property
261 */
262 @Override
263 @Deprecated
264 public void setNestedButtonStateUpdater(NestedButtonStateUpdater<V> buttonUpdater) {
265 // not needed
266 }
267
268 class WeaklyRelatedEntityButtonUpdater implements NestedButtonStateUpdater<String> {
269
270 private static final long serialVersionUID = 4472031263172275012L;
271
272 WeaklyRelatedEntityCombobox<V> toOneRelatedEntityField;
273
274 public WeaklyRelatedEntityButtonUpdater(WeaklyRelatedEntityCombobox<V> toOneRelatedEntityField){
275 this.toOneRelatedEntityField = toOneRelatedEntityField;
276 updateButtons(toOneRelatedEntityField.getValue());
277 toOneRelatedEntityField.setEditButtonEnabled(false);
278 }
279
280 @Override
281 public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
282
283 String value = (String)event.getProperty().getValue();
284 updateButtons(value);
285 }
286
287 @Override
288 public void updateButtons(String value) {
289
290 UUID uuid = null;
291 if(value != null && filterablePagingProvider != null){
292 uuid = filterablePagingProvider.idFor(value);
293 }
294 boolean userIsAllowedToUpdate = uuid != null && UserHelperAccess.userHelper().userHasPermission(type, uuid, CRUD.UPDATE);
295 boolean userIsAllowedToCreate = UserHelperAccess.userHelper().userHasPermission(type, CRUD.CREATE);
296 boolean isReadOnlyField = ((Field)toOneRelatedEntityField).isReadOnly();
297
298 toOneRelatedEntityField.setAddButtonEnabled(!isReadOnlyField && userIsAllowedToCreate);
299 toOneRelatedEntityField.setEditButtonEnabled(!isReadOnlyField && userIsAllowedToUpdate);
300 }
301 }
302 }