
org.geomajas.internal.layer.entity.EntityAttributeServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of geomajas-impl Show documentation
Show all versions of geomajas-impl Show documentation
Geomajas server: Main - Implementation
The newest version!
/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2016 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.layer.entity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.geomajas.configuration.AbstractAttributeInfo;
import org.geomajas.configuration.AssociationAttributeInfo;
import org.geomajas.configuration.EditableAttributeInfo;
import org.geomajas.configuration.FeatureInfo;
import org.geomajas.configuration.PrimitiveAttributeInfo;
import org.geomajas.global.ExceptionCode;
import org.geomajas.global.GeomajasException;
import org.geomajas.layer.LayerException;
import org.geomajas.layer.entity.Entity;
import org.geomajas.layer.entity.EntityAttributeService;
import org.geomajas.layer.entity.EntityCollection;
import org.geomajas.layer.entity.EntityMapper;
import org.geomajas.layer.feature.Attribute;
import org.geomajas.layer.feature.attribute.AssociationAttribute;
import org.geomajas.layer.feature.attribute.AssociationValue;
import org.geomajas.layer.feature.attribute.ManyToOneAttribute;
import org.geomajas.layer.feature.attribute.OneToManyAttribute;
import org.geomajas.layer.feature.attribute.PrimitiveAttribute;
import org.geomajas.service.DtoConverterService;
import org.geomajas.service.FilterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Default implementation of {@link EntityAttributeService}. This class maps attributes in a generic way using an
* operation tree.
*
* @author Jan De Moerloose
*/
@Component
public class EntityAttributeServiceImpl implements EntityAttributeService {
public static final String ATTRIBUTE_SEPARATOR = "[/.]+";
@Autowired
private DtoConverterService dtoConverterService;
@Override
public void setAttributes(Object object, FeatureInfo featureInfo, EntityMapper mapper,
Map> attributes) throws LayerException {
UpdateEntityOperation operation = new UpdateEntityOperation(mapper, mapper.asEntity(object), featureInfo,
attributes);
operation.execute();
}
@Override
public Attribute> getAttribute(Object feature, FeatureInfo featureInfo, EntityMapper mapper, String name)
throws LayerException {
String[] path = name.split(ATTRIBUTE_SEPARATOR);
return getRecursiveAttribute(mapper.asEntity(feature), featureInfo, path);
}
private Attribute> getRecursiveAttribute(Entity entity, FeatureInfo featureInfo, String[] path)
throws LayerException {
String name = path[0];
AssociationAttributeInfo associationAttributeInfo = null;
// check attribute type
Set names = new HashSet();
// check for id
AbstractAttributeInfo identifier = featureInfo.getIdentifier();
if (identifier.getName().equals(name) || FilterService.ATTRIBUTE_ID.equalsIgnoreCase(name)) {
try {
return dtoConverterService
.toDto(entity == null ? null : entity.getId(identifier.getName()), identifier);
} catch (GeomajasException e) {
throw new LayerException(e, ExceptionCode.CONVERSION_PROBLEM);
}
}
for (AbstractAttributeInfo attributeInfo : featureInfo.getAttributes()) {
names.add(attributeInfo.getName());
if (attributeInfo.getName().equals(name)) {
if (attributeInfo instanceof AssociationAttributeInfo) {
associationAttributeInfo = (AssociationAttributeInfo) attributeInfo;
} else if (attributeInfo instanceof PrimitiveAttributeInfo) {
try {
return dtoConverterService.toDto(entity == null ? null : entity.getAttribute(name),
attributeInfo);
} catch (GeomajasException e) {
throw new LayerException(e, ExceptionCode.CONVERSION_PROBLEM);
}
} else {
throw new IllegalStateException("UnHandled attribute type, " + attributeInfo.getClass().getName());
}
}
}
// association attribute
if (associationAttributeInfo == null) {
throw new LayerException(ExceptionCode.ATTRIBUTE_UNKNOWN, name, names);
} else if (path.length > 1) {
// go deeper
return getRecursiveAttribute(entity == null ? null : entity.getChild(name),
associationAttributeInfo.getFeature(), Arrays.copyOfRange(path, 1, path.length));
} else {
switch (associationAttributeInfo.getType()) {
case MANY_TO_ONE:
ManyToOneAttribute manyToOne = new ManyToOneAttribute();
if (entity != null) {
Entity oneEntity = entity.getChild(associationAttributeInfo.getName());
AssociationValue value = getAssociationValue(oneEntity, associationAttributeInfo);
manyToOne.setValue(value);
}
return manyToOne;
case ONE_TO_MANY:
OneToManyAttribute oneToMany = new OneToManyAttribute();
if (entity != null) {
EntityCollection children = entity.getChildCollection(associationAttributeInfo.getName());
List values = new ArrayList();
for (Entity manyEntity : children) {
values.add(getAssociationValue(manyEntity, associationAttributeInfo));
}
oneToMany.setValue(values);
}
return oneToMany;
}
return null;
}
}
private AssociationValue getAssociationValue(Entity entity, AssociationAttributeInfo associationAttributeInfo)
throws LayerException {
if (entity == null) {
return null;
}
AbstractAttributeInfo idInfo = associationAttributeInfo.getFeature().getIdentifier();
FeatureInfo childInfo = associationAttributeInfo.getFeature();
PrimitiveAttribute> id;
try {
id = (PrimitiveAttribute) dtoConverterService.toDto(entity.getId(idInfo.getName()), idInfo);
} catch (GeomajasException e) {
throw new LayerException(e, ExceptionCode.CONVERSION_PROBLEM);
}
Map> attributes = new HashMap>();
for (AbstractAttributeInfo attributeInfo : childInfo.getAttributes()) {
attributes.put(attributeInfo.getName(),
getRecursiveAttribute(entity, childInfo, new String[] { attributeInfo.getName() }));
}
return new AssociationValue(id, attributes, false);
}
/**
* Operation interface.
*
* @author Jan De Moerloose
*/
public interface Operation {
/**
* Execute the operation.
*
* @throws LayerException oops
*/
void execute() throws LayerException;
}
/**
* Operation for updating the top entity belonging to the {@link FeatureInfo} of this class.
*
* @author Jan De Moerloose
*/
class UpdateEntityOperation extends AbstractOperation {
public UpdateEntityOperation(EntityMapper mapper, Entity entity, FeatureInfo featureInfo,
Map> attributes) throws LayerException {
super(mapper, entity, featureInfo, attributes);
addChildOperations();
}
}
/**
* Operation for creating a {@link ManyToOneAttribute}.
*
* @author Jan De Moerloose
*/
class CreateManyToOneOperation extends AbstractOperation {
private final Entity parent;
private final String name;
public CreateManyToOneOperation(EntityMapper mapper, Entity parent, AssociationAttributeInfo attributeInfo,
AssociationValue value) throws LayerException {
super(mapper, null, attributeInfo.getFeature(), value.getAllAttributes());
this.parent = parent;
this.name = attributeInfo.getName();
Object id = null;
if (value.getId() != null) {
id = value.getId().getValue();
}
setEntity(getMapper().findOrCreateEntity(attributeInfo.getFeature().getDataSourceName(), id));
addChildOperations();
}
@Override
public void execute() throws LayerException {
parent.setChild(name, getEntity());
super.execute();
}
}
/**
* Operation for updating a {@link ManyToOneAttribute}.
*
* @author Jan De Moerloose
*/
class UpdateManyToOneOperation extends AbstractOperation {
public UpdateManyToOneOperation(EntityMapper mapper, AssociationAttributeInfo attributeInfo, Entity existing,
AssociationValue value) throws LayerException {
super(mapper, existing, attributeInfo.getFeature(), value.getAllAttributes());
addChildOperations();
}
}
/**
* Operation for deleting a {@link ManyToOneAttribute}.
*
* @author Jan De Moerloose
*/
class DeleteManyToOneOperation extends AbstractOperation {
private final Entity parent;
private final String name;
public DeleteManyToOneOperation(EntityMapper mapper, Entity parent, AssociationAttributeInfo attributeInfo)
throws LayerException {
super(mapper, null, attributeInfo.getFeature(), null);
this.parent = parent;
this.name = attributeInfo.getName();
}
@Override
public void execute() throws LayerException {
parent.setChild(name, null);
}
}
/**
* Operation for deleting a {@link OneToManyAttribute}.
*
* @author Jan De Moerloose
*/
class DeleteOneToManyOperation extends AbstractOperation {
private final Entity parent;
private final String name;
public DeleteOneToManyOperation(EntityMapper mapper, Entity parent, AssociationAttributeInfo attributeInfo)
throws LayerException {
super(mapper, null, attributeInfo.getFeature(), null);
this.parent = parent;
this.name = attributeInfo.getName();
}
@Override
public void execute() throws LayerException {
parent.setChild(name, null);
}
}
/**
* Operation for removing value from a {@link OneToManyAttribute}.
*
* @author Jan De Moerloose
*/
class RemoveManyValue extends AbstractOperation {
private final Entity toDelete;
private final EntityCollection existing;
public RemoveManyValue(EntityMapper mapper, EntityCollection existing, Entity toDelete) throws LayerException {
super(mapper, null, null, null);
this.toDelete = toDelete;
this.existing = existing;
}
@Override
public void execute() throws LayerException {
existing.removeEntity(toDelete);
}
}
/**
* Operation for updating an existing value of a {@link OneToManyAttribute}.
*
* @author Jan De Moerloose
*/
class UpdateManyValue extends AbstractOperation {
public UpdateManyValue(EntityMapper mapper, Entity entity, AssociationAttributeInfo attributeInfo,
AssociationValue associationValue) throws LayerException {
super(mapper, entity, attributeInfo.getFeature(), associationValue.getAllAttributes());
addChildOperations();
}
}
/**
* Operation for adding a value to a {@link OneToManyAttribute}.
*
* @author Jan De Moerloose
*/
class AddManyValue extends AbstractOperation {
private final EntityCollection collection;
public AddManyValue(EntityMapper mapper, EntityCollection collection, AssociationAttributeInfo attributeInfo,
AssociationValue associationValue) throws LayerException {
super(mapper, null, attributeInfo.getFeature(), associationValue.getAllAttributes());
this.collection = collection;
Object id = null;
if (associationValue.getId() != null) {
id = associationValue.getId().getValue();
}
setEntity(mapper.findOrCreateEntity(attributeInfo.getFeature().getDataSourceName(), id));
addChildOperations();
}
@Override
public void execute() throws LayerException {
collection.addEntity(getEntity());
super.execute();
}
}
/**
* Abstract attribute operation that is able to generate a tree of operations and recursively execute it. The tree
* consists of all elementary operations needed to merge a new attribute map with a map of existing attributes as
* reflected in the bean properties.
*
* @author Jan De Moerloose
*/
abstract class AbstractOperation implements Operation {
private Entity entity;
private final Map> primitives = new HashMap>();
private final Map> attributes;
private final FeatureInfo featureInfo;
private final EntityMapper mapper;
private List children = new ArrayList();
public AbstractOperation(EntityMapper mapper, Entity entity, FeatureInfo featureInfo,
Map> attributes) throws LayerException {
this.mapper = mapper;
this.entity = entity;
this.featureInfo = featureInfo;
this.attributes = attributes;
}
protected void addChildOperations() throws LayerException {
Map attributesMap = featureInfo.getAttributesMap();
for (Map.Entry> entry : attributes.entrySet()) {
Attribute> attribute = entry.getValue();
AbstractAttributeInfo attributeInfo = attributesMap.get(entry.getKey());
if (attributeInfo instanceof EditableAttributeInfo &&
((EditableAttributeInfo) attributeInfo).isEditable()) {
if (attributeInfo instanceof PrimitiveAttributeInfo) {
addPrimitive(entry.getKey(), (PrimitiveAttribute>) attribute);
} else if (attributeInfo instanceof AssociationAttributeInfo) {
AssociationAttribute> association = (AssociationAttribute>) attribute;
AssociationAttributeInfo associationAttributeInfo = (AssociationAttributeInfo) attributeInfo;
switch (associationAttributeInfo.getType()) {
case MANY_TO_ONE:
association = (association == null ? new ManyToOneAttribute() : association);
addManyToOne(entry.getKey(), associationAttributeInfo,
(ManyToOneAttribute) association);
break;
case ONE_TO_MANY:
association = (association == null ? new OneToManyAttribute() : association);
addOneToMany(entry.getKey(), associationAttributeInfo,
(OneToManyAttribute) association);
break;
}
}
}
}
}
@Override
public void execute() throws LayerException {
for (Map.Entry> entry : primitives.entrySet()) {
Object value = (entry.getValue() == null ? null : entry.getValue().getValue());
entity.setAttribute(entry.getKey(), value);
}
for (Operation operation : children) {
operation.execute();
}
}
public EntityMapper getMapper() {
return mapper;
}
public Entity getEntity() {
return entity;
}
public void setEntity(Entity entity) {
this.entity = entity;
}
public void addChild(Operation child) {
children.add(child);
}
public void addOneToMany(String name, AssociationAttributeInfo attributeInfo, OneToManyAttribute association)
throws LayerException {
EntityCollection collection = entity.getChildCollection(name);
if (association.getValue() == null) {
addChild(new DeleteOneToManyOperation(getMapper(), getEntity(), attributeInfo));
} else {
// sort on create, update, delete
Map
© 2015 - 2025 Weber Informatics LLC | Privacy Policy