Package org.locationtech.geogig.geotools.data

Source Code of org.locationtech.geogig.geotools.data.GeogigFeatureStore$ModifyingFunction

/* Copyright (c) 2013-2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.geotools.data;

import java.io.IOException;
import java.util.AbstractList;
import java.util.Iterator;
import java.util.List;

import org.geotools.data.EmptyFeatureReader;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Query;
import org.geotools.data.QueryCapabilities;
import org.geotools.data.ResourceInfo;
import org.geotools.data.Transaction;
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureStore;
import org.geotools.data.store.ContentState;
import org.geotools.data.store.FeatureIteratorIterator;
import org.geotools.factory.Hints;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.FeatureReaderIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.filter.identity.FeatureIdVersionedImpl;
import org.geotools.filter.visitor.SimplifyingFilterVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.locationtech.geogig.api.DefaultProgressListener;
import org.locationtech.geogig.api.Node;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ProgressListener;
import org.locationtech.geogig.repository.WorkingTree;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureVisitor;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.identity.FeatureId;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;

/**
*
*/
class GeogigFeatureStore extends ContentFeatureStore {

    /**
     * geogig feature source to delegate to, we do this b/c we can't inherit from both
     * ContentFeatureStore and {@link GeogigFeatureSource} at the same time
     */
    private GeogigFeatureSource delegate;

    /**
     * @param entry
     * @param query
     */
    public GeogigFeatureStore(ContentEntry entry) {
        super(entry, (Query) null);
        delegate = new GeogigFeatureSource(entry, query) {
            @Override
            public void setTransaction(Transaction transaction) {
                super.setTransaction(transaction);

                // keep this feature store in sync
                GeogigFeatureStore.this.setTransaction(transaction);
            }
        };
        super.hints = delegate.getSupportedHints();
    }

    /** We handle events internally */
    protected boolean canEvent() {
        return true;
    }

    @Override
    public GeoGigDataStore getDataStore() {
        return delegate.getDataStore();
    }

    public GeogigFeatureSource getFeatureSource() {
        return delegate;
    }

    @Override
    public ContentEntry getEntry() {
        return delegate.getEntry();
    }

    @Override
    public ResourceInfo getInfo() {
        return delegate.getInfo();
    }

    @Override
    public Name getName() {
        return delegate.getName();
    }

    @Override
    public QueryCapabilities getQueryCapabilities() {
        return delegate.getQueryCapabilities();
    }

    @Override
    public ContentState getState() {
        return delegate.getState();
    }

    @Override
    public synchronized Transaction getTransaction() {
        return delegate.getTransaction();
    }

    @Override
    public synchronized void setTransaction(Transaction transaction) {
        // we need to set both super and delegate transactions.
        super.setTransaction(transaction);

        // this guard ensures that a recursive loop will not form
        if (delegate.getTransaction() != transaction) {
            delegate.setTransaction(transaction);
        }
        if (!Transaction.AUTO_COMMIT.equals(transaction)) {
            GeogigTransactionState geogigTx;
            geogigTx = (GeogigTransactionState) transaction.getState(GeogigTransactionState.class);
            if (geogigTx == null) {
                geogigTx = new GeogigTransactionState(getEntry());
                transaction.putState(GeogigTransactionState.class, geogigTx);
            }
        }
    }

    @Override
    protected SimpleFeatureType buildFeatureType() throws IOException {
        return delegate.buildFeatureType();
    }

    @Override
    protected int getCountInternal(Query query) throws IOException {
        return delegate.getCount(query);
    }

    @Override
    protected ReferencedEnvelope getBoundsInternal(Query query) throws IOException {
        return delegate.getBoundsInternal(query);
    }

    @Override
    protected boolean canFilter() {
        return delegate.canFilter();
    }

    @Override
    protected boolean canSort() {
        return delegate.canSort();
    }

    @Override
    protected boolean canRetype() {
        return delegate.canRetype();
    }

    @Override
    protected boolean canLimit() {
        return delegate.canLimit();
    }

    @Override
    protected boolean canOffset() {
        return delegate.canOffset();
    }

    @Override
    protected boolean canTransact() {
        return delegate.canTransact();
    }

    @Override
    protected FeatureReader<SimpleFeatureType, SimpleFeature> getReaderInternal(Query query)
            throws IOException {
        return delegate.getReaderInternal(query);
    }

    @Override
    protected boolean handleVisitor(Query query, FeatureVisitor visitor) throws IOException {
        return delegate.handleVisitor(query, visitor);
    }

    @Override
    protected FeatureWriter<SimpleFeatureType, SimpleFeature> getWriterInternal(Query query,
            final int flags) throws IOException {

        Preconditions.checkArgument(flags != 0, "no write flags set");
        Preconditions.checkState(getDataStore().isAllowTransactions(),
                "Transactions not supported; head is not a local branch");

        FeatureReader<SimpleFeatureType, SimpleFeature> features;
        if ((flags | WRITER_UPDATE) == WRITER_UPDATE) {
            features = delegate.getReader(query);
        } else {
            features = new EmptyFeatureReader<SimpleFeatureType, SimpleFeature>(getSchema());
        }

        String path = delegate.getTypeTreePath();
        WorkingTree wtree = getFeatureSource().getWorkingTree();

        GeoGigFeatureWriter writer;
        if ((flags | WRITER_ADD) == WRITER_ADD) {
            writer = GeoGigFeatureWriter.createAppendable(features, path, wtree);
        } else {
            writer = GeoGigFeatureWriter.create(features, path, wtree);
        }
        return writer;
    }

    @Override
    public final List<FeatureId> addFeatures(
            FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection)
            throws IOException {

        if (Transaction.AUTO_COMMIT.equals(getTransaction())) {
            throw new UnsupportedOperationException("GeoGIG does not support AUTO_COMMIT");
        }
        Preconditions.checkState(getDataStore().isAllowTransactions(),
                "Transactions not supported; head is not a local branch");
        final WorkingTree workingTree = delegate.getWorkingTree();
        final String path = delegate.getTypeTreePath();

        ProgressListener listener = new DefaultProgressListener();

        final List<FeatureId> insertedFids = Lists.newArrayList();
        List<Node> deferringTarget = new AbstractList<Node>() {

            @Override
            public boolean add(Node node) {
                String fid = node.getName();
                String version = node.getObjectId().toString();
                insertedFids.add(new FeatureIdVersionedImpl(fid, version));
                return true;
            }

            @Override
            public Node get(int index) {
                throw new UnsupportedOperationException();
            }

            @Override
            public int size() {
                return 0;
            }
        };
        Integer count = (Integer) null;

        FeatureIterator<SimpleFeature> featureIterator = featureCollection.features();
        try {
            Iterator<SimpleFeature> features;
            features = new FeatureIteratorIterator<SimpleFeature>(featureIterator);
            /*
             * Make sure to transform the incoming features to the native schema to avoid situations
             * where geogig would change the metadataId of the RevFeature nodes due to small
             * differences in the default and incoming schema such as namespace or missing
             * properties
             */
            final SimpleFeatureType nativeSchema = delegate.getNativeType();

            features = Iterators.transform(features, new SchemaInforcer(nativeSchema));

            workingTree.insert(path, features, listener, deferringTarget, count);
        } catch (Exception e) {
            throw new IOException(e);
        } finally {
            featureIterator.close();
        }

        return insertedFids;
    }

    /**
     * Function used when inserted to check whether the {@link Hints#USE_PROVIDED_FID} in a Feature
     * {@link Feature#getUserData() user data} map is set to {@code Boolean.TRUE}, and only if so
     * let the feature unchanged, otherwise return a feature with the exact same contents but a
     * newly generaged feature id.
     */
    private static class SchemaInforcer implements Function<SimpleFeature, SimpleFeature> {

        private SimpleFeatureBuilder builder;

        public SchemaInforcer(final SimpleFeatureType targetSchema) {
            this.builder = new SimpleFeatureBuilder(targetSchema);
        }

        @Override
        public SimpleFeature apply(SimpleFeature input) {
            builder.reset();

            for (int i = 0; i < input.getType().getAttributeCount(); i++) {
                String name = input.getType().getDescriptor(i).getLocalName();
                builder.set(name, input.getAttribute(name));
            }

            String id;
            if (Boolean.TRUE.equals(input.getUserData().get(Hints.USE_PROVIDED_FID))) {
                id = input.getID();
            } else {
                id = null;
            }

            SimpleFeature feature = builder.buildFeature(id);
            if (!input.getUserData().isEmpty()) {
                feature.getUserData().putAll(input.getUserData());
            }
            return feature;
        }
    };

    @Override
    public void modifyFeatures(Name[] names, Object[] values, Filter filter) throws IOException {
        Preconditions.checkState(getDataStore().isAllowTransactions(),
                "Transactions not supported; head is not a local branch");

        final WorkingTree workingTree = delegate.getWorkingTree();
        final String path = delegate.getTypeTreePath();
        Iterator<SimpleFeature> features = modifyingFeatureIterator(names, values, filter);
        /*
         * Make sure to transform the incoming features to the native schema to avoid situations
         * where geogig would change the metadataId of the RevFeature nodes due to small differences
         * in the default and incoming schema such as namespace or missing properties
         */
        final SimpleFeatureType nativeSchema = delegate.getNativeType();

        features = Iterators.transform(features, new SchemaInforcer(nativeSchema));

        try {
            ProgressListener listener = new DefaultProgressListener();
            Integer count = (Integer) null;
            List<Node> target = (List<Node>) null;
            workingTree.insert(path, features, listener, target, count);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    /**
     * @param names
     * @param values
     * @param filter
     * @return
     * @throws IOException
     */
    private Iterator<SimpleFeature> modifyingFeatureIterator(final Name[] names,
            final Object[] values, final Filter filter) throws IOException {

        Iterator<SimpleFeature> iterator = featureIterator(filter);

        Function<SimpleFeature, SimpleFeature> modifyingFunction = new ModifyingFunction(names,
                values);

        Iterator<SimpleFeature> modifyingIterator = Iterators
                .transform(iterator, modifyingFunction);
        return modifyingIterator;
    }

    private Iterator<SimpleFeature> featureIterator(final Filter filter) throws IOException {
        FeatureReader<SimpleFeatureType, SimpleFeature> unchanged = getReader(filter);
        Iterator<SimpleFeature> iterator = new FeatureReaderIterator<SimpleFeature>(unchanged);
        return iterator;
    }

    @Override
    public void removeFeatures(Filter filter) throws IOException {
        Preconditions.checkState(getDataStore().isAllowTransactions(),
                "Transactions not supported; head is not a local branch");
        final WorkingTree workingTree = delegate.getWorkingTree();
        final String typeTreePath = delegate.getTypeTreePath();
        filter = (Filter) filter.accept(new SimplifyingFilterVisitor(), null);
        if (Filter.INCLUDE.equals(filter)) {
            workingTree.delete(typeTreePath);
            return;
        }
        if (Filter.EXCLUDE.equals(filter)) {
            return;
        }

        Iterator<SimpleFeature> featureIterator = featureIterator(filter);
        Iterator<String> affectedFeaturePaths = Iterators.transform(featureIterator,
                new Function<SimpleFeature, String>() {

                    @Override
                    public String apply(SimpleFeature input) {
                        String fid = input.getID();
                        return NodeRef.appendChild(typeTreePath, fid);
                    }
                });
        workingTree.delete(affectedFeaturePaths);
    }

    /**
    *
    */
    private static final class ModifyingFunction implements Function<SimpleFeature, SimpleFeature> {

        private Name[] names;

        private Object[] values;

        /**
         * @param names
         * @param values
         */
        public ModifyingFunction(Name[] names, Object[] values) {
            this.names = names;
            this.values = values;
        }

        @Override
        public SimpleFeature apply(SimpleFeature input) {
            for (int i = 0; i < names.length; i++) {
                Name attName = names[i];
                Object attValue = values[i];
                input.setAttribute(attName, attValue);
            }
            input.getUserData().put(Hints.USE_PROVIDED_FID, Boolean.TRUE);
            return input;
        }

    }
}
TOP

Related Classes of org.locationtech.geogig.geotools.data.GeogigFeatureStore$ModifyingFunction

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.