View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.form.field;
35  
36  import info.magnolia.cms.i18n.I18nContentSupport;
37  import info.magnolia.objectfactory.ComponentProvider;
38  import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
39  import info.magnolia.ui.form.field.definition.ConfiguredFieldDefinition;
40  import info.magnolia.ui.form.field.definition.SwitchableFieldDefinition;
41  import info.magnolia.ui.form.field.factory.FieldFactoryFactory;
42  
43  import java.util.HashMap;
44  import java.util.Map;
45  
46  import org.apache.commons.lang3.StringUtils;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  import com.vaadin.ui.Component;
51  import com.vaadin.v7.data.Item;
52  import com.vaadin.v7.data.util.PropertysetItem;
53  import com.vaadin.v7.ui.Field;
54  import com.vaadin.v7.ui.Label;
55  import com.vaadin.v7.ui.VerticalLayout;
56  
57  /**
58   * Switchable field.<br>
59   * Display a field composed of two main sections <br>
60   * - a Select Section (list or checkBox) <br>
61   * - a Field Section displaying the field currently selected. <br>
62   */
63  public class SwitchableField extends AbstractCustomMultiField<SwitchableFieldDefinition, PropertysetItem> {
64  
65      private static final Logger log = LoggerFactory.getLogger(SwitchableField.class);
66  
67      // - key : Field name. Should be the same as the related select value.<br>
68      // - value : Related Field. Created based on the definition coming from the Fields Definition list.
69      private final Map<String, Field<?>> fieldMap = new HashMap<>();
70  
71      private Field<?> selectField;
72  
73      public SwitchableField(SwitchableFieldDefinition definition, FieldFactoryFactory fieldFactoryFactory, ComponentProvider componentProvider, Item relatedFieldItem, I18NAuthoringSupport i18nAuthoringSupport) {
74          super(definition, fieldFactoryFactory, componentProvider, relatedFieldItem, i18nAuthoringSupport);
75      }
76  
77      /**
78       * @deprecated since 5.3.5 removing i18nContentSupport dependency (actually unused way before that). Besides, fields should use i18nAuthoringSupport for internationalization.
79       */
80      @Deprecated
81      public SwitchableField(SwitchableFieldDefinition definition, FieldFactoryFactory fieldFactoryFactory, I18nContentSupport i18nContentSupport, ComponentProvider componentProvider, Item relatedFieldItem) {
82          this(definition, fieldFactoryFactory, componentProvider, relatedFieldItem, componentProvider.getComponent(I18NAuthoringSupport.class));
83      }
84  
85      @Override
86      protected Component initContent() {
87          // Initialize root
88          root = new VerticalLayout();
89          setWidth(100, Unit.PERCENTAGE);
90          setHeight(-1, Unit.PIXELS);
91          addStyleName("switchablefield");
92          root.setWidth(100, Unit.PERCENTAGE);
93          root.setHeight(-1, Unit.PIXELS);
94          root.setSpacing(true);
95  
96          // Initialize Existing field
97          initFields();
98          return root;
99      }
100 
101     @Override
102     protected void initFields(PropertysetItem fieldValues) {
103         root.removeAllComponents();
104         fieldMap.clear();
105         // Create all Fields including the select Field.
106         for (ConfiguredFieldDefinition fieldDefinition : definition.getFields()) {
107             String name = fieldDefinition.getName();
108             // Only propagate read only if the parent definition is read only
109             if (definition.isReadOnly()) {
110                 fieldDefinition.setReadOnly(true);
111             }
112             Field<?> field = createLocalField(fieldDefinition, fieldValues.getItemProperty(fieldDefinition.getName()), false);
113             // Do not add hidden field.
114             if (!field.isVisible()) {
115                 continue;
116             }
117             if (fieldValues.getItemProperty(fieldDefinition.getName()) == null) {
118                 fieldValues.addItemProperty(fieldDefinition.getName(), field.getPropertyDataSource());
119             }
120             field.setWidth(100, Unit.PERCENTAGE);
121             fieldMap.put(name, field);
122             // set select field at the first position
123             if (StringUtils.equals(fieldDefinition.getName(), definition.getName())) {
124                 root.addComponentAsFirst(field);
125             } else {
126                 root.addComponent(field);
127             }
128         }
129 
130         // add listener to the select field
131         selectField = fieldMap.get(definition.getName());
132         selectField.setCaption(null);
133         selectField.addValueChangeListener(createSelectValueChangeListener());
134         selectField.addValueChangeListener(selectionListener);
135         switchField((String) selectField.getValue());
136     }
137 
138     /**
139      * Returns a {@link Field} if it's existing. Otherwise returns a null.
140      */
141     protected Field<?> getFieldByName(String fieldName) {
142         return fieldMap.get(fieldName);
143     }
144 
145     /**
146      * Change Listener bound to the select field. Once a selection is done, <br>
147      * the value change listener will switch to the field linked to the current select value.
148      */
149     private ValueChangeListener createSelectValueChangeListener() {
150         return (ValueChangeListener) event -> switchField(String.valueOf(event.getProperty().getValue()));
151     }
152 
153     /**
154      * Switch to the desired field. It the field is not part of the List, display a warn label.
155      */
156     private void switchField(String fieldName) {
157         // Check
158         if (root.getComponentCount() < 2 && StringUtils.equals(root.getComponent(0).getId(), definition.getName())) {
159             log.warn("{} is not associated to a field. Nothing will be displayed.", fieldName);
160             root.addComponent(new Label("No field configured for this switchable field "), 1);
161             return;
162         }
163 
164         for (String innerFieldName : fieldMap.keySet()) {
165             Field<?> field = fieldMap.get(innerFieldName);
166             // Set the select component visible and the selected field
167             if (StringUtils.equals(innerFieldName, fieldName) || StringUtils.equals(innerFieldName, definition.getName())) {
168                 field.setVisible(true);
169             } else {
170                 field.setVisible(false);
171             }
172         }
173     }
174 
175     @Override
176     public Class<? extends PropertysetItem> getType() {
177         return PropertysetItem.class;
178     }
179 
180     /**
181      * A switchable field is empty when no choice has been made yet. This method is called by Vaadin framework to perform a basic isRequired check.
182      */
183     @Override
184     public boolean isEmpty() {
185         return selectField.isEmpty();
186     }
187 }