/*******************************************************************************
* Copyright (c) 2013, 2017 GoPivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.wizard;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Supplier;
import org.eclipse.core.runtime.Assert;
import org.springframework.ide.eclipse.boot.wizard.CheckBoxesSection.CheckBoxModel;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.Validator;
import org.springsource.ide.eclipse.commons.livexp.core.ValueListener;
/**
* Model for a UI widget that offers multiple choices. Could be represented
* by a set of Checkboxes or multi-selection enabled list/tree viewer.
*
* @author Kris De Volder
*/
public class MultiSelectionFieldModel<T> {
private static final LiveExpression<Boolean> ALLWAYS_ENABLED = LiveExpression.constant(true);
private Class<T> type; //Type of data stored in the field.
private String name; // used to submit value to some service that handles the form
private String label; // Label to display in forms
private LiveExpression<ValidationResult> validator;
private Map<T,String> labelMap = new LinkedHashMap<T, String>();
private Map<T,Supplier<String>> tooltipsHtml = null; //don't allocate unless used.
private boolean mustSort;
private Map<T, LiveExpression<Boolean>> enablements = null;
//selection state for each individual choice (created on an as-needed basis).
private Map<T, LiveVariable<Boolean>> selections = new HashMap<T, LiveVariable<Boolean>>();
public MultiSelectionFieldModel(Class<T> type, String name) {
this.type = type;
this.name = name;
this.label = name;
this.validator = Validator.OK;
}
public Class<T> getType() {
return type;
}
public String getLabel() {
return label;
}
public String getLabel(T value) {
return labelMap.get(value);
}
public String getName() {
return name;
}
public LiveExpression<ValidationResult> getValidator() {
return validator;
}
public MultiSelectionFieldModel<T> validator(LiveExpression<ValidationResult> v) {
this.validator = v;
return this;
}
public void select(T v) {
getSelection(v).setValue(true);
}
public synchronized LiveVariable<Boolean> getSelection(T v) {
LiveVariable<Boolean> existing = selections.get(v);
if (existing==null) {
selections.put(v, existing = new LiveVariable<Boolean>(false));
}
return existing;
}
public void unselect(T v) {
getSelection(v).setValue(false);
}
/**
* Add a valid choice to the multi selection model.
* @param label String to show the choice to a user
* @param key Value added to the set when user selects this choice.
*/
public MultiSelectionFieldModel<T> choice(String label, T value) {
Assert.isLegal(labelMap.get(value)==null, "Duplicate choice "+value+" already added");
labelMap.put(value, label);
return this;
}
public void choice(String label, T value, Supplier<String> tooltipHtml) {
choice(label, value);
setTooltipHtml(value, tooltipHtml);
}
public void choice(String label, T value, Supplier<String> tooltipHtml, LiveExpression<Boolean> enablement) {
choice(label, value, tooltipHtml);
if (enablements==null) {
enablements = new HashMap<T, LiveExpression<Boolean>>();
}
enablements.put(value, enablement);
}
public void setTooltipHtml(T value, Supplier<String> tooltipHtml) {
if (tooltipsHtml==null) {
tooltipsHtml = new HashMap<T, Supplier<String>>();
}
tooltipsHtml.put(value, tooltipHtml);
}
public Supplier<String> getTooltipHtml(T value) {
if (tooltipsHtml!=null) {
return tooltipsHtml.get(value);
}
return null;
}
public MultiSelectionFieldModel<T> label(String label) {
this.label = label;
return this;
}
@SuppressWarnings("unchecked")
public synchronized T[] getChoices() {
Collection<T> values = labelMap.keySet();
T[] choices = values.toArray((T[]) Array.newInstance(getType(), values.size()));
if (mustSort) {
Arrays.sort(choices, new Comparator<T>() {
public int compare(T o1, T o2) {
String s1 = getLabel(o1);
String s2 = getLabel(o2);
return s1.compareTo(s2);
}
});
}
return choices;
}
public void sort() {
mustSort = true;
}
public LiveExpression<Boolean> getEnablement(T choice) {
if (enablements!=null) {
LiveExpression<Boolean> e = enablements.get(choice);
if (e!=null) {
return e;
}
}
return ALLWAYS_ENABLED;
}
public synchronized List<T> getCurrentSelection() {
List<T> selecteds = new ArrayList<T>();
for (Entry<T, LiveVariable<Boolean>> e : selections.entrySet()) {
if (e.getValue().getValue()) {
selecteds.add(e.getKey());
}
}
return Collections.unmodifiableList(selecteds);
}
/**
* Converts all the current choices into CheckBoxModel objects. One checkbox to
* represent each choice.
*/
public List<CheckBoxModel<T>> getCheckBoxModels() {
List<CheckBoxModel<T>> checkboxes = new ArrayList<CheckBoxModel<T>>(labelMap.size());
for (T choice : labelMap.keySet()) {
CheckBoxModel<T> cb;
checkboxes.add(cb = new CheckBoxModel<T>(getLabel(choice), choice, getSelection(choice), getEnablement(choice)));
cb.setTooltipHtml(getTooltipHtml(choice));
}
return Collections.unmodifiableList(checkboxes);
}
public void clearSelection() {
selections.values().forEach(liveVar -> {
liveVar.setValue(false);
});
}
public void addSelectionListener(ValueListener<Boolean> listener) {
selections.values().forEach(livevar -> {
livevar.addListener(listener);
});
}
}