/** * <copyright> * </copyright> * * $Id$ */ package org.geotools.data.efeature.internal; import java.lang.ref.WeakReference; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.geotools.data.Transaction; import org.geotools.data.efeature.EFeatureAttributeInfo; import org.geotools.data.efeature.EFeatureProperty; import org.geotools.data.efeature.EStructureInfo; import org.opengis.feature.Property; /** * An abstract implementation of the model data type {@link EFeatureProperty}. * * @author kengu, 22. apr. 2011 */ public abstract class EFeaturePropertyDelegate<V, T extends Property, S extends EStructuralFeature> implements EFeatureProperty<V, T> { /** * Cached {@link EFeatureInternal} instance containing the {@link Property#getValue() property value}. * <p> * The {@link #eObject delegate} is stored as a {@link WeakReference weak reference} so it is * not prevented from being finalized and garbage collected. */ protected WeakReference<EFeatureInternal> eInternal; /** * Cached {@link Property property data} instance */ protected T eData; /** * The actual data class. */ protected Class<T> eDataType; /** * The actual value class. */ protected Class<V> eValueType; /** * Cached {@link EFeatureAttributeInfo} */ protected EFeatureAttributeInfo eStructure; /** * The id of the {@link EStructuralFeature structural feature} * of the {@link #eObject implementation} which contains * the {@link #getData() property data} */ protected WeakReference<EStructuralFeature> eStructuralFeature; /** * Cached value when {@link #isDetached() detached} */ protected V eValue; /** * Flag indication that detached value should be updated */ protected Boolean eInitDetached = true; // ----------------------------------------------------- // Constructors // ----------------------------------------------------- /** * Default constructor. * <p> * @param eInternal * @param eStructuralFeature * @param dataType - {@link #getData() data} type. * @param valueType - {@link #getValue() value} type. */ protected EFeaturePropertyDelegate(EFeatureInternal eInternal, String eName, Class<T> dataType, Class<V> valueType) { // // Forward // super(); // // Get EFeatureAttribute structure // this.eStructure = eInternal.eStructure.eGetAttributeInfo(eName, true); // // EAttribute not found? // if (this.eStructure == null) { throw new IllegalArgumentException("EStructuralFeature '" + eName + "'" + " not found"); } // // Do value type sanity check // EAttribute eAttribute = eStructure.eAttribute(); Class<?> actualType = eAttribute.getEAttributeType().getInstanceClass(); if (!valueType.isAssignableFrom(actualType)) { // // Not correct type // throw new IllegalArgumentException("Value type '" + valueType.getName() + "'" + " mismatch"); } // // Construct delegate // this.eDataType = dataType; this.eValueType = valueType; this.eInternal = new WeakReference<EFeatureInternal>(eInternal); this.eStructuralFeature = new WeakReference<EStructuralFeature>(eAttribute); } // ----------------------------------------------------- // EFeatureProperty implementation // ----------------------------------------------------- @Override public String getName() { return getStructuralFeature().getName(); } /** * Check if EFeaturePropertyDelegate is disposed. * <p> * * @return <code>true</code> if EFeaturePropertyDelegate is disposed. */ public final boolean isDisposed() { return eInternal.get() == null || eStructuralFeature.get() == null; } /** * @throws IllegalStateException If delegate is {@link #isDisposed() disposed}. */ @Override public final T getData() { if (isDisposed()) { throw new NullPointerException("Data can not be set to null"); } if (eData == null) { eData = create(); } return eData; } /** * @throws IllegalStateException If delegate is {@link #isDisposed() disposed}. */ @Override public final void setData(T newData) { // Sanity checks // if (newData == null) { throw new NullPointerException("Data can not be set to null"); } else if (isDisposed()) { throw new NullPointerException("EFeaturePropertyDelegate is disposed"); } // Validate data and return new value // V newValue = validate(newData); // // Set new value // eObject().eSet(eStructuralFeature.get(), newValue); } @Override public final Class<T> getDataType() { return eDataType; } @Override public final Class<V> getValueType() { return eValueType; } @Override public boolean isDetached() { return eInternal().eHints.eValuesDetached(); } /** * @throws IllegalStateException If delegate is {@link #isDisposed() disposed}. */ @Override public final V getValue() { // // Detached? // if(isDetached()) { // // Initialize detached value? // if(eInitDetached) { return read(); } // // Finished // return eValue; } else { // // Release value // eValue = null; // // Set trigger for detached initialization // eInitDetached = true; // // Value is attached, read it from structure // return eValueType.cast(eObject().eGet(eStructuralFeature())); } } /** * @throws IllegalStateException If delegate is {@link #isDisposed() disposed}. * @throws NullPointerException If new value is <code>null</code>. */ @Override public final void setValue(V newValue) { if (newValue == null && !getData().isNillable()) { throw new NullPointerException("Value can not be set to null"); } V value = eValueType.cast(newValue); if(isDetached()) { eValue = newValue; } else { eObject().eSet(eStructuralFeature(), value); } } @Override public V read() throws IllegalStateException { return read(eInternal().eTx); } @Override public V read(Transaction transaction) throws IllegalStateException { // // Check if detached // if(isDetached()) { // // TODO Implement read lock check // // // Read value from object only when detached // eValue = eValueType.cast(eObject().eGet(eStructuralFeature())); // // Flag that detached initialization is completed // eInitDetached = false; } // // Finished // return getValue(); } @Override public V write() throws IllegalStateException { return write(eInternal().eTx); } @Override public V write(Transaction transaction) throws IllegalStateException { // // Decide if value is allowed to be updated from backing store // if(!isDetached()) { throw new IllegalStateException("EFeatureProperty " + getName() + " is not detached"); } // // TODO Implement write lock check // eObject().eSet(eStructuralFeature(), eValue); // // Finished // return eValue; } /** * @throws IllegalStateException * If {@link EFeatureInternal internal implementation} * is {@link #isDisposed() disposed}. */ @Override public final EObject eObject() { return eInternal().eImpl(); } /** * @throws IllegalStateException If delegate is {@link #isDisposed() disposed}. */ @Override public final EStructuralFeature getStructuralFeature() { return eStructuralFeature(); } @Override public EStructureInfo<?> getStructure() { return eStructure; } @Override public String toString() { StringBuffer result = new StringBuffer(super.toString()); result.append(" (valueType: "); result.append(eValueType); result.append(", dataType: "); result.append(eDataType); result.append(')'); return result.toString(); } // ----------------------------------------------------- // EFeaturePropertyDelegate methods // ----------------------------------------------------- /** * @throws IllegalStateException * If {@link EFeatureInternal internal implementation} * is {@link #isDisposed() disposed}. */ public final EFeatureInternal eInternal() { if (isDisposed()) { throw new IllegalStateException("EFeatureInternal instance is disposed"); } return eInternal.get(); } // ----------------------------------------------------- // Abstract EFeaturePropertyDelegate methods // ----------------------------------------------------- /** * Create new {@link Property property} instance. */ protected abstract T create(); /** * Validate {@link Property property data} instance. * * @throws IllegalArgumentException If data is not valid. * @return property value casted to {@link #getValueType() value type} if valid. */ protected abstract V validate(T data) throws IllegalArgumentException; /** * Validate {@link Property#getValue() property value}. * * @throws IllegalArgumentException If value is not valid. * @return property value casted to {@link #getValueType() value type} if valid. */ protected abstract V validate(Object value) throws IllegalArgumentException; // ----------------------------------------------------- // EFeaturePropertyDelegate helper methods // ----------------------------------------------------- protected final void eSetInitDetachedValues() { eInitDetached=true; } /** * @throws IllegalStateException If delegate is {@link #isDisposed() disposed}. */ protected final EStructuralFeature eStructuralFeature() { if (isDisposed()) { throw new IllegalStateException("EFeaturePropertyDelegate is disposed"); } return eStructuralFeature.get(); } } // EFeaturePropertyDelegate