org.geomajas.internal.service.DtoConverterServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.geomajas.internal.service.DtoConverterServiceImpl.java

Source

/*
 * This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
 *
 * Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium.
 *
 * The program is available in open source according to the GNU Affero
 * General Public License. All contributions in this program are covered
 * by the Geomajas Contributors License Agreement. For full licensing
 * details, see LICENSE.txt in the project root.
 */

package org.geomajas.internal.service;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtilsBean;
import org.geomajas.configuration.AbstractAttributeInfo;
import org.geomajas.configuration.AssociationAttributeInfo;
import org.geomajas.configuration.AssociationType;
import org.geomajas.configuration.AttributeInfo;
import org.geomajas.configuration.PrimitiveAttributeInfo;
import org.geomajas.geometry.Bbox;
import org.geomajas.geometry.Geometry;
import org.geomajas.geometry.conversion.jts.GeometryConverterService;
import org.geomajas.geometry.conversion.jts.JtsConversionException;
import org.geomajas.global.ExceptionCode;
import org.geomajas.global.GeomajasException;
import org.geomajas.internal.layer.feature.InternalFeatureImpl;
import org.geomajas.internal.layer.vector.lazy.LazyAttribute;
import org.geomajas.layer.LayerType;
import org.geomajas.layer.VectorLayerService;
import org.geomajas.layer.feature.Attribute;
import org.geomajas.layer.feature.Feature;
import org.geomajas.layer.feature.InternalFeature;
import org.geomajas.layer.feature.attribute.ArrayAttribute;
import org.geomajas.layer.feature.attribute.AssociationAttribute;
import org.geomajas.layer.feature.attribute.AssociationValue;
import org.geomajas.layer.feature.attribute.BooleanAttribute;
import org.geomajas.layer.feature.attribute.CurrencyAttribute;
import org.geomajas.layer.feature.attribute.DateAttribute;
import org.geomajas.layer.feature.attribute.DoubleAttribute;
import org.geomajas.layer.feature.attribute.FloatAttribute;
import org.geomajas.layer.feature.attribute.ImageUrlAttribute;
import org.geomajas.layer.feature.attribute.IntegerAttribute;
import org.geomajas.layer.feature.attribute.LongAttribute;
import org.geomajas.layer.feature.attribute.ManyToOneAttribute;
import org.geomajas.layer.feature.attribute.OneToManyAttribute;
import org.geomajas.layer.feature.attribute.PrimitiveAttribute;
import org.geomajas.layer.feature.attribute.ShortAttribute;
import org.geomajas.layer.feature.attribute.StringAttribute;
import org.geomajas.layer.feature.attribute.UrlAttribute;
import org.geomajas.layer.tile.InternalTile;
import org.geomajas.layer.tile.VectorTile;
import org.geomajas.service.DtoConverterService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;

/**
 * Default implementation of DTO converter.
 * 
 * @author Jan De Moerloose
 * @author Joachim Van der Auwera
 * @author Pieter De Graef
 */
@Component()
public class DtoConverterServiceImpl implements DtoConverterService {

    private ConvertUtilsBean cub = new ConvertUtilsBean();

    // -------------------------------------------------------------------------
    // Attribute conversion:
    // -------------------------------------------------------------------------

    /**
     * Converts a DTO attribute into a generic attribute object.
     * 
     * @param attribute
     *            The DTO attribute.
     * @return The server side attribute representation. As we don't know at this point what kind of object the
     *         attribute is (that's a problem for the <code>FeatureModel</code>), we return an <code>Object</code>.
     */
    public Object toInternal(Attribute<?> attribute) throws GeomajasException {
        if (attribute instanceof PrimitiveAttribute<?>) {
            return toPrimitiveObject((PrimitiveAttribute<?>) attribute);
        } else if (attribute instanceof AssociationAttribute<?>) {
            return toAssociationObject((AssociationAttribute<?>) attribute);
        } else {
            throw new GeomajasException(ExceptionCode.CONVERSION_PROBLEM, attribute);
        }
    }

    @Override
    public Attribute<?> toDto(Object object, AttributeInfo info) throws GeomajasException {
        return toDto(object, (AbstractAttributeInfo) info);
    }

    @Override
    public Attribute<?> toDto(Object object, AbstractAttributeInfo info) throws GeomajasException {
        if (info instanceof PrimitiveAttributeInfo) {
            if (object instanceof Object[]) {
                return toArrayDto((Object[]) object, (PrimitiveAttributeInfo) info);
            } else {
                return toPrimitiveDto(object, (PrimitiveAttributeInfo) info);
            }
        } else if (info instanceof AssociationAttributeInfo) {
            return toAssociationDto(object, (AssociationAttributeInfo) info);
        } else {
            throw new GeomajasException(ExceptionCode.CONVERSION_PROBLEM, info.getName());
        }
    }

    // -------------------------------------------------------------------------
    // Private methods - Attribute conversion:
    // -------------------------------------------------------------------------

    private Attribute<?> toAssociationDto(Object value, AssociationAttributeInfo associationAttributeInfo)
            throws GeomajasException {
        if (associationAttributeInfo.getType() == AssociationType.MANY_TO_ONE) {
            return new ManyToOneAttribute(createAssociationValue(value, associationAttributeInfo));
        } else if (associationAttributeInfo.getType() == AssociationType.ONE_TO_MANY) {
            // Value should be an array of objects...
            List<AssociationValue> associationValues = new ArrayList<AssociationValue>();
            if (value != null && value instanceof Object[]) {
                Object[] array = (Object[]) value;
                for (Object bean : array) {
                    associationValues.add(createAssociationValue(bean, associationAttributeInfo));
                }
            }
            return new OneToManyAttribute(associationValues);
        }
        return null;
    }

    private AssociationValue createAssociationValue(Object value, AssociationAttributeInfo associationAttributeInfo)
            throws GeomajasException {
        if (value == null) {
            return null;
        }
        Map<String, Attribute<?>> attributes = new HashMap<String, Attribute<?>>();
        boolean primitiveOnly = true;
        for (AbstractAttributeInfo attributeInfo : associationAttributeInfo.getFeature().getAttributes()) {
            Object propertyValue = getBeanProperty(value, attributeInfo.getName());
            Attribute<?> attrib = toDto(propertyValue, attributeInfo);
            attributes.put(attributeInfo.getName(), attrib);
            if (!attrib.isPrimitive()) {
                primitiveOnly = false;
            }
        }
        PrimitiveAttribute<?> id = (PrimitiveAttribute<?>) toDto(
                getBeanProperty(value, associationAttributeInfo.getFeature().getIdentifier().getName()),
                associationAttributeInfo.getFeature().getIdentifier());
        return new AssociationValue(id, attributes, primitiveOnly);
    }

    private Object getBeanProperty(Object bean, String property) throws GeomajasException {
        PropertyDescriptor d = BeanUtils.getPropertyDescriptor(bean.getClass(), property);
        if (d != null) {
            Method m = d.getReadMethod();
            if (m != null) {
                if (!Modifier.isPublic(m.getDeclaringClass().getModifiers())) {
                    m.setAccessible(true);
                }
                Object value;
                try {
                    value = m.invoke(bean);
                } catch (Exception e) { // NOSONAR
                    throw new GeomajasException(e);
                }
                return value;
            }
        }
        return null;
    }

    private Attribute<?> toPrimitiveDto(Object value, PrimitiveAttributeInfo info) {
        switch (info.getType()) {
        case BOOLEAN:
            return new BooleanAttribute((Boolean) convertToClass(value, Boolean.class));
        case SHORT:
            return new ShortAttribute((Short) convertToClass(value, Short.class));
        case INTEGER:
            return new IntegerAttribute((Integer) convertToClass(value, Integer.class));
        case LONG:
            return new LongAttribute((Long) convertToClass(value, Long.class));
        case FLOAT:
            return new FloatAttribute((Float) convertToClass(value, Float.class));
        case DOUBLE:
            return new DoubleAttribute((Double) convertToClass(value, Double.class));
        case CURRENCY:
            return new CurrencyAttribute((String) convertToClass(value, String.class));
        case STRING:
            return new StringAttribute(value == null ? null : value.toString());
        case DATE:
            return new DateAttribute((Date) value);
        case URL:
            return new UrlAttribute((String) value);
        case IMGURL:
            return new ImageUrlAttribute((String) value);
        default:
            throw new IllegalArgumentException("Cannot create primitive attribute of type " + info);
        }
    }

    private ArrayAttribute<?> toArrayDto(Object[] value, PrimitiveAttributeInfo info) {
        switch (info.getType()) {
        case BOOLEAN:
            Boolean[] booleans = new Boolean[value.length];
            for (int i = 0; i < value.length; i++) {
                booleans[i] = (Boolean) convertToClass(value[i], Boolean.class);
            }
            return new ArrayAttribute<Boolean>(booleans);
        case SHORT:
            Short[] shorts = new Short[value.length];
            for (int i = 0; i < value.length; i++) {
                shorts[i] = (Short) convertToClass(value[i], Short.class);
            }
            return new ArrayAttribute<Short>(shorts);
        case INTEGER:
            Integer[] ints = new Integer[value.length];
            for (int i = 0; i < value.length; i++) {
                ints[i] = (Integer) convertToClass(value[i], Integer.class);
            }
            return new ArrayAttribute<Integer>(ints);
        case LONG:
            Long[] longs = new Long[value.length];
            for (int i = 0; i < value.length; i++) {
                longs[i] = (Long) convertToClass(value[i], Long.class);
            }
            return new ArrayAttribute<Long>(longs);
        case FLOAT:
            Float[] floats = new Float[value.length];
            for (int i = 0; i < value.length; i++) {
                floats[i] = (Float) convertToClass(value[i], Float.class);
            }
            return new ArrayAttribute<Float>(floats);
        case DOUBLE:
            Double[] doubles = new Double[value.length];
            for (int i = 0; i < value.length; i++) {
                doubles[i] = (Double) convertToClass(value[i], Double.class);
            }
            return new ArrayAttribute<Double>(doubles);
        case CURRENCY:
            String[] values = new String[value.length];
            for (int i = 0; i < value.length; i++) {
                values[i] = (String) convertToClass(value[i], String.class);
            }
            return new ArrayAttribute<String>(values);
        case STRING:
            String[] strings = new String[value.length];
            for (int i = 0; i < value.length; i++) {
                strings[i] = (value[i] == null ? null : value[i].toString());
            }
            return new ArrayAttribute<String>(strings);
        case DATE:
            Date[] dates = new Date[value.length];
            for (int i = 0; i < value.length; i++) {
                dates[i] = (Date) convertToClass(value[i], Date.class);
            }
            return new ArrayAttribute<Date>(dates);
        case URL:
            String[] urls = new String[value.length];
            for (int i = 0; i < value.length; i++) {
                urls[i] = (value[i] == null ? null : value[i].toString());
            }
            return new ArrayAttribute<String>(urls);
        case IMGURL:
            String[] images = new String[value.length];
            for (int i = 0; i < value.length; i++) {
                images[i] = (value[i] == null ? null : value[i].toString());
            }
            return new ArrayAttribute<String>(images);
        }
        throw new IllegalArgumentException("Cannot create primitive attribute of type " + info);
    }

    private Object toAssociationObject(AssociationAttribute<?> primitiveAttribute) {
        // TODO: implement
        throw new UnsupportedOperationException("toAssociationObject() not implemented");
    }

    private Object toPrimitiveObject(PrimitiveAttribute<?> primitive) {
        return primitive.getValue();
    }

    private Object convertToClass(Object object, Class<?> c) {
        if (object == null) {
            return null;
        } else if (c.isInstance(object)) {
            return object;
        }
        return cub.convert(object.toString(), c);
    }

    // -------------------------------------------------------------------------
    // Feature conversion:
    // -------------------------------------------------------------------------

    /**
     * Convert the server side feature to a DTO feature that can be sent to the client.
     * 
     * @param feature
     *            The server-side feature representation.
     * @param featureIncludes
     *            Indicate which aspects of the should be included see {@link VectorLayerService}
     * @return Returns the DTO feature.
     */
    public Feature toDto(InternalFeature feature, int featureIncludes) throws GeomajasException {
        if (feature == null) {
            return null;
        }
        Feature dto = new Feature(feature.getId());
        if ((featureIncludes & VectorLayerService.FEATURE_INCLUDE_ATTRIBUTES) != 0
                && null != feature.getAttributes()) {
            // need to assure lazy attributes are converted to non-lazy attributes
            Map<String, Attribute> attributes = new HashMap<String, Attribute>();
            for (Map.Entry<String, Attribute> entry : feature.getAttributes().entrySet()) {
                Attribute value = entry.getValue();
                if (value instanceof LazyAttribute) {
                    value = ((LazyAttribute) value).instantiate();
                }
                attributes.put(entry.getKey(), value);
            }
            dto.setAttributes(attributes);
        }
        if ((featureIncludes & VectorLayerService.FEATURE_INCLUDE_LABEL) != 0) {
            dto.setLabel(feature.getLabel());
        }
        if ((featureIncludes & VectorLayerService.FEATURE_INCLUDE_GEOMETRY) != 0) {
            dto.setGeometry(toDto(feature.getGeometry()));
        }
        if ((featureIncludes & VectorLayerService.FEATURE_INCLUDE_STYLE) != 0 && null != feature.getStyleInfo()) {
            dto.setStyleId(feature.getStyleInfo().getStyleId());
        }
        InternalFeatureImpl vFeature = (InternalFeatureImpl) feature;
        dto.setClipped(vFeature.isClipped());
        dto.setUpdatable(feature.isEditable());
        dto.setDeletable(feature.isDeletable());
        return dto;
    }

    @Override
    public Feature toDto(InternalFeature feature) throws GeomajasException {
        return toDto(feature, VectorLayerService.FEATURE_INCLUDE_ALL);
    }

    @Override
    public InternalFeature toInternal(Feature dto) throws GeomajasException {
        if (dto == null) {
            return null;
        }
        InternalFeatureImpl feature = new InternalFeatureImpl();
        feature.setAttributes(dto.getAttributes());
        feature.setId(dto.getId());
        feature.setLabel(dto.getLabel());
        feature.setGeometry(toInternal(dto.getGeometry()));
        feature.setClipped(dto.isClipped());
        feature.setEditable(dto.isUpdatable());
        feature.setDeletable(dto.isDeletable());
        return feature;
    }

    // -------------------------------------------------------------------------
    // Geometry conversion:
    // -------------------------------------------------------------------------

    @Override
    public Geometry toDto(com.vividsolutions.jts.geom.Geometry geometry) throws GeomajasException {
        if (geometry == null) {
            return null;
        }
        try {
            return GeometryConverterService.fromJts(geometry);
        } catch (JtsConversionException jce) {
            throw new GeomajasException(jce, ExceptionCode.CANNOT_CONVERT_GEOMETRY, geometry.getClass().getName());
        }
    }

    @Override
    public com.vividsolutions.jts.geom.Geometry toInternal(Geometry geometry) throws GeomajasException {
        if (geometry == null) {
            return null;
        }
        try {
            return GeometryConverterService.toJts(geometry);
        } catch (JtsConversionException jce) {
            throw new GeomajasException(jce, ExceptionCode.CANNOT_CONVERT_GEOMETRY, geometry.getGeometryType());
        }
    }

    // -------------------------------------------------------------------------
    // Tile conversion:
    // -------------------------------------------------------------------------

    /**
     * Convert a server-side tile representations into a DTO tile.
     * 
     * @param tile
     *            The server-side representation of a tile.
     * @param crs
     *            crs to include in features (if any)
     * @param featureIncludes
     *            Indicate which aspects of the should be included see {@link org.geomajas.layer.VectorLayerService}
     * @return Returns the DTO version that can be sent to the client.
     */
    public VectorTile toDto(InternalTile tile, String crs, int featureIncludes) throws GeomajasException {
        return toDto(tile);
    }

    /**
     * Convert a server-side tile representations into a DTO tile.
     *
     * @param tile
     *            The server-side representation of a tile.
     * @return Returns the DTO version that can be sent to the client.
     */
    public VectorTile toDto(InternalTile tile) throws GeomajasException {
        if (null != tile) {
            VectorTile dto = new VectorTile();
            dto.setClipped(tile.isClipped());
            dto.setCode(tile.getCode());
            dto.setCodes(tile.getCodes());
            dto.setScreenHeight(tile.getScreenHeight());
            dto.setScreenWidth(tile.getScreenWidth());
            dto.setFeatureContent(tile.getFeatureContent());
            dto.setLabelContent(tile.getLabelContent());
            dto.setContentType(tile.getContentType());
            return dto;
        }
        return null;
    }

    // -------------------------------------------------------------------------
    // Bounding box conversion:
    // -------------------------------------------------------------------------

    /**
     * Convert a {@link Bbox} to a JTS envelope.
     * 
     * @param bbox bounding box
     * @return JTS envelope
     */
    public Envelope toInternal(Bbox bbox) {
        return new Envelope(bbox.getX(), bbox.getMaxX(), bbox.getY(), bbox.getMaxY());
    }

    /**
     * Convert JTS envelope into a {@link Bbox}.
     * 
     * @param envelope
     *            JTS envelope
     * @return Geomajas {@link Bbox}
     */
    public Bbox toDto(Envelope envelope) {
        return new Bbox(envelope.getMinX(), envelope.getMinY(), envelope.getWidth(), envelope.getHeight());
    }

    /**
     * Convert a layer type to a geometry class.
     * 
     * @param layerType
     *            layer type
     * @return JTS class
     */
    public Class<? extends com.vividsolutions.jts.geom.Geometry> toInternal(LayerType layerType) {
        switch (layerType) {
        case GEOMETRY:
            return com.vividsolutions.jts.geom.Geometry.class;
        case LINESTRING:
            return LineString.class;
        case MULTILINESTRING:
            return MultiLineString.class;
        case POINT:
            return Point.class;
        case MULTIPOINT:
            return MultiPoint.class;
        case POLYGON:
            return Polygon.class;
        case MULTIPOLYGON:
            return MultiPolygon.class;
        case RASTER:
            return null;
        default:
            throw new IllegalStateException("Don't know how to handle layer type " + layerType);
        }
    }

    /**
     * Convert a geometry class to a layer type.
     * 
     * @param geometryClass
     *            JTS geometry class
     * @return Geomajas layer type
     */
    public LayerType toDto(Class<? extends com.vividsolutions.jts.geom.Geometry> geometryClass) {
        if (geometryClass == LineString.class) {
            return LayerType.LINESTRING;
        } else if (geometryClass == MultiLineString.class) {
            return LayerType.MULTILINESTRING;
        } else if (geometryClass == Point.class) {
            return LayerType.POINT;
        } else if (geometryClass == MultiPoint.class) {
            return LayerType.MULTIPOINT;
        } else if (geometryClass == Polygon.class) {
            return LayerType.POLYGON;
        } else if (geometryClass == MultiPolygon.class) {
            return LayerType.MULTIPOLYGON;
        } else {
            return LayerType.GEOMETRY;
        }
    }
}