All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tentackle.fx.rdc.translate.PdoStringTranslator Maven / Gradle / Ivy

There is a newer version: 21.16.1.0
Show newest version
/*
 * 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