package org.geotools.data.efeature.internal; import static org.geotools.data.efeature.internal.ESimpleFeatureAdapter.create; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.impl.ENotificationImpl; import org.geotools.data.Transaction; import org.geotools.data.efeature.EFeature; import org.geotools.data.efeature.EFeatureAttributeInfo; import org.geotools.data.efeature.EFeatureConstants; import org.geotools.data.efeature.EFeatureContext; 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.EFeatureProperty; import org.geotools.data.efeature.EFeatureReader; import org.geotools.data.efeature.EFeatureStatus; import org.geotools.data.efeature.EFeatureUtils; 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 org.opengis.feature.Property; import com.vividsolutions.jts.geom.Geometry; /** * This is an internal implementation of the {@link EFeature} contract. * <p> * It does not implement the {@link EFeature} interfaces, just the interface * methods. This allows for a minimal base class common for both * {@link EFeatureImpl} and {@link EFeatureDelegate} classes. * </p> * * @author kengu - 29. mai 2011 * * @see {@link EFeature} - the interface which this class is a minimal base for * @see {@link EFeatureImpl} - default implementation of {@link EFeature}. All models * which extend the {@link EFeature} EMF model implements this class. * @see {@link EFeatureDelegate} - a class which is able to delegate {@link EFeature} delegate */ public class EFeatureInternal { /** * The default value of the '{@link #getSRID() <em>SRID</em>}' attribute. */ protected static final String SRID_EDEFAULT = EFeatureConstants.DEFAULT_SRID; /** * The default value of the '{@link #getData(Transaction) <em>Data</em>}' attribute. */ protected static final Feature DATA_EDEFAULT = EFeatureConstants.DEFAULT_FEATURE; /** * The default value of the '{@link #isSimple() <em>Simple</em>}' attribute. */ protected static final boolean SIMPLE_EDEFAULT = EFeatureConstants.DEFAULT_IS_SIMPLE; /** * The default value of the '{@link #getDefault() <em>Default</em>}' attribute. */ protected static final String DEFAULT_EDEFAULT = EFeatureConstants.DEFAULT_GEOMETRY_NAME; /** * The default value of the '{@link #getStructure() <em>Structure</em>}' attribute. */ protected static final EFeatureInfo STRUCTURE_EDEFAULT = EFeatureConstants.DEFAULT_FEATURE_STRUCTURE; /** * Current {@link EFeature} ID. */ protected String eID; /** * The cached value of the '{@link #getStructure() <em>Structure</em>}' attribute. */ protected EFeatureInfo eStructure = STRUCTURE_EDEFAULT; /** * Cached {@link EObject} which this delegates to. * <p> * It is cached using a weak reference which allows cyclic reference between this and the * implementation. */ protected WeakReference<EFeature> eImpl; /** * Flag indicating that the {@link EFeature#getID EFeature ID} * holder check is not performed. */ protected boolean doIDHolderCheck = true; /** * Flag indicating that the {@link EFeature#getID EFeature ID} * holder check is in progress. If {@link #getID()} is * called when this flag is <code>true</code>, then this is the ID * holder. This follows from the fact that the * {@link #eImpl() actual implementation} is delegating its * {@link EAttribute ID attribute} to {@link #getID() this}. */ protected boolean isIDHolderChecking = false; /** * Flag indicating that the {@link EFeature#getID()} value is hold by this. */ protected boolean isIDHolder = false; /** * Cached list of to {@link EFeatureProperty} instances in same order * as {@link EAttribute}s in current {@link #eStructure EFeatureInfo}. */ protected List<EFeaturePropertyDelegate<?, ? extends Property, ? extends EStructuralFeature>> eProperties; /** * Cached property name to {@link EFeatureProperty} instances map. */ protected Map<String, EFeaturePropertyDelegate<?, ? extends Property, ? extends EStructuralFeature>> ePropertyMap; /** * Flag storing current detached values state. Since {@link #eHints} is mutable, * current state change must be cached in order to track actual changes made to hints. */ protected boolean eValuesDetached = false; /** * Cached set of validated {@link EFeatureInfo#eUID}s */ protected static Map<EClass,Set<Long>> eValidatedClassMap = Collections.synchronizedMap(new WeakHashMap<EClass,Set<Long>>()); /** * Transaction used when not explicitly specified */ protected Transaction eTx = Transaction.AUTO_COMMIT; /** * Cached {@link EFeatureHints} */ protected EFeatureHints eHints; /** * Cached {@link ESimpleFeature} singleton instance. * <p> * This is used {@link ThreadLocal thread local} to allow * multiple threads to work on {@link ESimpleFeature} * singletons without concurrent modification problems. * </p> * @see {@link EFeatureHints#EFEATURE_SINGLETON_FEATURES} */ protected static ThreadLocal<ESimpleFeatureInternal> eSingleton = new ThreadLocal<ESimpleFeatureInternal>(); // ----------------------------------------------------- // Constructors // ----------------------------------------------------- /** * Context-unaware constructor. * <p> * Use this constructor when the {@link EFeatureContext} is unknown. * <p> * The implementation is weakly referenced to allow garbage collection * when the implementation is no longer referenced. * <p> * {@link EFeatureContext Context} and {@link EFeatureInfo structure} must * be set before it can be read by {@link EFeatureReader}. * </p> * @param eImpl - {@link EObject} instance which implements {@link EFeature}. * * @see {@link EFeatureContextHelper} - read more about the context startup problem. * @see {@link #setStructure(EFeatureInfo)} - set {@link EFeatureInfo#eContext() context} * and {@link EFeatureInfo structure}. * */ public EFeatureInternal(EFeature eImpl) { this(eInternalContext(eImpl),eImpl); } /** * Auto-configuring structure constructor. * <p> * This constructor builds the {@link EFeatureInfo structure} * from given {@link EObject} instance using EMF reflection. * <p> * <b>Note</b>: The constructor verifies the structure. * If invalid, a {@link IllegalArgumentException} is thrown. * </p> * @param eContext - the {@link EFeatureContext} which this belongs * @param eImpl - {@link EObject} instance which implements {@link EFeature}. * @throws IllegalArgumentException If the {@link EFeatureInfo structure} of this feature * {@link EFeatureInfo#validate(EPackage, EClass) fails to validate}. * */ public EFeatureInternal(EFeatureContext eContext, EFeature eImpl) throws IllegalArgumentException { // // Build EFeature structure from EObject instance // this(EFeatureInfo.create(eContext, eImpl, new EFeatureHints()), eImpl, null); } /** * Explicit-configuring constructor. * <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 eHints - {@link EFeatureHints} instance. If <code>null</code>, * {@link EFeatureInfo#eHints() structure hints} is used. * @throws IllegalArgumentException If the {@link EFeatureInfo structure} of this feature * {@link EFeatureInfo#validate(EPackage, EClass) fails to validate}. */ public EFeatureInternal(EFeatureInfo eStructure, EFeature eImpl, EFeatureHints eHints) throws IllegalArgumentException { // // Cache weak reference to implementation // this.eImpl = new WeakReference<EFeature>(eImpl); // // Copy structure hints // this.eHints = (eHints==null ? new EFeatureHints(eStructure.eHints()) : eHints); // // Set structure // setStructure(eStructure); } // ----------------------------------------------------- // EFeature implementation // ----------------------------------------------------- public String getID() { // // ----------------------------------------------------- // Some EMF GenModel Feature delegating pattern // choices (f.ex. 'None') will result in recursive // calls to from EFeature#getID() to // EFeatureInternal#getID() because of the dynamic // implementation of eGet(EStructuralFeature) methods. // // This method implements re-entry guards which // detects the situation, indication that the EFeature // implementation is abstract, delegating the storage // of the ID value to this EFeatureInternal instance // (hence the 'ID holder' check and flags) // ----------------------------------------------------- // // Verify current state // verify(); // // Is checking if this is the ID holder? // if(isIDHolderChecking) { // // The re-entry implies that this is the ID holder // isIDHolder = true; } // // Get ID held by this? // if(isIDHolder) { return eID; } // // ------------------------------------ // Enter "Check if ID Holder" mode? // ------------------------------------ // This is only done once per instance // else if(doIDHolderCheck) { // // -------------------------------- // If this method is called // recursively, then this must be // an ID holder. // -------------------------------- // // Enable recursive call detection // isIDHolderChecking = true; } // // Implementation holds the ID value. Get EFeature ID attribute. // EAttribute eAttribute = getStructure().eIDAttribute(); // // Get current EFeature ID (will recurse into this method if ID holder) // eID = (String)eImpl().eGet(eAttribute); // // Reset ID holder check flags? // if(doIDHolderCheck) { // // Run check only once // doIDHolderCheck = false; // // Notify that the check is completed // isIDHolderChecking = false; } // // Finished // return (eID!=null ? eID.toString() : null); } public void setID(String eNewID) { // // Forward // eSetID(eNewID, true); } public String getSRID() { return eStructure==null ? SRID_EDEFAULT : getStructure().getSRID(); } public void setSRID(String newSRID) { verify(); // // Prepare change delta // String oldSRID = getSRID(); // Is changed? // if (newSRID != oldSRID) { // // Update SRID for structure. // Current bounds is invalidated // by callback to eStructureInfoListener // getStructure().setSRID(newSRID); // // Forward // eNotify(EFeaturePackage.EFEATURE__SRID, oldSRID, newSRID); } } public ESimpleFeature getData(Transaction transaction) { // // Do sanity checks // verify(); // // Get ESimpleFeature instance // return eSimpleFeature(this,eHints); } public ESimpleFeature setData(Feature newData, Transaction transaction) { // // Sanity checks // verify(); if (newData == null) { throw new NullPointerException("Data can not be set to null"); } // // Prepare adaption of new data to given structure // ESimpleFeatureAdapter eAdapter = create(eStructure, eImpl(), newData); // // Adapt data to ESimpleFeature // (EMF notifications are raised if anything changed) // if(eHints.eValuesDetached()) { return eAdapter.eAdapt(eStructure, eSimpleFeature(this,eHints), eTx); } else { return eAdapter.eAdapt(eStructure, eImpl(), eTx); } } public String getDefault() { verify(); return getStructure().eGetDefaultGeometryName(); } public void setDefault(String newDefault) { verify(); String oldDefault = getDefault(); getStructure().eSetDefaultGeometryName(newDefault); eNotify(EFeaturePackage.EFEATURE__DEFAULT, oldDefault, newDefault); } public EFeatureInfo getStructure() { return eStructure; } public boolean setStructure(EFeatureInfo eNewStructure) { // // Do sanity checks // if(eNewStructure==null) { throw new NullPointerException("EFeatureInfo structure can not be null"); } // // Calculate change // boolean bFlag = this.eStructure!=eNewStructure; // // Is changed // if(bFlag) { // // Cache reference to old structure // EFeatureInfo eOldStructure = this.eStructure; // // ------------------------------------------------------ // Validate implementation against structure? // ------------------------------------------------------ // This is an optimization exploiting the fact that // structures are immutable once created, and that // each structure can thus be uniquely identified // by a unique ID. It is therefore only required to // validate this implementation against each unique // structure once. // if(!eNewStructure.eEqualTo(eOldStructure)) { validate(eNewStructure, eImpl()); } // // Is valid, initialize this instance // this.eStructure = eNewStructure; // // Reset id information, forcing ID holder checks // eInitID(); // // Reset property collections // this.eProperties = null; this.ePropertyMap = null; // // Tell listeners in old structure about the change? // if(eOldStructure!=null) { eOldStructure.eNotify(this, EFeaturePackage.EFEATURE__STRUCTURE, eOldStructure, eNewStructure); } } // // Finished // return bFlag; } public <V> EFeatureAttributeList<V> getAttributeList(Class<V> valueType) { verify(); return new EFeatureAttributeList<V>(getPropertyList(valueType), valueType); } public <V extends Geometry> EFeatureGeometryList<V> getGeometryList(Class<V> valueType) { verify(); return new EFeatureGeometryList<V>(getPropertyList(valueType), valueType); } // ----------------------------------------------------- // EFeatureInternal methods // ----------------------------------------------------- public void enter(Transaction transaction) { this.eTx = transaction; } public void leave() { this.eTx = Transaction.AUTO_COMMIT; } public EFeatureHints eHints() { return eHints; } public void eReplace(EFeatureHints eHints, boolean eSetInitDetachedValues) { // // Verify state // if(eStructure==null) { throw new IllegalStateException("EFeatureInternal is not created"); } // // Replace hints // this.eHints = eHints; // // Discover ID? (only possible added to resource and not a proxy) // if( !(eImpl().eResource()==null || eImpl().eIsProxy()) ) { // // Structure not change (eID was not reset) // eInitID(); // // Get EFeature ID (assignment is done by method) // getID(); } // // Tell all EFeatureProperty instances to initialize detached values? // if(eSetInitDetachedValues) eSetInitDetachedValues(); } public void eReplace(EFeatureInfo eStructure, EFeature eImpl, EFeatureHints eHints, boolean eSetInitDetachedValues) { // // Replace weak reference? // if(this.eImpl.get()!=eImpl) { this.eImpl = new WeakReference<EFeature>(eImpl); } // // Replace hints // this.eHints = eHints; // // Discover ID? (only possible added to resource and not a proxy) // if( !(setStructure(eStructure) || eImpl.eResource()==null || eImpl.eIsProxy()) ) { // // Structure not change (eID was not reset) // eInitID(); // // Get EFeature ID (assignment is done by method) // getID(); } // // Tell all EFeatureProperty instances to initialize detached values? // if(eSetInitDetachedValues) eSetInitDetachedValues(); } public String eSetID(String eNewID, boolean eSetUsage) { // // Verify current state // verify(); // // Tell ID factory of ID usage? // if(eSetUsage) { // // Get ID factory // EFeatureIDFactory eIDFactory = getStructure().eContext().eIDFactory(); // // ------------------------------------------------------- // Notify ID usage to factory? // ------------------------------------------------------- // This is part of the context startup problem solution. // When constructing EFeatures from XMI, the context is // unknown. This comes from the fact that values are // serialized before instances are added to the resource // (context). Therefore, a internal context is used // instead. This internal context should not not create // IDs, since IDs only have meaning in the context that // that they belong. The ID factory in the internal // context does therefore not support creation and // usage of IDs (throws OperationUnsupportedException). // if(!(eIDFactory instanceof EFeatureVoidIDFactory)) { // // Tell ID factory of ID usage, a new ID is returned if not unique // eNewID = eIDFactory.useID(eImpl(),eNewID); } } // // Is ID held by this? // if(isIDHolder) { // // Cache old for later use // String eOldID = eID; // // Cache ID in this // eID = eNewID; // // Notify? // eNotify(EFeaturePackage.EFEATURE__ID, eOldID, eNewID); } else { // // Implementation holds the ID value. Get EFeature ID attribute. // EAttribute eAttribute = getStructure().eIDAttribute(); // // Since the implementation holds the ID value, the // the eAttribute must be changeable. // if(!eAttribute.isChangeable()) { throw new IllegalStateException("EAttribute must be " + "changeable when the eImpl() is the " + "ID holder."); } // // Update ID attribute value // eImpl().eSet(eAttribute,eNewID); } // // Finished // return eNewID; } // ----------------------------------------------------- // Public static helper methods // ----------------------------------------------------- public static final EFeatureInternal eInternal(EFeatureInfo eStructure, EObject eObject) throws IllegalArgumentException { if(eObject instanceof EFeatureInternal) { return (EFeatureInternal)eObject; } else if(eObject instanceof EFeatureImpl) { return ((EFeatureImpl)eObject).eInternal(); } else if(eObject instanceof EFeatureDelegate) { return ((EFeatureDelegate)eObject).eInternal(); } throw new IllegalArgumentException("EObject " + eObject + " does not implement EFeature"); } // ----------------------------------------------------- // Protected helper methods // ----------------------------------------------------- /** * Reset ID information, forcing ID holder checks */ protected void eInitID() { eID = null; doIDHolderCheck = true; isIDHolder = false; isIDHolderChecking = false; } // /** // * Set {@link EFeatureHints} instance. // * @param eHints - new {@link EFeatureHints} instance. // * @return <code>true</code> if {@link EFeatureHints#EFEATURE_VALUES_DETACHED} has changed // */ // protected boolean eSetHints(EFeatureHints eHints) { // // // // Check if hint is changed // // // boolean bFlag = eHints.eValuesDetached()!=eValuesDetached; // // // // Replace hints // // // this.eHints = eHints; // // // // Set flag tracking changes to EFEATURE_VALUES_DETACHED flag // // // eValuesDetached = eHints.eValuesDetached(); // // // // Finished // // // return bFlag; // } protected void eSetInitDetachedValues() { if(eProperties!=null) { for(EFeaturePropertyDelegate<?, ?, ?> it : eProperties) { it.eSetInitDetachedValues(); } } } /** * Verify that state is available */ protected void verify() throws IllegalStateException { if(eStructure==null) throw new IllegalStateException(this + " is not valid. " + "Please specify the structure."); } /** * Get {@link InternalEObject} instance containing {@link EFeature} data. * @return a {@link InternalEObject} instance. * @throws NullPointerException If garbage collected (is weakly referenced) */ protected InternalEObject eImpl() throws NullPointerException { InternalEObject eObject = (InternalEObject)eImpl.get(); if(eObject instanceof EFeatureDelegate) { eObject = ((EFeatureDelegate)eObject).eImpl(); } if (eObject == null) { throw (NullPointerException)(new NullPointerException("EFeature implementation " + eStructure.eClassName() + " is finalized (garbage collected).")).fillInStackTrace(); } return eObject; } /** * Get {@link InternalEObject} instance implementing {@link EFeature} data. * @return a {@link EFeature} instance. * @throws NullPointerException If garbage collected (is weakly referenced) */ protected InternalEObject eFeature() throws NullPointerException { InternalEObject eObject = (InternalEObject)this.eImpl.get(); if (eObject == null) { throw (NullPointerException)(new NullPointerException("EFeature implementation " + eStructure.eClassName() + " is finalized (garbage collected).")).fillInStackTrace(); } return eObject; } protected void eNotify(int feature, Object oldValue, Object newValue) { eNotify(eImpl(), feature, oldValue, newValue); } /** * Get current list of {@link EFeatureProperty} instances. * <p> * This method implements lazy creation of {@link EFeatureProperty} instances. * </p> * * @return list of {@link EFeatureProperty} instances. */ protected List<EFeaturePropertyDelegate<?, ? extends Property, ? extends EStructuralFeature>> getProperties() { if (eProperties == null) { // // Get all attributes in structure // List<EFeatureAttributeInfo> eList = getStructure().eGetAttributeInfoList(true); // // Initialize map // eProperties = EFeatureUtils.newList(eList.size()); // // Loop over all attributes // for (EFeatureAttributeInfo it : eList) { EAttribute eAttribute = it.eAttribute(); Class<?> type = eAttribute.getEAttributeType().getInstanceClass(); eProperties.add(newProperty(this, it.eName(), type)); } } return eProperties; } /** * Get {@link EFeatureProperty} name to instances mappin. * <p> * This method implements lazy creation of instance map. * </p> * * @return map of {@link EFeatureProperty} instances. */ protected Map<String, ? extends EFeaturePropertyDelegate<?, ? extends Property, ? extends EStructuralFeature>> getPropertyMap() { if (ePropertyMap == null) { // // Get all properties in structure // List<EFeaturePropertyDelegate<?, ? extends Property, ? extends EStructuralFeature>> eList = getProperties(); // // Initialize map // ePropertyMap = EFeatureUtils.newMap(eList.size()); // // Loop over all attributes // for (EFeaturePropertyDelegate<?, ? extends Property, ? extends EStructuralFeature> it : eList) { String eName = it.getName(); ePropertyMap.put(eName, it); } } return ePropertyMap; } /** * Get list of {@link EFeatureProperty} instances filtered on value type. * <p> * This method implements lazy creation of {@link EFeatureProperty} instances. After creation, * the instances are cached in {@link #ePropertyMap} * </p> * * @return list of {@link EFeatureProperty} instances. */ @SuppressWarnings("unchecked") protected <V> List<? extends EFeatureProperty<V, Property>> getPropertyList(Class<V> type) { // // Initialize // Collection<? extends EFeatureProperty<?, ? extends Property>> eList = getProperties(); List<EFeatureProperty<V, Property>> eSelected = EFeatureUtils.newList(eList.size()); // // Select EFeatureProperty instances with correct value type // for (EFeatureProperty<?, ? extends Property> it : eList) { // // Has correct value type? // if (type.isAssignableFrom(it.getValueType())) { eSelected.add((EFeatureProperty<V, Property>) it); } } // // Finished selection // return eSelected; } // ----------------------------------------------------- // Static helper methods // ----------------------------------------------------- protected static void eNotify(InternalEObject eObject, int feature, Object oldValue, Object newValue) { if (eObject.eNotificationRequired()) { eObject.eNotify( new ENotificationImpl(eObject, Notification.SET, feature, oldValue, newValue)); } } /** * Get a {@link ESimpleFeature} instance. * <p> * This method decides from inspection of * {@link EFeatureInfo#eHints()} if a new instance * or the singleton instance should be returned * </p> * @param eInternal EFeatureInternal that contains the ESimpleFeature data * @return a {@link ESimpleFeature} instance */ protected static ESimpleFeature eSimpleFeature(EFeatureInternal eInternal, EFeatureHints eHints) { // // Prepare // ESimpleFeatureInternal eData = null; // // Is instance singleton? // if(eHints.eSingletonFeatures()) { // // Get singleton instance // eData = eSingleton.get(); // // Create new singleton instance? // if(eData==null || eData.isReleased()) { // // Create instance // eData = new ESimpleFeatureInternal(eInternal); // // Update thread local instance // eSingleton.set(eData); } // // else, set internal implementation // else eData.eReplace(eInternal); } else { // // Create new instance // eData = new ESimpleFeatureInternal(eInternal); } // // Finished // return eData; } protected static final EFeatureContext eInternalContext(EFeature eImpl) { return EFeatureContextHelper.eContext(eImpl); } protected static final void validate(EFeatureInfo eStructure, EObject eObject) throws IllegalArgumentException { // // Synchronize multiple threads access to map // synchronized(eValidatedClassMap) { // // Prepare // EClass eClass = eObject.eClass(); EPackage ePackage = eClass.getEPackage(); // // Get structures already verified for implementing class // Set<Long> eValidSet = eValidatedClassMap.get(eClass); // // Found no validated structures? // if( eValidSet == null) { eValidSet = new HashSet<Long>(); eValidatedClassMap.put(eClass, eValidSet); } if( !eValidSet.contains(eStructure.eUID())) { // // Get parent class? // EClass eParent = null; if (!eStructure.isRoot()) { eParent = EFeatureUtils.eGetContainingClass(eObject); } // // Validate structure against EObject instance EClass // EFeatureStatus s; if (!(s = eStructure.validate(ePackage, eParent)).isSuccess()) { throw new IllegalArgumentException(s.getMessage(), s.getCause()); } // // Add to verified structures // eValidSet.add(eStructure.eUID()); } } } @SuppressWarnings("unchecked") protected static EFeaturePropertyDelegate<?, ? extends Property, ? extends EStructuralFeature> newProperty( EFeatureInternal eInternal, String eName, Class<?> type) { if (Geometry.class.isAssignableFrom(type)) { return newGeometry(eInternal, eName, (Class<? extends Geometry>)type); } return newAttribute(eInternal, eName, type); } protected static <V> EFeatureAttributeDelegate<V> newAttribute(EFeatureInternal eInternal, String eName, Class<V> type) { return new EFeatureAttributeDelegate<V>(eInternal, eName, type); } protected static <V extends Geometry> EFeatureGeometryDelegate<V> newGeometry( EFeatureInternal eInternal, String eName, Class<V> type) { return new EFeatureGeometryDelegate<V>(eInternal, eName, type); } }