All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.ui.AbstractSingleSelect Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * Copyright (C) 2000-2024 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * license.
 */
package com.vaadin.ui;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.jsoup.nodes.Element;

import com.vaadin.data.HasValue;
import com.vaadin.data.SelectionModel.Single;
import com.vaadin.data.provider.DataCommunicator;
import com.vaadin.data.provider.DataGenerator;
import com.vaadin.event.selection.SingleSelectionEvent;
import com.vaadin.event.selection.SingleSelectionListener;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.selection.SelectionServerRpc;
import com.vaadin.shared.ui.AbstractSingleSelectState;
import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.ui.declarative.DesignException;

import elemental.json.Json;
import elemental.json.JsonObject;

/**
 * An abstract base class for listing components that only support single
 * selection and no lazy loading of data items.
 *
 * @author Vaadin Ltd.
 *
 * @param 
 *            the item date type
 *
 * @see com.vaadin.data.SelectionModel.Single
 *
 * @since 8.0
 */
public abstract class AbstractSingleSelect extends AbstractListing
        implements SingleSelect {

    private T selectedItem = null;

    /**
     * Creates a new {@code AbstractListing} with a default data communicator.
     * 

*/ protected AbstractSingleSelect() { init(); } /** * Creates a new {@code AbstractSingleSelect} with the given custom data * communicator. *

* Note: This method is for creating an * {@code AbstractSingleSelect} with a custom communicator. In the common * case {@link AbstractSingleSelect#AbstractSingleSelect()} should be used. *

* * @param dataCommunicator * the data communicator to use, not null */ protected AbstractSingleSelect(DataCommunicator dataCommunicator) { super(dataCommunicator); init(); } /** * Adds a selection listener to this select. The listener is called when the * selection is changed either by the user or programmatically. * * @param listener * the selection listener, not null * @return a registration for the listener */ public Registration addSelectionListener( SingleSelectionListener listener) { return addListener(SingleSelectionEvent.class, listener, SingleSelectionListener.SELECTION_CHANGE_METHOD); } /** * Returns the currently selected item, or an empty optional if no item is * selected. * * @return an optional of the selected item if any, an empty optional * otherwise */ public Optional getSelectedItem() { return Optional.ofNullable(selectedItem); } /** * Sets the current selection to the given item or clears selection if given * {@code null}. * * @param item * the item to select or {@code null} to clear selection */ public void setSelectedItem(T item) { setSelectedItem(item, false); } /** * Returns the current value of this object which is the currently selected * item. *

* The call is delegated to {@link #getSelectedItem()} * * @return the current selection, may be {@code null} * * @see #getSelectedItem() * @see Single#getSelectedItem */ @Override public T getValue() { return getSelectedItem().orElse(null); } /** * Sets the value of this object which is an item to select. If the new * value is not equal to {@code getValue()}, fires a value change event. If * value is {@code null} then it deselects currently selected item. *

* The call is delegated to {@link #setSelectedItem(Object)}. * * @see #setSelectedItem(Object) * @see Single#setSelectedItem(Object) * * @param value * the item to select or {@code null} to clear selection */ @Override public void setValue(T value) { setSelectedItem(value); } @Override public Registration addValueChangeListener( HasValue.ValueChangeListener listener) { return addSelectionListener( event -> listener.valueChange(new ValueChangeEvent<>(this, event.getOldValue(), event.isUserOriginated()))); } @Override protected AbstractSingleSelectState getState() { return (AbstractSingleSelectState) super.getState(); } @Override protected AbstractSingleSelectState getState(boolean markAsDirty) { return (AbstractSingleSelectState) super.getState(markAsDirty); } @Override public void setRequiredIndicatorVisible(boolean visible) { super.setRequiredIndicatorVisible(visible); } @Override public boolean isRequiredIndicatorVisible() { return super.isRequiredIndicatorVisible(); } @Override public void setReadOnly(boolean readOnly) { super.setReadOnly(readOnly); } @Override public boolean isReadOnly() { return super.isReadOnly(); } /** * Returns the item that the given key is assigned to, or {@code null} if * there is no such item. * * @param key * the key whose item to return * @return the associated item if any, {@code null} otherwise. */ protected T keyToItem(String key) { return getDataCommunicator().getKeyMapper().get(key); } /** * Returns whether the given item is currently selected. * * @param item * the item to check, not null * @return {@code true} if the item is selected, {@code false} otherwise */ public boolean isSelected(T item) { if (Objects.equals(selectedItem, item)) { return true; } if (item == null || selectedItem == null) { return false; } return Objects.equals(getDataProvider().getId(selectedItem), getDataProvider().getId(item)); } @Override protected Element writeItem(Element design, T item, DesignContext context) { Element element = super.writeItem(design, item, context); if (isSelected(item)) { element.attr("selected", true); } return element; } @Override protected void readItems(Element design, DesignContext context) { Set selected = new HashSet<>(); List items = design.children().stream() .map(child -> readItem(child, selected, context)) .collect(Collectors.toList()); if (!items.isEmpty()) { setItems(items); } selected.forEach(this::setValue); } /** * Reads an Item from a design and inserts it into the data source. * Hierarchical select components should override this method to recursively * recursively read any child items as well. * * @param child * a child element representing the item * @param selected * A set accumulating selected items. If the item that is read is * marked as selected, its item id should be added to this set. * @param context * the DesignContext instance used in parsing * @return the item id of the new item * * @throws DesignException * if the tag name of the {@code child} element is not * {@code option}. */ protected T readItem(Element child, Set selected, DesignContext context) { T item = readItem(child, context); if (child.hasAttr("selected")) { selected.add(item); } return item; } @Override protected Collection getCustomAttributes() { Collection attributes = super.getCustomAttributes(); // "value" is not an attribute for the component. "selected" attribute // is used in "option"'s tag to mark selection which implies value for // single select component attributes.add("value"); return attributes; } private void init() { registerRpc(new SelectionServerRpc() { @Override public void select(String key) { setSelectedItem(keyToItem(key), true); } @Override public void deselect(String key) { T item = keyToItem(key); if (isSelected(item)) { setSelectedItem(null, true); } } }); addDataGenerator(new DataGenerator() { @Override public void generateData(T item, JsonObject jsonObject) { if (isSelected(item)) { // Deferred update of state. updateSelectedItemState(item); } } @Override public void refreshData(T item) { if (isSelected(item)) { selectedItem = item; // Invalidate old data updateSelectedItemState(null); } } }); } /** * This method updates the internal selection state of the server-side of * {@code AbstractSingleSelect}. * * @param value * the value that should be selected * @param userOriginated * {@code true} if selection was done by user, {@code false} if * not * * @since 8.5 */ protected void setSelectedItem(T value, boolean userOriginated) { if (isSelected(value)) { return; } // Update selection T oldValue = selectedItem; selectedItem = value; // Re-generate selected item data if (oldValue != null) { getDataCommunicator().refresh(oldValue); } if (value != null) { getDataCommunicator().refresh(value); } // Deselection can be handled immediately updateSelectedItemState(value); // Update diffstate to make sure null can be selected later. updateDiffstate("selectedItemKey", Json.createObject()); fireEvent(new SingleSelectionEvent<>(AbstractSingleSelect.this, oldValue, userOriginated)); } /** * This method updates the shared selection state of the * {@code AbstractSingleSelect}. * * @param value * the value that is selected; may be {@code null} * * @since 8.5 */ protected void updateSelectedItemState(T value) { // FIXME: If selecting a value that does not exist, this will leave and // extra object in the key mapper that will not be dropped any time. getState().selectedItemKey = value != null ? getDataCommunicator().getKeyMapper().key(value) : null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy