package org.geotools.data.efeature; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.NoSuchElementException; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.geotools.data.Query; import org.geotools.data.Transaction; import org.geotools.data.efeature.impl.EFeatureImpl; import org.geotools.data.efeature.internal.EFeatureDelegate; import org.geotools.data.efeature.internal.ESimpleFeatureDelegate; import org.geotools.data.simple.SimpleFeatureReader; import org.geotools.util.logging.Logging; import org.opengis.feature.Feature; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; /** * {@link EFeature} reader implementation. * * @author kengu * */ public class EFeatureReader implements SimpleFeatureReader { /** * Static LOGGER for all {@link EFEatureReader} instances */ private static final Logger LOGGER = Logging.getLogger(EFeatureReader.class); /** * Cached {@link EFeatureInfo} instance */ protected final EFeatureInfo eStructure; /** * Cached {@link EFeatureAttributeReader} instance. Must be closed when this reader is closed to * prevent memory leakage. */ protected final EFeatureAttributeReader eReader; /** * Cached {@link EFeatureDataStore} instance. Reference must be reset when the this reader is closed * to prevent memory leakage. */ protected WeakReference<EFeatureDataStore> eDataStore; /** * Cached {@link Transaction}. Can contain locking information */ protected final Transaction eTx; /** * Cached Query hints */ protected EFeatureHints eHints; // ----------------------------------------------------- // Constructors // ----------------------------------------------------- /** * The {@link EFeatureReader} constructor. * * @param eDataStore - {@link EFeatureDataStore} instance containing {@link EFeature} resource * information * @param query - {@link Query} instance. Note that {@link Query#getTypeName()} * is expected to be a name of a {@link SimpleFeatureType} in given data store. * <p> * {@link SimpleFeatureType} names have the following format: * * <pre> * eName=<eFolder>.<eReference> * * where * * eFolder = {@link EFeature} folder name * eReference = {@link EFeature} reference name * </pre> * @throws IOException */ protected EFeatureReader(EFeatureDataStore eDataStore, Query query) throws IOException { this(eDataStore, query, Transaction.AUTO_COMMIT); } /** * The {@link EFeatureReader} constructor. * * @param eDataStore - {@link EFeatureDataStore} instance containing {@link EFeature} resource * information * @param query - {@link Query} instance. Note that {@link Query#getTypeName()} * is expected to be a name of a {@link SimpleFeatureType} in given data store. * <p> * {@link SimpleFeatureType} names have the following format: * * <pre> * eName=<eFolder>.<eReference> * * where * * eFolder = {@link EFeature} folder name * eReference = {@link EFeature} reference name * </pre> * * @throws IOException */ protected EFeatureReader(EFeatureDataStore eDataStore, Query query, Transaction eTx) throws IOException { // // Cache references // this.eDataStore = new WeakReference<EFeatureDataStore>(eDataStore); this.eStructure = eDataStore.eStructure().eGetFeatureInfo(query.getTypeName()); this.eReader = new EFeatureAttributeReader(eDataStore, query); this.eTx = eTx; // // Copy query hints // if(query.getHints() instanceof EFeatureHints) { this.eHints = new EFeatureHints(query.getHints()); }else { this.eHints = new EFeatureHints(this.eStructure.eHints); this.eHints.add(query.getHints()); } } // ----------------------------------------------------- // EFeatureReader implementation // ----------------------------------------------------- /** * Get {@link EFeatureHints}. */ public EFeatureHints eHints() { return eHints; } /** * Reset current iterator. * @throws IOException */ public void reset() throws IOException { this.eReader.reset(); } @Override public void close() throws IOException { this.eReader.close(); this.eDataStore = null; } public EFeatureInfo eStructure() { return eStructure; } public EFeatureDataStore eDataStore() { return eDataStore.get(); } @Override public SimpleFeatureType getFeatureType() { return eReader.getFeatureType(); } @Override public boolean hasNext() throws IOException { return eReader.hasNext(); } @Override public ESimpleFeature next() throws IOException, IllegalArgumentException, NoSuchElementException { // // Sanity check // if (!hasNext()) { throw new NoSuchElementException(); } try { // // Access to EFeatureInfo hints must be synchronized (thread safe) // eStructure.eLock(); // // Initialize // Feature feature = null; // // Get current EFeature or EFeature data compatible EObject // EObject eObject = eReader.get(); // // Adapt given object to EFeature structure // EFeature eFeature = eAdapt(eStructure, eObject, eHints); // // Get feature from EFeature // feature = eFeature.getData(eTx); // // Implements ESimpleFeature? // if(feature instanceof ESimpleFeature) { // // This is always the case if EFeatureInternal is used. // return (ESimpleFeature)feature; } else { // // If EFeatureInternal is NOT used, this may be required since // the getData() method of EFeature defines Feature, not // SimpleFeature. Since use of EFeatureInternal is strongly // recommended (solves several general problems, like the context // startup problem etc.), a WARNING message is issued telling them that // the this operation may be unsafe. // LOGGER.log(Level.WARNING,"Non-standard Feature implementation " + "found. Unpredictable behavior may occur."); // // Forward ESimpleFeature delegate // return new ESimpleFeatureDelegate(eStructure, eFeature, (SimpleFeature)feature, eHints); } } finally { // // Release lock on structure // eStructure.eUnlock(); // // Progress to next feature // eReader.next(); } } protected static EFeature eAdapt(EFeatureInfo eStructure, EObject eObject, EFeatureHints eHints) { // // Adapt directly? // if(eObject instanceof EFeature) { // // Replace // if(eObject instanceof EFeatureImpl) { ((EFeatureImpl)eObject).eInternal().eReplace(eStructure,(EFeature)eObject,eHints, true); } // // TODO: Do we need this? Is never called in current implementation... // else if(eObject instanceof EFeatureDelegate) { ((EFeatureDelegate)eObject).eInternal().eReplace(eStructure,(EFeature)eObject,eHints, true); } // // Finished // return (EFeature)eObject; } // // Create new delegate and return it // return EFeatureDelegate.create(eStructure, (InternalEObject)eObject, true, eHints); } /** * Get current {@link EFeature} id. * * @return a {@link EFeature} id. * @see {@link EcoreUtil#getID(EObject)} */ protected String getFeatureID() { return eReader.getFeatureID(); } protected String getContextID() { return getStructure().eContextID; } protected EFeatureContextFactory getFactory() { return getStructure().eFactory(); } protected EFeaturePackageInfo getStructure() { return eDataStore.get().ePackageInfo; } }