<commit_before>/*
 * Copyright (C) 2017 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package nl.b3p.viewer.stripes;

import com.vividsolutions.jts.geom.Geometry;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.After;
import net.sourceforge.stripes.action.Before;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ErrorResolution;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StrictBinding;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.validation.Validate;
import nl.b3p.viewer.config.app.ApplicationLayer;
import nl.b3p.viewer.config.app.ConfiguredAttribute;
import nl.b3p.viewer.config.security.Authorizations;
import nl.b3p.viewer.config.services.FeatureTypeRelation;
import nl.b3p.viewer.config.services.FeatureTypeRelationKey;
import nl.b3p.viewer.config.services.Layer;
import nl.b3p.viewer.config.services.SimpleFeatureType;
import nl.b3p.viewer.util.FeatureToJson;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.visitor.BoundsVisitor;
import org.geotools.filter.identity.FeatureIdImpl;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.json.JSONArray;
import org.json.JSONObject;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.geometry.BoundingBox;
import org.stripesstuff.stripersist.Stripersist;

/**
 * @author <NAME>
 */
@UrlBinding("/action/featurereport")
@StrictBinding
public class FeatureReportActionBean implements ActionBean {

    private static final Log LOG = LogFactory.getLog(FeatureReportActionBean.class);

    public static final String FID = FeatureInfoActionBean.FID;

    private static final int TIMEOUT = 5000;

    @Validate
    private ApplicationLayer appLayer;
    private Layer layer = null;
    private boolean unauthorized;
    private ActionBeanContext context;
    /**
     * feature id to report.
     */
    @Validate
    private String fid;

    /**
     * max. number of related features to retrieve.
     */
    @Validate
    private int maxrelatedfeatures = 10;

    /**
     * printparams json.
     */
    @Validate
    private String printparams;

    @Before(stages = LifecycleStage.EventHandling)
    public void checkAuthorization() {
        if (appLayer == null
                || !Authorizations.isLayerReadAuthorized(layer, context.getRequest(), Stripersist.getEntityManager())) {
            unauthorized = true;
        }
    }

    @After(stages = LifecycleStage.BindingAndValidation)
    public void loadLayer() {
        this.layer = appLayer.getService().getSingleLayer(appLayer.getLayerName(), Stripersist.getEntityManager());
    }

    /**
     * Create a PDF print for the given feature. This method forwards to
     * {@link PrintActionBean} after modifying the params object; modifications
     * include setting a proper bbox for the selected object, add a highlighted
     * version of the object as an overlay, add feature data and add releted
     * feature data.
     *
     * @throws URISyntaxException if getting the image fails in the
     * printgenerator
     * @throws IOException if saving the image fails in the printgenerator
     * @throws Exception when setting the InfoText on the PrintExtraInfo fails
     *
     * @return PDF as requested
     */
    @DefaultHandler
    public Resolution print() throws URISyntaxException, IOException, Exception {
        LOG.debug("Start processing feature report request voor FID: " + this.fid);
        if (appLayer == null) {
            return new ErrorResolution(500, "Invalid parameters");
        } else if (unauthorized) {
            return new ErrorResolution(403, "Not authorized");
        } else {
            JSONObject params = new JSONObject(printparams);

            FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
            FeatureSource fs = layer.getFeatureType().openGeoToolsFeatureSource(TIMEOUT);
            String geomAttribute = fs.getSchema().getGeometryDescriptor().getLocalName();
            Filter fidFilter = ff.id(new FeatureIdImpl(this.fid));

            // set (buffered) bbox from feature
            BoundingBox b = this.getExtent(fs, fidFilter);
            params.put("bbox", b.getMinX() + "," + b.getMinY() + "," + b.getMaxX() + "," + b.getMaxY());

            // add highlighted feature geometry
            Geometry geom = this.getGeometry(fs, fidFilter);
            if (geom != null) {
                JSONObject wktGeom = new JSONObject();
                wktGeom.put("_wktgeom", geom.toText());
                wktGeom.put("color", "FF00FF");
                wktGeom.put("label", "Geselecteerd object: " + this.fid.replace(layer.getFeatureType().getTypeName() + ".", ""));
                wktGeom.put("strokeWidth", 8);
                params.getJSONArray("geometries").put(wktGeom);
            }

            // get feature data
            List<Long> attributesToInclude = new ArrayList<>();
            List<ConfiguredAttribute> attrs = appLayer.getAttributes(layer.getFeatureType(), true);
            attrs.forEach((attr) -> {
                attributesToInclude.add(attr.getId());
            });

            Query q = new Query(fs.getName().toString(), fidFilter);
            q.setMaxFeatures(1);
            q.setHandle("FeatureReportActionBean_attributes");

            FeatureToJson ftjson = new FeatureToJson(false, false, false, true, false, attributesToInclude);
            JSONArray features = ftjson.getJSONFeatures(appLayer, layer.getFeatureType(), fs, q);

            // if there are more than one something is very wrong in datamodel or datasource
            JSONObject jFeat = features.getJSONObject(0);
            // remove __fid, related_featuretypes and geometry nodes from json and add feature attrs to extra data
            jFeat.remove(FID);
            jFeat.remove(geomAttribute);
            jFeat.remove("related_featuretypes");

            JSONObject extra = new JSONObject();
            extra.put("className", "feature").put("componentName", "report").put("info", jFeat);
            params.getJSONArray("extra").put(extra);
            fs.getDataStore().dispose();

            // get related features and add to extra data
            if (layer.getFeatureType().hasRelations()) {
                String label;
                ftjson = new FeatureToJson(false, false, false, true, true, attributesToInclude);
                for (FeatureTypeRelation rel : layer.getFeatureType().getRelations()) {
                    if (rel.getType().equals(FeatureTypeRelation.RELATE)) {
                        SimpleFeatureType fType = rel.getForeignFeatureType();
                        label = fType.getDescription() == null ? fType.getTypeName() : fType.getDescription();
                        LOG.debug("Processing related featuretype: " + label);

                        List<FeatureTypeRelationKey> keys = rel.getRelationKeys();
                        String leftSide = keys.get(0).getLeftSide().getName();
                        String rightSide = keys.get(0).getRightSide().getName();
                        
                        JSONObject info = new JSONObject();
                        if (jFeat.get(leftSide)!=null) {
                            String type = keys.get(0).getLeftSide().getExtJSType();
                            String query = rightSide + "=";
                            if (type.equalsIgnoreCase("string")
                                    || type.equalsIgnoreCase("date")
                                    || type.equalsIgnoreCase("auto")) {
                                query += "'" + jFeat.get(leftSide) + "'";
                            } else {<commit_msg>fix for jsonobject.get()<commit_after>/*
 * Copyright (C) 2017 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package nl.b3p.viewer.stripes;

import com.vividsolutions.jts.geom.Geometry;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.After;
import net.sourceforge.stripes.action.Before;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ErrorResolution;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StrictBinding;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.validation.Validate;
import nl.b3p.viewer.config.app.ApplicationLayer;
import nl.b3p.viewer.config.app.ConfiguredAttribute;
import nl.b3p.viewer.config.security.Authorizations;
import nl.b3p.viewer.config.services.FeatureTypeRelation;
import nl.b3p.viewer.config.services.FeatureTypeRelationKey;
import nl.b3p.viewer.config.services.Layer;
import nl.b3p.viewer.config.services.SimpleFeatureType;
import nl.b3p.viewer.util.FeatureToJson;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.visitor.BoundsVisitor;
import org.geotools.filter.identity.FeatureIdImpl;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.json.JSONArray;
import org.json.JSONObject;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.geometry.BoundingBox;
import org.stripesstuff.stripersist.Stripersist;

/**
 * @author <NAME>
 */
@UrlBinding("/action/featurereport")
@StrictBinding
public class FeatureReportActionBean implements ActionBean {

    private static final Log LOG = LogFactory.getLog(FeatureReportActionBean.class);

    public static final String FID = FeatureInfoActionBean.FID;

    private static final int TIMEOUT = 5000;

    @Validate
    private ApplicationLayer appLayer;
    private Layer layer = null;
    private boolean unauthorized;
    private ActionBeanContext context;
    /**
     * feature id to report.
     */
    @Validate
    private String fid;

    /**
     * max. number of related features to retrieve.
     */
    @Validate
    private int maxrelatedfeatures = 10;

    /**
     * printparams json.
     */
    @Validate
    private String printparams;

    @Before(stages = LifecycleStage.EventHandling)
    public void checkAuthorization() {
        if (appLayer == null
                || !Authorizations.isLayerReadAuthorized(layer, context.getRequest(), Stripersist.getEntityManager())) {
            unauthorized = true;
        }
    }

    @After(stages = LifecycleStage.BindingAndValidation)
    public void loadLayer() {
        this.layer = appLayer.getService().getSingleLayer(appLayer.getLayerName(), Stripersist.getEntityManager());
    }

    /**
     * Create a PDF print for the given feature. This method forwards to
     * {@link PrintActionBean} after modifying the params object; modifications
     * include setting a proper bbox for the selected object, add a highlighted
     * version of the object as an overlay, add feature data and add releted
     * feature data.
     *
     * @throws URISyntaxException if getting the image fails in the
     * printgenerator
     * @throws IOException if saving the image fails in the printgenerator
     * @throws Exception when setting the InfoText on the PrintExtraInfo fails
     *
     * @return PDF as requested
     */
    @DefaultHandler
    public Resolution print() throws URISyntaxException, IOException, Exception {
        LOG.debug("Start processing feature report request voor FID: " + this.fid);
        if (appLayer == null) {
            return new ErrorResolution(500, "Invalid parameters");
        } else if (unauthorized) {
            return new ErrorResolution(403, "Not authorized");
        } else {
            JSONObject params = new JSONObject(printparams);

            FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
            FeatureSource fs = layer.getFeatureType().openGeoToolsFeatureSource(TIMEOUT);
            String geomAttribute = fs.getSchema().getGeometryDescriptor().getLocalName();
            Filter fidFilter = ff.id(new FeatureIdImpl(this.fid));

            // set (buffered) bbox from feature
            BoundingBox b = this.getExtent(fs, fidFilter);
            params.put("bbox", b.getMinX() + "," + b.getMinY() + "," + b.getMaxX() + "," + b.getMaxY());

            // add highlighted feature geometry
            Geometry geom = this.getGeometry(fs, fidFilter);
            if (geom != null) {
                JSONObject wktGeom = new JSONObject();
                wktGeom.put("_wktgeom", geom.toText());
                wktGeom.put("color", "FF00FF");
                wktGeom.put("label", "Geselecteerd object: " + this.fid.replace(layer.getFeatureType().getTypeName() + ".", ""));
                wktGeom.put("strokeWidth", 8);
                params.getJSONArray("geometries").put(wktGeom);
            }

            // get feature data
            List<Long> attributesToInclude = new ArrayList<>();
            List<ConfiguredAttribute> attrs = appLayer.getAttributes(layer.getFeatureType(), true);
            attrs.forEach((attr) -> {
                attributesToInclude.add(attr.getId());
            });

            Query q = new Query(fs.getName().toString(), fidFilter);
            q.setMaxFeatures(1);
            q.setHandle("FeatureReportActionBean_attributes");

            FeatureToJson ftjson = new FeatureToJson(false, false, false, true, false, attributesToInclude);
            JSONArray features = ftjson.getJSONFeatures(appLayer, layer.getFeatureType(), fs, q);

            // if there are more than one something is very wrong in datamodel or datasource
            JSONObject jFeat = features.getJSONObject(0);
            // remove __fid, related_featuretypes and geometry nodes from json and add feature attrs to extra data
            jFeat.remove(FID);
            jFeat.remove(geomAttribute);
            jFeat.remove("related_featuretypes");

            JSONObject extra = new JSONObject();
            extra.put("className", "feature").put("componentName", "report").put("info", jFeat);
            params.getJSONArray("extra").put(extra);
            fs.getDataStore().dispose();

            // get related features and add to extra data
            if (layer.getFeatureType().hasRelations()) {
                String label;
                ftjson = new FeatureToJson(false, false, false, true, true, attributesToInclude);
                for (FeatureTypeRelation rel : layer.getFeatureType().getRelations()) {
                    if (rel.getType().equals(FeatureTypeRelation.RELATE)) {
                        SimpleFeatureType fType = rel.getForeignFeatureType();
                        label = fType.getDescription() == null ? fType.getTypeName() : fType.getDescription();
                        LOG.debug("Processing related featuretype: " + label);

                        List<FeatureTypeRelationKey> keys = rel.getRelationKeys();
                        String leftSide = keys.get(0).getLeftSide().getName();
                        String rightSide = keys.get(0).getRightSide().getName();
                        
                        JSONObject info = new JSONObject();
                        if (jFeat.has(leftSide)) {
                            String type = keys.get(0).getLeftSide().getExtJSType();
                            String query = rightSide + "=";
                            if (type.equalsIgnoreCase("string")
                                    || type.equalsIgnoreCase("date")
                                    || type.equalsIgnoreCase("auto")) {
                                query += "'" + jFeat.get(leftSide) + "'";
                            } else {
{'fraction_non_alphanumeric': 0.10113103448275862, 'fraction_numerical': 0.004303448275862069, 'mean_word_length': 2.7559055118110236, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1695778', 'n_tokens_mistral': 4978, 'n_tokens_neox': 4837, 'n_words': 1204}