Package org.geotools.arcsde.data

Source Code of org.geotools.arcsde.data.ArcSDEDataStore

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation;
*    version 2.1 of the License.
*
*    This library 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
*    Lesser General Public License for more details.
*
*/
package org.geotools.arcsde.data;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.sf.jsqlparser.statement.select.PlainSelect;

import org.geotools.arcsde.ArcSdeException;
import org.geotools.arcsde.data.view.QueryInfoParser;
import org.geotools.arcsde.data.view.SelectQualifier;
import org.geotools.arcsde.session.ISession;
import org.geotools.arcsde.session.ISessionPool;
import org.geotools.arcsde.session.SessionWrapper;
import org.geotools.arcsde.session.UnavailableConnectionException;
import org.geotools.arcsde.versioning.ArcSdeVersionHandler;
import org.geotools.arcsde.versioning.AutoCommitVersionHandler;
import org.geotools.data.DataAccess;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultServiceInfo;
import org.geotools.data.EmptyFeatureReader;
import org.geotools.data.FeatureListenerManager;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.FilteringFeatureReader;
import org.geotools.data.LockingManager;
import org.geotools.data.MaxFeatureReader;
import org.geotools.data.Query;
import org.geotools.data.ReTypeFeatureReader;
import org.geotools.data.ServiceInfo;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.GeoTools;
import org.geotools.factory.Hints;
import org.geotools.feature.FeatureTypes;
import org.geotools.feature.SchemaException;
import org.geotools.geometry.jts.LiteCoordinateSequenceFactory;
import org.geotools.util.logging.Logging;
import org.hsqldb.Session;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;

import com.esri.sde.sdk.client.SeException;
import com.esri.sde.sdk.client.SeQueryInfo;
import com.esri.sde.sdk.client.SeVersion;
import com.vividsolutions.jts.geom.CoordinateSequenceFactory;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.PrecisionModel;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequenceFactory;

/**
* DataStore implementation to work upon an ArcSDE spatial database gateway.
* <p>
* Takes ownership of the provided {@link ISessionPool} so make sure to call {@link #dispose()} in
* order to release resources (ArcSDE connections).
* </p>
*
* @author Gabriel Roldan (TOPP)
*
*
* @source $URL$
*         http://svn.geotools.org/geotools/trunk/gt/modules/unsupported/arcsde/datastore/src/main
*         /java/org/geotools/arcsde/data/ArcSDEDataStore.java $
* @version $Id$
*/
public class ArcSDEDataStore implements DataStore {

    private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.data");

    /**
     * Default value for how often, in seconds, to update the feature type name cache.
     *
     * @see FeatureTypeInfoCache
     */
    private static final int DEFAULT_LAYER_NAMES_CACHE_UPDATE_FREQ_SECS = 60;

    /**
     * Manages listener lists for SimpleFeatureSource implementation
     */
    final FeatureListenerManager listenerManager = new FeatureListenerManager();

    final ISessionPool connectionPool;

    final FeatureTypeInfoCache typeInfoCache;

    /**
     * Name of the arcsde version to work upon
     *
     * @see #getVersionHandler(String, Transaction)
     */
    private String version;

    /**
     * Creates a new ArcSDE DataStore working over the given connection pool
     *
     * @param connPool
     *            pool of {@link Session} this datastore works upon.
     * @throws IOException
     */
    public ArcSDEDataStore(final ISessionPool connPool) throws IOException {
        this(connPool, null, null, false);
    }

    /**
     * Creates a new ArcSDE DataStore working over the given connection pool
     *
     * @param connPool
     *            pool of {@link Session} this datastore works upon.
     * @param namespaceUri
     *            namespace URI for the {@link SimpleFeatureType}s, {@link AttributeType}s, and
     *            {@link AttributeDescriptor}s created by this datastore. May be {@code null}.
     * @param versionName
     *            the name of the ArcSDE version to work upon, or {@code null} for the
     *            {@link SeVersion#SE_QUALIFIED_DEFAULT_VERSION_NAME DEFAULT} version
     * @param allowNonSpatialTables
     *            whether ArcSDE registered, non-spatial tables are to be published
     * @throws IOException
     */
    public ArcSDEDataStore(final ISessionPool connPool, final String namespaceUri,
            final String versionName, final boolean allowNonSpatialTables) throws IOException {
        this.connectionPool = connPool;
        this.version = versionName == null ? SeVersion.SE_QUALIFIED_DEFAULT_VERSION_NAME
                : versionName;
        this.typeInfoCache = new FeatureTypeInfoCache(connectionPool, namespaceUri,
                DEFAULT_LAYER_NAMES_CACHE_UPDATE_FREQ_SECS, allowNonSpatialTables);
    }

    /**
     * Retrieve the connection for the provided transaction.
     * <p>
     * The connection is held open until while the transaction is underway. A a Transaction.State is
     * registered for this SessionPool in order to hold the session.
     * </p>
     *
     * @param transaction
     * @return the session associated with the transaction
     */
    public ISession getSession(Transaction transaction) throws IOException {
        if (transaction == null) {
            throw new NullPointerException(
                    "transaction can't be null. Did you mean Transaction.AUTO_COMMIT?");
        }
        final ISession session;
        if (Transaction.AUTO_COMMIT.equals(transaction)) {
            try {
                session = connectionPool.getSession(false);
            } catch (UnavailableConnectionException e) {
                throw new RuntimeException("Session pool exhausted", e);
            }
        } else {
            SessionTransactionState state;
            state = SessionTransactionState.getState(transaction, this.connectionPool);
            session = state.getConnection();
        }
        return session;
    }

    private ArcTransactionState getState(final Transaction transaction) {
        if (Transaction.AUTO_COMMIT.equals(transaction)) {
            return null;
        }
        ArcTransactionState state = (ArcTransactionState) transaction.getState(this);
        if (state == null) {
            state = new ArcTransactionState(this, listenerManager);
            transaction.putState(this, state);
        }
        return state;
    }

    /**
     * Creates a new ArcSDE FeatureClass if {@code featureType} has at least one geometry attribute,
     * or an ObjectClass (aka, non spatial registered table) if this data store instance allows non
     * spatial tables and there's no geometry attribute in {@code featureType}.
     * <p>
     * The new arcsde table created will have an SDE managed column to be used as primary key.
     * <p>
     *
     * @see DataStore#createSchema(SimpleFeatureType)
     * @see #createSchema(SimpleFeatureType, Map)
     */
    public void createSchema(final SimpleFeatureType featureType) throws IOException {
        Map<String, String> hints = new HashMap<String, String>();
        hints.put("rowid.column.type", "SDE");
        String rowIdName = "SDE_ROW_ID";
        while (featureType.getDescriptor(rowIdName) != null) {
            rowIdName += "2";
        }
        hints.put("rowid.column.name", rowIdName);
        createSchema(featureType, hints);
    }

    /**
     * Obtains the schema for the given featuretype name.
     *
     * @see DataStore#getSchema(String)
     */
    public SimpleFeatureType getSchema(final String typeName) throws java.io.IOException {
        FeatureTypeInfo typeInfo = typeInfoCache.getFeatureTypeInfo(typeName);
        SimpleFeatureType schema = typeInfo.getFeatureType();
        return schema;
    }

    /**
     * List of type names; should be a list of all feature classes.
     *
     * @return the list of full qualified feature class names on the ArcSDE database this DataStore
     *         works on. An ArcSDE full qualified class name is composed of three dot separated
     *         strings: "DATABASE.USER.CLASSNAME", wich is usefull enough to use it as namespace
     * @throws RuntimeException
     *             if an exception occurs while retrieving the list of registeres feature classes on
     *             the backend, or while obtaining the full qualified name of one of them
     */
    public String[] getTypeNames() throws IOException {
        List<String> layerNames = typeInfoCache.getTypeNames();
        return layerNames.toArray(new String[layerNames.size()]);
    }

    public ServiceInfo getInfo() {
        DefaultServiceInfo info = new DefaultServiceInfo();
        info.setTitle("ArcSDE connection to " + connectionPool.getConfig().getServerName()
                + " using database version '" + version + "'");
        info.setDescription("GeoTools ArcSDE DataStore plugin");
        try {
            info.setSchema(typeInfoCache.getNamesapceURI() == null ? FeatureTypes.DEFAULT_NAMESPACE
                    : new URI(typeInfoCache.getNamesapceURI()));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return info;
    }

    /**
     * Disposes this ArcSDEDataStore, which means disposing its session pool and hence closing all
     * the SeConnection objects held.
     *
     * @see DataStore#dispose()
     */
    public void dispose() {
        LOGGER.fine("Disposing " + connectionPool);
        this.typeInfoCache.dispose();
        if (!connectionPool.isClosed()) {
            this.connectionPool.close();
        }
        LOGGER.fine("Session pool disposed");
    }

    @Override
    protected void finalize() {
        dispose();
    }

    /**
     * Returns an {@link ArcSDEFeatureReader}
     * <p>
     * Preconditions:
     * <ul>
     * <li><code>query != null</code>
     * <li><code>query.getTypeName() != null</code>
     * <li><code>query.getFilter != null</code>
     * <li><code>transaction != null</code>
     * </ul>
     * </p>
     *
     * @see DataStore#getFeatureReader(Query, Transaction)
     * @return {@link ArcSDEFeatureReader} aware of the transaction state
     */
    public FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(final Query query,
            final Transaction transaction) throws IOException {
        assert query != null;
        assert query.getTypeName() != null;
        assert query.getFilter() != null;
        assert transaction != null;

        final SimpleFeatureType featureType = getQueryType(query);

        return getFeatureReader(query, transaction, featureType);
    }

    public FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(final Query query,
            final Transaction transaction, final SimpleFeatureType featureType) throws IOException {
        Filter filter = query.getFilter();
        if (filter == Filter.EXCLUDE || filter.equals(Filter.EXCLUDE)) {
            return new EmptyFeatureReader<SimpleFeatureType, SimpleFeature>(featureType);
        }

        final String typeName = query.getTypeName();
        ArcSdeVersionHandler versionHandler = getVersionHandler(typeName, transaction);
        ISession session = getSession(transaction);

        FeatureReader<SimpleFeatureType, SimpleFeature> reader;
        try {
            reader = getFeatureReader(query, featureType, session, versionHandler);
        } catch (IOException ioe) {
            session.dispose();
            throw ioe;
        } catch (RuntimeException re) {
            session.dispose();
            throw re;
        }
        return reader;
    }

    /**
     * Returns an {@link ArcSDEFeatureReader} for the given query that works against the given
     * connection.
     * <p>
     * Explicitly stating the connection to use allows for the feature reader to fetch the
     * differences (additions/modifications/deletions) made while a transaction is in progress.
     * </p>
     *
     * @param query
     *            the Query containing the request criteria
     * @param session
     *            the session to use to retrieve content.
     * @return
     * @throws IOException
     */
    private FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(final Query query,
            final SimpleFeatureType targetSchema, final ISession session,
            final ArcSdeVersionHandler versionHandler) throws IOException {

        final String typeName = query.getTypeName();

        final FeatureTypeInfo typeInfo = typeInfoCache.getFeatureTypeInfo(typeName, session);
        final SimpleFeatureType completeSchema = typeInfo.getFeatureType();
        final ArcSDEQuery sdeQuery;

        final Filter queryFilter = query.getFilter();

        if (queryFilter == Filter.EXCLUDE || queryFilter.equals(Filter.EXCLUDE)) {
            return new EmptyFeatureReader<SimpleFeatureType, SimpleFeature>(targetSchema);
        }

        if (typeInfo.isInProcessView()) {
            SeQueryInfo definitionQuery = typeInfo.getSdeDefinitionQuery();
            PlainSelect viewSelectStatement = typeInfo.getDefinitionQuery();
            sdeQuery = ArcSDEQuery.createInprocessViewQuery(session, completeSchema, query,
                    definitionQuery, viewSelectStatement);
        } else {
            final FIDReader fidStrategy = typeInfo.getFidStrategy();
            sdeQuery = ArcSDEQuery.createQuery(session, completeSchema, query, fidStrategy,
                    versionHandler);
        }

        final GeometryFactory geometryFactory = getGeometryFactory(query.getHints());

        // this is the one which's gonna close the connection when done
        final ArcSDEAttributeReader attReader;
        attReader = new ArcSDEAttributeReader(sdeQuery, geometryFactory, session);

        FeatureReader<SimpleFeatureType, SimpleFeature> reader;
        final Filter unsupportedFilter = sdeQuery.getFilters().getUnsupportedFilter();

        try {
            final ArcSDEFeatureReader sdeReader;
            sdeReader = new ArcSDEFeatureReader(attReader);
            reader = sdeReader;
        } catch (SchemaException e) {
            throw new RuntimeException("Schema missmatch, should never happen!: " + e.getMessage(),
                    e);
        }

        // SDE geometry filters are setup to be same or less restrictive than the JTS ones,
        // so we do post filtering in memory with the full filter (it's fast anyways)
        if (!unsupportedFilter.equals(Filter.INCLUDE)) {
            // use the full filter in this case, no sure how it was unpacked
            reader = new FilteringFeatureReader<SimpleFeatureType, SimpleFeature>(reader,
                    queryFilter);
        } else if (!Filter.INCLUDE.equals(sdeQuery.getFilters().getGeometryFilter())) {
            // its ok to just use the geometry filter in-process
            reader = new FilteringFeatureReader<SimpleFeatureType, SimpleFeature>(reader, sdeQuery
                    .getFilters().getGeometryFilter());
        }

        if (!targetSchema.equals(reader.getFeatureType())) {
            LOGGER.fine("Recasting feature type to subtype by using a ReTypeFeatureReader");
            reader = new ReTypeFeatureReader(reader, targetSchema, false);
        }

        if (query.getMaxFeatures() != Query.DEFAULT_MAX) {
            reader = new MaxFeatureReader<SimpleFeatureType, SimpleFeature>(reader,
                    query.getMaxFeatures());
        }

        return reader;
    }

    private GeometryFactory getGeometryFactory(final Hints queryHints) {
        // setup the geometry factory according to the hints
        final Hints hints;
        if (queryHints == null) {
            hints = GeoTools.getDefaultHints();
        } else {
            hints = queryHints;
        }

        GeometryFactory gf = (GeometryFactory) hints.get(Hints.JTS_GEOMETRY_FACTORY);

        if (gf == null) {
            PrecisionModel pm = (PrecisionModel) hints.get(Hints.JTS_PRECISION_MODEL);
            if (pm == null) {
                pm = new PrecisionModel();
            }
            Integer SRID = (Integer) hints.get(Hints.JTS_SRID);
            int srid = SRID == null ? 0 : SRID.intValue();
            Integer dimension = (Integer) hints.get(Hints.COORDINATE_DIMENSION);
            CoordinateSequenceFactory csFactory = (CoordinateSequenceFactory) hints
                    .get(Hints.JTS_COORDINATE_SEQUENCE_FACTORY);
            if (csFactory == null) {
                if (dimension == null || dimension <= 3) {
                    csFactory = CoordinateArraySequenceFactory.instance();
                } else {
                    csFactory = new LiteCoordinateSequenceFactory();
                }
            }
            gf = new GeometryFactory(pm, srid, csFactory);
        }
        return gf;
    }

    public SimpleFeatureType getQueryType(Query query) throws IOException {
        final String typeName = query.getTypeName();
        final String propertyNames[] = query.getPropertyNames();

        final FeatureTypeInfo typeInfo = typeInfoCache.getFeatureTypeInfo(typeName);
        final SimpleFeatureType completeSchema = typeInfo.getFeatureType();

        SimpleFeatureType featureType = completeSchema;

        if (!query.retrieveAllProperties() || query.getCoordinateSystem() != null) {
            try {
                featureType = DataUtilities.createSubType(featureType, propertyNames,
                        query.getCoordinateSystem());
            } catch (SchemaException e) {
                LOGGER.log(Level.FINEST, e.getMessage(), e);
                throw new DataSourceException("Could not create Feature Type for query", e);
            }
        }
        return featureType;
    }

    /**
     * @see DataStore#getFeatureSource(String)
     * @return {@link FeatureSource} or {@link FeatureStore} depending on if the user has write
     *         permissions over <code>typeName</code>
     */
    public SimpleFeatureSource getFeatureSource(final String typeName) throws IOException {
        final FeatureTypeInfo typeInfo = typeInfoCache.getFeatureTypeInfo(typeName);
        SimpleFeatureSource fsource;
        if (typeInfo.isWritable()) {
            fsource = new ArcSdeFeatureStore(typeInfo, this);
        } else {
            fsource = new ArcSdeFeatureSource(typeInfo, this);
        }
        return fsource;
    }

    /**
     * Delegates to {@link #getFeatureWriter(String, Filter, Transaction) getFeatureWriter(typeName,
     * Filter.INCLUDE, transaction)}
     *
     * @see DataStore#getFeatureWriter(String, Transaction)
     */
    public ArcSdeFeatureWriter getFeatureWriter(final String typeName, final Transaction transaction)
            throws IOException {
        return getFeatureWriter(typeName, Filter.INCLUDE, transaction);
    }

    /**
     * @param typeName
     * @param transaction
     * @return
     * @throws IOException
     */
    ArcSdeVersionHandler getVersionHandler(final String typeName, final Transaction transaction)
            throws IOException {
        ArcSdeVersionHandler versionHandler = ArcSdeVersionHandler.NONVERSIONED_HANDLER;
        {
            final FeatureTypeInfo featureTypeInfo = typeInfoCache.getFeatureTypeInfo(typeName);
            final boolean versioned = featureTypeInfo.isVersioned();

            ArcTransactionState state = getState(transaction);
            if (null == state) {
                if (versioned) {
                    versionHandler = new AutoCommitVersionHandler(version);
                }
            } else {
                versionHandler = state.getVersionHandler(versioned, version);
            }
        }
        return versionHandler;
    }

    /**
     * @see DataStore#getFeatureWriter(String, Filter, Transaction)
     */
    public ArcSdeFeatureWriter getFeatureWriter(final String typeName, final Filter filter,
            final Transaction transaction) throws IOException {
        final ArcSdeVersionHandler versionHandler = getVersionHandler(typeName, transaction);
        // get the connection the streamed writer content has to work over
        // so the reader and writer share it
        final ISession session = getSession(transaction);

        try {
            final FeatureTypeInfo typeInfo = typeInfoCache.getFeatureTypeInfo(typeName, session);
            if (!typeInfo.isWritable()) {
                throw new DataSourceException(typeName + " is not writable");
            }
            final SimpleFeatureType featureType = typeInfo.getFeatureType();

            final FeatureReader<SimpleFeatureType, SimpleFeature> reader;
            if (Filter.EXCLUDE.equals(filter)) {
                reader = new EmptyFeatureReader<SimpleFeatureType, SimpleFeature>(featureType);
            } else {
                final Query query = new Query(typeName, filter);
                final ISession nonDisposableSession = new SessionWrapper(session) {
                    @Override
                    public void dispose() throws IllegalStateException {
                        // do nothing, we don't want the reader to close the session
                    }
                };
                reader = getFeatureReader(query, featureType, nonDisposableSession, versionHandler);
            }

            final ArcSdeFeatureWriter writer;

            final FIDReader fidReader = typeInfo.getFidStrategy();

            final ArcTransactionState state = getState(transaction);

            if (Transaction.AUTO_COMMIT == transaction) {
                writer = new AutoCommitFeatureWriter(fidReader, featureType, reader, session,
                        listenerManager, versionHandler);
            } else {
                // if there's a transaction, the reader and the writer will
                // share the connection held in the transaction state
                writer = new TransactionFeatureWriter(fidReader, featureType, reader, state,
                        versionHandler, listenerManager);
            }
            return writer;
        } catch (IOException e) {
            try {
                session.rollbackTransaction();
            } finally {
                session.dispose();
            }
            throw e;
        } catch (RuntimeException e) {
            try {
                session.rollbackTransaction();
            } catch (IOException e1) {
                LOGGER.log(Level.SEVERE, "Error rolling back transaction on " + session, e);
            } finally {
                session.dispose();
            }
            throw e;
        }
    }

    /**
     * Delegates to {@link #getFeatureWriter(String, Filter, Transaction) getFeatureWriter(typeName,
     * Filter.EXCLUDE, transaction)}
     *
     * @see DataStore#getFeatureWriterAppend(String, Transaction)
     */
    public ArcSdeFeatureWriter getFeatureWriterAppend(final String typeName,
            final Transaction transaction) throws IOException {
        return getFeatureWriter(typeName, Filter.EXCLUDE, transaction);
    }

    /**
     * @return <code>null</code>, no locking yet
     * @see DataStore#getLockingManager()
     */
    public LockingManager getLockingManager() {
        return null;
    }

    /**
     * This operation is not supported at this version of the GeoTools ArcSDE plugin.
     *
     * @see DataStore#updateSchema(String, SimpleFeatureType)
     */
    public void updateSchema(final String typeName, final SimpleFeatureType featureType)
            throws IOException {
        throw new UnsupportedOperationException("Schema modification not supported");
    }

    /**
     * Delegates to {@link #getFeatureSource(String)} with {@code name.getLocalPart()}
     *
     * @since 2.5
     * @see DataAccess#getFeatureSource(Name)
     */
    public SimpleFeatureSource getFeatureSource(Name typeName) throws IOException {
        return getFeatureSource(typeName.getLocalPart());
    }

    /**
     * Returns the same list of names than {@link #getTypeNames()} meaning the returned Names have
     * no namespace set.
     *
     * @since 2.5
     * @see DataAccess#getNames()
     */
    public List<Name> getNames() throws IOException {
        return typeInfoCache.getNames();
    }

    /**
     * Delegates to {@link #getSchema(String)} with {@code name.getLocalPart()}
     *
     * @since 2.5
     * @see DataAccess#getSchema(Name)
     */
    public SimpleFeatureType getSchema(Name name) throws IOException {
        return getSchema(name.getLocalPart());
    }

    /**
     * Delegates to {@link #updateSchema(String, SimpleFeatureType)} with
     * {@code name.getLocalPart()}
     *
     * @since 2.5
     * @see DataAccess#getFeatureSource(Name)
     */
    public void updateSchema(Name typeName, SimpleFeatureType featureType) throws IOException {
        updateSchema(typeName.getLocalPart(), featureType);
    }

    // ////// NON API Methods /////////

    /**
     * Creates a given FeatureType on the ArcSDE instance this DataStore is running over.
     * <p>
     * This deviation from the {@link DataStore#createSchema(SimpleFeatureType)} API is to allow the
     * specification of ArcSDE specific hints for the "Feature Class" to create:
     * <ul>
     * At this time the following hints may be passed:
     * <li><b>configuration.keywords</b>: database configuration keyword to use for the newly create
     * feature type. In not present, <code>"DEFAULTS"</code> will be used.</li>
     * <li><b>rowid.column.name</b>: indicates the name of the table column to set up as the unique
     * identifier, and thus to be used as feature id.</li>
     * <li><b>rowid.column.type</b>: The row id column type. Must be one of the following allowed
     * values: <code>"NONE"</code>, <code>"USER"</code>, <code>"SDE"</code> in order to set up the
     * row id column name to not be managed at all, to be user managed or to be managed by ArcSDE,
     * respectively. Refer to the ArcSDE documentation for an explanation of the meanings of those
     * terms.</li>
     * </ul>
     * </p>
     *
     * @param featureType
     * @param hints
     * @throws IOException
     * @throws IllegalArgumentException
     */
    public void createSchema(final SimpleFeatureType featureType, final Map<String, String> hints)
            throws IOException, IllegalArgumentException {

        if (featureType.getGeometryDescriptor() == null && !typeInfoCache.isAllowNonSpatialTables()) {
            throw new DataSourceException("This DataStore does not allow FeatureTypes with no "
                    + "geometry attributes");
        }

        final ISession session = getSession(Transaction.AUTO_COMMIT);
        try {
            ArcSDEAdapter.createSchema(featureType, hints, session);
        } finally {
            session.dispose();
        }
        typeInfoCache.reset();
    }

    /**
     * Supported constructs:
     * <ul>
     * <li>FromItems
     * <li>SelectItems
     * <li>Top (as in SELECT TOP 10 * FROM...)
     * <li>Where
     * </ul>
     *
     * @param typeName
     * @param select
     * @throws IOException
     */
    void registerView(final String typeName, final PlainSelect select) throws IOException {
        if (typeName == null)
            throw new NullPointerException("typeName");
        if (select == null)
            throw new NullPointerException("select");
        if (Arrays.asList(getTypeNames()).contains(typeName)) {
            throw new IllegalArgumentException(typeName + " already exists as a FeatureType");
        }

        verifyQueryIsSupported(select);

        final ISession session = getSession(Transaction.AUTO_COMMIT);

        try {
            final PlainSelect qualifiedSelect = SelectQualifier.qualify(session, select);
            // System.out.println(qualifiedSelect);

            final SeQueryInfo queryInfo;
            try {
                LOGGER.fine("creating definition query info");
                queryInfo = QueryInfoParser.parse(session, qualifiedSelect);
            } catch (SeException e) {
                throw new ArcSdeException("Error Parsing select: " + qualifiedSelect, e);
            }
            FeatureTypeInfo typeInfo = ArcSDEAdapter.createInprocessViewSchema(session, typeName,
                    typeInfoCache.getNamesapceURI(), qualifiedSelect, queryInfo);

            typeInfoCache.addInprocessViewInfo(typeInfo);
        } finally {
            session.dispose();
        }
    }

    /**
     * Unsupported constructs:
     * <ul>
     * <li>GroupByColumnReferences
     * <li>Joins
     * <li>Into
     * <li>Limit
     * </ul>
     * Not yet verified to work:
     * <ul>
     * <li>Distinct
     * <li>Having
     * <li>
     * </ul>
     *
     * @param select
     * @throws UnsupportedOperationException
     *             if any of the unsupported constructs are found on <code>select</code>
     */
    private void verifyQueryIsSupported(PlainSelect select) throws UnsupportedOperationException {
        List<Object> errors = new LinkedList<Object>();
        // @TODO errors.add(select.getDistinct());
        // @TODO errors.add(select.getHaving());
        verifyUnsupportedSqlConstruct(errors, select.getGroupByColumnReferences());
        verifyUnsupportedSqlConstruct(errors, select.getInto());
        verifyUnsupportedSqlConstruct(errors, select.getJoins());
        verifyUnsupportedSqlConstruct(errors, select.getLimit());
        if (errors.size() > 0) {
            throw new UnsupportedOperationException("The following constructs are not supported: "
                    + errors);
        }
    }

    /**
     * If construct is not null or an empty list, adds it to the list of errors.
     *
     * @param errors
     * @param construct
     */
    @SuppressWarnings("unchecked")
    private void verifyUnsupportedSqlConstruct(List<Object> errors, Object construct) {
        if (construct instanceof List) {
            List<Object> constructsList = (List<Object>) construct;
            if (constructsList.size() > 0) {
                errors.addAll(constructsList);
            }
        } else if (construct != null) {
            errors.add(construct);
        }
    }

    @Override
    public void removeSchema(Name typeName) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public void removeSchema(String typeName) throws IOException {
        throw new UnsupportedOperationException();
    }
}
TOP

Related Classes of org.geotools.arcsde.data.ArcSDEDataStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.