org.fujion.model.GenericBinder Maven / Gradle / Ivy
/*
* #%L
* fujion
* %%
* Copyright (C) 2008 - 2017 Regenstrief Institute, Inc.
* %%
* 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.
*
* #L%
*/
package org.fujion.model;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.function.Function;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.reflect.MethodUtils;
import org.fujion.ancillary.ConvertUtil;
import org.fujion.ancillary.DeferredInvocation;
import org.fujion.common.MiscUtil;
import org.fujion.component.BaseComponent;
import org.fujion.event.PropertychangeEvent;
import org.fujion.model.IBinding.IReadBinding;
import org.fujion.model.IBinding.IWriteBinding;
import org.springframework.util.Assert;
/**
* Generic data binder.
*
* @param Type of model object.
*/
public class GenericBinder implements IBinder, Observer {
private M model;
private List readBindings;
private List writeBindings;
private boolean updating;
private class GenericBinding implements IBinding {
private final String modelProperty;
private final Function, ?> readConverter;
private final Function, ?> writeConverter;
private DeferredInvocation> getter;
private DeferredInvocation> setter;
GenericBinding(String modelProperty, Function, ?> readConverter, Function, ?> writeConverter) {
this.modelProperty = modelProperty;
this.readConverter = readConverter;
this.writeConverter = writeConverter;
}
@Override
public void init(BaseComponent instance, String propertyName, Method getter, Method setter) {
if (this instanceof IReadBinding) {
Assert.notNull(setter, "Property is not writable: " + propertyName);
this.setter = toDeferred(instance, setter, propertyName, 2);
readBindings = readBindings == null ? new ArrayList<>() : readBindings;
readBindings.add(this);
read();
} else {
this.setter = null;
}
if (this instanceof IWriteBinding) {
Assert.notNull(getter, "Property is not readable: " + propertyName);
this.getter = toDeferred(instance, getter, propertyName, 1);
if (!(this instanceof IReadBinding)) {
writeBindings = writeBindings == null ? new ArrayList<>() : writeBindings;
writeBindings.add(this);
write();
}
instance.addEventListener(PropertychangeEvent.class, (event) -> {
if (!updating && ((PropertychangeEvent) event).getPropertyName().equals(propertyName)) {
write();
}
});
} else {
this.getter = null;
}
}
private DeferredInvocation> toDeferred(BaseComponent instance, Method method, String name, int maxArgs) {
return method == null ? null
: new DeferredInvocation<>(instance, method,
method.getParameterCount() == maxArgs ? new Object[] { name } : null);
}
private void read() {
try {
if (model != null) {
Object value = PropertyUtils.getProperty(model, modelProperty);
value = convert(readConverter, value);
if (value != IBinder.NOVALUE) {
setter.invoke(value);
}
}
} catch (Exception e) {
throw MiscUtil.toUnchecked(e);
}
}
private void write() {
try {
if (model != null) {
Object value = convert(writeConverter, getter.invoke());
if (value != IBinder.NOVALUE) {
BeanUtils.copyProperty(model, modelProperty, value);
}
}
} catch (Exception e) {
throw MiscUtil.toUnchecked(e);
}
}
@SuppressWarnings("unchecked")
private Object convert(Function, ?> converter, Object value) {
return converter == null ? value : ((Function
© 2015 - 2025 Weber Informatics LLC | Privacy Policy