<commit_before><?php

namespace Oro\Bundle\FilterBundle\Form\EventListener;

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

use Oro\Bundle\FilterBundle\Expression\Date\Compiler;
use Oro\Bundle\FilterBundle\Provider\DateModifierInterface;

class DateFilterSubscriber implements EventSubscriberInterface
{
    /** @var Compiler */
    protected $expressionCompiler;

    /** @var array */
    protected $processed = [];

    /**
     * @param Compiler $compiler
     */
    public function __construct(Compiler $compiler)
    {
        $this->expressionCompiler = $compiler;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            FormEvents::PRE_SUBMIT => 'preSubmit'
        ];
    }

    /**
     * Parses date expressions
     * If date part given, then replace value fields by choice fields with specific to that value choices
     *
     * @SuppressWarnings(PHPMD.CyclomaticComplexity) many cases in switch - have no sense to refactor this.
     */
    public function preSubmit(FormEvent $event)
    {
        $compiler = $this->expressionCompiler;

        $data = $event->getData();
        $form = $event->getForm();

        $oid = spl_object_hash($form);
        if (!empty($this->processed[$oid])) {
            // ensure that data is not processed
            // in case when DateTimeFilter already process and parent subscription is not necessary
            return;
        }

        $children = array_keys($form->get('value')->all());

        // compile expressions
        $this->mapValues(
            $children,
            $data,
            function ($data) use ($compiler) {
                return $compiler->compile($data);
            }
        );
        $data['part'] = isset($data['part']) ? $data['part'] : null;

        // change value type depending on date part
        // replace value form children to needed sub forms in case when part is selected
        switch ($data['part']) {
            case DateModifierInterface::PART_MONTH:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('m'));
                $this->replaceValueFields($form->get('value'), range(1, 12));
                break;
            case DateModifierInterface::PART_DOW:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('N'));
                $this->replaceValueFields($form->get('value'), range(1, 7));
                break;
            case DateModifierInterface::PART_WEEK:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('W'));
                $this->replaceValueFields($form->get('value'), range(1, 53));
                break;
            case DateModifierInterface::PART_DAY:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('d'));
                $this->replaceValueFields($form->get('value'), range(1, 31));
                break;
            case DateModifierInterface::PART_QUARTER:
                $this->mapValues(
                    $children,
                    $data,
                    function ($data) {
                        $quarter = null;
                        switch (true) {
                            case is_numeric($data):
                                $quarter = (int)$data;
                                break;
                            case ($data instanceof \DateTime):
                                $month   = (int)$data->format('m');
                                $quarter = ceil($month / 3);
                                break;
                            default:
                                throw new UnexpectedTypeException($data, 'integer or \DateTime');
                        }

                        return $quarter;
                    }
                );
                $this->replaceValueFields($form->get('value'), range(1, 4));
                break;
            case DateModifierInterface::PART_DOY:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('z'));
                $this->replaceValueFields($form->get('value'), range(1, 366));
                break;
            case DateModifierInterface::PART_YEAR:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('Y'));
                $this->replaceValueFields($form->get('value'), array_flip(range(date('Y') - 50, date('Y') + 50)));
                break;
            // in case we do not need any value to be mapped(converted), in other words - use as is
            case DateModifierInterface::PART_SOURCE:
                return;
            case DateModifierInterface::PART_VALUE:
            default:
                $this->mapValues(
                    $children,
                    $data,
                    function ($data) use ($compiler) {
                        // html5 format for intl
                        return $data instanceof \DateTime ? $data->format('Y-m-d H:i') : $data;
                    }
                );
                break;
        }

        $event->setData($data);
        $this->processed[$oid] = true;
    }

    /**
     * Replace values form children to "choice" type with predefined choice list
     *
     * @param FormInterface $form
     * @param array         $choices
     */
    private function replaceValueFields(FormInterface $form, array $choices)
    {
        $children = array_keys($form->all());

        foreach ($children as $child) {
            $form->add($child, 'choice', ['choices' => $choices]);
        }
    }<commit_msg>BAP-9418: Reports field filter with source or quarter type unexpected behaviour<commit_after><?php

namespace Oro\Bundle\FilterBundle\Form\EventListener;

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

use Oro\Bundle\FilterBundle\Expression\Date\Compiler;
use Oro\Bundle\FilterBundle\Provider\DateModifierInterface;

class DateFilterSubscriber implements EventSubscriberInterface
{
    /** @var Compiler */
    protected $expressionCompiler;

    /** @var array */
    protected $processed = [];

    /**
     * @param Compiler $compiler
     */
    public function __construct(Compiler $compiler)
    {
        $this->expressionCompiler = $compiler;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            FormEvents::PRE_SUBMIT => 'preSubmit'
        ];
    }

    /**
     * Parses date expressions
     * If date part given, then replace value fields by choice fields with specific to that value choices
     *
     * @SuppressWarnings(PHPMD.CyclomaticComplexity) many cases in switch - have no sense to refactor this.
     */
    public function preSubmit(FormEvent $event)
    {
        $compiler = $this->expressionCompiler;

        $data = $event->getData();
        $form = $event->getForm();

        $oid = spl_object_hash($form);
        if (!empty($this->processed[$oid])) {
            // ensure that data is not processed
            // in case when DateTimeFilter already process and parent subscription is not necessary
            return;
        }

        $children = array_keys($form->get('value')->all());

        // compile expressions
        $this->mapValues(
            $children,
            $data,
            function ($data) use ($compiler) {
                return $compiler->compile($data);
            }
        );
        $data['part'] = isset($data['part']) ? $data['part'] : null;

        // change value type depending on date part
        // replace value form children to needed sub forms in case when part is selected
        switch ($data['part']) {
            case DateModifierInterface::PART_MONTH:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('m'));
                $this->replaceValueFields($form->get('value'), range(1, 12));
                break;
            case DateModifierInterface::PART_DOW:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('N'));
                $this->replaceValueFields($form->get('value'), range(1, 7));
                break;
            case DateModifierInterface::PART_WEEK:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('W'));
                $this->replaceValueFields($form->get('value'), range(1, 53));
                break;
            case DateModifierInterface::PART_DAY:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('d'));
                $this->replaceValueFields($form->get('value'), range(1, 31));
                break;
            case DateModifierInterface::PART_QUARTER:
                $this->mapValues(
                    $children,
                    $data,
                    function ($data) {
                        $quarter = null;
                        switch (true) {
                            case is_numeric($data):
                                $quarter = (int)$data;
                                break;
                            case ($data instanceof \DateTime):
                                $month   = (int)$data->format('m');
                                $quarter = ceil($month / 3);
                                break;
                            default:
                                throw new UnexpectedTypeException($data, 'integer or \DateTime');
                        }

                        return $quarter;
                    }
                );
                $this->replaceValueFields($form->get('value'), range(1, 4));
                break;
            case DateModifierInterface::PART_DOY:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('z'));
                $this->replaceValueFields($form->get('value'), range(1, 366));
                break;
            case DateModifierInterface::PART_YEAR:
                $this->mapValues($children, $data, $this->getDatePartAccessorClosure('Y'));
                $this->replaceValueFields($form->get('value'), array_flip(range(date('Y') - 50, date('Y') + 50)));
                break;
            case DateModifierInterface::PART_SOURCE:
            case DateModifierInterface::PART_VALUE:
            default:
                $this->mapValues(
                    $children,
                    $data,
                    function ($data) use ($compiler) {
                        // html5 format for intl
                        return $data instanceof \DateTime ? $data->format('Y-m-d H:i') : $data;
                    }
                );
                break;
        }

        $event->setData($data);
        $this->processed[$oid] = true;
    }

    /**
     * Replace values form children to "choice" type with predefined choice list
     *
     * @param FormInterface $form
     * @param array         $choices
     */
    private function replaceValueFields(FormInterface $form, array $choices)
    {
        $children = array_keys($form->all());

        foreach ($children as $child) {
            $form->add($child, 'choice', ['choices' => $choices]);
        }
    }
{'fraction_non_alphanumeric': 0.12041839557399724, 'fraction_numerical': 0.004322268326417704, 'mean_word_length': 1.7447212336892053, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 93, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6890766', 'n_tokens_mistral': 2981, 'n_tokens_neox': 2813, 'n_words': 638}