<commit_before>/*
 * #%L
 * BroadleafCommerce Admin Module
 * %%
 * 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.admin.server.service.handler;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.admin.server.service.extension.ProductCustomPersistenceHandlerExtensionManager;
import org.broadleafcommerce.common.exception.ExceptionHelper;
import org.broadleafcommerce.common.exception.ServiceException;
import org.broadleafcommerce.common.extension.ExtensionResultStatusType;
import org.broadleafcommerce.common.presentation.client.OperationType;
import org.broadleafcommerce.common.service.ParentCategoryLegacyModeService;
import org.broadleafcommerce.common.service.ParentCategoryLegacyModeServiceImpl;
import org.broadleafcommerce.common.util.dao.QueryUtils;
import org.broadleafcommerce.core.catalog.domain.Category;
import org.broadleafcommerce.core.catalog.domain.CategoryProductXref;
import org.broadleafcommerce.core.catalog.domain.CategoryProductXrefImpl;
import org.broadleafcommerce.core.catalog.domain.Product;
import org.broadleafcommerce.core.catalog.domain.ProductBundle;
import org.broadleafcommerce.core.catalog.domain.ProductImpl;
import org.broadleafcommerce.core.catalog.domain.Sku;
import org.broadleafcommerce.core.catalog.service.CatalogService;
import org.broadleafcommerce.core.catalog.service.type.ProductBundlePricingModelType;
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.PersistencePackage;
import org.broadleafcommerce.openadmin.dto.PersistencePerspective;
import org.broadleafcommerce.openadmin.server.dao.DynamicEntityDao;
import org.broadleafcommerce.openadmin.server.service.handler.CustomPersistenceHandlerAdapter;
import org.broadleafcommerce.openadmin.server.service.persistence.module.EmptyFilterValues;
import org.broadleafcommerce.openadmin.server.service.persistence.module.InspectHelper;
import org.broadleafcommerce.openadmin.server.service.persistence.module.RecordHelper;
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.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.From;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;

/**
 * @author <NAME>
 */
@Component("blProductCustomPersistenceHandler")
public class ProductCustomPersistenceHandler extends CustomPersistenceHandlerAdapter {
    
    @Resource(name = "blCatalogService")
    protected CatalogService catalogService;

    @Resource(name = "blProductCustomPersistenceHandlerExtensionManager")
    protected ProductCustomPersistenceHandlerExtensionManager extensionManager;
    
    @Resource(name = "blParentCategoryLegacyModeService")
    protected ParentCategoryLegacyModeService parentCategoryLegacyModeService;

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

    @Override
    public Boolean canHandleAdd(PersistencePackage persistencePackage) {
        String ceilingEntityFullyQualifiedClassname = persistencePackage.getCeilingEntityFullyQualifiedClassname();
        String[] customCriteria = persistencePackage.getCustomCriteria();
        return !ArrayUtils.isEmpty(customCriteria) && "productDirectEdit".equals(customCriteria[0]) && Product.class.getName().equals(ceilingEntityFullyQualifiedClassname);
    }

    @Override
    public Boolean canHandleUpdate(PersistencePackage persistencePackage) {
        return canHandleAdd(persistencePackage);
    }

    @Override
    public Boolean canHandleRemove(PersistencePackage persistencePackage) {
        return canHandleAdd(persistencePackage);
    }

    @Override
    public Boolean canHandleFetch(PersistencePackage persistencePackage) {
        return canHandleAdd(persistencePackage);
    }

    @Override
    public Boolean canHandleInspect(PersistencePackage persistencePackage) {
        return canHandleAdd(persistencePackage);
    }

    @Override
    public DynamicResultSet inspect(PersistencePackage persistencePackage, DynamicEntityDao dynamicEntityDao, InspectHelper helper) throws ServiceException {
        Map<String, FieldMetadata> md = getMetadata(persistencePackage, helper);

        if (!isDefaultCategoryLegacyMode()) {
            md.remove("allParentCategoryXrefs");

            BasicFieldMetadata defaultCategory = ((BasicFieldMetadata) md.get("defaultCategory"));
            defaultCategory.setFriendlyName("ProductImpl_Parent_Category");
        }

        return getResultSet(persistencePackage, helper, md);
    }

    @Override
    public DynamicResultSet fetch(PersistencePackage persistencePackage, CriteriaTransferObject cto, DynamicEntityDao
            dynamicEntityDao, RecordHelper helper) throws ServiceException {
        
        boolean legacy = parentCategoryLegacyModeService.isLegacyMode();
        
        //the following code applies when filters are present only:
        //"legacy" means that the parent category filter still utilizes Product.defaultCategory as the field to be matched
        //against the categories chosen in the listGrid filter. The default behavior up to this point, assumes the "legacy" mode. 
        //This means that one of the FilterAndSortCriterias will try to match the chosen values against "defaultCategory".
        //If "legacy" is false, we remove that FilterAndSortCriteria from the CTO, and inject a new FilterMapping in cto.additionalFilterMappings,
        //which seeks matching values in  allParentCategoryXRefs instead
        if (!legacy) {
            FilterAndSortCriteria fsc = cto.getCriteriaMap().get("defaultCategory");
            if (fsc != null) {
                List<String> filterValues = fsc.getFilterValues();
                cto.getCriteriaMap().remove("defaultCategory");
                FilterMapping filterMapping = new FilterMapping()
                        .withFieldPath(new FieldPath().withTargetProperty("allParentCategoryXrefs.category.id"))
                        .withDirectFilterValues(filterValues)
                        .withRestriction(new Restriction()
                                .withPredicateProvider(new PredicateProvider() {
                                    @Override
                                    public Predicate buildPredicate(CriteriaBuilder builder, FieldPathBuilder fieldPathBuilder,
                                            From root, String ceilingEntity,
                                            String fullPropertyName, Path explicitPath, List directValues) {

                                        //the property to be matched against (allParentCategoryXrefs.category.id) comes as "explicitPath"
                                        //the specifics of what values are acceptable (those given as filter values, that in addition are defaults)
                                        //are resolved in a sub-query
                                        Subquery<Long> sub = fieldPathBuilder.getCriteria().subquery(Long.class);
                                        Root<CategoryProductXrefImpl> subRoot = sub.from(CategoryProductXrefImpl.class);
                                        sub.select(subRoot.get("category").get("id").as(Long.class));
                                        List<Predicate> subRestrictions = new ArrayList<Predicate>();
                                        subRestrictions.add(builder.equal(subRoot.get("defaultReference"), Boolean.TRUE));
                                        subRestrictions.add(subRoot.get("category").get("id").in(directValues));
                                        //archived?
                                        QueryUtils.notArchived(builder, subRestrictions, subRoot, "archiveStatus");

                                        sub.where(subRestrictions.toArray(new Predicate[subRestrictions.size()]));

                                        return explicitPath.in(sub);
                                    }
                                }));

                cto.getAdditionalFilterMappings().add(filterMapping);
            }
        }

        cto.getNonCountAdditionalFilterMappings().add(new FilterMapping()
                .withDirectFilterValues(new EmptyFilterValues())
                .withRestriction(new Restriction()
                                .withPredicateProvider(new PredicateProvider() {
                                    public Predicate buildPredicate(CriteriaBuilder builder,
                                                                    FieldPathBuilder fieldPathBuilder, From root,
                                                                    String ceilingEntity,
                                                                    String fullPropertyName, Path explicitPath,
                                                                    List directValues) {
                                        root.fetch("defaultSku", JoinType.LEFT);
                                        root.fetch("defaultCategory", JoinType.LEFT);
                                        return null;
                                    }
                                })
                ));
        return helper.getCompatibleModule(OperationType.BASIC).fetch(persistencePackage, cto);
    }

    @Override
    public Entity add(PersistencePackage persistencePackage, DynamicEntityDao dynamicEntityDao, RecordHelper helper) throws ServiceException {
        Entity entity  = persistencePackage.getEntity();
        try {
            PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
            Product adminInstance = (Product) Class.forName(entity.getType()[0]).newInstance();
            Map<String, FieldMetadata> adminProperties = helper.getSimpleMergedProperties(Product.class.getName(), persistencePerspective);

            if (adminInstance instanceof ProductBundle) {<commit_msg>BroadleafCommerce/QA#989 - Fix the parameter used for the IN clause in the query<commit_after>/*
 * #%L
 * BroadleafCommerce Admin Module
 * %%
 * 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.admin.server.service.handler;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.admin.server.service.extension.ProductCustomPersistenceHandlerExtensionManager;
import org.broadleafcommerce.common.exception.ExceptionHelper;
import org.broadleafcommerce.common.exception.ServiceException;
import org.broadleafcommerce.common.extension.ExtensionResultStatusType;
import org.broadleafcommerce.common.presentation.client.OperationType;
import org.broadleafcommerce.common.service.ParentCategoryLegacyModeService;
import org.broadleafcommerce.common.service.ParentCategoryLegacyModeServiceImpl;
import org.broadleafcommerce.common.util.dao.QueryUtils;
import org.broadleafcommerce.core.catalog.domain.Category;
import org.broadleafcommerce.core.catalog.domain.CategoryProductXref;
import org.broadleafcommerce.core.catalog.domain.CategoryProductXrefImpl;
import org.broadleafcommerce.core.catalog.domain.Product;
import org.broadleafcommerce.core.catalog.domain.ProductBundle;
import org.broadleafcommerce.core.catalog.domain.ProductImpl;
import org.broadleafcommerce.core.catalog.domain.Sku;
import org.broadleafcommerce.core.catalog.service.CatalogService;
import org.broadleafcommerce.core.catalog.service.type.ProductBundlePricingModelType;
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.PersistencePackage;
import org.broadleafcommerce.openadmin.dto.PersistencePerspective;
import org.broadleafcommerce.openadmin.server.dao.DynamicEntityDao;
import org.broadleafcommerce.openadmin.server.service.handler.CustomPersistenceHandlerAdapter;
import org.broadleafcommerce.openadmin.server.service.persistence.module.EmptyFilterValues;
import org.broadleafcommerce.openadmin.server.service.persistence.module.InspectHelper;
import org.broadleafcommerce.openadmin.server.service.persistence.module.RecordHelper;
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.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.From;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;

/**
 * @author <NAME>
 */
@Component("blProductCustomPersistenceHandler")
public class ProductCustomPersistenceHandler extends CustomPersistenceHandlerAdapter {
    
    @Resource(name = "blCatalogService")
    protected CatalogService catalogService;

    @Resource(name = "blProductCustomPersistenceHandlerExtensionManager")
    protected ProductCustomPersistenceHandlerExtensionManager extensionManager;
    
    @Resource(name = "blParentCategoryLegacyModeService")
    protected ParentCategoryLegacyModeService parentCategoryLegacyModeService;

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

    @Override
    public Boolean canHandleAdd(PersistencePackage persistencePackage) {
        String ceilingEntityFullyQualifiedClassname = persistencePackage.getCeilingEntityFullyQualifiedClassname();
        String[] customCriteria = persistencePackage.getCustomCriteria();
        return !ArrayUtils.isEmpty(customCriteria) && "productDirectEdit".equals(customCriteria[0]) && Product.class.getName().equals(ceilingEntityFullyQualifiedClassname);
    }

    @Override
    public Boolean canHandleUpdate(PersistencePackage persistencePackage) {
        return canHandleAdd(persistencePackage);
    }

    @Override
    public Boolean canHandleRemove(PersistencePackage persistencePackage) {
        return canHandleAdd(persistencePackage);
    }

    @Override
    public Boolean canHandleFetch(PersistencePackage persistencePackage) {
        return canHandleAdd(persistencePackage);
    }

    @Override
    public Boolean canHandleInspect(PersistencePackage persistencePackage) {
        return canHandleAdd(persistencePackage);
    }

    @Override
    public DynamicResultSet inspect(PersistencePackage persistencePackage, DynamicEntityDao dynamicEntityDao, InspectHelper helper) throws ServiceException {
        Map<String, FieldMetadata> md = getMetadata(persistencePackage, helper);

        if (!isDefaultCategoryLegacyMode()) {
            md.remove("allParentCategoryXrefs");

            BasicFieldMetadata defaultCategory = ((BasicFieldMetadata) md.get("defaultCategory"));
            defaultCategory.setFriendlyName("ProductImpl_Parent_Category");
        }

        return getResultSet(persistencePackage, helper, md);
    }

    @Override
    public DynamicResultSet fetch(PersistencePackage persistencePackage, CriteriaTransferObject cto, DynamicEntityDao
            dynamicEntityDao, RecordHelper helper) throws ServiceException {
        
        boolean legacy = parentCategoryLegacyModeService.isLegacyMode();
        
        //the following code applies when filters are present only:
        //"legacy" means that the parent category filter still utilizes Product.defaultCategory as the field to be matched
        //against the categories chosen in the listGrid filter. The default behavior up to this point, assumes the "legacy" mode. 
        //This means that one of the FilterAndSortCriterias will try to match the chosen values against "defaultCategory".
        //If "legacy" is false, we remove that FilterAndSortCriteria from the CTO, and inject a new FilterMapping in cto.additionalFilterMappings,
        //which seeks matching values in  allParentCategoryXRefs instead
        if (!legacy) {
            FilterAndSortCriteria fsc = cto.getCriteriaMap().get("defaultCategory");
            if (fsc != null) {
                List<String> filterValues = fsc.getFilterValues();
                cto.getCriteriaMap().remove("defaultCategory");
                FilterMapping filterMapping = new FilterMapping()
                        .withFieldPath(new FieldPath().withTargetProperty("allParentCategoryXrefs.id"))
                        .withDirectFilterValues(filterValues)
                        .withRestriction(new Restriction()
                                .withPredicateProvider(new PredicateProvider() {
                                    @Override
                                    public Predicate buildPredicate(CriteriaBuilder builder, FieldPathBuilder fieldPathBuilder,
                                            From root, String ceilingEntity,
                                            String fullPropertyName, Path explicitPath, List directValues) {
                                        //We want to limit the product we find to those who have a CategoryProductXref that
                                        //is unarchived, is the defaultReference, and is the selected filter category. We
                                        //do this in a subquery to avoid an additional join that would cause duplicate query
                                        //results.
                                        Subquery<Long> sub = fieldPathBuilder.getCriteria().subquery(Long.class);
                                        Root<CategoryProductXrefImpl> subRoot = sub.from(CategoryProductXrefImpl.class);
                                        sub.select(subRoot.get("id").as(Long.class));
                                        List<Predicate> subRestrictions = new ArrayList<Predicate>();
                                        subRestrictions.add(builder.equal(subRoot.get("defaultReference"), Boolean.TRUE));
                                        subRestrictions.add(subRoot.get("category").get("id").in(directValues));
                                        //archived?
                                        QueryUtils.notArchived(builder, subRestrictions, subRoot, "archiveStatus");

                                        sub.where(subRestrictions.toArray(new Predicate[subRestrictions.size()]));

                                        return explicitPath.in(sub);
                                    }
                                }));

                cto.getAdditionalFilterMappings().add(filterMapping);
            }
        }

        cto.getNonCountAdditionalFilterMappings().add(new FilterMapping()
                .withDirectFilterValues(new EmptyFilterValues())
                .withRestriction(new Restriction()
                                .withPredicateProvider(new PredicateProvider() {
                                    public Predicate buildPredicate(CriteriaBuilder builder,
                                                                    FieldPathBuilder fieldPathBuilder, From root,
                                                                    String ceilingEntity,
                                                                    String fullPropertyName, Path explicitPath,
                                                                    List directValues) {
                                        root.fetch("defaultSku", JoinType.LEFT);
                                        return null;
                                    }
                                })
                ));
        return helper.getCompatibleModule(OperationType.BASIC).fetch(persistencePackage, cto);
    }

    @Override
    public Entity add(PersistencePackage persistencePackage, DynamicEntityDao dynamicEntityDao, RecordHelper helper) throws ServiceException {
        Entity entity  = persistencePackage.getEntity();
        try {
            PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
            Product adminInstance = (Product) Class.forName(entity.getType()[0]).newInstance();
            Map<String, FieldMetadata> adminProperties = helper.getSimpleMergedProperties(Product.class.getName(), persistencePerspective);

            if (adminInstance instanceof ProductBundle) {
{'fraction_non_alphanumeric': 0.07456457863135034, 'fraction_numerical': 0.0014052718988204233, 'mean_word_length': 2.8790882061446976, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 69, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3879372', 'n_tokens_mistral': 5355, 'n_tokens_neox': 5249, 'n_words': 1138}