Package org.geomajas.internal.layer.entity

Source Code of org.geomajas.internal.layer.entity.EntityAttributeServiceImpl$RemoveManyValue

/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2011 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.AssociationAttributeInfo;
import org.geomajas.configuration.AttributeInfo;
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.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;

  public void setAttributes(Object object, FeatureInfo featureInfo, EntityMapper mapper,
      Map<String, Attribute<?>> attributes) throws LayerException {
    UpdateEntityOperation operation = new UpdateEntityOperation(mapper, mapper.asEntity(object), featureInfo,
        attributes);
    operation.execute();
  }

  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<String> names = new HashSet<String>();
    // check for id (support for backwards compatibility)
    if (featureInfo.getIdentifier().getName().equals(name)) {
      try {
        return dtoConverterService.toDto(entity == null ? null : entity.getAttribute(name),
            featureInfo.getIdentifier());
      } catch (GeomajasException e) {
        throw new LayerException(e, ExceptionCode.CONVERSION_PROBLEM);
      }
    }
    for (AttributeInfo attributeInfo : featureInfo.getAttributes()) {
      names.add(attributeInfo.getName());
      if (attributeInfo.getName().equals(name)) {
        if (attributeInfo instanceof AssociationAttributeInfo) {
          associationAttributeInfo = (AssociationAttributeInfo) attributeInfo;
        } else if (attributeInfo instanceof PrimitiveAttributeInfo) {
          // primitive, return the attribute
          PrimitiveAttributeInfo primitiveAttributeInfo = (PrimitiveAttributeInfo) attributeInfo;
          try {
            return dtoConverterService.toDto(entity == null ? null : entity.getAttribute(name),
                primitiveAttributeInfo);
          } catch (GeomajasException e) {
            throw new LayerException(e, ExceptionCode.CONVERSION_PROBLEM);
          }
        }
      }
    }
    // 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<AssociationValue> values = new ArrayList<AssociationValue>();
            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;
    }
    PrimitiveAttributeInfo 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<String, Attribute<?>> attributes = new HashMap<String, Attribute<?>>();
    for (AttributeInfo attributeInfo : childInfo.getAttributes()) {
      attributes.put(attributeInfo.getName(),
          getRecursiveAttribute(entity, childInfo, new String[] { attributeInfo.getName() }));
    }
    AssociationValue value = new AssociationValue(id, attributes, false);
    return value;
  }

  /**
   * Operation interface.
   *
   * @author Jan De Moerloose
   *
   */
  public interface Operation {

    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<String, Attribute<?>> attributes) throws LayerException {
      super(mapper, entity, featureInfo, attributes);
      addChildOperations();
    }

  }

  /**
   * Operation for creating a {@link ManyToOneAttribute}.
   *
   * @author Jan De Moerloose
   *
   */
  class CreateManyToOneOperation extends AbstractOperation {

    private Entity parent;

    private 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();
    }

    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 Entity parent;

    private 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();
    }

    public void execute() throws LayerException {
      parent.setChild(name, null);
    }
  }

  /**
   * Operation for deleting a {@link OneToManyAttribute}.
   *
   * @author Jan De Moerloose
   *
   */
  class DeleteOneToManyOperation extends AbstractOperation {

    private Entity parent;

    private 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();
    }

    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 Entity toDelete;

    private EntityCollection existing;

    public RemoveManyValue(EntityMapper mapper, EntityCollection existing, Entity toDelete) throws LayerException {
      super(mapper, null, null, null);
      this.toDelete = toDelete;
      this.existing = existing;
    }

    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 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();
    }

    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 Map<String, PrimitiveAttribute<?>> primitives = new HashMap<String, PrimitiveAttribute<?>>();

    private Map<String, Attribute<?>> attributes = new HashMap<String, Attribute<?>>();

    private FeatureInfo featureInfo;

    private EntityMapper mapper;

    private List<Operation> children = new ArrayList<Operation>();

    public AbstractOperation(EntityMapper mapper, FeatureInfo featureInfo, Map<String, Attribute<?>> attributes)
        throws LayerException {
      this(mapper, null, featureInfo, attributes);
    }

    public AbstractOperation(EntityMapper mapper, Entity entity, FeatureInfo featureInfo,
        Map<String, Attribute<?>> attributes) throws LayerException {
      this.mapper = mapper;
      this.entity = entity;
      this.featureInfo = featureInfo;
      this.attributes = attributes;
    }

    protected void addChildOperations() throws LayerException {
      Map<String, AssociationAttributeInfo> associationMap = new HashMap<String, AssociationAttributeInfo>();
      Map<String, PrimitiveAttributeInfo> primitiveMap = new HashMap<String, PrimitiveAttributeInfo>();
      for (AttributeInfo attributeInfo : featureInfo.getAttributes()) {
        if (attributeInfo.isEditable()) {
          if (attributeInfo instanceof AssociationAttributeInfo) {
            associationMap.put(attributeInfo.getName(), (AssociationAttributeInfo) attributeInfo);
          } else if (attributeInfo instanceof PrimitiveAttributeInfo) {
            primitiveMap.put(attributeInfo.getName(), (PrimitiveAttributeInfo) attributeInfo);
          }
        }
      }
      for (Map.Entry<String, Attribute<?>> entry : attributes.entrySet()) {
        Attribute<?> attribute = entry.getValue();
        if (primitiveMap.containsKey(entry.getKey())) {
          addPrimitive(entry.getKey(), (PrimitiveAttribute<?>) attribute);
        } else if (associationMap.containsKey(entry.getKey())) {
          AssociationAttribute<?> association = (AssociationAttribute<?>) attribute;
          AssociationAttributeInfo associationAttributeInfo = associationMap.get(entry.getKey());
          switch (associationAttributeInfo.getType()) {
            case MANY_TO_ONE:
              association = (association == null ? new ManyToOneAttribute() : association);
              addManyToOne(entry.getKey(), associationMap.get(entry.getKey()),
                  (ManyToOneAttribute) association);
              break;
            case ONE_TO_MANY:
              association = (association == null ? new OneToManyAttribute() : association);
              addOneToMany(entry.getKey(), associationMap.get(entry.getKey()),
                  (OneToManyAttribute) association);
              break;
          }
        }
      }
    }

    public void execute() throws LayerException {
      for (Map.Entry<String, PrimitiveAttribute<?>> 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<Object, Entity> existingMap = new HashMap<Object, Entity>();
        Set<Object> oldIds = new HashSet<Object>();
        if (collection != null) {
          for (Entity entity : collection) {
            String idName = attributeInfo.getFeature().getIdentifier().getName();
            Object id = entity.getId(idName);
            oldIds.add(id);
            existingMap.put(id, entity);
          }
        }
        Set<Object> newIds = new HashSet<Object>();
        for (AssociationValue associationValue : association.getValue()) {
          if (associationValue.getId() != null && !associationValue.getId().isEmpty()) {
            newIds.add(associationValue.getId().getValue());
          }
        }
        Set<Object> updateIds = new HashSet<Object>(newIds);
        Set<Object> deleteIds = new HashSet<Object>(oldIds);
        updateIds.retainAll(oldIds);
        deleteIds.removeAll(newIds);

        for (AssociationValue associationValue : association.getValue()) {
          Object id = null;
          if (associationValue.getId() != null) {
            id = associationValue.getId().getValue();
          }
          if (updateIds.contains(id)) {
            addChild(new UpdateManyValue(getMapper(), existingMap.get(id), attributeInfo,
                associationValue));
          } else {
            addChild(new AddManyValue(getMapper(), collection, attributeInfo,
                associationValue));
          }
        }
        for (Object id : deleteIds) {
          addChild(new RemoveManyValue(getMapper(), collection, existingMap.get(id)));
        }
      }
    }

    public void addManyToOne(String name, AssociationAttributeInfo attributeInfo, ManyToOneAttribute association)
        throws LayerException {
      Entity existing = entity.getChild(name);
      AssociationValue value = association.getValue();
      if (value == null) {
        addChild(new DeleteManyToOneOperation(getMapper(), getEntity(), attributeInfo));
      } else if (existing == null) {
        addChild(new CreateManyToOneOperation(getMapper(), getEntity(), attributeInfo, association.getValue()));
      } else {
        Object id = existing.getId(attributeInfo.getFeature().getIdentifier().getName());
        if (value.getId() == null || value.getId().isEmpty() || !value.getId().getValue().equals(id)) {
          addChild(new CreateManyToOneOperation(getMapper(), getEntity(), attributeInfo,
              association.getValue()));
        } else {
          addChild(new UpdateManyToOneOperation(getMapper(), attributeInfo, existing, value));
        }
      }
    }

    public void addPrimitive(String name, PrimitiveAttribute attribute) {
      primitives.put(name, attribute);
    }

  }

}
TOP

Related Classes of org.geomajas.internal.layer.entity.EntityAttributeServiceImpl$RemoveManyValue

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.