org.geoserver.catalog.impl.CatalogPropertyAccessor.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.catalog.impl.CatalogPropertyAccessor.java

Source

/* Copyright (c) 2001 - 2013 OpenPlans - www.openplans.org. All rights reserved.
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.catalog.impl;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Throwables.propagate;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.geoserver.catalog.Info;
import org.geoserver.catalog.Predicates;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.ows.util.OwsUtils;
import org.geotools.filter.expression.PropertyAccessor;
import org.geotools.util.Converters;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Closeables;

/**
 * Extracts a property from a {@link Info} object.
 * <p>
 * The property can be nested (p1.p2.p3), indexed (p1[3]), collection (colProp), or a combination
 * (colProp1.nonColProp.colProp2[1]).
 * <p>
 * In the later case, indicates {@code colProp1} is a collection property and a list of all the id
 * values from all the objects in the p1 property shall be returned.
 */
public class CatalogPropertyAccessor implements PropertyAccessor {

    @Override
    public boolean canHandle(Object object, String xpath, Class<?> target) {
        return object instanceof Info;
    }

    @Override
    public <T> void set(Object object, String xpath, T value, Class<T> target) throws IllegalArgumentException {

        throw new UnsupportedOperationException();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Object object, String xpath, Class<T> target) throws IllegalArgumentException {
        Object value = getProperty((Info) object, xpath);
        T result;
        if (null != target && null != value) {
            result = Converters.convert(value, target);
        } else {
            result = (T) value;
        }
        return result;
    }

    /**
     * @param input the object to extract the (possibly nested,indexed, or collection) property from
     * @param propertyName the property to extract from {@code input}
     * @return the evaluated value of the given property, or {@code null} if a prior nested property
     *         in the path is null;
     * @throws IllegalArgumentException if no such property exists for the given object
     */
    public Object getProperty(final Object input, final String propertyName) throws IllegalArgumentException {

        if (input instanceof Info && Predicates.ANY_TEXT.getPropertyName().equals(propertyName)) {
            return getAnyText((Info) input);
        }
        String[] propertyNames = propertyName.split("\\.");
        return getProperty(input, propertyNames, 0);
    }

    /**
     * @param input
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<String> getAnyText(final Info input) {

        final Set<String> propNames = fullTextProperties(input);
        List<String> textProps = new ArrayList<String>(propNames.size());
        for (String propName : propNames) {
            Object property = getProperty(input, propName);
            if (property instanceof Collection) {
                textProps.addAll(((Collection<String>) property));
            } else if (property != null) {
                textProps.add(String.valueOf(property));
            }
        }
        return textProps;
    }

    public Object getProperty(final Object input, final String[] propertyNames, final int offset)
            throws IllegalArgumentException {

        if (offset < 0 || offset > propertyNames.length) {
            throw new ArrayIndexOutOfBoundsException("offset: " + offset + ", properties: " + propertyNames.length);
        }

        if (offset == propertyNames.length) {
            return input;
        }

        final String propName = propertyNames[offset];

        if (null == input) {
            throw new IllegalArgumentException(
                    "Property not found: " + Joiner.on('.').join(Arrays.copyOf(propertyNames, offset + 1)));
        }

        // indexed property?
        if (propName.indexOf('[') > 0 && propName.endsWith("]")) {
            return getIndexedProperty(input, propertyNames, offset);
        }
        if (input instanceof Collection) {
            @SuppressWarnings("unchecked")
            Collection<Object> col = (Collection<Object>) input;
            List<Object> result = new ArrayList<Object>(col.size());
            for (Object o : col) {
                Object value = getProperty(o, propName);
                result.add(value);
            }
            return result;
        }

        Object value;
        if (input instanceof Map) {
            if (!((Map<?, ?>) input).containsKey(propName)) {
                throw new IllegalArgumentException("Property " + propName + " does not exist in Map property "
                        + (offset > 0 ? propertyNames[offset - 1] : ""));
            }
            value = ((Map<?, ?>) input).get(propName);
        } else {
            //special case for ResourceInfo bounding box, used the derived property
            if ("boundingBox".equalsIgnoreCase(propName) && input instanceof ResourceInfo) {
                try {
                    value = ((ResourceInfo) input).boundingBox();
                } catch (Exception e) {
                    throw new IllegalArgumentException(e);
                }
            } else {
                value = OwsUtils.get(input, propName);
            }
        }

        return getProperty(value, propertyNames, offset + 1);
    }

    private Object getIndexedProperty(Object input, final String[] propertyNames, final int offset) {

        final String indexedPropName = propertyNames[offset];

        final String colPropName = indexedPropName.substring(0, indexedPropName.indexOf('['));

        final int index;
        {
            final int beginIndex = indexedPropName.indexOf('[') + 1;
            final int endIndex = indexedPropName.length() - 1;
            final String indexStr = indexedPropName.substring(beginIndex, endIndex);
            index = Integer.parseInt(indexStr);
            Preconditions.checkArgument(index > 0,
                    "Illegal indexed property, index shall be > 0: " + indexedPropName);
        }

        Collection<Object> col = getCollectionProperty(input, colPropName);
        if (col == null) {
            return false;
        }
        if (!(col instanceof List)) {
            throw new RuntimeException("Indexed property access is not valid for property " + colPropName);
        }
        List<Object> list = (List<Object>) col;
        if (index > list.size()) {
            return null;
        }
        Object indexedValue = list.get(index - 1);
        return getProperty(indexedValue, propertyNames, offset + 1);
    }

    private Collection<Object> getCollectionProperty(Object input, String colPropName) {

        Object colProp = OwsUtils.get(input, colPropName);
        if (null == colProp) {
            return null;
        }
        if (colProp.getClass().isArray()) {
            int length = Array.getLength(colProp);
            List<Object> array = new ArrayList<Object>(length);
            for (int j = 0; j < length; j++) {
                array.add(Array.get(colProp, j));
            }
            colProp = array;
        }
        if (!(colProp instanceof Collection)) {
            throw new IllegalArgumentException(
                    "Specified property " + colPropName + " is not a collection or array: " + colProp);
        }
        @SuppressWarnings("unchecked")
        Collection<Object> col = (Collection<Object>) colProp;
        return col;
    }

    private static Map<Class<?>, Set<String>> FULL_TEXT_PROPERTIES = Maps.newHashMap();

    private static Set<String> fullTextProperties(Info obj) {
        Set<String> props = ImmutableSet.of();
        if (obj != null) {
            Class<?> clazz = ModificationProxy.unwrap(obj).getClass();
            ClassMappings classMappings = ClassMappings.fromImpl(clazz);
            checkState(classMappings != null, "No class mappings found for class " + clazz.getName());
            Class<?> interf = classMappings.getInterface();
            props = fullTextProperties(interf);
        }
        return props;
    }

    public static Set<String> fullTextProperties(Class<?> type) {
        if (FULL_TEXT_PROPERTIES.isEmpty()) {
            loadFullTextProperties();
        }
        Set<String> props = FULL_TEXT_PROPERTIES.get(type);
        if (props == null) {
            props = ImmutableSet.of();
        }
        return props;
    }

    /**
     * 
     */
    private static synchronized void loadFullTextProperties() {
        if (!FULL_TEXT_PROPERTIES.isEmpty()) {
            return;
        }
        final String resource = "CatalogPropertyAccessor_FullTextProperties.properties";
        Properties properties = new Properties();
        InputStream stream = CatalogPropertyAccessor.class.getResourceAsStream(resource);
        try {
            properties.load(stream);
        } catch (IOException e) {
            throw Throwables.propagate(e);
        } finally {
            Closeables.closeQuietly(stream);
        }
        Map<String, String> map = Maps.fromProperties(properties);
        for (Map.Entry<String, String> e : map.entrySet()) {
            Class<?> key;
            try {
                key = Class.forName(e.getKey());
            } catch (ClassNotFoundException e1) {
                throw propagate(e1);
            }
            String[] split = e.getValue().split(",");
            Set<String> set = Sets.newHashSet();
            for (String s : split) {
                set.add(s.trim());
            }

            FULL_TEXT_PROPERTIES.put(key, ImmutableSet.copyOf(set));
        }
    }
}