package org.geotools.data.efeature.internal;
import static org.geotools.data.efeature.internal.EFeatureInternal.DATA_EDEFAULT;
import static org.geotools.data.efeature.internal.EFeatureInternal.DEFAULT_EDEFAULT;
import static org.geotools.data.efeature.internal.EFeatureInternal.SRID_EDEFAULT;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Internal;
import org.geotools.data.Transaction;
import org.geotools.data.efeature.EFeature;
import org.geotools.data.efeature.EFeatureHints;
import org.geotools.data.efeature.EFeatureIDFactory;
import org.geotools.data.efeature.EFeatureInfo;
import org.geotools.data.efeature.EFeaturePackage;
import org.geotools.data.efeature.EFeatureStatus;
import org.geotools.data.efeature.ESimpleFeature;
import org.geotools.data.efeature.impl.EFeatureImpl;
import org.geotools.data.efeature.util.EFeatureAttributeList;
import org.geotools.data.efeature.util.EFeatureGeometryList;
import org.opengis.feature.Feature;
import com.vividsolutions.jts.geom.Geometry;
/**
* {@link EFeature} delegate class.
* <p>
* This class implements {@link EFeature} by delegating to a
* EMF {@link InternalEObject object}. The object is mapped to
* {@link EFeature} by applying a {@link EFeatureInfo structure} to it.
* <p>
* <b>Limitations</b>
* <p>
* <nl>
* <li>It is only able to delegate to a {@link InternalEObject object}
* if it's {@link EFeatureInfo#validate(EObject) structure is valid}.
* </li>
* <li>Getter and setter methods unique to {@link InternalEObject} interface,
* delegates to {@link EFeature} only (see below).
* </li>
* </nl>
* <p>
* <b>InternalEObject</b>
* <p>
* The methods:
* <nl>
* <li>{@link InternalEObject#eGet(int, boolean, boolean)} </li>
* <li>{@link InternalEObject#eIsSet(int)} </li>
* <li>{@link InternalEObject#eSet(int, Object)} </li>
* <li>{@link InternalEObject#eUnset(int)} </li>
* </nl>
* <p>
* do not supply any {@link EClass} information. Because the {@link EClass} of
* {@link #eImpl()} can not in general be assumed to be a subtype of
* {@link EFeaturePackage#EFEATURE}, it is impossible to decide which {@link EClass}
* given feature ID belongs to. As a result, all these methods assume
* that feature IDs belong to {@link EFeature} only.
* </p>
* @author kengu - 3. juli 2011
*
* @source $URL$
*/
public class EFeatureDelegate implements EFeature, InternalEObject {
/**
* Cached {@link InternalEObject} which this delegates to.
* It is cached using a strong reference prevents garbage
* collection to occur until all references to this delegate
* is released.
*/
protected InternalEObject eImpl;
/**
* Cached {@link EFeatureInternal} which this delegates to.
*/
protected EFeatureInternal eInternal;
/**
* Cached {@link EFeatureDelegate} singleton instance.
* <p>
* This {@link ThreadLocal thread local variable} allow
* multiple threads to work on {@link EFeatureDelegate}
* singletons without the risk of any memory inconsistencies.
* </p>
* @see {@link EFeatureHints#EFEATURE_SINGLETON_FEATURES}
*/
protected static ThreadLocal<EFeatureDelegate>
eSingleton = new ThreadLocal<EFeatureDelegate>();
// -----------------------------------------------------
// Constructors
// -----------------------------------------------------
/**
* Explicit-configuring delegate constructor.
* <p>
* This class
* <p>
* <b>Note</b>: This constructor verifies the structure.
* If invalid, a {@link IllegalArgumentException} is thrown.
* </p>
* @param eStructure - {@link EFeatureInfo structure} instance.
* @param eImpl - {@link EObject} instance which implements {@link EFeature}.
* @param eTrusted - if <code>true</code>, the constructor trusts that 'eStructure'
* is a valid structure for 'eImpl'. Use this when option to optimize construction
* when 'eImpl' is already validated against given 'eStructure'.
* @param eHints TODO
* @throws IllegalArgumentException If the {@link EFeatureInfo structure} of this feature
* {@link EFeatureInfo#validate(EPackage, EClass) fails to validate}.
*/
public EFeatureDelegate(EFeatureInfo eStructure, InternalEObject eImpl,
boolean eTrusted, EFeatureHints eHints) throws IllegalArgumentException {
//
// Forward
//
eReplace(eStructure, eImpl, eTrusted, eHints);
}
// -----------------------------------------------------
// EFeatureDelegate methods
// -----------------------------------------------------
public InternalEObject eImpl() {
return eImpl;
}
public EFeatureInternal eInternal() {
return eInternal;
}
// -----------------------------------------------------
// EFeature delegation
// -----------------------------------------------------
@Override
public String getID() {
return eInternal().getID();
}
@Override
public void setID(String value) {
eInternal().eSetID(value,true);
}
@Override
public String getSRID() {
return eInternal().getSRID();
}
@Override
public String getDefault() {
return eInternal().getDefault();
}
@Override
public EFeatureInfo getStructure() {
return eInternal().getStructure();
}
@Override
public <V extends Geometry> EFeatureGeometryList<V> getGeometryList(Class<V> valueType) {
return eInternal().getGeometryList(valueType);
}
@Override
public ESimpleFeature getData() {
return eInternal().getData(Transaction.AUTO_COMMIT);
}
@Override
public ESimpleFeature getData(Transaction transaction) {
return eInternal().getData(transaction);
}
@Override
public <V> EFeatureAttributeList<V> getAttributeList(Class<V> valueType) {
return eInternal().getAttributeList(valueType);
}
@Override
public void setSRID(String newSRID) {
eInternal().setSRID(newSRID);
}
@Override
public void setData(Feature newData) {
eInternal().setData(newData, Transaction.AUTO_COMMIT);
}
@Override
public ESimpleFeature setData(Feature newData, Transaction transaction) {
return eInternal().setData(newData, transaction);
}
@Override
public void setDefault(String newDefault) {
eInternal().setDefault(newDefault);
}
@Override
public void setStructure(EFeatureInfo eStructure) {
eInternal().setStructure(eStructure);
}
// -----------------------------------------------------
// InternalEObject delegate implementation
// -----------------------------------------------------
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case EFeaturePackage.EFEATURE__ID:
return getID();
case EFeaturePackage.EFEATURE__SRID:
return getSRID();
case EFeaturePackage.EFEATURE__DATA:
return getData();
case EFeaturePackage.EFEATURE__DEFAULT:
return getDefault();
case EFeaturePackage.EFEATURE__STRUCTURE:
return getStructure();
}
return eImpl().eGet(featureID, resolve, coreType);
}
@Override
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case EFeaturePackage.EFEATURE__ID:
setID((String) newValue);
return;
case EFeaturePackage.EFEATURE__SRID:
setSRID((String) newValue);
return;
case EFeaturePackage.EFEATURE__DATA:
setData((Feature) newValue);
return;
case EFeaturePackage.EFEATURE__DEFAULT:
setDefault((String) newValue);
return;
case EFeaturePackage.EFEATURE__STRUCTURE:
setStructure((EFeatureInfo) newValue);
return;
}
eImpl().eSet(featureID, newValue);
}
@Override
public void eUnset(int featureID) {
switch (featureID) {
case EFeaturePackage.EFEATURE__ID:
setID(null);
case EFeaturePackage.EFEATURE__SRID:
setSRID(SRID_EDEFAULT);
return;
case EFeaturePackage.EFEATURE__DATA:
setData(DATA_EDEFAULT);
return;
case EFeaturePackage.EFEATURE__DEFAULT:
setDefault(DEFAULT_EDEFAULT);
return;
case EFeaturePackage.EFEATURE__STRUCTURE:
setStructure(eInternal().eStructure.eContext().eStructure().eGetFeatureInfo(eImpl()));
return;
}
eImpl().eUnset(featureID);
}
@Override
public boolean eIsSet(int featureID) {
switch (featureID) {
case EFeaturePackage.EFEATURE__ID:
return getID() != null;
case EFeaturePackage.EFEATURE__DATA:
return DATA_EDEFAULT == null ? getData() != null : !DATA_EDEFAULT.equals(getData());
case EFeaturePackage.EFEATURE__SRID:
return SRID_EDEFAULT == null ? getSRID() != null : !SRID_EDEFAULT.equals(getSRID());
case EFeaturePackage.EFEATURE__DEFAULT:
return DEFAULT_EDEFAULT == null ? getDefault() != null : !DEFAULT_EDEFAULT.equals(getDefault());
case EFeaturePackage.EFEATURE__STRUCTURE:
return getStructure() != null;
}
return eImpl().eIsSet(featureID);
}
@Override
public EList<Adapter> eAdapters() {
return eImpl().eAdapters();
}
@Override
public boolean eDeliver() {
return eImpl().eDeliver();
}
@Override
public boolean eNotificationRequired() {
return eImpl().eNotificationRequired();
}
@Override
public void eSetDeliver(boolean deliver) {
eImpl().eSetDeliver(deliver);
}
@Override
public void eNotify(Notification notification) {
eImpl().eNotify(notification);
}
@Override
public String eURIFragmentSegment(EStructuralFeature eFeature, EObject eObject) {
return eImpl().eURIFragmentSegment(eFeature, eObject);
}
@Override
public EObject eObjectForURIFragmentSegment(String uriFragmentSegment) {
return eImpl().eObjectForURIFragmentSegment(uriFragmentSegment);
}
@Override
public void eSetClass(EClass eClass) {
eImpl().eSetClass(eClass);
}
@Override
public EClass eClass() {
return eImpl().eClass();
}
@Override
public Setting eSetting(EStructuralFeature feature) {
return eImpl().eSetting(feature);
}
@Override
public int eBaseStructuralFeatureID(int derivedFeatureID, Class<?> baseClass) {
return eImpl().eBaseStructuralFeatureID(derivedFeatureID, baseClass);
}
@Override
public Resource eResource() {
return eImpl().eResource();
}
@Override
public int eContainerFeatureID() {
return eImpl().eContainerFeatureID();
}
@Override
public EObject eContainer() {
return eImpl().eContainer();
}
@Override
public int eDerivedStructuralFeatureID(int baseFeatureID, Class<?> baseClass) {
return eImpl().eDerivedStructuralFeatureID(baseFeatureID, baseClass);
}
@Override
public int eDerivedOperationID(int baseOperationID, Class<?> baseClass) {
return eImpl().eDerivedOperationID(baseOperationID, baseClass);
}
@Override
public EStructuralFeature eContainingFeature() {
return eImpl().eContainingFeature();
}
@Override
public NotificationChain eSetResource(Internal resource, NotificationChain notifications) {
return eImpl().eSetResource(resource, notifications);
}
@Override
public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID,
Class<?> baseClass, NotificationChain notifications) {
return eImpl().eInverseAdd(otherEnd, featureID, baseClass, notifications);
}
@Override
public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID,
Class<?> baseClass, NotificationChain notifications) {
return eImpl().eInverseRemove(otherEnd, featureID, baseClass, notifications);
}
@Override
public EReference eContainmentFeature() {
return eImpl().eContainmentFeature();
}
@Override
public NotificationChain eBasicSetContainer(InternalEObject newContainer,
int newContainerFeatureID, NotificationChain notifications) {
return eImpl().eBasicSetContainer(newContainer, newContainerFeatureID, notifications);
}
@Override
public NotificationChain eBasicRemoveFromContainer(NotificationChain notifications) {
return eImpl().eBasicRemoveFromContainer(notifications);
}
@Override
public URI eProxyURI() {
return eImpl().eProxyURI();
}
@Override
public EList<EObject> eContents() {
return eImpl().eContents();
}
@Override
public void eSetProxyURI(URI uri) {
eImpl().eSetProxyURI(uri);
}
@Override
public EObject eResolveProxy(InternalEObject proxy) {
return eImpl().eResolveProxy(proxy);
}
@Override
public InternalEObject eInternalContainer() {
return eImpl().eInternalContainer();
}
@Override
public TreeIterator<EObject> eAllContents() {
return eImpl().eAllContents();
}
@Override
public Internal eInternalResource() {
return eImpl().eInternalResource();
}
@Override
public Internal eDirectResource() {
return eImpl().eDirectResource();
}
@Override
public boolean eIsProxy() {
return eImpl().eIsProxy();
}
@Override
public EStore eStore() {
return eImpl().eStore();
}
@Override
public void eSetStore(EStore store) {
eImpl().eSetStore(store);
}
@Override
public EList<EObject> eCrossReferences() {
return eImpl().eCrossReferences();
}
@Override
public Object eGet(EStructuralFeature feature) {
return eGet(feature, true);
}
@Override
public Object eGet(EStructuralFeature feature, boolean resolve) {
//
// 1) Check unmappable features first
//
if(feature == EFeaturePackage.eINSTANCE.getEFeature_Data()) {
return getData();
} else if(feature == EFeaturePackage.eINSTANCE.getEFeature_Structure()) {
return getData();
}
//
// 2) Then check structure mappings, replacing feature with mapped
//
EFeatureInfo eStructure = eInternal().eStructure;
if(eStructure.eMappingExists(feature)) {
feature = eStructure.eMappedTo((EAttribute)feature);
}
//
// Forward to implementation
//
return eImpl().eGet(feature, resolve);
}
@Override
public Object eGet(EStructuralFeature feature, boolean resolve, boolean coreType) {
//
// 1) Check unmappable features first
//
if(feature == EFeaturePackage.eINSTANCE.getEFeature_Data()) {
return getData();
} else if(feature == EFeaturePackage.eINSTANCE.getEFeature_Structure()) {
return getData();
}
//
// 2) Then check structure mappings, replacing feature with mapped
//
EFeatureInfo eStructure = eInternal().eStructure;
if(eStructure.eMappingExists(feature)) {
feature = eStructure.eMappedTo((EAttribute)feature);
}
//
// Forward to implementation
//
return eImpl().eGet(feature, resolve, coreType);
}
@Override
public void eSet(EStructuralFeature feature, Object newValue) {
//
// 1) Check unmappable features first
//
if(feature == EFeaturePackage.eINSTANCE.getEFeature_Data()) {
setData((Feature)newValue);
} else if(feature == EFeaturePackage.eINSTANCE.getEFeature_Structure()) {
setStructure((EFeatureInfo)newValue);
}
//
// 2) Then check structure mappings, replacing feature with mapped
//
EFeatureInfo eStructure = eInternal().eStructure;
if(eStructure.eMappingExists(feature)) {
feature = eStructure.eMappedTo((EAttribute)feature);
}
//
// Forward to implementation
//
eImpl().eSet(feature, newValue);
}
@Override
public boolean eIsSet(EStructuralFeature feature) {
//
// 1) Check unmappable features first
//
if(feature == EFeaturePackage.eINSTANCE.getEFeature_Data()) {
return getData()!=null;
} else if(feature == EFeaturePackage.eINSTANCE.getEFeature_Structure()) {
return getStructure()!=null;
}
//
// 2) Then check structure mappings, replacing feature with mapped
//
EFeatureInfo eStructure = eInternal().eStructure;
if(eStructure.eMappingExists(feature)) {
feature = eStructure.eMappedTo((EAttribute)feature);
}
//
// Forward to implementation
//
return eImpl().eIsSet(feature);
}
@Override
public void eUnset(EStructuralFeature feature) {
//
// 1) Check unmappable features first
//
if(feature == EFeaturePackage.eINSTANCE.getEFeature_Data()) {
throw new IllegalStateException("'Data' can not be unset");
} else if(feature == EFeaturePackage.eINSTANCE.getEFeature_Structure()) {
throw new IllegalStateException("'Structure' can not be unset");
}
//
// 2) Then check structure mappings, replacing feature with mapped
//
EFeatureInfo eStructure = eInternal().eStructure;
if(eStructure.eMappingExists(feature)) {
feature = eStructure.eMappedTo((EAttribute)feature);
}
//
// Forward to implementation
//
eImpl().eUnset(feature);
}
@Override
public Object eInvoke(EOperation operation, EList<?> arguments)
throws InvocationTargetException {
return eImpl().eInvoke(operation, arguments);
}
@Override
public Object eInvoke(int operationID, EList<?> arguments) throws InvocationTargetException {
return eImpl().eInvoke(operationID, arguments);
}
// -----------------------------------------------------
// Object implementation
// -----------------------------------------------------
@Override
public String toString() {
if (eIsProxy())
return super.toString();
return eInternal().toString();
}
// -----------------------------------------------------
// Public helper methods
// -----------------------------------------------------
/**
* Create new {@link EFeatureDelegate} instance
* <p>
* If structure {@link EFeatureInfo#eHints() hints} indicate
* {@link EFeatureHints#EFEATURE_SINGLETON_FEATURES singletons} should be
* used, a {@link ThreadLocal thread local} singleton {@link EFeatureDelegate}
* instance is returned. Otherwise, a new instance is returned.
* <p>
* This hint optimize memory usage and runtime speed, but should only be used
* with read operations from {@link #eImpl()}.
* </p>
* @param eStructure - EFeature {@link EFeatureInfo structure}.
* @param eImpl - new {@link InternalEObject} implementation which this delegates to
* @param eTrusted - if <code>true</code>, the method will not validate structure against implementation.
* @throws IllegalArgumentException If implementation does not validate
* against given structure
*/
public static final EFeatureDelegate create(EFeatureInfo eStructure, InternalEObject eImpl, boolean eTrusted) {
return create(eStructure, eImpl, eTrusted, eStructure.eHints());
}
/**
* Create new {@link EFeatureDelegate} instance
* </p>
* @param eStructure - EFeature {@link EFeatureInfo structure}.
* @param eImpl - new {@link InternalEObject} implementation which this delegates to
* @param eTrusted - if <code>true</code>, the method will not validate structure against implementation.
* @param eHints - if {@link EFeatureHints#EFEATURE_SINGLETON_FEATURES} is <code>true</code>,
* a {@link ThreadLocal thread local} singleton instance is returned
* @throws IllegalArgumentException If implementation does not validate
* against given structure
*/
public static final EFeatureDelegate create(EFeatureInfo eStructure,
InternalEObject eImpl, boolean eTrusted, EFeatureHints eHints) {
//
// Initialize
//
EFeatureDelegate eDelegate;
//
// Is EFeatureDelegate a singleton?
//
if(eHints.eSingletonFeatures()) {
//
// Get singleton instance
//
eDelegate = eSingleton.get();
//
// No singleton instance found?
//
if(eDelegate==null) {
//
// Create new delegate
//
eDelegate = new EFeatureDelegate(eStructure, eImpl, eTrusted, eHints);
//
// Update thread local variable
//
eSingleton.set(eDelegate);
}
//
// Adapt directly? (replaces current delegate)
//
else if(eImpl instanceof EFeatureDelegate) {
//
// Cast to EFeatureDelegate
//
eDelegate = (EFeatureDelegate)eImpl;
//
// Replace structure
//
eDelegate.eReplace(eStructure, eDelegate.eImpl, false, eHints);
}
//
// Replace implementation of singleton delegate
//
else {
//
// Replace implementation
//
eDelegate.eReplace(eStructure, eImpl, eTrusted, eHints);
}
}
//
// Adapt directly? (returns same current delegate)
//
else if(eImpl instanceof EFeatureDelegate) {
//
// Cast to EFeatureDelegate
//
eDelegate = (EFeatureDelegate)eImpl;
//
// Replace structure
//
eDelegate.eReplace(eStructure, eDelegate.eImpl, false, eHints);
}
//
// Construct new instance
//
else {
eDelegate = new EFeatureDelegate(eStructure, eImpl, eTrusted, eHints);
}
//
// Get current ID if set
//
String eSetID = eDelegate.getID();
//
// Get ID factory from context
//
EFeatureIDFactory eIDFactory = eStructure.eContext().eIDFactory();
//
// Set ID as used?
//
if(!(eSetID==null || eSetID.length()==0)) {
//
// Set ID as used for this delegate
//
eIDFactory.useID(eDelegate, eSetID);
} else {
//
// Create new ID for this delegate
//
eSetID = eIDFactory.createID(eDelegate);
}
//
// Finished
//
return eDelegate;
}
// -----------------------------------------------------
// Protected helper methods
// -----------------------------------------------------
/**
* Replace current hints
* <p>
* @param eHints - {@link EFeatureHints} instance. If <code>null</code>,
* {@link EFeatureInfo#eHints() structure hints} is used instead.
*/
protected void eReplace(EFeatureHints eHints) {
//
// Verify state
//
if(eInternal==null) {
throw new IllegalStateException("EFeatureDelegate is not created");
}
//
// Ensure hints exists
//
eHints = (eHints==null ? new EFeatureHints(eInternal.eStructure.eHints()) : eHints);
//
// Replace hints
//
this.eInternal.eReplace(eHints, true);
}
/**
* Replace current delegation
* <p>
* @param eStructure - EFeature {@link EFeatureInfo structure}.
* @param eImpl - new {@link EObject} implementation which this delegates to
* @param eTrusted - if <code>true</code>, the method will not validate
* structure against implementation.
* @param eHints - {@link EFeatureHints} instance. If <code>null</code>,
* {@link EFeatureInfo#eHints() structure hints} is used instead.
* @throws IllegalArgumentException If implementation does not validate
* against given structure
*/
protected void eReplace(EFeatureInfo eStructure, InternalEObject eImpl, boolean eTrusted, EFeatureHints eHints) {
//
// Validate structure?
//
if(!eTrusted) {
EFeatureStatus eStatus;
if((eStatus = eStructure.validate(eImpl)).isFailure()) {
throw new IllegalArgumentException(
"EObject implementation is not valid. " +
eStatus.getMessage(), eStatus.getCause());
}
}
//
// Ensure hints exists
//
eHints = (eHints==null ? new EFeatureHints(eStructure.eHints()) : eHints);
//
// Is known implementation?
//
if(eImpl instanceof EFeatureImpl) {
//
// --------------------------------------------
// This is a replace (copy) operation.
// --------------------------------------------
//
// Cache strong reference to implementation,
// ensuring that it is not garbage collected
// until this delegate is.
//
this.eImpl = eImpl;
//
// Get internal EFeature implementation from EFeatureImpl
//
this.eInternal = ((EFeatureImpl)eImpl).eInternal();
//
// Replace implementation
//
this.eInternal.eReplace(eStructure, this, eHints, true);
}
else if(eImpl instanceof EFeatureDelegate) {
//
// --------------------------------------------
// This is a replace (copy) operation.
// --------------------------------------------
//
// Cache strong reference to EObject implementation,
// ensuring that it is not garbage collected until
// this delegate is. This acts like a copy constructor.
//
this.eImpl = ((EFeatureDelegate)eImpl).eImpl();
//
// Get internal EFeature implementation from delegate
//
this.eInternal = ((EFeatureDelegate)eImpl).eInternal();
//
// Replace implementation
//
this.eInternal.eReplace(eStructure, this, eHints, true);
}
else {
//
// Cache strong reference to EObject implementation,
// ensuring that it is not garbage collected until
// this delegate is
//
// (EFeatureInternal only has a weak reference).
//
this.eImpl = eImpl;
//
// --------------------------------------------
// This is the replace (or create) operation.
// --------------------------------------------
//
if(this.eInternal==null) {
//
// Create internal implementation
//
this.eInternal = new EFeatureInternal(eStructure, this, eHints);
}
else {
//
// Replace implementation
//
this.eInternal.eReplace(eStructure, this, eHints, true);
}
}
}
} // EFeatureDelegate