/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2014-2015, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotoolkit.data.bean;
import com.vividsolutions.jts.geom.Geometry;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.sis.feature.AbstractFeature;
import org.apache.sis.feature.builder.AttributeRole;
import org.apache.sis.feature.builder.AttributeTypeBuilder;
import org.apache.sis.feature.builder.FeatureTypeBuilder;
import org.geotoolkit.data.FeatureStoreRuntimeException;
import org.geotoolkit.filter.identity.DefaultFeatureId;
import org.opengis.filter.identity.FeatureId;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.feature.Attribute;
import org.opengis.feature.AttributeType;
import org.opengis.feature.FeatureType;
import org.opengis.feature.InvalidPropertyValueException;
import org.opengis.feature.Property;
import org.opengis.feature.PropertyNotFoundException;
import org.opengis.feature.PropertyType;
import org.opengis.util.GenericName;
/**
*
* @author Johann Sorel (Geomatys)
*/
public class BeanFeature extends AbstractFeature{
public static final String KEY_BEAN = "bean";
private final Object bean;
private final Mapping mapping;
public BeanFeature(Object bean, Mapping mapping){
super(mapping.featureType);
this.bean = bean;
this.mapping = mapping;
}
public Object getBean(){
return bean;
}
@Override
public Property getProperty(String string) throws PropertyNotFoundException {
final PropertyType pt = mapping.featureType.getProperty(string);
return new BeanAttributeProperty((AttributeType) pt);
}
@Override
public void setProperty(Property prprt) throws IllegalArgumentException {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public Object getPropertyValue(String string) throws PropertyNotFoundException {
return getProperty(string).getValue();
}
@Override
public void setPropertyValue(String string, Object o) throws IllegalArgumentException {
Property property = getProperty(string);
if(property instanceof Attribute){
((Attribute)property).setValue(o);
}else{
throw new IllegalArgumentException("Property "+string+" can not be set.");
}
}
public static class Mapping {
public final FeatureType featureType;
public final String idField;
public final Map<String,java.beans.PropertyDescriptor> accessors = new HashMap<>();
public java.beans.PropertyDescriptor idAccessor;
public Mapping(Class clazz, String namespace, CoordinateReferenceSystem crs, String idField) {
this(clazz,namespace,crs,idField,null, new Predicate<java.beans.PropertyDescriptor>() {
@Override
public boolean test(java.beans.PropertyDescriptor t) {
return true;
}
});
}
public Mapping(Class clazz, String namespace, CoordinateReferenceSystem crs, String idField, String defaultGeom, Predicate<java.beans.PropertyDescriptor> filter) {
this.idField = idField;
final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
ftb.setName(namespace,clazz.getSimpleName());
try {
for (java.beans.PropertyDescriptor pd : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
final String propName = pd.getName();
if(propName.equals(idField)){
//ignore the id field, it will be used as featureId
idAccessor = pd;
continue;
}
final Method readMethod = pd.getReadMethod();
if(readMethod==null) continue;
if(!filter.test(pd)) continue;
final Class propClazz = readMethod.getReturnType();
if(Geometry.class.isAssignableFrom(propClazz)){
final AttributeTypeBuilder atb = ftb.addAttribute(propClazz).setName(propName).setCRS(crs);
if(defaultGeom==null || defaultGeom.equals(atb.getName().tip().toString())){
defaultGeom = atb.getName().tip().toString();
atb.addRole(AttributeRole.DEFAULT_GEOMETRY);
}
}else{
ftb.addAttribute(propClazz).setName(propName);
}
accessors.put(propName, pd);
}
} catch (IntrospectionException e) {
throw new RuntimeException(e.getMessage(), e);
}
featureType = ftb.build();
}
public FeatureId buildId(Object bean){
try {
return new DefaultFeatureId(String.valueOf(idAccessor.getReadMethod().invoke(bean)));
} catch (ReflectiveOperationException | IllegalArgumentException ex) {
throw new FeatureStoreRuntimeException(ex);
}
}
}
private class BeanAttributeProperty implements Attribute{
private final AttributeType desc;
public BeanAttributeProperty(AttributeType desc){
this.desc = desc;
}
@Override
public Object getValue() {
try {
final Method m = mapping.accessors.get(getName().tip().toString()).getReadMethod();
if(m==null) return null;
return m.invoke(bean);
} catch (ReflectiveOperationException | IllegalArgumentException ex) {
throw new FeatureStoreRuntimeException(ex);
}
}
@Override
public void setValue(Object newValue) {
try {
final Method m = mapping.accessors.get(getName().tip().toString()).getWriteMethod();
if(m==null) return;
m.invoke(bean,newValue);
} catch (ReflectiveOperationException | IllegalArgumentException ex) {
throw new FeatureStoreRuntimeException(ex);
}
}
@Override
public GenericName getName() {
return getType().getName();
}
@Override
public AttributeType getType() {
return desc;
}
@Override
public Collection<Object> getValues() {
return Collections.singleton(getValue());
}
@Override
public void setValues(Collection clctn) throws InvalidPropertyValueException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Map characteristics() {
return Collections.EMPTY_MAP;
}
}
}