/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.form.binding.swing;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.util.Assert;
import org.valkyriercp.binding.form.FormModel;
import org.valkyriercp.binding.value.ValueModel;
import org.valkyriercp.component.ShuttleList;
import org.valkyriercp.form.binding.Binding;
import org.valkyriercp.form.binding.support.AbstractBinder;
import org.valkyriercp.list.BeanPropertyValueListRenderer;
import javax.swing.*;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
/**
* Binder for handling ShuttleList component. Use the following keys to
* configure the produced binding:
* <dt><code>SELECTABLE_ITEMS_HOLDER_KEY</code></dt>
* <dd> to specify the list of "available" source values (this may be a
* Collection or an array).</dd>
* <p>
* <dt><code>SELECTED_ITEMS_HOLDER_KEY</code></dt>
* <dd>to specify the value holder into which the selected items will be placed
* (this may be a Collection or an array). Initially, this set must contain only
* values that exist in the selectable set.</dd>
* <p>
* <dt><code>SELECTED_ITEM_TYPE_KEY</code></dt>
* <dd>to specify the underlying type of the elements in the selected and
* selectable value sets.</dd>
* <p>
* <dt><code>COMPARATOR_KEY</code></dt>
* <dd>to specify the Comparator to use for comparing elements in the selected
* and selectable value sets.</dd>
* <p>
* <dt><code>RENDERER_KEY</code></dt>
* <dd>to specify the a {@link javax.swing.ListCellRenderer} for elements of the value
* sets. This is typically used if the String value of the elements is not
* appropriate for use in the shuttle lists.</dd>
* <p>
* <dt><code>FORM_ID</code></dt>
* <dd>to specify formId in which this ShuttleList appears, this allow
* form-specific settings like the texts and icon.</dd>
*
* In order to have this Binder selected properly when adding ShuttleList
* components to a form, you will need to have the following context
* configuration:
*
* <pre>
* <bean id="binderSelectionStrategy" class="org.springframework.richclient.form.binding.swing.SwingBinderSelectionStrategy">
* <property name="bindersForControlTypes">
* <map>
* <entry>
* <key>
* <value type="java.lang.Class">org.springframework.richclient.components.ShuttleList</value>
* </key>
* <bean class="org.springframework.richclient.components.ShuttleListBinder" />
* </entry>
* </map>
* </property>
* </bean>
* </pre>
*
* <p>
* Also, see {@link org.valkyriercp.form.binding.swing.SwingBindingFactoryProvider} and
* {@link org.valkyriercp.form.binding.swing.SwingBindingFactoryProvider} for how to configure and use that binding
* factory, which offers convenience methods for constructing a bound shuttle
* list. With the provider, factory, and binder registered, the following code
* can be used to add a bound shuttle list to a form:
*
* <pre>
* final SandboxSwingBindingFactory bf = (SandboxSwingBindingFactory) getBindingFactory();
* TableFormBuilder formBuilder = new TableFormBuilder( bf );
* ...
* String[] languages = new String[] { "java", "perl", "ruby", "C#" };
* List languagesList = Arrays.asList( languages );
* formBuilder.add( bf.createBoundShuttleList( "languageSkills", languagesList ), "align=left" );
* </pre>
*
* @author Larry Streepy
* @author Benoit Xhenseval
*/
public class ShuttleListBinder extends AbstractBinder {
public static final String SELECTABLE_ITEMS_HOLDER_KEY = "selectableItemsHolder";
public static final String SELECTED_ITEMS_HOLDER_KEY = "selectedItemHolder";
public static final String SELECTED_ITEM_TYPE_KEY = "selectedItemType";
public static final String MODEL_KEY = "model";
public static final String FORM_ID = "formId";
public static final String COMPARATOR_KEY = "comparator";
public static final String RENDERER_KEY = "renderer";
private boolean showEditButton = true;
public void setShowEditButton(boolean showEditButton) {
this.showEditButton = showEditButton;
}
/**
* Utility method to construct the context map used to configure instances
* of {@link ShuttleListBinding} created by this binder.
* <p>
* Binds the values specified in the collection contained within
* <code>selectableItemsHolder</code> to a {@link ShuttleList}, with any
* user selection being placed in the form property referred to by
* <code>selectionFormProperty</code>. Each item in the list will be
* rendered by looking up a property on the item by the name contained in
* <code>renderedProperty</code>, retrieving the value of the property,
* and rendering that value in the UI.
* <p>
* Note that the selection in the bound list will track any changes to the
* <code>selectionFormProperty</code>. This is especially useful to
* preselect items in the list - if <code>selectionFormProperty</code> is
* not empty when the list is bound, then its content will be used for the
* initial selection.
*
* @param selectionFormProperty form property to hold user's selection. This
* property must be a <code>Collection</code> or array type.
* @param selectableItemsHolder <code>ValueModel</code> containing the
* items with which to populate the list.
* @param renderedProperty the property to be queried for each item in the
* list, the result of which will be used to render that item in the
* UI. May be null, in which case the selectable items will be
* rendered as strings.
*/
public static Map createBindingContext( FormModel formModel, String selectionFormProperty,
ValueModel selectableItemsHolder, String renderedProperty ) {
final Map context = new HashMap(4);
context.put(ShuttleListBinder.FORM_ID, formModel.getId());
final ValueModel selectionValueModel = formModel.getValueModel(selectionFormProperty);
context.put(SELECTED_ITEMS_HOLDER_KEY, selectionValueModel);
final Class selectionPropertyType = formModel.getFieldMetadata(selectionFormProperty).getPropertyType();
if( selectionPropertyType != null ) {
context.put(SELECTED_ITEM_TYPE_KEY, selectionPropertyType);
}
context.put(SELECTABLE_ITEMS_HOLDER_KEY, selectableItemsHolder);
if( renderedProperty != null ) {
context.put(RENDERER_KEY, new BeanPropertyValueListRenderer(renderedProperty));
context.put(COMPARATOR_KEY, new PropertyComparator(renderedProperty, true, true));
}
return context;
}
/**
* Constructor.
*/
public ShuttleListBinder() {
super(null, new String[] { SELECTABLE_ITEMS_HOLDER_KEY, SELECTED_ITEMS_HOLDER_KEY, SELECTED_ITEM_TYPE_KEY,
MODEL_KEY, COMPARATOR_KEY, RENDERER_KEY, FORM_ID });
}
/**
* Constructor allowing the specification of additional/alternate context keys. This
* is for use by derived classes.
*
* @param supportedContextKeys Context keys supported by subclass
*/
protected ShuttleListBinder( final String[] supportedContextKeys ) {
super(null, supportedContextKeys);
}
/**
* @inheritDoc
*/
protected Binding doBind( JComponent control, FormModel formModel, String formPropertyPath, Map context ) {
Assert.isTrue(control instanceof ShuttleList, formPropertyPath);
ShuttleListBinding binding = new ShuttleListBinding((ShuttleList) control, formModel, formPropertyPath);
applyContext(binding, context);
return binding;
}
/**
* Apply the values from the context to the specified binding.
*
* @param binding Binding to update
* @param context Map of context values to apply to the binding
*/
protected void applyContext( ShuttleListBinding binding, Map context ) {
if( context.containsKey(MODEL_KEY) ) {
binding.setModel((ListModel) context.get(MODEL_KEY));
}
if( context.containsKey(SELECTABLE_ITEMS_HOLDER_KEY) ) {
binding.setSelectableItemsHolder((ValueModel) context.get(SELECTABLE_ITEMS_HOLDER_KEY));
}
if( context.containsKey(SELECTED_ITEMS_HOLDER_KEY) ) {
binding.setSelectedItemsHolder((ValueModel) context.get(SELECTED_ITEMS_HOLDER_KEY));
}
if( context.containsKey(RENDERER_KEY) ) {
binding.setRenderer((ListCellRenderer) context.get(RENDERER_KEY));
}
if( context.containsKey(COMPARATOR_KEY) ) {
binding.setComparator((Comparator) context.get(COMPARATOR_KEY));
}
if( context.containsKey(SELECTED_ITEM_TYPE_KEY) ) {
binding.setSelectedItemType((Class) context.get(SELECTED_ITEM_TYPE_KEY));
}
if( context.containsKey(FORM_ID) ) {
binding.setFormId((String) context.get(FORM_ID));
}
}
/**
* @inheritDoc
*/
protected JComponent createControl( Map context ) {
ShuttleList shuttleList = new ShuttleList(showEditButton);
return shuttleList;
}
}