View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.client.ui.commons.markup.html.form;
20  
21  import com.googlecode.wicket.jquery.core.Options;
22  import com.googlecode.wicket.jquery.ui.JQueryUIBehavior;
23  import com.googlecode.wicket.jquery.ui.form.spinner.AjaxSpinner;
24  import com.googlecode.wicket.jquery.ui.form.spinner.SpinnerAdapter;
25  import com.googlecode.wicket.jquery.ui.form.spinner.SpinnerBehavior;
26  import java.io.Serializable;
27  import java.util.List;
28  import java.util.Optional;
29  import java.util.function.Predicate;
30  import org.apache.commons.lang3.StringUtils;
31  import org.apache.commons.lang3.math.NumberUtils;
32  import org.apache.syncope.client.ui.commons.Constants;
33  import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
34  import org.apache.syncope.common.lib.Attr;
35  import org.apache.syncope.common.lib.Attributable;
36  import org.apache.wicket.ajax.AjaxRequestTarget;
37  import org.apache.wicket.markup.html.list.ListItem;
38  import org.apache.wicket.model.IModel;
39  import org.apache.wicket.model.Model;
40  import org.apache.wicket.model.ResourceModel;
41  
42  public final class AjaxSpinnerFieldPanel<T extends Number> extends FieldPanel<T> {
43  
44      private static final long serialVersionUID = 6413819574530703577L;
45  
46      private final Class<T> reference;
47  
48      private final IModel<T> model;
49  
50      private final Options options;
51  
52      private final boolean convertValuesToString;
53  
54      private SpinnerBehavior behavior;
55  
56      private AjaxSpinnerFieldPanel(
57              final String id,
58              final String name,
59              final Class<T> reference,
60              final IModel<T> model,
61              final Options options,
62              final boolean enableOnChange,
63              final boolean convertValuesToString) {
64  
65          super(id, name, model);
66  
67          field = new AjaxSpinner<>("spinner", model, options, reference) {
68  
69              private static final long serialVersionUID = -3624755213720060594L;
70  
71              @Override
72              public JQueryUIBehavior newWidgetBehavior(final String selector) {
73                  behavior = new SpinnerBehavior(selector, new SpinnerAdapter());
74                  behavior.setOptions(options);
75                  return behavior;
76              }
77          };
78  
79          if (enableOnChange && !isReadOnly()) {
80              field.add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
81  
82                  private static final long serialVersionUID = -1107858522700306810L;
83  
84                  @Override
85                  protected void onUpdate(final AjaxRequestTarget target) {
86                      // nothing to do
87                  }
88              });
89          }
90  
91          add(field.setLabel(new ResourceModel(name, name)).setOutputMarkupId(true));
92  
93          this.name = name;
94          this.model = model;
95          this.reference = reference;
96          this.options = options;
97          this.convertValuesToString = convertValuesToString;
98      }
99  
100     @Override
101     public AjaxSpinnerFieldPanel<T> setNewModel(final List<Serializable> list) {
102         setNewModel(new Model<>() {
103 
104             private static final long serialVersionUID = 527651414610325237L;
105 
106             @Override
107             public T getObject() {
108                 T value = null;
109 
110                 if (list != null && !list.isEmpty()
111                         && list.get(0) != null && StringUtils.isNotBlank(list.get(0).toString())) {
112 
113                     value = reference.equals(Integer.class)
114                             ? reference.cast(NumberUtils.toInt(list.get(0).toString()))
115                             : reference.equals(Long.class)
116                             ? reference.cast(NumberUtils.toLong(list.get(0).toString()))
117                             : reference.equals(Short.class)
118                             ? reference.cast(NumberUtils.toShort(list.get(0).toString()))
119                             : reference.equals(Float.class)
120                             ? reference.cast(NumberUtils.toFloat(list.get(0).toString()))
121                             : reference.equals(byte.class)
122                             ? reference.cast(NumberUtils.toByte(list.get(0).toString()))
123                             : reference.cast(NumberUtils.toDouble(list.get(0).toString()));
124                 }
125 
126                 return value;
127             }
128 
129             @Override
130             public void setObject(final T object) {
131                 list.clear();
132                 Optional.ofNullable(object).ifPresent(v -> list.add(convertValuesToString ? v.toString() : v));
133             }
134         });
135 
136         return this;
137     }
138 
139     @SuppressWarnings("rawtypes")
140     @Override
141     public AjaxSpinnerFieldPanel<T> setNewModel(final ListItem item) {
142         field.setModel(new Model<>() {
143 
144             private static final long serialVersionUID = 6799404673615637845L;
145 
146             @Override
147             public T getObject() {
148                 T number = null;
149 
150                 final Object obj = item.getModelObject();
151 
152                 if (obj != null && !obj.toString().isEmpty()) {
153                     if (obj instanceof String) {
154                         try {
155                             number = reference.equals(Integer.class)
156                                     ? reference.cast(Integer.valueOf((String) obj))
157                                     : reference.equals(Long.class)
158                                     ? reference.cast(Long.valueOf((String) obj))
159                                     : reference.equals(Short.class)
160                                     ? reference.cast(Short.valueOf((String) obj))
161                                     : reference.equals(Float.class)
162                                     ? reference.cast(Float.valueOf((String) obj))
163                                     : reference.equals(byte.class)
164                                     ? reference.cast(Byte.valueOf((String) obj))
165                                     : reference.cast(Double.valueOf((String) obj));
166                         } catch (NumberFormatException e) {
167                             LOG.error("While attempting to parse {}", obj, e);
168                         }
169                     } else if (obj instanceof Number) {
170                         number = reference.equals(Integer.class)
171                                 ? reference.cast(((Number) obj).intValue())
172                                 : reference.equals(Long.class)
173                                 ? reference.cast(((Number) obj).longValue())
174                                 : reference.equals(Short.class)
175                                 ? reference.cast(((Number) obj).shortValue())
176                                 : reference.equals(Float.class)
177                                 ? reference.cast(((Number) obj).floatValue())
178                                 : reference.equals(byte.class)
179                                 ? reference.cast(((Number) obj).byteValue())
180                                 : reference.cast(((Number) obj).doubleValue());
181                     }
182                 }
183 
184                 return number;
185             }
186 
187             @Override
188             @SuppressWarnings("unchecked")
189             public void setObject(final T object) {
190                 item.setModelObject(Optional.ofNullable(object).
191                         map(v -> convertValuesToString ? v.toString() : v).
192                         orElse(null));
193             }
194         });
195 
196         return this;
197     }
198 
199     @SuppressWarnings({ "unchecked", "rawtypes" })
200     @Override
201     public FieldPanel<T> setNewModel(final Attributable attributable, final String schema) {
202         field.setModel(new Model() {
203 
204             private static final long serialVersionUID = -4214654722524358000L;
205 
206             @Override
207             public Serializable getObject() {
208                 return attributable.getPlainAttr(schema).map(Attr::getValues).filter(Predicate.not(List::isEmpty)).
209                         map(values -> reference.equals(Integer.class)
210                         ? reference.cast(NumberUtils.toInt(values.get(0)))
211                         : reference.equals(Long.class)
212                         ? reference.cast(NumberUtils.toLong(values.get(0)))
213                         : reference.equals(Short.class)
214                         ? reference.cast(NumberUtils.toShort(values.get(0)))
215                         : reference.equals(Float.class)
216                         ? reference.cast(NumberUtils.toFloat(values.get(0)))
217                         : reference.equals(byte.class)
218                         ? reference.cast(NumberUtils.toByte(values.get(0)))
219                         : reference.cast(NumberUtils.toDouble(values.get(0)))).
220                         orElse(null);
221             }
222 
223             @Override
224             public void setObject(final Serializable object) {
225                 attributable.getPlainAttr(schema).ifPresent(plainAttr -> {
226                     plainAttr.getValues().clear();
227                     Optional.ofNullable(object).ifPresent(o -> plainAttr.getValues().add(o.toString()));
228                 });
229             }
230         });
231 
232         return this;
233     }
234 
235     @Override
236     public AjaxSpinnerFieldPanel<T> clone() {
237         AjaxSpinnerFieldPanel<T> panel = new AjaxSpinnerFieldPanel<>(
238                 getId(), name, reference, model, options, false, convertValuesToString);
239 
240         panel.setRequired(isRequired());
241         panel.setReadOnly(isReadOnly());
242         panel.setTitle(title);
243 
244         if (isRequiredLabelAdded) {
245             panel.addRequiredLabel();
246         }
247 
248         return panel;
249     }
250 
251     @Override
252     public FieldPanel<T> setReadOnly(final boolean readOnly) {
253         super.setReadOnly(readOnly);
254         AjaxSpinner.class.cast(field).setEnabled(!readOnly);
255         options.set("disabled", readOnly);
256         if (behavior != null) {
257             behavior.setOptions(options);
258         }
259         return this;
260     }
261 
262     public static class Builder<T extends Number> {
263 
264         private final Options options = new Options();
265 
266         private boolean enableOnChange = false;
267 
268         private boolean convertValuesToString = true;
269 
270         public Builder<T> min(final T min) {
271             options.set("min", min);
272             return this;
273         }
274 
275         public Builder<T> max(final T max) {
276             options.set("max", max);
277             return this;
278         }
279 
280         public Builder<T> step(final T step) {
281             options.set("step", step);
282             return this;
283         }
284 
285         public Builder<T> enableOnChange() {
286             enableOnChange = true;
287             return this;
288         }
289 
290         public Builder<T> convertValuesToString(final boolean convertValuesToString) {
291             this.convertValuesToString = convertValuesToString;
292             return this;
293         }
294 
295         public AjaxSpinnerFieldPanel<T> build(
296                 final String id,
297                 final String name,
298                 final Class<T> reference,
299                 final IModel<T> model) {
300 
301             return new AjaxSpinnerFieldPanel<>(
302                     id, name, reference, model, options, enableOnChange, convertValuesToString);
303         }
304     }
305 }