/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * *** * * Community License: GPL 3.0 * * This file is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * This file 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * *** * * Available Commercial License: GraniteDS SLA 1.0 * * This is the appropriate option if you are creating proprietary * applications and you are not prepared to distribute and share the * source code of your application under the GPL v3 license. * * Please visit http://www.granitedataservices.com/license for more * details. */ package org.granite.client.javafx.tide; import java.lang.reflect.Method; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javax.inject.Inject; import javax.inject.Named; import org.granite.client.javafx.util.ChainedObservableValue; import org.granite.client.javafx.util.ChainedObservableValue.ObservableValueGetter; import org.granite.client.javafx.util.ChainedProperty; import org.granite.client.javafx.util.ChainedProperty.PropertyGetter; import org.granite.client.javafx.util.ChangeWatcher; import org.granite.client.javafx.util.ChangeWatcher.Trigger; import org.granite.client.tide.Context; import org.granite.client.tide.ContextAware; import org.granite.client.tide.data.EntityManager; import org.granite.client.tide.data.PersistenceManager; /** * @author William DRAI */ @Named public class ManagedEntity<T> implements ContextAware { private ObjectProperty<T> instance = new SimpleObjectProperty<T>(this, "instance"); private ChangeWatcher<T> instanceWatcher = ChangeWatcher.watch(instance); @Inject private EntityManager entityManager; private JavaFXDataManager dataManager; private ReadOnlyBooleanWrapper saved = new ReadOnlyBooleanWrapper(this, "saved", true); private ReadOnlyBooleanWrapper dirty = new ReadOnlyBooleanWrapper(this, "dirty", false); public ManagedEntity() { } public ManagedEntity(EntityManager entityManager) { init(entityManager); } public ManagedEntity(EntityManager entityManager, T value) { init(entityManager); instance.set(value); } public ObjectProperty<T> instanceProperty() { return instance; } public T getInstance() { return instance.get(); } public void setInstance(T value) { this.instance.set(value); } public <X> ChainedProperty<T, X> instanceProperty(PropertyGetter<T, X> getter) { return new ChainedProperty<T, X>(instanceWatcher, getter); } public <X> ChainedObservableValue<T, X> instanceObservableValue(ObservableValueGetter<T, X> getter) { return new ChainedObservableValue<T, X>(instanceWatcher, getter); } public ReadOnlyBooleanProperty savedProperty() { return saved.getReadOnlyProperty(); } public boolean isSaved() { return saved.get(); } public ReadOnlyBooleanProperty dirtyProperty() { return dirty.getReadOnlyProperty(); } public boolean isDirty() { return dirty.get(); } private Trigger<T, Object> entityChangeTrigger = new Trigger<T, Object>() { @Override public Object beforeChange(T oldInstance) { if (oldInstance != null) { entityManager.resetEntity(oldInstance); ReadOnlyProperty<Object> versionProperty = getVersionProperty(oldInstance); versionProperty.removeListener(versionChangeListener); dirty.unbind(); } return null; } @Override public void afterChange(T newInstance, Object value) { if (newInstance == null) return; ReadOnlyProperty<Object> versionProperty = getVersionProperty(newInstance); versionProperty.addListener(versionChangeListener); saved.set(versionProperty.getValue() != null); EntityManager entityManager = PersistenceManager.getEntityManager(newInstance); if (entityManager == null) ManagedEntity.this.entityManager.mergeExternalData(newInstance); else if (entityManager != ManagedEntity.this.entityManager) throw new RuntimeException("Entity " + newInstance + " cannot be attached: already attached to another entity manager"); dirty.bind(dataManager.deepDirtyEntity(newInstance)); } }; private ChangeListener<Object> versionChangeListener = new ChangeListener<Object>() { @Override public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) { saved.set(newValue != null); } }; @Override public void setContext(Context context) { init(context.getEntityManager()); } private void init(EntityManager entityManager) { if (this.entityManager == null) this.entityManager = entityManager; this.dataManager = (JavaFXDataManager)this.entityManager.getDataManager(); this.instanceWatcher.addTrigger(entityChangeTrigger); } public void reset() { if (instance.get() == null) return; entityManager.resetEntity(instance.get()); } @SuppressWarnings("unchecked") private ReadOnlyProperty<Object> getVersionProperty(Object value) { String versionPropertyName = dataManager.getVersionPropertyName(value); if (versionPropertyName == null) throw new RuntimeException("No version property found on entity " + value); try { Method m = value.getClass().getMethod(versionPropertyName + "Property"); return (ReadOnlyProperty<Object>)m.invoke(value); } catch (Exception e) { throw new RuntimeException("Could not get version property on entity " + value, e); } } }