public static function validate(Reflection $reflection, array $filter)
    {
        if (self::isGroup($filter)) {
            // Filter group

            foreach ($filter as $modifier => $item) {

                if (!is_array($item) || empty($item)) {
                    throw new Exception\FilterException(
                        "Invalid filter group structure!"
                    );
                }

                self::validate($reflection, $item);
            }
        } else {
            // Filter item

            foreach ($filter as $name => $item) {

                if (!is_array($item) || !is_string($name)) {
                    throw new Exception\FilterException(
                        "Invalid filter structure!"
                    );
                }

                if (!$reflection->hasProperty($name)) {
                    throw new Exception\FilterException(
                        "Undefined property name '" . $name . "' used in filter!"
                    );
                }
                $property = $reflection->getProperty($name);

                foreach ($item as $modifier => $value) {

                    if (!in_array($modifier, self::$modifiers, true)) {
                        throw new Exception\FilterException(
                            "Invalid filter modifier '" . $modifier . "'!"
                        );
                    }

                    if ($property->hasOption(Assoc::KEY)
                        || $property->hasOption(Computed::KEY)
                        || ($property->hasOption(Reflection\Property\Option\Map::KEY)
                            && !$property->getOption(Reflection\Property\Option\Map::KEY))
                        || $property->getType() === Reflection\Property::TYPE_COLLECTION
                        || $property->getType() === Reflection\Property::TYPE_ENTITY
                    ) {
                        throw new Exception\FilterException(
                            "Filter can not be used with computed, collections, entities and disabled mapping!"
                        );
                    }

                    // Validate value type
                    try {

                        if (is_array($value)
                            && $property->getType() !== Reflection\Property::TYPE_ARRAY
                            && in_array($modifier, [self::EQUAL, self::NOT], true)
                        ) {
                            // Array values

                            foreach ($value as $index => $valueItem) {
                                $property->validateValueType($valueItem);
                            }
                        } else {
                            $property->validateValueType($value);
                        }
                    } catch (Exception\InvalidArgumentException $e) {
                        throw new Exception\FilterException($e->getMessage());
                    }
                }
            }
        }
    }