/**
* 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.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionService;
import org.springframework.util.Assert;
import org.valkyriercp.binding.form.FormModel;
import org.valkyriercp.binding.value.ValueModel;
import org.valkyriercp.form.binding.support.AbstractBinding;
import org.valkyriercp.list.AbstractFilteredListModel;
import org.valkyriercp.list.DefaultFilteredListModel;
import org.valkyriercp.list.SortedListModel;
import org.valkyriercp.rules.constraint.Constraint;
import org.valkyriercp.util.ReflectiveVisitorHelper;
import org.valkyriercp.util.ValkyrieRepository;
import javax.swing.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Comparator;
import java.util.Observable;
import java.util.Observer;
/**
* @author Mathias Broekelmann
*
*/
public abstract class AbstractListBinding extends AbstractBinding {
private JComponent component;
final ReflectiveVisitorHelper visitorHelper = new ReflectiveVisitorHelper();
private final SelectableItemsVisitor selectableItemsVisitor = new SelectableItemsVisitor();
private Object selectableItems;
private final FilterConstraint filterConstraint = new FilterConstraint();
private final BindingComparator bindingComparator = new BindingComparator();
private ListModel bindingModel;
private AbstractFilteredListModel filteredModel;
public AbstractListBinding(JComponent component, FormModel formModel, String formPropertyPath,
Class requiredSourceClass) {
super(formModel, formPropertyPath, requiredSourceClass);
this.component = component;
}
protected void enabledChanged() {
component.setEnabled(!isReadOnly() && isEnabled());
}
protected void readOnlyChanged() {
enabledChanged();
}
public Object getSelectableItems() {
return selectableItems;
}
public JComponent getComponent() {
return component;
}
public final void setSelectableItems(Object selectableItems) {
Assert.notNull(selectableItems);
if (!selectableItems.equals(this.selectableItems)) {
this.selectableItems = selectableItems;
selectableItemsChanged();
}
}
protected ConversionService getConversionService() {
return ValkyrieRepository.getInstance().getApplicationConfig().conversionService();
}
public final void setComparator(Comparator comparator) {
bindingComparator.setComparator(comparator);
}
public Comparator getComparator() {
return bindingComparator.getComparator();
}
public final void setFilter(Constraint filter) {
filterConstraint.setFilter(filter);
}
protected final JComponent doBindControl() {
doBindControl(getBindingModel());
return getComponent();
}
protected abstract void doBindControl(ListModel bindingModel);
protected void selectableItemsChanged() {
if (filteredModel != null) {
filteredModel.setFilteredModel(createModel());
}
}
protected ListModel createModel() {
return (ListModel) visitorHelper.invokeVisit(selectableItemsVisitor, selectableItems);
}
protected AbstractFilteredListModel getFilteredModel() {
if (filteredModel == null) {
filteredModel = createFilteredModel(createModel(), filterConstraint);
}
return filteredModel;
}
protected ListModel getBindingModel() {
if (bindingModel == null) {
bindingModel = createSortedListModel(getFilteredModel(), bindingComparator);
}
return bindingModel;
}
protected ListModel createSortedListModel(ListModel listModel, Comparator comparator) {
return new SortedListModel(listModel, comparator);
}
protected AbstractFilteredListModel createFilteredModel(ListModel model, Constraint constraint) {
return new DefaultFilteredListModel(model, constraint);
}
/**
* Converts the given object value into the given targetClass
*
* @param value
* the value to convert
* @param targetClass
* the target class to convert the value to
* @return the converted value
*
* @throws org.springframework.binding.convert.ConversionException
* if the value can not be converted
*/
protected Object convertValue(Object value, Class targetClass) throws ConversionException {
Assert.notNull(value);
Assert.notNull(targetClass);
return getConversionService().getConversionExecutor(value.getClass(), targetClass).execute(value);
}
protected abstract ListModel getDefaultModel();
public Constraint getFilter() {
return filterConstraint.getFilter();
}
class SelectableItemsVisitor {
ListModel visit(ValueModel valueModel) {
Assert.notNull(valueModel.getValue(),
"value of ValueModel must not be null. Use an empty Collection or Array");
ListModel model = (ListModel) visitorHelper.invokeVisit(this, valueModel.getValue());
return new ValueModelFilteredListModel(model, valueModel);
}
ListModel visit(Object object) {
return (ListModel) convertValue(object, ListModel.class);
}
ListModel visitNull() {
return getDefaultModel();
}
}
class ValueModelFilteredListModel extends AbstractFilteredListModel implements PropertyChangeListener {
private final ValueModel valueModel;
public ValueModelFilteredListModel(ListModel model, ValueModel valueModel) {
super(model);
this.valueModel = valueModel;
valueModel.addValueChangeListener(this);
}
public void propertyChange(PropertyChangeEvent evt) {
setFilteredModel((ListModel) visitorHelper.invokeVisit(selectableItemsVisitor, valueModel.getValue()));
}
}
class FilterConstraint extends Observable implements Constraint, Observer {
private Constraint filter;
public boolean test(Object argument) {
if (filter != null) {
return filter.test(argument);
}
return true;
}
public Constraint getFilter() {
return filter;
}
public void setFilter(Constraint filter) {
if (filter != this.filter || (filter != null && !filter.equals(this.filter))) {
if (this.filter instanceof Observable) {
((Observable) this.filter).deleteObserver(this);
}
this.filter = filter;
if (filter instanceof Observable) {
((Observable) this.filter).addObserver(this);
}
update();
}
}
public void update() {
setChanged();
notifyObservers();
}
public void update(Observable o, Object arg) {
update();
}
}
class BindingComparator extends Observable implements Comparator, Observer {
private Comparator comparator;
public int compare(Object o1, Object o2) {
if (comparator != null) {
return comparator.compare(o1, o2);
}
return 0;
}
public void setComparator(Comparator comparator) {
if (comparator != this.comparator || (comparator != null && !comparator.equals(this.comparator))) {
if (this.comparator instanceof Observable) {
((Observable) this.comparator).deleteObserver(this);
}
this.comparator = comparator;
if (comparator instanceof Observable) {
((Observable) this.comparator).addObserver(this);
}
update();
}
}
public Comparator getComparator() {
return comparator;
}
void update() {
setChanged();
notifyObservers();
}
public void update(Observable o, Object arg) {
update();
}
}
}