
net.ulrice.databinding.directbinding.ModelBinding Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ulrice-databinding Show documentation
Show all versions of ulrice-databinding Show documentation
Ulrice-Databinding is a databinding-extension for Ulrice
The newest version!
package net.ulrice.databinding.directbinding;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import net.ulrice.databinding.ErrorHandler;
import net.ulrice.databinding.UlriceDatabinding;
import net.ulrice.databinding.converter.IFValueConverter;
import net.ulrice.databinding.converter.ValueConverterException;
import net.ulrice.databinding.directbinding.table.ColumnAdapter;
import net.ulrice.databinding.directbinding.table.DefaultTableModelAdapter;
import net.ulrice.databinding.directbinding.table.DefaultTableModelColumnAdapter;
import net.ulrice.databinding.directbinding.table.EditableTableModel;
import net.ulrice.databinding.directbinding.table.ExpressionColumnSpec;
import net.ulrice.databinding.directbinding.table.TableModelAdapter;
import net.ulrice.databinding.directbinding.table.WithTypesPerColumn;
import net.ulrice.databinding.modelaccess.IFIndexedModelValueAccessor;
import net.ulrice.databinding.modelaccess.IFModelValueAccessor;
import net.ulrice.databinding.modelaccess.IndexedPredicate;
import net.ulrice.databinding.modelaccess.ModelChangeListener;
import net.ulrice.databinding.modelaccess.ModelNotificationAdapter;
import net.ulrice.databinding.modelaccess.Predicate;
import net.ulrice.databinding.modelaccess.PropertyChangeSupportModelNotificationAdapter;
import net.ulrice.databinding.modelaccess.impl.OgnlMVA;
import net.ulrice.databinding.modelaccess.impl.OgnlPredicate;
import net.ulrice.databinding.modelaccess.impl.OgnlSingleListIndexedMVA;
import net.ulrice.databinding.validation.IFValidator;
import net.ulrice.databinding.validation.ValidationResult;
import net.ulrice.databinding.viewadapter.IFViewAdapter;
import net.ulrice.databinding.viewadapter.IFViewChangeListener;
import net.ulrice.databinding.viewadapter.impl.factory.HeuristicViewAdapterFactory;
/**
* Dies ist die Einstiegsklasse - sie repräsentiert bzw. sammelt die Detail zu einem Binding von UI-Elementen
* an ein Model.
*/
public class ModelBinding {
private final Object model;
private final List bindings = new ArrayList ();
private final List tableBindings = new ArrayList ();
private boolean isUpdatingView = false;
/**
* Wenn das Modell PropertyChangeSupport unterstützt, muss man keinen expliziten Adapter angeben
*/
public ModelBinding (Object model) {
this (model, new PropertyChangeSupportModelNotificationAdapter (model));
}
public ModelBinding (Object model, ModelNotificationAdapter modelNotificationAdapter) {
this.model = model;
modelNotificationAdapter.addModelChangeListener (new ModelChangeListener() {
public void modelChanged () {
updateViews ();
}
});
}
private void updateViews () {
for (Binding b: bindings) {
updateView (b);
}
for (TableBinding b: tableBindings) {
updateView (b);
}
}
private void updateView (final TableBinding b) {
final int numRows = getNumRows (b);
b.getTableViewAdapter ().setSize (numRows, b.getColumnBindings ().size ());
for (int row=0; row < numRows; row++) {
for (int col=0; col < b.getColumnBindings ().size (); col++) {
final IndexedBinding cb = b.getColumnBindings ().get (col);
final Object raw = cb.getModelValueAccessor ().getValue (row);
final Object converted = cb.getConverter ().modelToView (raw, null);
isUpdatingView = true;
try {
cb.getViewAdapter ().setValue (row, converted);
}
finally {
isUpdatingView = false;
}
}
}
}
private int getNumRows (TableBinding b) {
int result = 0;
for (IndexedBinding colBinding: b.getColumnBindings ()) {
final int colSize = (Integer) colBinding.getModelValueAccessor().getSize();
if (colSize > result) {
result = colSize;
}
}
return result;
}
private void updateView (final Binding b) {
if (! b.hasDataBinding ()) {
return;
}
final Object converted = b.getCurrentValue();
final Object oldValue = b.getViewAdapter ().getValue ();
if (oldValue == null && converted == null) {
return;
}
if (oldValue != null && oldValue.equals (converted)) {
return;
}
isUpdatingView = true;
try {
calculateState(b);
b.getViewAdapter ().updateFromBinding(b);
}
finally {
isUpdatingView = false;
}
}
private void updateModelFromTable (TableModel tableModel, List columnBindings, TableModelEvent e) {
try {
if (isUpdatingView) {
return;
}
if (e.getType () != TableModelEvent.UPDATE) {
return;
}
if (e.getColumn () == TableModelEvent.ALL_COLUMNS) {
return; //TODO anders behandeln?
}
final IndexedBinding columnBinding = columnBindings.get (e.getColumn ());
if (columnBinding.isReadOnly ()) {
return;
}
final Object raw = tableModel.getValueAt (e.getFirstRow (), e.getColumn ());
final Object converted = columnBinding.getConverter ().viewToModel (raw, null);
columnBinding.getModelValueAccessor ().setValue (e.getFirstRow (), converted);
}
finally {
validateAll ();
}
}
private void updateModel (Binding b) {
try {
if (isUpdatingView) {
return;
}
if (b.isReadOnly ()) {
return;
}
try {
final Object raw = b.getViewAdapter ().getValue ();
b.setCurrentValue(raw);
}
catch (ValueConverterException exc) {
}
}
finally {
validateAll ();
}
}
private void calculateState(Binding bind) {
if(bind.getValidationFailures() != null && !bind.getValidationFailures().isEmpty()) {
bind.setValid(false);
return;
}
// Dirty handling
Object curr = bind.getCurrentValue();
Object orig = bind.getCurrentValue();
if(curr == null) {
bind.setDirty(orig != null);
} else {
bind.setDirty(curr.equals(curr));
}
}
private void validateAll () {
final ValidationResult validationResult = new ValidationResult ();
for (Binding b: bindings) {
validate (b, validationResult);
}
for (Binding b: bindings) {
final List raw = validationResult.getMessagesByBinding(b);
b.setValidationFailures(raw != null ? raw : new ArrayList ());
calculateState(b);
b.getViewAdapter ().updateFromBinding(b);
b.getViewAdapter ().setEditable(b.isWidgetEnabled (validationResult.isValid (), model));
}
}
private void validate (Binding b, ValidationResult validationResult) {
if (b.isReadOnly ()) {
return;
}
try {
final Object raw = b.getViewAdapter ().getValue ();
final Object converted = b.getConverter ().viewToModel (raw, null);
for (IFValidator v: b.getValidators ()) {
ValidationResult validationErrors = v.isValid(b, converted, raw);
if(validationErrors != null) {
validationResult.addValidationErrors(validationErrors.getValidationErrors());
}
}
}
catch (ValueConverterException exc) {
validationResult.addFailure (b, "Fehler bei der Konvertierung");
}
}
private void ensureEventThread () {
if (!SwingUtilities.isEventDispatchThread ())
ErrorHandler.handle (new RuntimeException ("nicht im Event-Thread")); //TODO Fehlerbehandlung
}
public Binding registerWithoutData (Object viewElement, String enabledExpression) {
ensureEventThread ();
final IFViewAdapter va = HeuristicViewAdapterFactory.createAdapter (viewElement);
final Predicate enabledPredicate = new OgnlPredicate (enabledExpression);
Binding binding = new Binding (va, null, enabledPredicate, null, new ArrayList> (), true);
bindings.add (binding);
va.setBindWithoutValue(true);
updateViews ();
return binding;
}
//TODO soll auch ohne Type gehen
public Binding register (Object viewElement, String modelPath, Class> modelType, IFValidator>... validators) {
final IFModelValueAccessor mva = new OgnlMVA (model, modelPath, modelType);
final IFViewAdapter va = HeuristicViewAdapterFactory.createAdapter (viewElement);
final Predicate enabledPredicate = mva.isReadOnly () ? Predicate.FALSE : Predicate.TRUE;
return register (va, mva, enabledPredicate, Arrays.asList (validators), mva.isReadOnly () || !va.isEditable());
}
public Binding register (Object viewElement, String modelPath, Class> modelType, boolean enabled, IFValidator>... validators) {
final IFModelValueAccessor mva = new OgnlMVA (model, modelPath, modelType);
final IFViewAdapter va = HeuristicViewAdapterFactory.createAdapter (viewElement);
final Predicate enabledPredicate = enabled ? Predicate.FALSE : Predicate.TRUE;
return register (va, mva, enabledPredicate, Arrays.asList (validators), mva.isReadOnly () || va.isEditable());
}
public Binding register (Object viewElement, String modelPath, Class> modelType, String enabledExpression, IFValidator>... validators) {
final IFModelValueAccessor mva = new OgnlMVA (model, modelPath, modelType);
return register (HeuristicViewAdapterFactory.createAdapter (viewElement), mva, new OgnlPredicate (enabledExpression), Arrays.asList (validators), mva.isReadOnly ());
}
public Binding register (IFViewAdapter viewAdapter, IFModelValueAccessor modelValueAccessor, Predicate enabledPredicate, List> validators, boolean isReadOnly) {
return register (viewAdapter, UlriceDatabinding.getConverterFactory().createConverter (viewAdapter.getViewType (), modelValueAccessor.getModelType ()), enabledPredicate, modelValueAccessor, validators, isReadOnly);
}
public Binding register (IFViewAdapter viewAdapter, IFValueConverter viewConverter, Predicate enabledPredicate, IFModelValueAccessor modelValueAccessor, List> validators, boolean isReadOnly) {
ensureEventThread ();
final Binding b = new Binding (viewAdapter, viewConverter, enabledPredicate, modelValueAccessor, validators, isReadOnly);
bindings.add (b);
updateViews ();
viewAdapter.addViewChangeListener (new IFViewChangeListener() {
@Override
public void viewValueChanged(IFViewAdapter viewAdapter) {
updateModel (b);
}
});
return b;
}
//TODO flexibleres Tabellen-Binding ohne BaseExpression, dafür mit #index
public void registerSingleListTable (Object oTableModel, String baseExpression, String... columnExpressions) {
final ExpressionColumnSpec[] columnSpecs = new ExpressionColumnSpec [columnExpressions.length];
for (int i=0; i columnBindings = new ArrayList ();
for (int col=0; col < columnSpecs.length; col++) {
final String expr = columnSpecs [col].getExpression ();
final Class> columnType = columnSpecs [col].getType ();
final IFValueConverter converter = UlriceDatabinding.getConverterFactory().createConverter (columnType, columnType);
final ColumnAdapter viewAdapter = new DefaultTableModelColumnAdapter (tableModel, columnType, col, ! canBeEditable); //TODO readOnly noch über Typen filtern?
final IFIndexedModelValueAccessor modelValueAccessor = new OgnlSingleListIndexedMVA (columnType, null, model, baseExpression, expr, numRowsAccessor);
columnBindings.add (new IndexedBinding (viewAdapter, converter, IndexedPredicate.TRUE, modelValueAccessor, viewAdapter.isReadOnly () || modelValueAccessor.isReadOnly ())); //TODO: enabled
}
final TableModelAdapter tableViewAdapter = new DefaultTableModelAdapter (tableModel);
tableBindings.add (new TableBinding (tableViewAdapter, columnBindings));
if (tableModel instanceof WithTypesPerColumn) {
final List> columnTypes = new ArrayList> ();
for (IndexedBinding b: columnBindings) {
columnTypes.add (b.getViewAdapter ().getViewType ());
}
((WithTypesPerColumn) tableModel).setColumnTypes (columnTypes);
}
if (canBeEditable) {
final List columnsEditable = new ArrayList ();
for (IndexedBinding b: columnBindings) {
columnsEditable.add (! b.isReadOnly ());
}
((EditableTableModel) tableModel).setEditable (columnsEditable);
}
updateViews ();
tableModel.addTableModelListener (new TableModelListener () {
public void tableChanged (TableModelEvent e) {
updateModelFromTable (tableModel, columnBindings, e);
}
});
}
public void commit() {
for (Binding b: bindings) {
b.setDirty(false);
b.setValid(true);
Object value = b.getModelValueAccessor().getValue();
b.setCurrentValue(value);
b.setOriginalValue(value);
}
}
public void rollback() {
for (Binding b: bindings) {
b.setDirty(false);
b.setValid(true);
b.setCurrentValue(b.getOriginalValue());
updateModel(b);
}
}
public boolean isValid() {
boolean result = true;
for (Binding b: bindings) {
result &= b.isValid();
}
return result;
}
public boolean isDirty() {
boolean result = false;
for (Binding b: bindings) {
result |= b.isDirty();
}
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy