org.broadleafcommerce.openadmin.server.service.persistence.module.AdornedTargetListPersistenceModule.java Source code

Java tutorial

Introduction

Here is the source code for org.broadleafcommerce.openadmin.server.service.persistence.module.AdornedTargetListPersistenceModule.java

Source

/*
 * #%L
 * BroadleafCommerce Open Admin Platform
 * %%
 * Copyright (C) 2009 - 2013 Broadleaf Commerce
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.broadleafcommerce.openadmin.server.service.persistence.module;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.exception.SecurityServiceException;
import org.broadleafcommerce.common.exception.ServiceException;
import org.broadleafcommerce.common.presentation.client.OperationType;
import org.broadleafcommerce.common.presentation.client.PersistencePerspectiveItemType;
import org.broadleafcommerce.common.presentation.client.SupportedFieldType;
import org.broadleafcommerce.openadmin.dto.AdornedTargetList;
import org.broadleafcommerce.openadmin.dto.BasicFieldMetadata;
import org.broadleafcommerce.openadmin.dto.CriteriaTransferObject;
import org.broadleafcommerce.openadmin.dto.DynamicResultSet;
import org.broadleafcommerce.openadmin.dto.Entity;
import org.broadleafcommerce.openadmin.dto.FieldMetadata;
import org.broadleafcommerce.openadmin.dto.FilterAndSortCriteria;
import org.broadleafcommerce.openadmin.dto.ForeignKey;
import org.broadleafcommerce.openadmin.dto.MergedPropertyType;
import org.broadleafcommerce.openadmin.dto.PersistencePackage;
import org.broadleafcommerce.openadmin.dto.PersistencePerspective;
import org.broadleafcommerce.openadmin.dto.Property;
import org.broadleafcommerce.openadmin.server.service.persistence.module.criteria.FieldPath;
import org.broadleafcommerce.openadmin.server.service.persistence.module.criteria.FieldPathBuilder;
import org.broadleafcommerce.openadmin.server.service.persistence.module.criteria.FilterMapping;
import org.broadleafcommerce.openadmin.server.service.persistence.module.criteria.Restriction;
import org.broadleafcommerce.openadmin.server.service.persistence.module.criteria.predicate.PredicateProvider;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;

/**
 * @author jfischer
 */
@Component("blAdornedTargetListPersistenceModule")
@Scope("prototype")
public class AdornedTargetListPersistenceModule extends BasicPersistenceModule {

    private static final Log LOG = LogFactory.getLog(AdornedTargetListPersistenceModule.class);

    @Override
    public boolean isCompatible(OperationType operationType) {
        return OperationType.ADORNEDTARGETLIST.equals(operationType);
    }

    @Override
    public void extractProperties(Class<?>[] inheritanceLine,
            Map<MergedPropertyType, Map<String, FieldMetadata>> mergedProperties, List<Property> properties)
            throws NumberFormatException {
        if (mergedProperties.get(MergedPropertyType.ADORNEDTARGETLIST) != null) {
            extractPropertiesFromMetadata(inheritanceLine,
                    mergedProperties.get(MergedPropertyType.ADORNEDTARGETLIST), properties, true,
                    MergedPropertyType.ADORNEDTARGETLIST);
        }
    }

    public List<FilterMapping> getBasicFilterMappings(PersistencePerspective persistencePerspective,
            CriteriaTransferObject cto, Map<String, FieldMetadata> mergedProperties, String cefqcn) {
        return getFilterMappings(persistencePerspective, cto, cefqcn, mergedProperties);
    }

    public List<FilterMapping> getAdornedTargetFilterMappings(PersistencePerspective persistencePerspective,
            CriteriaTransferObject cto, Map<String, FieldMetadata> mergedProperties,
            AdornedTargetList adornedTargetList) throws ClassNotFoundException {
        List<FilterMapping> filterMappings = getFilterMappings(persistencePerspective, cto,
                adornedTargetList.getAdornedTargetEntityClassname(), mergedProperties);
        FilterMapping filterMapping = new FilterMapping()
                .withFieldPath(new FieldPath().withTargetProperty(
                        adornedTargetList.getLinkedObjectPath() + "." + adornedTargetList.getLinkedIdProperty()))
                .withFilterValues(cto.get(adornedTargetList.getCollectionFieldName()).getFilterValues())
                .withRestriction(
                        new Restriction().withPredicateProvider(new PredicateProvider<Serializable, String>() {
                            @Override
                            public Predicate buildPredicate(CriteriaBuilder builder,
                                    FieldPathBuilder fieldPathBuilder, From root, String ceilingEntity,
                                    String fullPropertyName, Path<Serializable> explicitPath,
                                    List<String> directValues) {
                                if (String.class.isAssignableFrom(explicitPath.getJavaType())) {
                                    return builder.equal(explicitPath, directValues.get(0));
                                } else {
                                    return builder.equal(explicitPath, Long.parseLong(directValues.get(0)));
                                }
                            }
                        }));
        filterMappings.add(filterMapping);
        FilterMapping filterMapping2 = new FilterMapping()
                .withFieldPath(new FieldPath().withTargetProperty(
                        adornedTargetList.getTargetObjectPath() + "." + adornedTargetList.getTargetIdProperty()))
                .withFilterValues(cto.get(adornedTargetList.getCollectionFieldName() + "Target").getFilterValues())
                .withRestriction(
                        new Restriction().withPredicateProvider(new PredicateProvider<Serializable, String>() {
                            @Override
                            public Predicate buildPredicate(CriteriaBuilder builder,
                                    FieldPathBuilder fieldPathBuilder, From root, String ceilingEntity,
                                    String fullPropertyName, Path<Serializable> explicitPath,
                                    List<String> directValues) {
                                if (String.class.isAssignableFrom(explicitPath.getJavaType())) {
                                    return builder.equal(explicitPath, directValues.get(0));
                                } else {
                                    return builder.equal(explicitPath, Long.parseLong(directValues.get(0)));
                                }
                            }
                        }));
        filterMappings.add(filterMapping2);

        return filterMappings;
    }

    protected Serializable createPopulatedAdornedTargetInstance(AdornedTargetList adornedTargetList, Entity entity)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException, NumberFormatException,
            InvocationTargetException, NoSuchMethodException, FieldNotAvailableException {
        Serializable instance = (Serializable) Class
                .forName(StringUtils.isEmpty(adornedTargetList.getAdornedTargetEntityPolymorphicType())
                        ? adornedTargetList.getAdornedTargetEntityClassname()
                        : adornedTargetList.getAdornedTargetEntityPolymorphicType())
                .newInstance();
        String targetPath = adornedTargetList.getTargetObjectPath() + "." + adornedTargetList.getTargetIdProperty();
        String linkedPath = adornedTargetList.getLinkedObjectPath() + "." + adornedTargetList.getLinkedIdProperty();
        getFieldManager().setFieldValue(instance, linkedPath,
                Long.valueOf(entity.findProperty(linkedPath).getValue()));

        Object test1 = getFieldManager().getFieldValue(instance, adornedTargetList.getLinkedObjectPath());
        Object test1PersistedObject = persistenceManager.getDynamicEntityDao().retrieve(test1.getClass(),
                Long.valueOf(entity.findProperty(linkedPath).getValue()));
        Assert.isTrue(test1PersistedObject != null, "Entity not found");

        Class<?> type = getFieldManager().getField(instance.getClass(), targetPath).getType();
        if (String.class.isAssignableFrom(type)) {
            getFieldManager().setFieldValue(instance, targetPath, entity.findProperty(targetPath).getValue());
        } else {
            getFieldManager().setFieldValue(instance, targetPath,
                    Long.valueOf(entity.findProperty(targetPath).getValue()));
        }

        Object test2 = getFieldManager().getFieldValue(instance, adornedTargetList.getTargetObjectPath());
        Object test2PersistedObject;
        if (String.class.isAssignableFrom(type)) {
            test2PersistedObject = persistenceManager.getDynamicEntityDao().retrieve(test2.getClass(),
                    entity.findProperty(targetPath).getValue());
        } else {
            test2PersistedObject = persistenceManager.getDynamicEntityDao().retrieve(test2.getClass(),
                    Long.valueOf(entity.findProperty(targetPath).getValue()));
        }
        Assert.isTrue(test2PersistedObject != null, "Entity not found");

        return instance;
    }

    @Override
    public void updateMergedProperties(PersistencePackage persistencePackage,
            Map<MergedPropertyType, Map<String, FieldMetadata>> allMergedProperties) throws ServiceException {
        String ceilingEntityFullyQualifiedClassname = persistencePackage.getCeilingEntityFullyQualifiedClassname();
        try {
            PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
            AdornedTargetList adornedTargetList = (AdornedTargetList) persistencePerspective
                    .getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST);
            if (adornedTargetList != null) {
                Class<?>[] entities = persistenceManager
                        .getPolymorphicEntities(adornedTargetList.getAdornedTargetEntityClassname());
                Map<String, FieldMetadata> joinMergedProperties = persistenceManager.getDynamicEntityDao()
                        .getMergedProperties(adornedTargetList.getAdornedTargetEntityClassname(), entities, null,
                                new String[] {}, new ForeignKey[] {}, MergedPropertyType.ADORNEDTARGETLIST,
                                persistencePerspective.getPopulateToOneFields(),
                                persistencePerspective.getIncludeFields(),
                                persistencePerspective.getExcludeFields(),
                                persistencePerspective.getConfigurationKey(), "");
                String idProp = null;
                for (String key : joinMergedProperties.keySet()) {
                    if (joinMergedProperties.get(key) instanceof BasicFieldMetadata
                            && ((BasicFieldMetadata) joinMergedProperties.get(key))
                                    .getFieldType() == SupportedFieldType.ID) {
                        idProp = key;
                        break;
                    }
                }
                if (idProp != null) {
                    joinMergedProperties.remove(idProp);
                }
                allMergedProperties.put(MergedPropertyType.ADORNEDTARGETLIST, joinMergedProperties);
            }
        } catch (Exception e) {
            throw new ServiceException("Unable to fetch results for " + ceilingEntityFullyQualifiedClassname, e);
        }
    }

    @Override
    public Entity add(PersistencePackage persistencePackage) throws ServiceException {
        String[] customCriteria = persistencePackage.getCustomCriteria();
        if (customCriteria != null && customCriteria.length > 0) {
            LOG.warn(
                    "custom persistence handlers and custom criteria not supported for add types other than BASIC");
        }
        PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
        String ceilingEntityFullyQualifiedClassname = persistencePackage.getCeilingEntityFullyQualifiedClassname();
        Entity entity = persistencePackage.getEntity();
        AdornedTargetList adornedTargetList = (AdornedTargetList) persistencePerspective
                .getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST);
        if (!adornedTargetList.getMutable()) {
            throw new SecurityServiceException("Field is not mutable");
        }
        Entity payload;
        try {
            Class<?>[] entities = persistenceManager.getPolymorphicEntities(ceilingEntityFullyQualifiedClassname);
            Map<String, FieldMetadata> mergedPropertiesTarget = persistenceManager.getDynamicEntityDao()
                    .getMergedProperties(ceilingEntityFullyQualifiedClassname, entities, null,
                            persistencePerspective.getAdditionalNonPersistentProperties(),
                            persistencePerspective.getAdditionalForeignKeys(), MergedPropertyType.PRIMARY,
                            persistencePerspective.getPopulateToOneFields(),
                            persistencePerspective.getIncludeFields(), persistencePerspective.getExcludeFields(),
                            persistencePerspective.getConfigurationKey(), "");
            Class<?>[] entities2 = persistenceManager
                    .getPolymorphicEntities(adornedTargetList.getAdornedTargetEntityClassname());
            Map<String, FieldMetadata> mergedProperties = persistenceManager.getDynamicEntityDao()
                    .getMergedProperties(adornedTargetList.getAdornedTargetEntityClassname(), entities2, null,
                            new String[] {}, new ForeignKey[] {}, MergedPropertyType.ADORNEDTARGETLIST, false,
                            new String[] {}, new String[] {}, null, "");

            CriteriaTransferObject ctoInserted = new CriteriaTransferObject();
            FilterAndSortCriteria filterCriteriaInsertedLinked = ctoInserted
                    .get(adornedTargetList.getCollectionFieldName());
            String linkedPath;
            String targetPath;
            if (adornedTargetList.getInverse()) {
                linkedPath = adornedTargetList.getTargetObjectPath() + "."
                        + adornedTargetList.getTargetIdProperty();
                targetPath = adornedTargetList.getLinkedObjectPath() + "."
                        + adornedTargetList.getLinkedIdProperty();
            } else {
                targetPath = adornedTargetList.getTargetObjectPath() + "."
                        + adornedTargetList.getTargetIdProperty();
                linkedPath = adornedTargetList.getLinkedObjectPath() + "."
                        + adornedTargetList.getLinkedIdProperty();
            }
            filterCriteriaInsertedLinked.setFilterValue(
                    entity.findProperty(adornedTargetList.getInverse() ? targetPath : linkedPath).getValue());
            FilterAndSortCriteria filterCriteriaInsertedTarget = ctoInserted
                    .get(adornedTargetList.getCollectionFieldName() + "Target");
            filterCriteriaInsertedTarget.setFilterValue(
                    entity.findProperty(adornedTargetList.getInverse() ? linkedPath : targetPath).getValue());
            List<FilterMapping> filterMappingsInserted = getAdornedTargetFilterMappings(persistencePerspective,
                    ctoInserted, mergedProperties, adornedTargetList);
            List<Serializable> recordsInserted = getPersistentRecords(
                    adornedTargetList.getAdornedTargetEntityClassname(), filterMappingsInserted,
                    ctoInserted.getFirstResult(), ctoInserted.getMaxResults());
            if (recordsInserted.size() > 0) {
                payload = getRecords(mergedPropertiesTarget, recordsInserted, mergedProperties,
                        adornedTargetList.getTargetObjectPath())[0];
            } else {
                Serializable instance = createPopulatedAdornedTargetInstance(adornedTargetList, entity);
                instance = createPopulatedInstance(instance, entity, mergedProperties, false,
                        persistencePackage.isValidateUnsubmittedProperties());
                instance = createPopulatedInstance(instance, entity, mergedPropertiesTarget, false,
                        persistencePackage.isValidateUnsubmittedProperties());
                FieldManager fieldManager = getFieldManager();
                if (fieldManager.getField(instance.getClass(), "id") != null) {
                    fieldManager.setFieldValue(instance, "id", null);
                }
                if (adornedTargetList.getSortField() != null) {
                    // Construct a query that gets the last element in the join list ordered by the sort property. This will
                    // ensure that the new record is always the last element in the list
                    CriteriaTransferObject cto = new CriteriaTransferObject();
                    FilterAndSortCriteria filterCriteria = cto.get(adornedTargetList.getCollectionFieldName());
                    filterCriteria.setFilterValue(entity
                            .findProperty(adornedTargetList.getInverse() ? targetPath : linkedPath).getValue());
                    FilterAndSortCriteria sortCriteria = cto.get(adornedTargetList.getSortField());
                    // criteria for which way to sort should be the opposite of how it is normally sorted so that it is
                    // always inserted at the end
                    sortCriteria.setSortAscending(!adornedTargetList.getSortAscending());
                    List<FilterMapping> filterMappings = getAdornedTargetFilterMappings(persistencePerspective, cto,
                            mergedProperties, adornedTargetList);
                    List<Serializable> joinList = getPersistentRecords(
                            adornedTargetList.getAdornedTargetEntityClassname(), filterMappings, 0, 1);

                    Object adornedLastOrdering = null;
                    if (CollectionUtils.isNotEmpty(joinList)) {
                        adornedLastOrdering = fieldManager.getFieldValue(joinList.get(0),
                                adornedTargetList.getSortField());
                    }
                    Field sortFieldDef = fieldManager.getField(instance.getClass(),
                            adornedTargetList.getSortField());
                    int add = (adornedLastOrdering == null) ? 0 : 1;
                    if (sortFieldDef.getType().isAssignableFrom(Long.class)) {
                        adornedLastOrdering = (adornedLastOrdering == null) ? new Long(0) : adornedLastOrdering;
                        fieldManager.setFieldValue(instance, adornedTargetList.getSortField(),
                                new Long(((Long) adornedLastOrdering).longValue() + add));
                    } else if (sortFieldDef.getType().isAssignableFrom(Integer.class)) {
                        adornedLastOrdering = (adornedLastOrdering == null) ? new Integer(0) : adornedLastOrdering;
                        fieldManager.setFieldValue(instance, adornedTargetList.getSortField(),
                                new Integer(((Integer) adornedLastOrdering).intValue() + add));
                    } else if (sortFieldDef.getType().isAssignableFrom(BigDecimal.class)) {
                        adornedLastOrdering = (adornedLastOrdering == null) ? BigDecimal.ZERO : adornedLastOrdering;
                        fieldManager.setFieldValue(instance, adornedTargetList.getSortField(),
                                ((BigDecimal) adornedLastOrdering).add(new BigDecimal(add)));
                    }
                }
                instance = persistenceManager.getDynamicEntityDao().merge(instance);
                persistenceManager.getDynamicEntityDao().clear();

                List<Serializable> recordsInserted2 = getPersistentRecords(
                        adornedTargetList.getAdornedTargetEntityClassname(), filterMappingsInserted,
                        ctoInserted.getFirstResult(), ctoInserted.getMaxResults());

                payload = getRecords(mergedPropertiesTarget, recordsInserted2, mergedProperties,
                        adornedTargetList.getTargetObjectPath())[0];
            }
        } catch (Exception e) {
            throw new ServiceException("Problem adding new entity : " + e.getMessage(), e);
        }

        return payload;
    }

    @Override
    public Entity update(PersistencePackage persistencePackage) throws ServiceException {
        String[] customCriteria = persistencePackage.getCustomCriteria();
        if (customCriteria != null && customCriteria.length > 0) {
            LOG.warn(
                    "custom persistence handlers and custom criteria not supported for update types other than BASIC");
        }
        PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
        Entity entity = persistencePackage.getEntity();
        AdornedTargetList adornedTargetList = (AdornedTargetList) persistencePerspective
                .getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST);
        if (!adornedTargetList.getMutable()) {
            throw new SecurityServiceException("Field is not mutable");
        }
        try {
            AdornedTargetRetrieval adornedTargetRetrieval = new AdornedTargetRetrieval(persistencePackage, entity,
                    adornedTargetList).invokeForUpdate();
            List<Serializable> records = adornedTargetRetrieval.getRecords();

            Assert.isTrue(!CollectionUtils.isEmpty(records), "Entity not found");

            int index = adornedTargetRetrieval.getIndex();
            Map<String, FieldMetadata> mergedProperties = adornedTargetRetrieval.getMergedProperties();
            FieldManager fieldManager = getFieldManager();

            Serializable myRecord;
            if (adornedTargetList.getSortField() != null
                    && entity.findProperty(adornedTargetList.getSortField()).getValue() != null) {
                myRecord = records.get(index);

                BigDecimal requestedSequence = new BigDecimal(
                        entity.findProperty(adornedTargetList.getSortField()).getValue());
                BigDecimal previousSequence = new BigDecimal(String
                        .valueOf(getFieldManager().getFieldValue(myRecord, adornedTargetList.getSortField())));

                if (!previousSequence.equals(requestedSequence)) {
                    // Sequence has changed. Rebalance the list
                    myRecord = records.remove(index);
                    myRecord = createPopulatedInstance(myRecord, entity, mergedProperties, false);
                    if (CollectionUtils.isEmpty(records)) {
                        records.add(myRecord);
                    } else {
                        records.add(requestedSequence.intValue() - 1, myRecord);
                    }

                    index = 1;
                    Class<?> type = fieldManager.getField(myRecord.getClass(), adornedTargetList.getSortField())
                            .getType();
                    boolean isBigDecimal = BigDecimal.class.isAssignableFrom(type);
                    for (Serializable record : records) {
                        fieldManager.setFieldValue(record, adornedTargetList.getSortField(),
                                isBigDecimal ? new BigDecimal(index) : Long.valueOf(index));
                        index++;
                    }
                }
            } else {
                myRecord = records.get(index);
            }

            String ceilingEntityFullyQualifiedClassname = persistencePackage
                    .getCeilingEntityFullyQualifiedClassname();
            Class<?>[] entities = persistenceManager.getPolymorphicEntities(ceilingEntityFullyQualifiedClassname);
            Map<String, FieldMetadata> mergedPropertiesTarget = persistenceManager.getDynamicEntityDao()
                    .getMergedProperties(ceilingEntityFullyQualifiedClassname, entities, null,
                            persistencePerspective.getAdditionalNonPersistentProperties(),
                            persistencePerspective.getAdditionalForeignKeys(), MergedPropertyType.PRIMARY,
                            persistencePerspective.getPopulateToOneFields(),
                            persistencePerspective.getIncludeFields(), persistencePerspective.getExcludeFields(),
                            persistencePerspective.getConfigurationKey(), "");
            myRecord = createPopulatedInstance(myRecord, entity, mergedProperties, false);
            myRecord = persistenceManager.getDynamicEntityDao().merge(myRecord);
            List<Serializable> myList = new ArrayList<Serializable>();
            myList.add(myRecord);
            Entity[] payload = getRecords(mergedPropertiesTarget, myList, mergedProperties,
                    adornedTargetList.getTargetObjectPath());
            entity = payload[0];

            return entity;
        } catch (Exception e) {
            throw new ServiceException("Problem updating entity : " + e.getMessage(), e);
        }
    }

    @Override
    public void remove(PersistencePackage persistencePackage) throws ServiceException {
        String[] customCriteria = persistencePackage.getCustomCriteria();
        if (customCriteria != null && customCriteria.length > 0) {
            LOG.warn(
                    "custom persistence handlers and custom criteria not supported for remove types other than BASIC");
        }
        PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
        Entity entity = persistencePackage.getEntity();
        try {
            AdornedTargetList adornedTargetList = (AdornedTargetList) persistencePerspective
                    .getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST);
            if (!adornedTargetList.getMutable()) {
                throw new SecurityServiceException("Field is not mutable");
            }
            Class<?>[] entities = persistenceManager
                    .getPolymorphicEntities(adornedTargetList.getAdornedTargetEntityClassname());
            Map<String, FieldMetadata> mergedProperties = persistenceManager.getDynamicEntityDao()
                    .getMergedProperties(adornedTargetList.getAdornedTargetEntityClassname(), entities, null,
                            new String[] {}, new ForeignKey[] {}, MergedPropertyType.ADORNEDTARGETLIST, false,
                            new String[] {}, new String[] {}, null, "");
            CriteriaTransferObject ctoInserted = new CriteriaTransferObject();
            FilterAndSortCriteria filterCriteriaInsertedLinked = ctoInserted
                    .get(adornedTargetList.getCollectionFieldName());
            filterCriteriaInsertedLinked.setFilterValue(entity
                    .findProperty(
                            adornedTargetList.getLinkedObjectPath() + "." + adornedTargetList.getLinkedIdProperty())
                    .getValue());
            FilterAndSortCriteria filterCriteriaInsertedTarget = ctoInserted
                    .get(adornedTargetList.getCollectionFieldName() + "Target");
            filterCriteriaInsertedTarget.setFilterValue(entity
                    .findProperty(
                            adornedTargetList.getTargetObjectPath() + "." + adornedTargetList.getTargetIdProperty())
                    .getValue());
            List<FilterMapping> filterMappings = getAdornedTargetFilterMappings(persistencePerspective, ctoInserted,
                    mergedProperties, adornedTargetList);
            List<Serializable> recordsInserted = getPersistentRecords(
                    adornedTargetList.getAdornedTargetEntityClassname(), filterMappings,
                    ctoInserted.getFirstResult(), ctoInserted.getMaxResults());

            Assert.isTrue(!CollectionUtils.isEmpty(recordsInserted), "Entity not found");

            persistenceManager.getDynamicEntityDao().remove(recordsInserted.get(0));
        } catch (Exception e) {
            throw new ServiceException("Problem removing entity : " + e.getMessage(), e);
        }
    }

    @Override
    public DynamicResultSet fetch(PersistencePackage persistencePackage, CriteriaTransferObject cto)
            throws ServiceException {
        PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
        String ceilingEntityFullyQualifiedClassname = persistencePackage.getCeilingEntityFullyQualifiedClassname();
        AdornedTargetList adornedTargetList = (AdornedTargetList) persistencePerspective
                .getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST);
        Entity[] payload;
        int totalRecords;
        try {
            Class<?>[] entities = persistenceManager.getPolymorphicEntities(ceilingEntityFullyQualifiedClassname);
            Map<String, FieldMetadata> mergedPropertiesTarget = persistenceManager.getDynamicEntityDao()
                    .getMergedProperties(ceilingEntityFullyQualifiedClassname, entities, null,
                            persistencePerspective.getAdditionalNonPersistentProperties(),
                            persistencePerspective.getAdditionalForeignKeys(), MergedPropertyType.PRIMARY,
                            persistencePerspective.getPopulateToOneFields(),
                            persistencePerspective.getIncludeFields(), persistencePerspective.getExcludeFields(),
                            persistencePerspective.getConfigurationKey(), "");

            AdornedTargetRetrieval adornedTargetRetrieval = new AdornedTargetRetrieval(persistencePackage,
                    adornedTargetList, cto).invokeForFetch();
            List<Serializable> records = adornedTargetRetrieval.getRecords();
            Map<String, FieldMetadata> mergedProperties = adornedTargetRetrieval.getMergedProperties();
            payload = getRecords(mergedPropertiesTarget, records, mergedProperties,
                    adornedTargetList.getTargetObjectPath());
            totalRecords = getTotalRecords(adornedTargetList.getAdornedTargetEntityClassname(),
                    adornedTargetRetrieval.getFilterMappings());
        } catch (Exception e) {
            throw new ServiceException(
                    "Unable to fetch results for " + adornedTargetList.getAdornedTargetEntityClassname(), e);
        }

        DynamicResultSet results = new DynamicResultSet(null, payload, totalRecords);

        return results;
    }

    public class AdornedTargetRetrieval {
        private final PersistencePackage persistencePackage;
        private final PersistencePerspective persistencePerspective;
        private Entity entity;
        private final AdornedTargetList adornedTargetList;
        private Map<String, FieldMetadata> mergedProperties;
        private List<Serializable> records;
        private int index;
        private List<FilterMapping> filterMappings;
        private final CriteriaTransferObject cto;

        // This constructor is used by the update method
        public AdornedTargetRetrieval(PersistencePackage persistencePackage, Entity entity,
                AdornedTargetList adornedTargetList) {
            this(persistencePackage, adornedTargetList, new CriteriaTransferObject());
            this.entity = entity;
        }

        // This constructor is used by the fetch method
        public AdornedTargetRetrieval(PersistencePackage persistencePackage, AdornedTargetList adornedTargetList,
                CriteriaTransferObject cto) {
            this.persistencePackage = persistencePackage;
            this.persistencePerspective = persistencePackage.getPersistencePerspective();
            this.adornedTargetList = adornedTargetList;
            this.cto = cto;
        }

        public Map<String, FieldMetadata> getMergedProperties() {
            return mergedProperties;
        }

        public List<Serializable> getRecords() {
            return records;
        }

        public int getIndex() {
            return index;
        }

        public List<FilterMapping> getFilterMappings() {
            return filterMappings;
        }

        public AdornedTargetRetrieval invokeForFetch()
                throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
                InvocationTargetException, FieldNotAvailableException, NoSuchFieldException {
            invokeInternal();
            return this;
        }

        public AdornedTargetRetrieval invokeForUpdate()
                throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
                InvocationTargetException, FieldNotAvailableException, NoSuchFieldException {
            FilterAndSortCriteria filterCriteria = cto.get(adornedTargetList.getCollectionFieldName());
            filterCriteria.setFilterValue(entity
                    .findProperty(
                            adornedTargetList.getLinkedObjectPath() + "." + adornedTargetList.getLinkedIdProperty())
                    .getValue());

            invokeInternal();

            index = 0;
            Long myEntityId = Long.valueOf(entity
                    .findProperty(
                            adornedTargetList.getTargetObjectPath() + "." + adornedTargetList.getTargetIdProperty())
                    .getValue());
            FieldManager fieldManager = getFieldManager();
            for (Serializable record : records) {
                Long targetId = (Long) fieldManager.getFieldValue(record,
                        adornedTargetList.getTargetObjectPath() + "." + adornedTargetList.getTargetIdProperty());
                if (myEntityId.equals(targetId)) {
                    break;
                }
                index++;
            }

            return this;
        }

        private void invokeInternal() throws ClassNotFoundException {
            if (adornedTargetList.getSortField() != null) {
                FilterAndSortCriteria sortCriteria = cto.get(adornedTargetList.getSortField());
                sortCriteria.setSortAscending(adornedTargetList.getSortAscending());
            }

            Class<?>[] entities = persistenceManager
                    .getPolymorphicEntities(adornedTargetList.getAdornedTargetEntityClassname());
            mergedProperties = persistenceManager.getDynamicEntityDao().getMergedProperties(
                    adornedTargetList.getAdornedTargetEntityClassname(), entities, null, new String[] {},
                    new ForeignKey[] {}, MergedPropertyType.ADORNEDTARGETLIST,
                    persistencePerspective.getPopulateToOneFields(), persistencePerspective.getIncludeFields(),
                    persistencePerspective.getExcludeFields(), persistencePerspective.getConfigurationKey(), "");
            filterMappings = getAdornedTargetFilterMappings(persistencePerspective, cto, mergedProperties,
                    adornedTargetList);

            if (CollectionUtils.isNotEmpty(cto.getAdditionalFilterMappings())) {
                filterMappings.addAll(cto.getAdditionalFilterMappings());
            }

            String ceilingEntityFullyQualifiedClassname = persistencePackage
                    .getCeilingEntityFullyQualifiedClassname();
            Class<?>[] entities2 = persistenceManager.getPolymorphicEntities(ceilingEntityFullyQualifiedClassname);
            Map<String, FieldMetadata> mergedPropertiesTarget = persistenceManager.getDynamicEntityDao()
                    .getMergedProperties(ceilingEntityFullyQualifiedClassname, entities2, null,
                            persistencePerspective.getAdditionalNonPersistentProperties(),
                            persistencePerspective.getAdditionalForeignKeys(), MergedPropertyType.PRIMARY,
                            persistencePerspective.getPopulateToOneFields(),
                            persistencePerspective.getIncludeFields(), persistencePerspective.getExcludeFields(),
                            persistencePerspective.getConfigurationKey(), "");

            // We need to make sure that the target merged properties have the target object path prefix
            Map<String, FieldMetadata> convertedMergedPropertiesTarget = new HashMap<String, FieldMetadata>();
            String prefix = adornedTargetList.getTargetObjectPath();
            for (Entry<String, FieldMetadata> entry : mergedPropertiesTarget.entrySet()) {
                convertedMergedPropertiesTarget.put(prefix + "." + entry.getKey(), entry.getValue());
            }

            records = getPersistentRecords(adornedTargetList.getAdornedTargetEntityClassname(), filterMappings,
                    cto.getFirstResult(), cto.getMaxResults());
        }
    }
}