IbisMergeFeaturesActionBean.java

/*
 * Copyright (C) 2015 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package nl.b3p.viewer.stripes;

import org.locationtech.jts.geom.Geometry;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.sourceforge.stripes.action.StrictBinding;
import net.sourceforge.stripes.action.UrlBinding;
import nl.b3p.viewer.ibis.util.IbisConstants;
import nl.b3p.viewer.ibis.util.WorkflowStatus;
import nl.b3p.viewer.ibis.util.WorkflowUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.data.DataUtilities;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.util.Converter;
import org.geotools.data.util.GeometryTypeConverterFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.type.GeometryType;
import org.opengis.filter.Filter;
import org.opengis.filter.identity.FeatureId;
import org.stripesstuff.stripersist.Stripersist;

/**
 * A workflow-supporting merge action bean for ibis.
 *
 * @author mprins
 */
@UrlBinding("/action/feature/ibismerge")
@StrictBinding
public class IbisMergeFeaturesActionBean extends MergeFeaturesActionBean implements IbisConstants {

    private static final Log log = LogFactory.getLog(IbisMergeFeaturesActionBean.class);
    private Object terreinID = null;
    private WorkflowStatus newWorkflowStatus = WorkflowStatus.definitief;

    /**
     * Force the workflow status attribute on the feature. This will handle the
     * case where the {@code extraData} attribute is a piecs of json with the
     * workflow, eg
     * {@code {workflow_status:'afgevoerd',datum_mutatie:'2015-12-01Z00:00'}}.
     *
     * @param features A list of features to be modified
     * @return the list of modified features that are about to be committed to
     * the datastore
     *
     * @throws JSONException if json parsing failed
     */
    @Override
    protected List<SimpleFeature> handleExtraData(List<SimpleFeature> features) throws JSONException {
        JSONObject json = new JSONObject(this.getExtraData());
        Iterator items = json.keys();
        while (items.hasNext()) {
            String key = (String) items.next();
            for (SimpleFeature f : features) {
                log.debug(String.format("Setting value: %s for attribute: %s on feature %s", json.get(key), key, f.getID()));
                f.setAttribute(key, json.get(key));
                if (key.equalsIgnoreCase(WORKFLOW_FIELDNAME)) {
                    newWorkflowStatus = WorkflowStatus.valueOf(json.getString(key));
                }
            }
        }
        return features;
    }

    /**
     * Overrides deleting features by archiving them instead. {@inheritDoc}
     */
    @Override
    protected List<FeatureId> handleStrategy(SimpleFeature featureA, SimpleFeature featureB,
            Geometry newGeom, Filter filterA, Filter filterB, SimpleFeatureStore localStore, String localStrategy) throws Exception {
        List<FeatureId> ids = new ArrayList();

        if (!this.getLayer().getName().equalsIgnoreCase(KAVEL_LAYER_NAME)) {
            throw new IllegalArgumentException("Aborting as merge layer is not " + KAVEL_LAYER_NAME);
        }

        String geomAttrName = localStore.getSchema().getGeometryDescriptor().getLocalName();
        GeometryType type = localStore.getSchema().getGeometryDescriptor().getType();
        GeometryTypeConverterFactory cf = new GeometryTypeConverterFactory();
        Converter c = cf.createConverter(Geometry.class,
                localStore.getSchema().getGeometryDescriptor().getType().getBinding(),
                null);

        if (this.getStrategy().equalsIgnoreCase("new")) {
            // archive the source feature (A)
            localStore.modifyFeatures(WORKFLOW_FIELDNAME, WorkflowStatus.archief, filterA);

            // update B with status afgevoerd, and a null terreinid
            String[] fields = new String[]{WORKFLOW_FIELDNAME, KAVEL_TERREIN_ID_FIELDNAME};
            Object[] values = new Object[]{WorkflowStatus.afgevoerd, null};
            localStore.modifyFeatures(fields, values, filterB);

            // create a new feature with the attributes of A but a new geom
            SimpleFeature newAfeat = DataUtilities.createFeature(featureA.getType(),
                    DataUtilities.encodeFeature(featureA, false));
            newAfeat.setAttribute(geomAttrName, c.convert(newGeom, type.getBinding()));

            // remember terreinID
            this.terreinID = newAfeat.getAttribute(KAVEL_TERREIN_ID_FIELDNAME);

            List<SimpleFeature> newFeats = new ArrayList();
            newFeats.add(newAfeat);
            // update new feature set mut. date, workflow status etc.
            newFeats = this.handleExtraData(newFeats);
            ids = localStore.addFeatures(DataUtilities.collection(newFeats));
        } else {
            throw new IllegalArgumentException("Unknown merge strategy '" + this.getStrategy() + "', cannot merge.");
        }
        return ids;
    }

    @Override
    protected void afterMerge(List<FeatureId> ids) {
        if (this.terreinID != null) {
            WorkflowUtil.updateTerreinGeometry(Integer.parseInt(this.terreinID.toString()), this.getLayer(),
                    this.newWorkflowStatus, this.getApplication(), Stripersist.getEntityManager());
        }
    }
}