Package org.geoserver.wms.dimension

Source Code of org.geoserver.wms.dimension.DynamicDefaultValueSelectionFactory

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2014 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wms.dimension;

import java.io.IOException;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.DimensionInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.MetadataMap;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.ows.AbstractDispatcherCallback;
import org.geoserver.ows.Dispatcher;
import org.geoserver.ows.Request;
import org.geoserver.ows.kvp.ElevationKvpParser;
import org.geoserver.ows.kvp.TimeKvpParser;
import org.geoserver.ows.util.CaseInsensitiveMap;
import org.geoserver.platform.ExtensionPriority;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
import org.geoserver.security.decorators.DecoratingFeatureTypeInfo;
import org.geoserver.wms.GetFeatureInfoRequest;
import org.geoserver.wms.GetMapRequest;
import org.geoserver.wms.WMS;
import org.geoserver.wms.dimension.DefaultValueConfiguration.DefaultValuePolicy;
import org.geoserver.wms.dimension.impl.CoverageMaximumValueSelectionStrategyImpl;
import org.geoserver.wms.dimension.impl.CoverageMinimumValueSelectionStrategyImpl;
import org.geoserver.wms.dimension.impl.CoverageNearestValueSelectionStrategyImpl;
import org.geoserver.wms.dimension.impl.DimensionDefaultValueSelectionStrategyFactoryImpl;
import org.geoserver.wms.dimension.impl.FixedValueStrategyImpl;
import org.geotools.coverage.grid.io.DimensionDescriptor;
import org.geotools.coverage.grid.io.GranuleSource;
import org.geotools.coverage.grid.io.StructuredGridCoverage2DReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.view.DefaultView;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.Hints;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.feature.visitor.FeatureCalc;
import org.geotools.feature.visitor.MaxVisitor;
import org.geotools.feature.visitor.MinVisitor;
import org.geotools.feature.visitor.NearestVisitor;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.util.DateRange;
import org.geotools.util.NumberRange;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.grid.GridCoverageReader;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Expression;
import org.opengis.util.ProgressListener;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
* A {@link DimensionDefaultValueSelectionStrategyFactory} implementation using the dynamic default
* values configurations, is present
*
* @author Andrea Aime - GeoSolutions
*/
public class DynamicDefaultValueSelectionFactory extends AbstractDispatcherCallback implements
        DimensionDefaultValueSelectionStrategyFactory, ExtensionPriority, ApplicationContextAware {
    static final Logger LOGGER = Logging.getLogger(DynamicDefaultValueSelectionFactory.class);

    static final ThreadLocal<Map<String, Map<String, Object>>> DYNAMIC_DEFAULTS = new ThreadLocal<Map<String, Map<String, Object>>>() {
        protected java.util.Map<String, java.util.Map<String, Object>> initialValue() {
            return new HashMap<String, Map<String, Object>>();
        };
    };

    FilterFactory ff = CommonFactoryFinder.getFilterFactory();

    DimensionDefaultValueSelectionStrategyFactory delegate;

    WMS wms;

    @Override
    public DimensionDefaultValueSelectionStrategy getStrategy(ResourceInfo resource,
            String dimensionName, DimensionInfo dimension) {
        Request request = Dispatcher.REQUEST.get();
        DefaultValueConfigurations config = getConfigurations(resource);
        if (config != null
                && request != null
                && ("GetMap".equalsIgnoreCase(request.getRequest()) || "GetFeatureInfo"
                        .equalsIgnoreCase(request.getRequest()))) {
            GetMapRequest getMap = getGetMap(request);
            try {
                Map<String, Object> defaults = getDefaultValues(resource, getMap, config);

                String key = dimensionName;
                if (dimensionName.startsWith(ResourceInfo.CUSTOM_DIMENSION_PREFIX)) {
                    key = dimensionName.substring(ResourceInfo.CUSTOM_DIMENSION_PREFIX.length());
                }
                if (defaults.containsKey(key)) {
                    Object defaultValue = defaults.get(key);
                    Object simple = getSimpleValue(defaultValue);
                    if (simple != null) {
                        return new DefaultFixedValueStrategyFactory()
                                .createFixedValueStrategy(simple);
                    }
                }
            } catch (IOException e) {
                throw new ServiceException("Failed to setup dynamic dimension default values", e);
            }

        }

        // was not a GetMap/GetFeatureInfo, or we did not have a dynamic default for this dimension
        return delegate.getStrategy(resource, dimensionName, dimension);
    }

    /**
     * Returns the default values configuration if present, not empty, and has at least a dynamic
     * dimension to compute
     *
     * @param resource
     * @return
     */
    private DefaultValueConfigurations getConfigurations(ResourceInfo resource) {
        DefaultValueConfigurations configurations = resource.getMetadata().get(
                DefaultValueConfigurations.KEY, DefaultValueConfigurations.class);
        if (configurations == null) {
            return null;
        }
        List<DefaultValueConfiguration> list = configurations.getConfigurations();
        if (list == null || list.isEmpty()) {
            LOGGER.fine("Skipping dynamic dimension configuration as it is empty");
            return null;
        }
        for (DefaultValueConfiguration config : list) {
            if (config.getPolicy() != null && config.getPolicy() != DefaultValuePolicy.STANDARD) {
                return configurations;
            }
        }

        LOGGER.fine("Skipping dynamic dimension cconfiguration as all dimensions are "
                + "using the standard policy (e.g. there is nothing dynamic there");
        return null;
    }

    private GetMapRequest getGetMap(Request request) {
        Operation op = request.getOperation();
        Object parsedRequest = op.getParameters()[0];
        GetMapRequest getMap;

        if (parsedRequest instanceof GetMapRequest) {
            getMap = (GetMapRequest) parsedRequest;
        } else if (parsedRequest instanceof GetFeatureInfoRequest) {
            getMap = ((GetFeatureInfoRequest) parsedRequest).getGetMapRequest();
        } else {
            throw new IllegalArgumentException(
                    "Could not get a GetMapRequest out of the parsed request, the parsed request object was:_"
                            + parsedRequest);
        }
        return getMap;
    }

    private Map<String, Object> getDefaultValues(ResourceInfo resource, GetMapRequest getMap,
            DefaultValueConfigurations configurations) throws IOException {

        Map<String, Map<String, Object>> defaults = DYNAMIC_DEFAULTS.get();
        String resourceKey = resource.getId();
        Map<String, Object> result = defaults.get(resourceKey);
        if (result == null) {
            result = buildDynamicDefaults(resource, getMap, configurations);
            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine("Computed the following dynamic dimension default values for layer "
                        + resource.prefixedName() + ":\n" + result + "\nbased on configuration:\n"
                        + configurations);
            }
            defaults.put(resourceKey, result);
        }
        return result;
    }

    @Override
    public void finished(Request request) {
        DYNAMIC_DEFAULTS.remove();
    }

    private Map<String, Object> buildDynamicDefaults(ResourceInfo resource, GetMapRequest getMap,
            DefaultValueConfigurations configsBean) throws IOException {

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("Building dynamic defaults for resource " + resource.prefixedName());
        }

        List<DefaultValueConfiguration> configurations = filterConfigurations(configsBean, resource);

        Map<String, Object> result;
        result = new HashMap<String, Object>();
        Map<String, List<? extends Object>> incompleteSpecs = new HashMap<String, List<? extends Object>>();

        // lookup for user specified restrictions and dimensions that have a static default
        for (Map.Entry<String, Serializable> entry : resource.getMetadata().entrySet()) {
            String key = entry.getKey();
            Serializable metadata = entry.getValue();
            if (metadata instanceof DimensionInfo) {
                // skip disabled dimensions
                DimensionInfo di = (DimensionInfo) metadata;
                if (!di.isEnabled()) {
                    continue;
                }

                // get the dimension name
                String dimensionName;
                if (key.startsWith(ResourceInfo.CUSTOM_DIMENSION_PREFIX)) {
                    dimensionName = key.substring(ResourceInfo.CUSTOM_DIMENSION_PREFIX.length());
                } else {
                    dimensionName = key;
                }

                // check if the values were provided already in the request, we are
                // going to use them for domain restriction
                boolean foundExplicitDefault = false;
                if (ResourceInfo.TIME.equals(dimensionName)
                        && getMap.getRawKvp().get("time") != null) {
                    List<Object> times = getMap.getTime();
                    addExplicitValues(dimensionName, times, result, incompleteSpecs);
                    foundExplicitDefault = true;
                } else if (ResourceInfo.ELEVATION.equals(dimensionName)
                        && getMap.getRawKvp().get("elevation") != null) {
                    List<Object> elevations = getMap.getElevation();
                    addExplicitValues(dimensionName, elevations, result, incompleteSpecs);
                    foundExplicitDefault = true;
                } else if (getMap.getRawKvp().get("dim_" + dimensionName) != null) {
                    List<String> customDimensions = getMap.getCustomDimension(dimensionName);
                    addExplicitValues(dimensionName, customDimensions, result, incompleteSpecs);
                    foundExplicitDefault = true;
                }

                if (!foundExplicitDefault) {
                    if (!hasDynamicConfiguration(configurations, dimensionName)) {
                        DimensionDefaultValueSelectionStrategy strategy = delegate.getStrategy(
                                resource, key, di);
                        if (strategy == null) {
                            LOGGER.warning("Skipping static setting of default dimension value for dimension "
                                    + dimensionName
                                    + " in layer "
                                    + resource.prefixedName()
                                    + " as the default value strategy could not be found");
                        } else {
                            Object staticDefault = strategy.getDefaultValue(resource,
                                    dimensionName, di, getDimensionClass(dimensionName));
                            if (staticDefault != null) {
                                result.put(dimensionName, wrapIntoList(staticDefault));
                            } else {
                                LOGGER.warning("Skipping static setting of default dimension value for dimension "
                                        + dimensionName
                                        + " in layer "
                                        + resource.prefixedName()
                                        + " as the default value strategy "
                                        + strategy
                                        + "returned a null value");

                            }
                        }
                    }
                }
            }
        }

        // if there are incomplete user value specifications, resolve them in the same order as the
        // dynamic defaults
        if (!incompleteSpecs.isEmpty()) {
            for (DefaultValueConfiguration config : configurations) {
                String dimensionName = config.getDimension();
                if (!incompleteSpecs.containsKey(dimensionName)) {
                    continue;
                }

                List<Object> values = (List<Object>) incompleteSpecs.remove(dimensionName);
                List<Object> defaulted = new ArrayList<Object>(values.size());
                for (int i = 0; i < values.size(); i++) {
                    Object v = values.get(i);
                    if (v != null) {
                        defaulted.add(v);
                    } else {
                        Object dynamicValue = getDynamicDefault(resource, result, config,
                                dimensionName);
                        if (dynamicValue != null) {
                            defaulted.add(dynamicValue);
                        }
                    }

                    result.put(dimensionName, defaulted);
                }
            }

            // the ones that have no dynamc behavior get copied straight
            result.putAll(incompleteSpecs);
        }

        // now go for the ones having dynamic defaults
        for (DefaultValueConfiguration config : configurations) {
            String dimensionName = config.getDimension();

            // do we have explicit defaults already?
            if (result.containsKey(dimensionName)) {
                continue;
            } else {
                // compute the dynamic default
                Object value = getDynamicDefault(resource, result, config, dimensionName);
                if (value != null) {
                    result.put(dimensionName, value);
                }
            }
        }
        return result;
    }

    private boolean hasDynamicConfiguration(List<DefaultValueConfiguration> configurations,
            String dimensionName) {
        for (DefaultValueConfiguration config : configurations) {
            if (dimensionName.equals(config.getDimension())) {
                return true;
            }
        }

        return false;
    }

    private List<DefaultValueConfiguration> filterConfigurations(
            DefaultValueConfigurations configsBean, ResourceInfo resource) {
        List<DefaultValueConfiguration> configs = configsBean.getConfigurations();
        List<DefaultValueConfiguration> result = new ArrayList<DefaultValueConfiguration>(
                configs.size());
        MetadataMap metadata = resource.getMetadata();
        for (DefaultValueConfiguration config : configs) {
            String key = getDimensionMetadataKey(config.getDimension());
            DimensionInfo di = metadata.get(key, DimensionInfo.class);
            if (di == null) {
                LOGGER.warning("Skipping dynamic default configuration for dimension "
                        + config.getDimension() + " as the base dimension configuration is missing");
            } else if (!di.isEnabled()) {
                LOGGER.warning("Skipping dynamic default configuration for dimension "
                        + config.getDimension() + " as the dimension is not enabled");
            } else {
                result.add(config);
            }
        }

        return result;
    }

    /**
     * Separates values provided by the user that do not need defaulting, from those where the user
     * asked explicitly for a default value
     *
     * @param dimensionName
     * @param values
     * @param completeSpecs
     * @param incompleteSpecs
     */
    private void addExplicitValues(String dimensionName, List<? extends Object> values,
            Map<String, Object> completeSpecs, Map<String, List<? extends Object>> incompleteSpecs) {
        if (values.contains(null)) {
            incompleteSpecs.put(dimensionName, values);
        } else {
            completeSpecs.put(dimensionName, values);
        }

    }

    private Object getDynamicDefault(ResourceInfo resource, Map<String, Object> result,
            DefaultValueConfiguration config, String dimensionName) throws IOException {
        // get the dimension info
        DimensionInfo di = resource.getMetadata().get(
                getDimensionMetadataKey(config.getDimension()), DimensionInfo.class);

        // ok, let's see what policy we have here
        Class<?> dimensionClass = getDimensionClass(config.getDimension());
        if (config.policy == DefaultValuePolicy.STANDARD) {
            if (di == null) {
                return null;
            } else {
                // we fetch the value anyways so that we can run domain restrictions later
                DimensionDefaultValueSelectionStrategy strategy = delegate.getStrategy(resource,
                        config.dimension, di);
                Object value = strategy
                        .getDefaultValue(resource, dimensionName, di, dimensionClass);
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.fine("Computed default value for dimension " + dimensionName
                            + " using standard policy " + strategy + " which resulted in: " + value);
                }
               
                return value;
            }
        } else if (config.policy == DefaultValuePolicy.EXPRESSION) {
            return applyExpression(result, config, dimensionName, dimensionClass);
        } else if (config.policy == DefaultValuePolicy.LIMIT_DOMAIN) {
            if (resource instanceof FeatureTypeInfo) {
                Object value = getVectorDefaultValue((FeatureTypeInfo) resource, config, result);
                return value;
            } else if (resource instanceof CoverageInfo) {
                DimensionDefaultValueSelectionStrategy delegateStrategy = delegate.getStrategy(
                        resource, getDimensionMetadataKey(config.getDimension()), di);
                Object value = getRasterDefaultValue((CoverageInfo) resource, config, result,
                        delegateStrategy);
                return value;
            } else {
                throw new IllegalArgumentException(
                        "Don't know how to handle domain restriction for layers of type "
                                + resource.getClass());
            }
        }

        return null;
    }

    private Object applyExpression(Map<String, Object> result, DefaultValueConfiguration config,
            String dimensionName, Class<?> dimensionClass) {
        Expression expression;
        String expressionStr = config.getDefaultValueExpression();
        try {
            expression = ECQL.toExpression(expressionStr);
        } catch (CQLException e) {
            throw new ServiceException("Failed to parse default value expression " + expressionStr,
                    e);
        }
        SimpleFeature values = buildSampleFeature(result);
        String strValue = expression.evaluate(values, String.class);
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("Computed default value for dimension " + dimensionName
                    + " using expression " + expression + " returned value " + strValue);
        }
        try {
            Object value;
            if (strValue == null) {
                value = null;
            } else if (Date.class.isAssignableFrom(dimensionClass)) {
                value = new TimeKvpParser("whatever").parse(strValue);
            } else if (Double.class.isAssignableFrom(dimensionClass)) {
                value = new ElevationKvpParser("whatever").parse(strValue);
            } else {
                value = strValue;
            }
            value = getSimpleValue(value);
            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine("Computed value " + strValue + " got parsed to " + value);
            }

            return value;
        } catch (ParseException e) {
            throw new ServiceException(
                    "Failed to parse value returned from dynamic default value expression: "
                            + strValue, e);
        }
    }

    /**
     * Dimension values are often wrapped in list and/or using ranges, unwrap them to get a single
     * value
     *
     * @param value
     * @return
     */
    private Object getSimpleValue(Object value) {
        if (value instanceof List) {
            List list = (List) value;
            if (list.isEmpty()) {
                value = null;
            } else {
                value = list.get(0);
            }
        }
        if (value instanceof DateRange) {
            value = ((DateRange) value).getMinValue();
        } else if (value instanceof NumberRange) {
            value = ((NumberRange) value).getMinimum();
        }
        return value;
    }

    private Object getRasterDefaultValue(CoverageInfo resource, DefaultValueConfiguration config,
            Map<String, Object> restrictions,
            DimensionDefaultValueSelectionStrategy delegateStrategy) throws IOException {
        String dimensionName = config.getDimension();
        DimensionInfo di = resource.getMetadata().get(
                getDimensionMetadataKey(config.getDimension()), DimensionInfo.class);

        // check if it's a fixed strategy, simple case, no domain restriction needed
        if (delegateStrategy instanceof FixedValueStrategyImpl) {
            Class<?> dimensionClass = getDimensionClass(config.getDimension());
            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine("Dynamic domain restriction on dimension " + dimensionName
                        + " in layer " + resource.prefixedName()
                        + " not possible, the default value strategy is a fixed value one");
            }
            return delegateStrategy.getDefaultValue(resource, dimensionName, di, dimensionClass);
        }

        // ok, grab the reader, and check it's a structured one
        GridCoverageReader genericReader = resource.getGridCoverageReader(null, null);
        if (!(genericReader instanceof StructuredGridCoverage2DReader)) {
            throw new IllegalStateException(
                    "Cannot perform dynaminc domain restriction unless the reader is a structured one");
        }

        // we have a descriptor, now we need to find the association between the exposed
        // dimension names and the granule source attributes
        StructuredGridCoverage2DReader reader = (StructuredGridCoverage2DReader) genericReader;

        String coverageName = resource.getNativeCoverageName();
        if (coverageName == null) {
            coverageName = reader.getGridCoverageNames()[0];
        }
        Map<String, DimensionDescriptor> descriptors = getDimensionDescriptors(reader, coverageName);
        DimensionFilterBuilder builder = new DimensionFilterBuilder(ff);
        for (Map.Entry<String, Object> entry : restrictions.entrySet()) {
            String name = entry.getKey();
            Object value = entry.getValue();
            List<Object> values = wrapIntoList(value);

            DimensionDescriptor descriptor = descriptors.get(name);
            if (descriptor == null) {
                throw new ServiceException("Could not find dimension " + name
                        + " in coverage reader backing " + resource.prefixedName());
            }

            builder.appendFilters(descriptor.getStartAttribute(), descriptor.getEndAttribute(),
                    values);
        }
        Filter domainRestriction = builder.getFilter();

        // get the restricted domain
        GranuleSource granules = reader.getGranules(coverageName, true);
        Query q = new Query(granules.getSchema().getTypeName(), domainRestriction);
        SimpleFeatureCollection fc = granules.getGranules(q);

        DimensionDescriptor dd = descriptors.get(dimensionName);
        if (dd == null) {
            throw new ServiceException("Could not find dimension " + dimensionName
                    + " in coverage reader backing " + resource.prefixedName());
        }
        FeatureCalc calc = getFeatureCalcForStrategy(delegateStrategy, dd);
        fc.accepts(calc, null);
        Object result = calc.getResult().getValue();
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("Computed default value for " + dimensionName + " in layer "
                    + resource.prefixedName() + " using " + domainRestriction
                    + " to limit the domain resulted in value: " + result);
        }
        return result;
    }

    private FeatureCalc getFeatureCalcForStrategy(
            DimensionDefaultValueSelectionStrategy delegateStrategy, DimensionDescriptor dd) {
        String sa = dd.getStartAttribute();
        String ea = dd.getEndAttribute();
        if (delegateStrategy instanceof CoverageMaximumValueSelectionStrategyImpl) {
            if (ea != null) {
                return new MaxVisitor(ea);
            } else {
                return new MaxVisitor(sa);
            }
        } else if (delegateStrategy instanceof CoverageMinimumValueSelectionStrategyImpl) {
            return new MinVisitor(sa);
        } else if (delegateStrategy instanceof CoverageNearestValueSelectionStrategyImpl) {
            CoverageNearestValueSelectionStrategyImpl impl = (CoverageNearestValueSelectionStrategyImpl) delegateStrategy;
            Object targetValue = impl.getTargetValue();
            return new NearestVisitor(ff.property(sa), targetValue);
        } else {
            throw new ServiceException("Don't konw how to restrict the domain for strategy "
                    + delegateStrategy);
        }
    }

    private Map<String, DimensionDescriptor> getDimensionDescriptors(
            StructuredGridCoverage2DReader reader, String coverageName) throws IOException {
        Map<String, DimensionDescriptor> result = new HashMap<String, DimensionDescriptor>();
        List<DimensionDescriptor> dimensionDescriptors = reader
                .getDimensionDescriptors(coverageName);
        for (DimensionDescriptor dd : dimensionDescriptors) {
            result.put(dd.getName(), dd);
        }

        return new CaseInsensitiveMap(result);

    }

    /**
     * Applies the normal policy, but restricted to the
     *
     * @param resource
     * @param config
     * @param restrictions
     * @return
     * @throws IOException
     */
    private Object getVectorDefaultValue(FeatureTypeInfo resource,
            DefaultValueConfiguration config, Map<String, Object> restrictions) throws IOException {
        DimensionInfo di = resource.getMetadata().get(
                getDimensionMetadataKey(config.getDimension()), DimensionInfo.class);
        DimensionDefaultValueSelectionStrategy strategy = delegate.getStrategy(resource,
                config.getDimension(), di);

        // vector data only support time and elevation now
        Object time = restrictions.get(ResourceInfo.TIME);
        List<Object> times = wrapIntoList(time);

        Object elevation = restrictions.get(ResourceInfo.ELEVATION);
        List<Object> elevations = wrapIntoList(elevation);

        // reuse the same logic as dimension selection to restrict the domain of possible values
        final Filter filter = wms.getTimeElevationToFilter(times, elevations, resource);

        ResourceInfo restrictedResource;
        if (filter != null && !Filter.INCLUDE.equals(filter)) {
            restrictedResource = new DecoratingFeatureTypeInfo(resource) {

                @Override
                public FeatureSource getFeatureSource(ProgressListener listener, Hints hints)
                        throws IOException {
                    FeatureSource fs = super.getFeatureSource(listener, hints);
                    if (!(fs instanceof SimpleFeatureSource)) {
                        throw new IllegalStateException(
                                "Cannot apply dynamic dimension restrictions to complex features");
                    }
                    SimpleFeatureSource simpleSource = (SimpleFeatureSource) fs;
                    try {
                        return new DefaultView(simpleSource, new Query(simpleSource.getSchema()
                                .getTypeName(), filter));
                    } catch (SchemaException e) {
                        throw new IOException("Failed to restrict the domain");
                    }
                }
            };

        } else {
            restrictedResource = resource;
        }

        Class<?> dimensionClass = getDimensionClass(config.getDimension());
        Object result = strategy.getDefaultValue(restrictedResource, config.getDimension(), di,
                dimensionClass);

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("Computing default value for" + config.getDimension() + " in layer "
                    + resource.prefixedName()
                    + " using the following filter to restrict the domain: " + filter);
        }
        return result;
    }

    /**
     * If the object is not a List, it wraps it into one
     *
     * @param value
     * @return
     */
    private List<Object> wrapIntoList(Object value) {
        List<Object> values;
        if (value == null) {
            return null;
        } else if (value instanceof List) {
            values = (List<Object>) value;
        } else {
            values = new ArrayList<Object>();
            values.add(value);
        }
        return values;
    }

    private SimpleFeature buildSampleFeature(Map<String, Object> values) {
        SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
        for (String dimension : values.keySet()) {
            tb.add(dimension, Object.class);
        }
        tb.setName("DimensionValues");
        SimpleFeatureType schema = tb.buildFeatureType();

        SimpleFeatureBuilder fb = new SimpleFeatureBuilder(schema);
        for (Map.Entry<String, Object> entry : values.entrySet()) {
            Object value = entry.getValue();
            value = getSimpleValue(value);
            fb.set(entry.getKey(), value);
        }

        return fb.buildFeature(null);
    }

    public String getDimensionMetadataKey(String dimension) {
        if (ResourceInfo.TIME.equals(dimension) || ResourceInfo.ELEVATION.equals(dimension)) {
            return dimension;
        } else {
            return ResourceInfo.CUSTOM_DIMENSION_PREFIX + dimension;
        }
    }

    public Class<?> getDimensionClass(String dimension) {
        if (ResourceInfo.TIME.equals(dimension)) {
            return Date.class;
        } else if (ResourceInfo.ELEVATION.equals(dimension)) {
            return Double.class;
        } else {
            return String.class;
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // get the normal dimension evaluator
        delegate = applicationContext
                .getBean(DimensionDefaultValueSelectionStrategyFactoryImpl.class);
        // avoid dependency loop
        wms = applicationContext.getBean(WMS.class);
    }

    @Override
    public int getPriority() {
        // pick an average value, we allow for further overrides if needs be
        return (ExtensionPriority.HIGHEST + ExtensionPriority.LOWEST) / 2;
    }

}
TOP

Related Classes of org.geoserver.wms.dimension.DynamicDefaultValueSelectionFactory

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.