org.tentackle.fx.rdc.translate.PdoStringTranslator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tentackle-fx-rdc Show documentation
Show all versions of tentackle-fx-rdc Show documentation
Rich Desktop Client based on FX
/*
* Tentackle - https://tentackle.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.tentackle.fx.rdc.translate;
import javafx.application.Platform;
import javafx.scene.Node;
import org.tentackle.bind.Bindable;
import org.tentackle.fx.FxFactory;
import org.tentackle.fx.FxRuntimeException;
import org.tentackle.fx.FxTextComponent;
import org.tentackle.fx.FxUtilities;
import org.tentackle.fx.ValueTranslatorService;
import org.tentackle.fx.translate.ValueStringTranslator;
import org.tentackle.log.Logger;
import org.tentackle.log.LoggerFactory;
import org.tentackle.pdo.DomainKey;
import org.tentackle.pdo.Pdo;
import org.tentackle.pdo.PersistentDomainObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
import java.util.function.Function;
/**
* PDO to String translator.
*
* TODO: DnD support
*
* @author harald
* @param the PDO type
*/
@ValueTranslatorService(modelClass = PersistentDomainObject.class, viewClass = String.class)
public class PdoStringTranslator> extends ValueStringTranslator {
private static final Logger LOGGER = LoggerFactory.getLogger(PdoStringTranslator.class);
private final PdoComponentAddon addon;
private ValueStringTranslator> udkTranslator;
private String viewValue;
/**
* Creates a translator.
*
* @param component the text component
*/
public PdoStringTranslator(FxTextComponent component) {
super(component);
// figure out the correct translator for the UDK
@SuppressWarnings("unchecked")
Class pdoClass = (Class) getComponent().getType();
try {
T proxy = Pdo.create(pdoClass);
Class> udkClass = proxy.getUniqueDomainKeyType();
// create translator for the unique domain key and configure the component (TextFormatter, for ex.)
udkTranslator = (ValueStringTranslator>) FxFactory.getInstance().createValueTranslator(udkClass, String.class, component);
// allow incomplete input to preset the search criteria in the finder if possible
udkTranslator.setLenient(true);
// apply binding options defined for the UDK in the PDO, if provided
Bindable bindable = getBindableAnnotation(proxy);
if (bindable != null) {
FxUtilities.getInstance().applyBindingOptions(component, component.getBinding().getMember(), bindable.options());
}
}
catch (RuntimeException rex) {
// no translator found, try default (see below)
}
addon = createPdoAddon();
}
@Override
@SuppressWarnings("unchecked")
public Function toViewFunction() {
return m -> {
if (m != null) {
Object udk = m.getUniqueDomainKey();
if (udkTranslator != null) {
viewValue = (String) ((Function) udkTranslator.toViewFunction()).apply(udk);
}
else {
viewValue = udk.toString();
}
}
else {
viewValue = null;
}
addon.setPdo(m);
return viewValue;
};
}
@Override
public Function toModelFunction() {
return v -> {
if (!addon.isInSearchOrEdit() && !Objects.equals(v, viewValue)) {
// view changed
if (v == null) {
addon.setPdo(null);
}
else {
T proxy = addon.createPdo();
Class> udkClass = proxy.getUniqueDomainKeyType();
if (udkTranslator != null) {
Object udk = udkTranslator.toModelFunction().apply(v);
addon.setPdo(proxy.findByUniqueDomainKey(udk));
if (addon.getPdo() == null) {
// no such pdo: try interactive search
proxy.setUniqueDomainKey(udk);
addon.setPdo(addon.searchPdo(proxy));
Platform.runLater(() -> ((Node) getComponent()).requestFocus());
}
}
else {
// default: no udk translator found
Object udk = v;
try {
if (!udkClass.isAssignableFrom(v.getClass())) {
// needs conversion
try {
Constructor> cons = udkClass.getConstructor(v.getClass());
udk = cons.newInstance(v);
}
catch (InvocationTargetException ix) {
// construction failed.
// this usually means that the input is malformed, incomplete, whatever.
// in such cases get the PDO from the search dialog
presetSearchCriteria(proxy, udkClass, v); // try to preset the proxy, if possible
addon.setPdo(addon.searchPdo(proxy));
Platform.runLater(() -> ((Node) getComponent()).requestFocus());
return addon.getPdo();
}
catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException |
IllegalArgumentException e) {
throw new FxRuntimeException("could not create domain key: " + udkClass.getName() +
"(" + v.getClass().getName() + ")", e);
}
}
addon.setPdo(proxy.findByUniqueDomainKey(udk));
if (addon.getPdo() == null) {
// no such pdo: try search
presetSearchCriteria(proxy, udkClass, v); // try to preset the proxy, if possible
addon.setPdo(addon.searchPdo(proxy));
Platform.runLater(() -> ((Node) getComponent()).requestFocus());
}
}
catch (RuntimeException rex) {
LOGGER.warning("loading PDO " + proxy.getClassBaseName() + " for " +
udkClass + "='" + udk + "' failed", rex);
throw rex;
}
}
}
}
return addon.getPdo();
};
}
/**
* Creates the PDO style implementation.
*
* @return the implementation
*/
protected PdoComponentAddon createPdoAddon() {
return new PdoComponentAddon<>(getComponent(), this::getPdo);
}
/**
* Gets the PDO via binding.
* For non-editable comboboxes this translator isnt used, but registered,
* because it could be changed to editable.
*
* @return the model value
*/
@SuppressWarnings("unchecked")
protected T getPdo() {
return (T) getComponent().getBinding().getModelValue();
}
/**
* Preset the given proxy with the string from the component.
*
* @param proxy the proxy pdo
* @param udkClass the class of the unique domain key
* @param v the view value
*/
protected void presetSearchCriteria(T proxy, Class> udkClass, String v) {
try {
/*
* Check if there is a factory method:
*
* .lenientValueOf(String)
*
* If so, create a udk and set it into the proxy.
*/
Method m = udkClass.getDeclaredMethod("lenientValueOf", String.class);
if (Modifier.isStatic(m.getModifiers()) && udkClass.isAssignableFrom(m.getReturnType())) {
Object udk = m.invoke(null, v);
if (udk != null) {
proxy.setUniqueDomainKey(udk);
}
}
}
catch (SecurityException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException e1) {
throw new FxRuntimeException("cannot create lenient domain key", e1);
}
catch (NoSuchMethodException nm) {
// no such method -> do nothing
}
}
/**
* Gets the {@link Bindable} annotation of the unique domain key.
*
* If the UDK consists of more than one member, the first Bindable annotation is returned.
*
* @param proxy the PDO class to inspect
* @return the annotation, null if not annotated with Bindable
*/
protected Bindable getBindableAnnotation(T proxy) {
for (Method method: proxy.getEffectiveClass().getMethods()) {
if (method.isAnnotationPresent(DomainKey.class)) {
Bindable bindable = method.getAnnotation(Bindable.class);
if (bindable != null) {
return bindable;
}
}
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy