text:
<?php

namespace Concrete\Controller\SinglePage\Dashboard\Blocks;

use Concrete\Core\Entity\Statistics\UsageTracker\StackUsageRecord;
use Concrete\Core\Http\Request;
use Concrete\Core\Http\Response;
use Concrete\Core\Multilingual\Page\Section\Section;
use Concrete\Core\Navigation\Breadcrumb\Dashboard\DashboardStacksBreadcrumbFactory;
use Concrete\Core\Page\Collection\Version\Version;
use Concrete\Core\Page\Controller\DashboardPageController;
use Concrete\Core\Page\Page;
use Concrete\Core\Page\Stack\Stack;
use Concrete\Core\Page\Stack\StackList;
use Concrete\Core\Permission\Checker;
use Concrete\Core\Support\Facade\StackFolder;
use Concrete\Core\User\User;
use Concrete\Core\Workflow\Request\ApprovePageRequest;
use Concrete\Core\Workflow\Request\ApproveStackRequest;
use Concrete\Core\Workflow\Request\DeletePageRequest;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Symfony\Component\HttpFoundation\JsonResponse;
use View;

class Stacks extends DashboardPageController
{
    protected $themeViewTemplate = 'full.php';

    public function view_global_areas()
    {
        $stm = new StackList();
        $stm->filterByGlobalAreas();
        $this->deliverStackList($stm);

        $factory = $this->createBreadcrumbFactory();
        $factory->setDisplayGlobalAreasLandingPage(true);
        $this->setBreadcrumb($factory->getBreadcrumb($this->getPageObject()));
    }

    public function view_details($cID, $msg = false)
    {
        $factory = $this->createBreadcrumbFactory();
        if (strpos($cID, '@') !== false) {
            list($cID, $locale) = explode('@', $cID, 2);
        } else {
            $locale = '';
        }

        $s = Stack::getByID($cID);
        if (is_object($s)) {
            $isGlobalArea = $s->getStackType() == Stack::ST_TYPE_GLOBAL_AREA;
            $factory->setDisplayGlobalAreasLandingPage($isGlobalArea);
            if ($s->isNeutralStack()) {
                $neutralStack = $s;
                $stackToEdit = $s;
            } else {
                $neutralStack = $s->getNeutralStack();
                $stackToEdit = $s;
            }
            $sections = $this->getMultilingualSections();
            $this->setBreadcrumb($factory->getBreadcrumb($this->getPageObject(), $s, $sections, $locale));
            if (!empty($sections)) {
                if ($stackToEdit !== $neutralStack) {
                    $section = $stackToEdit->getMultilingualSection();
                    if ($section !== null) {
                        $locale = $section->getLocale();
                    }
                }
                if (!isset($sections[$locale])) {
                    $locale = '';
                }
                if ($locale !== '') {
                    $this->set('localeCode', $locale);
                    $this->set('localeName', $sections[$locale]->getLanguageText());
                    $stackToEdit = $neutralStack->getLocalizedStack($sections[$locale]);
                }
            }
            if ($stackToEdit !== null) {
                $blocks = $stackToEdit->getBlocks('Main');
                $view = View::getInstance();
                foreach ($blocks as $b1) {
                    $btc = $b1->getController();
                    // now we inject any custom template CSS and JavaScript into the header
                    if ($btc instanceof \Concrete\Core\Block\BlockController) {
                        $btc->outputAutoHeaderItems();
                    }

                    $btc->runAction('on_page_view', [$view]);
                }

                $this->addHeaderItem($stackToEdit->outputCustomStyleHeaderItems(true));
                $this->set('blocks', $blocks);
            }

            $this->set('neutralStack', $neutralStack);
            $this->set('stackToEdit', $stackToEdit);
            $this->set('isGlobalArea', $isGlobalArea);
        } else {
            $folder = StackFolder::getByID($cID);
            if (is_object($folder)) {
                $stm = new StackList();
                $stm->filterByFolder($folder);
                $this->set('currentStackFolderID', $folder->getPage()->getCollectionID());
                $this->deliverStackList($stm);
                $this->set('canMoveStacks', $this->canMoveStacks($folder));
                $this->setBreadcrumb($factory->getBreadcrumb($this->getPageObject(), $folder));
            } else {
                $root = Page::getByPath(STACKS_PAGE_PATH);
                if ($root->getCollectionID() != $cID) {
                    $this->error->add(t('Invalid stack'));
                }
                $this->view();
            }
        }

        switch ($msg) {
            case 'stack_added':
                $this->set('flashMessage', t('Stack added successfully.'));
                break;
            case 'localized_stack_added':
                $this->set('flashMessage', t('Localized version of stack added successfully.'));
                break;
            case 'localized_global_area_added':
                $this->set('flashMessage', t('Localized version of global area added successfully.'));
                break;
            case 'stack_approved':
                $this->set('flashMessage', t('Stack approved successfully'));
                break;
            case 'global_area_approved':
                $this->set('flashMessage', t('Global area approved successfully'));
                break;
            case 'approve_saved':
                $this->set('flashMessage', t('Approve request saved. You must complete the approval workflow before these changes are publicly accessible.'));
                break;
            case 'stack_delete_saved':
                $this->set('flashMessage', t('Delete request saved. You must complete the delete workflow before this stack can be deleted.'));
                break;
            case 'folder_delete_saved':
                $this->set('flashMessage', t('Delete request saved. You must complete the delete workflow before this stack folder can be deleted.'));
                break;
            case 'global_area_delete_saved':
                $this->set('flashMessage', t('Delete request saved. You must complete the delete workflow before this version of the global area can be deleted.'));
                break;
            case 'rename_saved':
                $this->set('flashMessage', t('Rename request saved. You must complete the approval workflow before the name will be updated.'));
                break;
            case 'stack_deleted':
                $this->flash('success', t('Stack deleted successfully'));
                break;
            case 'global_area_cleared':
                $this->flash('success', t('Global area cleared successfully'));
                break;
            case 'localized_stack_deleted':
                $this->set('flashMessage', t('Localized version of stack deleted successfully'));
                break;
            case 'localized_global_area_deleted':
                $this->set('flashMessage', t('Localized version of global area deleted successfully'));
                break;
            case 'stack_duplicated':
                $this->set('flashMessage', t('Stack duplicated successfully'));
                break;
            case 'stack_renamed':
                $this->set('flashMessage', t('Stack renamed successfully'));
                break;
            case 'folder_renamed':
                $this->set('flashMessage', t('Stack Folder renamed successfully'));
                break;
            case 'folder_added':
                $this->set('flashMessage', t('Folder added successfully.'));
                break;
            case 'folder_deleted':
                $this->set('flashMessage', t('Folder deleted successfully.'));
                break;
        }

        $this->set('composer', $this->app->make('helper/concrete/composer'));
    }

    public function view()
    {
        //$parent = Page::getByPath(STACKS_PAGE_PATH, 'RECENT', $this->getSite());
        $parent = Page::getByPath(STACKS_PAGE_PATH);
        $stm = new StackList();
        $stm->filterByParentID($parent->getCollectionID());
        $stm->excludeGlobalAreas();
        $this->deliverStackList($stm);
        $this->set('canMoveStacks', $this->canMoveStacks($parent));
        $this->set('showGlobalAreasFolder', true);
    }

    public function add_stack()
    {
        if ($this->token->validate('add_stack')) {
            $stackName = $this->request->request->get('stackName');
            if ($this->app->make('helper/validation/strings')->notempty($stackName)) {
                $folder = null;
                $folderID = $this->request->request->get('stackFolderID');
                if ($this->app->make('helper/validation/numbers')->integer($folderID)) {
                    $folder = StackFolder::getByID($folderID);
                    if (!is_object($folder)) {
                        $this->error->add(t('Unable to find the specified stack folder.'));
                    }
                }

                if (!$this->error->has()) {
                    $stack = Stack::addStack($stackName, $folder);

                    return $this->buildRedirect($this->action('view_details', $stack->getCollectionID(), 'stack_added'));
                }
            } else {
                $this->error->add(t('You must give your stack a name.'));
            }
        } else {
            $this->error->add($this->token->getErrorMessage());
        }
    }

    public function add_localized_stack()
    {
        if ($this->token->validate('add_localized_stack')) {
            $neutralStack = Stack::getByID($this->post('stackID'));
            $isGlobalArea = false;
            if (!$neutralStack) {
                $this->error->add(t('Unable to find the specified stack'));
            } elseif ($neutralStack->getStackType() == Stack::ST_TYPE_GLOBAL_AREA) {
                $isGlobalArea = true;
            }
            $section = Section::getByLocale($this->post('locale'));
            if (!$section) {
                $this->error->add(t('Unable to find the specified language'));
            } elseif ($neutralStack && $neutralStack->getLocalizedStack($section) !== null) {
                if ($isGlobalArea) {
                    $this->error->add(t(// i18n %s is a language name
                            "There's already a version of this global area in %s",
                        $section->getLanguageText()
                    ) . ' (' . $section->getLocale() . ')');
                } else {
                    $this->error->add(t(// i18n %s is a language name
                            "There's already a version of this stack in %s",
                        $section->getLanguageText()
                    ) . ' (' . $section->getLocale() . ')');
                }
            }
            if ($neutralStack) {
                $cpc = new Checker($neutralStack);
                if (!$cpc->canAddSubpage()) {
                    $this->error->add(t('Access denied'));
                }
            }
            if (!$this->error->has()) {
                $neutralStack->addLocalizedStack($section);

                return $this->buildRedirect($this->action(
                    'view_details',
                    $neutralStack->getCollectionID() . rawurlencode('@' . $section->getLocale()),
                    $isGlobalArea ? 'localized_global_area_added' : 'localized_stack_added'
                ));
            }
        } else {
            $this->error->add($this->token->getErrorMessage());
        }
    }

    public function add_folder()
    {
        if (!$this->token->validate('add_folder')) {
            $this->error->add($this->token->getErrorMessage());
        }

        $folderName = $this->request->request->get('folderName');
        if (!$this->app->make('helper/validation/strings')->notempty($folderName)) {
            $this->error->add(t('You must give the folder a name.'));
        }

        $parentFolder = null;
        $stackFolderID = $this->request->request->get('stackFolderID');
        if ($this->app->make('helper/validation/numbers')->integer($stackFolderID)) {
            $parentFolder = StackFolder::getByID($stackFolderID);
            if (!is_object($parentFolder)) {
                $this->error->add(t('Unable to find the specified stack folder.'));
            }
        } else {
            $stackFolderID = null;
        }

        if (!$this->error->has()) {
            StackFolder::add($folderName, $parentFolder);
            $parentID = ($stackFolderID === null) ? Page::getByPath(STACKS_PAGE_PATH)->getCollectionID() : $stackFolderID;

            return $this->buildRedirect($this->action('view_details', $parentID, 'folder_added'));
        }

        $this->view();
    }

    public function delete_stack()
    {
        if ($this->token->validate('delete_stack')) {
            $s = Stack::getByID($this->request('stackID'));
            if (is_object($s)) {
                $isGlobalArea = $s->getStackType() == Stack::ST_TYPE_GLOBAL_AREA;
                $neutralStack = $s->getNeutralStack();
                if ($neutralStack === null) {
                    $nextID = $s->getCollectionParentID();
                    if ($isGlobalArea) {
                        $msg = 'global_area_cleared';
                    } else {
                        $msg = 'stack_deleted';
                    }
                } else {
                    $msg = $isGlobalArea ? 'localized_global_area_deleted' : 'localized_stack_deleted';
                    $nextID = $neutralStack->getCollectionID();
                    $section = $s->getMultilingualSection();
                    if ($section) {
                        $nextID .= '@' . $section->getLocale();
                    }
                }

                if (!$this->error->has()) {
                    $sps = new Checker($s);
                    if ($sps->canDeletePage()) {
                        $u = $this->app->make(User::class);
                        $pkr = new DeletePageRequest();
                        $pkr->setRequestedPage($s);
                        $pkr->setRequesterUserID($u->getUserID());
                        $response = $pkr->trigger();
                        if ($response instanceof \Concrete\Core\Workflow\Progress\Response) {
                            // we only get this response if we have skipped workflows and jumped straight in to an approve() step.
                            return $this->buildRedirect($this->action('view_details', rawurlencode($nextID), $msg));
                        }

                        return $this->buildRedirect($this->action('view_details', $s->getCollectionID(), $isGlobalArea ? 'global_area_delete_saved' : 'stack_delete_saved'));
                    }

                    if ($isGlobalArea) {
                        $this->error->add(t('You do not have access to delete this stack.'));
                    } else {
                        $this->error->add(t('You do not have access to delete this global area.'));
                    }
                }
            } else {
                $this->error->add(t('Invalid stack'));
            }
        } else {
            $this->error->add($this->token->getErrorMessage());
        }
    }

    public function approve_stack($stackID = false, $token = false)
    {
        if ($this->token->validate('approve_stack', $token)) {
            $s = Stack::getByID($stackID);
            if (is_object($s)) {
                $isGlobalArea = $s->getStackType() == Stack::ST_TYPE_GLOBAL_AREA;
                $sps = new Checker($s);
                if ($sps->canApprovePageVersions()) {
                    $u = $this->app->make(User::class);
                    $v = Version::get($s, 'RECENT');
                    $pkr = new ApproveStackRequest();
                    $pkr->setRequestedPage($s);
                    $pkr->setRequestedVersionID($v->getVersionID());
                    $pkr->setRequesterUserID($u->getUserID());
                    $response = $pkr->trigger();
                    if ($response instanceof \Concrete\Core\Workflow\Progress\Response) {
                        // we only get this response if we have skipped workflows and jumped straight in to an approve() step.
                        return $this->buildRedirect($this->action('view_details', $stackID, $isGlobalArea ? 'global_area_approved' : 'stack_approved'));
                    }

                    return $this->buildRedirect($this->action('view_details', $stackID, 'approve_saved'));
                }

                $this->error->add(t('You do not have access to approve this stack.'));
            } else {
                $this->error->add(t('Invalid stack'));
            }
        } else {
            $this->error->add($this->token->getErrorMessage());
        }
    }

    public function rename($cID)
    {
        $page = null;
        $stack = Stack::getByID($cID);
        if ($stack) {
            $neutralStack = $stack->getNeutralStack();
            if ($neutralStack !== null) {
                return $this->buildRedirect($this->action('rename', $neutralStack->getCollectionID()));
            }

            if ($stack->getStackType() == Stack::ST_TYPE_GLOBAL_AREA) {
                $this->error->add(t("You can't rename global areas"));

                return $this->view_details($cID);
            }

            $isFolder = false;
            $page = $stack;
            $viewCID = $cID;
        } else {
            $folder = StackFolder::getByID($cID);
            if ($folder) {
                $isFolder = true;
                $page = $folder->getPage();
                $viewCID = $page->getCollectionParentID();
            }
        }

        if ($page === null) {
            $this->error->add(t('Invalid stack'));
            $this->view();
        } else {
            $sps = new Checker($page);
            if (!$sps->canEditPageProperties()) {
                if ($isFolder) {
                    $this->error->add(t("You don't have the permission to rename this stack"));
                } else {
                    $this->error->add(t("You don't have the permission to rename this stack folder"));
                }

                $this->view_details($viewCID);
            } else {
                $this->set('renamePage', $page);
                $this->set('isFolder', $isFolder);
                $this->set('oldName', $isFolder ? $page->getCollectionName() : $stack->getStackName());

                if ($this->request->isMethod(Request::METHOD_POST)) {
                    if (!$this->token->validate('rename_stack')) {
                        $this->error->add($this->token->getErrorMessage());
                    } else {
                        $newName = $this->request->request->get('newName');
                        if (!$this->app->make('helper/validation/strings')->notempty($newName)) {
                            $this->error->add(t('The name cannot be empty.'));
                        } else {
                            $txt = $this->app->make('helper/text');
                            $v = $page->getVersionToModify();
                            $v->update([
                                'cName' => $newName,
                                'cHandle' => str_replace('-', $this->app['config']->get('concrete.seo.page_path_separator'), $txt->urlify($newName)),
                            ]);
                            $u = $this->app->make(User::class);
                            if ($isFolder) {
                                $pkr = new ApprovePageRequest();
                            } else {
                                $pkr = new ApproveStackRequest();
                            }

                            $pkr->setRequestedPage($page);
                            $pkr->setRequestedVersionID($v->getVersionID());
                            $pkr->setRequesterUserID($u->getUserID());
                            $response = $pkr->trigger();
                            if ($response instanceof \Concrete\Core\Workflow\Progress\Response) {
                                // we only get this response if we have skipped workflows and jumped straight in to an approve() step.
                                if ($isFolder) {
                                    return $this->buildRedirect($this->action('view_details', $viewCID, 'folder_renamed'));
                                }

                                return $this->buildRedirect($this->action('view_details', $viewCID, 'stack_renamed'));
                            }

                            return $this->buildRedirect($this->action('view_details', $viewCID, 'rename_saved'));
                        }
                    }
                }
            }
        }
    }

    public function move_to_folder()
    {
        if (!$this->token->validate('move_to_folder')) {
            throw new Exception($this->token->getErrorMessage());
        }

        $receivedSourceIDs = $this->request->request->get('sourceIDs');
        if (!is_array($receivedSourceIDs)) {
            throw new Exception(t('Bad parameter: %s', 'sourceIDs'));
        }

        $sourceIDs = [];
        $valn = $this->app->make('helper/validation/numbers');
        foreach ($receivedSourceIDs as $receivedSourceID) {
            if (!$valn->integer($receivedSourceID)) {
                throw new Exception(t('Bad parameter: %s', 'sourceIDs'));
            }
            $receivedSourceID = (int) $receivedSourceID;
            if (!in_array($receivedSourceID, $sourceIDs, true)) {
                $sourceIDs[] = $receivedSourceID;
            }
        }

        if (empty($sourceIDs)) {
            throw new Exception(t('Bad parameter: %s', 'sourceIDs'));
        }

        $moveStacks = [];
        $moveFolders = [];
        $checkedParents = [];
        foreach ($sourceIDs as $sourceID) {
            $parentID = null;
            $item = Stack::getByID($sourceID);
            if ($item) {
                $parentID = $item->getCollectionParentID();
                $moveStacks[] = $item;
            } else {
                $item = StackFolder::getByID($sourceID);
                if (is_object($item)) {
                    $parentID = $item->getPage()->getCollectionParentID();
                    $moveFolders[] = $item;
                } else {
                    throw new Exception(t('Unable to find the specified stack or folder'));
                }
            }

            if ($parentID && !isset($checkedParents[$parentID])) {
                $parent = Page::getByID($parentID);
                if ($parent && !$parent->isError() && !$this->canMoveStacks($parent)) {
                    throw new Exception(t('Access denied'));
                }

                $checkedParents[$parentID] = true;
            }
        }

        $destinationID = $this->request->request->get('destinationID');
        if ($destinationID === '') {
            $destinationPage = Page::getByPath(STACKS_PAGE_PATH);
        } else {
            if (!$valn->integer($destinationID)) {
                throw new Exception(t('Bad parameter: %s', 'destinationID'));
            }

            $destinationFolder = StackFolder::getByID($destinationID);
            if (!is_object($destinationFolder)) {
                throw new Exception(t('Unable to find the specified stack folder'));
            }

            $destinationPage = $destinationFolder->getPage();
        }

        foreach ($moveStacks as $moveStack) {
            $moveStack->move($destinationPage);
        }

        foreach ($moveFolders as $moveFolder) {
            $moveFolder->getPage()->move($destinationPage);
        }

        return new JsonResponse(
            t2('%d item has been moved under the folder %s', '%d items have been moved under the folder %s', count($sourceIDs), count($sourceIDs), h($destinationPage->getCollectionName()))
        );
    }

    public function duplicate($cID)
    {
        $s = Stack::getByID($cID);
        if (!$s) {
            $this->error->add(t('Invalid stack'));

            return $this->view();
        }

        if ($s->getStackType() == Stack::ST_TYPE_GLOBAL_AREA) {
            $this->error->add(t("You can't duplicate global areas"));
            $this->view_details($cID);
        } else {
            $ns = $s->getNeutralStack();
            if ($ns !== null) {
                return $this->buildRedirect($this->action('duplicate', $ns->getCollectionID()));
            }

            $sps = new Checker($s);
            if (!$sps->canMoveOrCopyPage()) {
                $this->error->add(t("You don't have the permission to clone this stack"));
                $this->view_details($cID);
            } else {
                $this->set('duplicateStack', $s);
                if ($this->request->isMethod(Request::METHOD_POST)) {
                    if (!$this->token->validate('duplicate_stack')) {
                        $this->error->add($this->token->getErrorMessage());
                    } else {
                        $stackName = $this->request->request->get('stackName');
                        if (!$this->app->make('helper/validation/strings')->notempty($stackName)) {
                            $this->error->add(t('You must give your stack a name.'));
                        } else {
                            $ns = $s->duplicate();
                            $ns->update([
                                'stackName' => $stackName,
                            ]);

                            $ns->copyLocalizedStacksFrom($s);

                            return $this->buildRedirect($this->action('view_details', $s->getCollectionParentID(), 'stack_duplicated'));
                        }
                    }
                }
            }
        }
    }

    public function list_page()
    {
        return Redirect::to('/');
    }

    public function delete_stackfolder()
    {
        $parentID = null;
        if (!$this->token->validate('delete_stackfolder')) {
            $this->error->add($this->token->getErrorMessage());
        } else {
            $folder = StackFolder::getByID($this->request->request->get('stackfolderID'));
            if (!is_object($folder)) {
                $this->error->add(t('Unable to find the specified stack folder.'));
            } else {
                $parentID = $folder->getPage()->getCollectionParentID();
                $sps = new Checker($folder->getPage());
                if (!$sps->canDeletePage()) {
                    $this->error->add(t('You do not have access to delete this stack folder.'));
                } else {
                    $u = $this->app->make(User::class);
                    $pkr = new DeletePageRequest();
                    $pkr->setRequestedPage($folder->getPage());
                    $pkr->setRequesterUserID($u->getUserID());
                    $response = $pkr->trigger();
                    if ($response instanceof \Concrete\Core\Workflow\Progress\Response) {
                        // we only get this response if we have skipped workflows and jumped straight in to an approve() step.
                        return $this->buildRedirect($this->action('view_details', $parentID, 'folder_deleted'));
                    }

                    return $this->buildRedirect($this->action('view_details', $parentID, 'folder_delete_saved'));
                }
            }
        }

        if ($parentID) {
            $this->view_details($parentID);
        } else {
            $this->view();
        }
    }

    public function usage($stackId)
    {
        $this->set('stackId', $stackId);

        /** @var EntityManagerInterface $entityManager */
        $entityManager = $this->app->make(EntityManagerInterface::class);
        $repository = $entityManager->getRepository(StackUsageRecord::class);

        $records = $repository->findBy([
            'stack_id' => $stackId,
        ]);

        $view = new \Concrete\Core\View\DialogView('dialogs/stack/usage');
        $view->setController($this);
        $view->addScopeItems([
            'records' => $this->getUsageGenerator($records),
        ]);

        return new Response($view->render());
    }

    public function stack_duplicated()
    {
        $this->set('message', t('Stack duplicated successfully'));
        $this->view();
    }

    protected function createBreadcrumbFactory(): DashboardStacksBreadcrumbFactory
    {
        return $this->app->make(DashboardStacksBreadcrumbFactory::class);
    }

    /**
     * @return Section[]
     */
    protected function getMultilingualSections()
    {
        $result = [];
        if ($this->app->make('multilingual/detector')->isEnabled()) {
            foreach (Section::getList() as $section) {
                // @var Section $section
                $result[$section->getLocale()] = $section;
            }
            uasort($result, function (Section $a, Section $b) {
                $r = strcasecmp($a->getLanguageText(), $b->getLanguageText());
                if ($r === 0) {
                    $r = strcasecmp($a->getLocale(), $b->getLocale());
                }

                return $r;
            });
        }

        return $result;
    }

    /**
     * Check if stacks in a Page or StackFolder can be moved.
     *
     * @param Page|StackFolder $parent
     *
     * @return bool
     */
    protected function canMoveStacks($parent)
    {
        $page = ($parent instanceof \Concrete\Core\Page\Page) ? $parent : $parent->getPage();
        $cpc = new Checker($page);

        return (bool) $cpc->canMoveOrCopyPage();
    }

    protected function deliverStackList(StackList $list)
    {
        $list->setFoldersFirst(true);
        // $list->setSiteTreeObject($this->getSite()->getSiteTreeObject());
        $this->set('list', $list);
        $this->set('stacks', $list->getResults());
    }

    /**
     * Generator for transforming a list of StackUsageRecords into Collection objects
     * This method can be used to do some interesting things with the list two sine it is ordered
     * by Collection ID /and/ Collection Version ID.
     *
     * @param StackUsageRecord[] $records
     *
     * @return \Generator
     */
    protected function getUsageGenerator(array $records)
    {
        $last_collection = null;

        foreach ($records as $record) {
            if ($last_collection && $last_collection->getCollectionID() == $record->getCollectionId()) {
                // This is the same collection as the last collection, lets use it again.
                $collection = $last_collection;
            } else {
                /** @var \Concrete\Core\Page\Collection\Collection $collection */
                $last_collection = $collection = Page::getByID($record->getCollectionId());
            }

            // Load in the version object
            $collection->loadVersionObject($record->getCollectionVersionId());

            // Yield the collection object
            yield $collection;
        }
    }
}


text:
{%- from 'macros/icons.html' import render_icon %}

      <button class="dropdown-toggle button">Aktionen {{ render_icon('caret-down') }}</button>

{{ render_icon('download') }} für Lexware exportieren (XML)

        {%- if order.is_invoiced %}

{{ render_icon('invoice') }} Rechnung wurde nicht versendet

        {%- else %}

{{ render_icon('invoice') }} Rechnung wurde versendet

        {%- endif %}

        {%- if not order.is_canceled %}

          {%- if order.is_open %}

{{ render_icon('email') }} E-Mail-Bestätigung erneut senden

          {%- endif %}

          {%- if order.is_open and g.current_user.has_permission(ShopOrderPermission.mark_as_paid) %}

{{ render_icon('payment') }} Bestellung als bezahlt markieren

          {%- endif %}

          {%- if g.current_user.has_permission(ShopOrderPermission.cancel) %}

{{ render_icon('disabled') }} Bestellung stornieren

          {%- endif %}

        {%- endif %}

        {%- if order.is_shipping_required %}

          {%- if order.is_shipped %}

{{ render_icon('shipping') }} Bestellung wurde nicht versendet

          {%- else %}

{{ render_icon('shipping') }} Bestellung wurde versendet

          {%- endif %}
        {%- endif %}

redpajama_set_name: RedPajamaGithub


text:
<bill session="115" type="h" number="797" updated="2017-07-11T20:15:10Z">
  <state datetime="2017-02-01">REFERRED</state>
  <status>
    <introduced datetime="2017-02-01"/>
  </status>
  <introduced datetime="2017-02-01"/>
  <titles>
    <title type="official" as="introduced">To amend title XIX of the Social Security Act to remove the matching requirement for a territory to use specially allocated Federal funds for Medicare covered part D drugs for low-income individuals.</title>
    <title type="display">To amend title XIX of the Social Security Act to remove the matching requirement for a territory to use specially allocated Federal funds for Medicare covered part D drugs for low-income individuals.</title>
  </titles>
  <sponsor bioguide_id="G000582"/>
  <cosponsors>
    <cosponsor bioguide_id="R000600" joined="2017-06-16"/>
    <cosponsor bioguide_id="S001200" joined="2017-06-16"/>
  </cosponsors>
  <actions>
    <action datetime="2017-02-01">
      <text>Introduced in House</text>
    </action>
    <action datetime="2017-02-01" state="REFERRED">
      <text>Referred to the House Committee on Energy and Commerce.</text>
    </action>
    <action datetime="2017-02-03">
      <text>Referred to the Subcommittee on Health.</text>
    </action>
  </actions>
  <committees>
    <committee subcommittee="" code="HSIF" name="House Energy and Commerce" activity="Referral"/>
    <committee subcommittee="Health" code="HSIF14" name="House Energy and Commerce" activity="Referral"/>
  </committees>
  <relatedbills/>
  <subjects>
    <term name="Health"/>
    <term name="Medicaid"/>
    <term name="Medicare"/>
    <term name="Prescription drugs"/>
    <term name="U.S. territories and protectorates"/>
  </subjects>
  <amendments/>
  <summary date="2017-02-01T05:00:00Z" status="Introduced in House">This bill amends title XIX (Medicaid) of the Social Security Act to establish a Federal Medical Assistance Percentage of 100% with respect to a U.S. territory's use of specified federal funds to provide covered drugs to low-income individuals who are eligible for the Medicare prescription drug benefit.</summary>
  <committee-reports/>
</bill>


text:
module ActiveModel
  class Serializer
    class Adapter
      class JsonApi < Adapter
        def initialize(serializer, options = {})
          super
          serializer.root = true
          @hash = { data: [] }

          if fields = options.delete(:fields)
            @fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key)
          else
            @fieldset = options[:fieldset]
          end
        end

        def serializable_hash(options = {})
          if serializer.respond_to?(:each)
            serializer.each do |s|
              result = self.class.new(s, @options.merge(fieldset: @fieldset)).serializable_hash
              @hash[:data] << result[:data]

              if result[:included]
                @hash[:included] ||= []
                @hash[:included] |= result[:included]
              end
            end
          else
            @hash = cached_object do
              @hash[:data] = attributes_for_serializer(serializer, @options)
              add_resource_links(@hash[:data], serializer)
              @hash
            end
          end
          @hash
        end

        private

        def add_links(resource, name, serializers)
          resource[:links] ||= {}
          resource[:links][name] ||= { linkage: [] }
          resource[:links][name][:linkage] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
        end

        def add_link(resource, name, serializer)
          resource[:links] ||= {}
          resource[:links][name] = { linkage: nil }

          if serializer && serializer.object
            resource[:links][name][:linkage] = { type: serializer.type, id: serializer.id.to_s }
          end
        end

        def add_included(resource_name, serializers, parent = nil)
          unless serializers.respond_to?(:each)
            return unless serializers.object
            serializers = Array(serializers)
          end
          resource_path = [parent, resource_name].compact.join('.')
          if include_assoc?(resource_path)
            @hash[:included] ||= []

            serializers.each do |serializer|
              attrs = attributes_for_serializer(serializer, @options)

              add_resource_links(attrs, serializer, add_included: false)

              @hash[:included].push(attrs) unless @hash[:included].include?(attrs)
            end
          end

          serializers.each do |serializer|
            serializer.each_association do |name, association, opts|
              add_included(name, association, resource_path) if association
            end if include_nested_assoc? resource_path
          end
        end

        def attributes_for_serializer(serializer, options)
          if serializer.respond_to?(:each)
            result = []
            serializer.each do |object|
              options[:fields] = @fieldset && @fieldset.fields_for(serializer)
              options[:required_fields] = [:id, :type]
              attributes = object.attributes(options)
              attributes[:id] = attributes[:id].to_s
              result << attributes
            end
          else
            options[:fields] = @fieldset && @fieldset.fields_for(serializer)
            options[:required_fields] = [:id, :type]
            result = serializer.attributes(options)
            result[:id] = result[:id].to_s
          end

          result
        end

        def include_assoc?(assoc)
          return false unless @options[:include]
          check_assoc("#{assoc}$")
        end

        def include_nested_assoc?(assoc)
          return false unless @options[:include]
          check_assoc("#{assoc}.")
        end

        def check_assoc(assoc)
          include_opt = @options[:include]
          include_opt = include_opt.split(',') if include_opt.is_a?(String)
          include_opt.any? do |s|
            s.match(/^#{assoc.gsub('.', '\.')}/)
          end
        end

        def add_resource_links(attrs, serializer, options = {})
          options[:add_included] = options.fetch(:add_included, true)

          serializer.each_association do |name, association, opts|
            attrs[:links] ||= {}

            if association.respond_to?(:each)
              add_links(attrs, name, association)
            else
              add_link(attrs, name, association)
            end

            if options[:add_included]
              Array(association).each do |association|
                add_included(name, association)
              end
            end
          end
        end
      end
    end
  end
end


text:
The MacClient is an open source OSX application for CIC.  This application requires CIC 4.0 SU6 or later and the ICWS license on the server.

This application is NOT and official Interactive Intelligence product and NOT officially supported. Interactive Intelligence assumes no liability. The latest version can be downloaded from https://s3.amazonaws.com/MacClient/MacClientLatest.zip and has an auto update feature built in.

Images are used from the oxygen-icons collection https://github.com/pasnox/oxygen-icons-png which has the LGPL license


text:
<?php

namespace CmsAuthentication\Options;

use CmsCommon\Form\Options\FormOptionsInterface as CommonFormOptionsInterface;

interface FormOptionsInterface extends AuthenticationOptionsInterface, CommonFormOptionsInterface
{
    /**
     * @param string $label
     * @return self
     */
    public function setIdentityFieldLabel($label);

    /**
     * @return string
     */
    public function getIdentityFieldLabel();

    /**
     * @param string $label
     * @return self
     */
    public function setCredentialFieldLabel($label);

    /**
     * @return string
    */
    public function getCredentialFieldLabel();

    /**
     * @param bool $flag
     * @return self
     */
    public function setUseRememberMeElement($flag);

    /**
     * @return bool
     */
    public function getUseRememberMeElement();
}


text:
/* TEMPLATE GENERATED TESTCASE FILE
Filename: CWE121_Stack_Based_Buffer_Overflow__CWE805_struct_alloca_loop_72a.cpp
Label Definition File: CWE121_Stack_Based_Buffer_Overflow__CWE805.label.xml
Template File: sources-sink-72a.tmpl.cpp
*/
/*
 * @description
 * CWE: 121 Stack Based Buffer Overflow
 * BadSource:  Set data pointer to the bad buffer
 * GoodSource: Set data pointer to the good buffer
 * Sinks: loop
 *    BadSink : Copy twoIntsStruct array to data using a loop
 * Flow Variant: 72 Data flow: data passed in a vector from one function to another in different source files
 *
 * */

#include "std_testcase.h"
#include <vector>

using namespace std;

namespace CWE121_Stack_Based_Buffer_Overflow__CWE805_struct_alloca_loop_72
{

#ifndef OMITBAD

/* bad function declaration */
void badSink(vector<twoIntsStruct *> dataVector);

void bad()
{
    twoIntsStruct * data;
    vector<twoIntsStruct *> dataVector;
    twoIntsStruct * dataBadBuffer = (twoIntsStruct *)ALLOCA(50*sizeof(twoIntsStruct));
    twoIntsStruct * dataGoodBuffer = (twoIntsStruct *)ALLOCA(100*sizeof(twoIntsStruct));
    /* FLAW: Set a pointer to a "small" buffer. This buffer will be used in the sinks as a destination
     * buffer in various memory copying functions using a "large" source buffer. */
    data = dataBadBuffer;
    /* Put data in a vector */
    dataVector.insert(dataVector.end(), 1, data);
    dataVector.insert(dataVector.end(), 1, data);
    dataVector.insert(dataVector.end(), 1, data);
    badSink(dataVector);
}

#endif /* OMITBAD */

#ifndef OMITGOOD

/* good function declarations */

/* goodG2B uses the GoodSource with the BadSink */
void goodG2BSink(vector<twoIntsStruct *> dataVector);

static void goodG2B()
{
    twoIntsStruct * data;
    vector<twoIntsStruct *> dataVector;
    twoIntsStruct * dataBadBuffer = (twoIntsStruct *)ALLOCA(50*sizeof(twoIntsStruct));
    twoIntsStruct * dataGoodBuffer = (twoIntsStruct *)ALLOCA(100*sizeof(twoIntsStruct));
    /* FIX: Set a pointer to a "large" buffer, thus avoiding buffer overflows in the sinks. */
    data = dataGoodBuffer;
    /* Put data in a vector */
    dataVector.insert(dataVector.end(), 1, data);
    dataVector.insert(dataVector.end(), 1, data);
    dataVector.insert(dataVector.end(), 1, data);
    goodG2BSink(dataVector);
}

void good()
{
    goodG2B();
}

#endif /* OMITGOOD */

} /* close namespace */

/* Below is the main(). It is only used when building this testcase on
 * its own for testing or for building a binary to use in testing binary
 * analysis tools. It is not used when compiling all the testcases as one
 * application, which is how source code analysis tools are tested.
 */

#ifdef INCLUDEMAIN

using namespace CWE121_Stack_Based_Buffer_Overflow__CWE805_struct_alloca_loop_72; /* so that we can use good and bad easily */

int main(int argc, char * argv[])
{
    /* seed randomness */
    srand( (unsigned)time(NULL) );
#ifndef OMITGOOD
    printLine("Calling good()...");
    good();
    printLine("Finished good()");
#endif /* OMITGOOD */
#ifndef OMITBAD
    printLine("Calling bad()...");
    bad();
    printLine("Finished bad()");
#endif /* OMITBAD */
    return 0;
}

#endif


text:
<?php

namespace Rhubarb\Patterns\Mvp\Crud\ModelForm;

use Rhubarb\Crown\Exceptions\ForceResponseException;
use Rhubarb\Crown\Response\RedirectResponse;
use Rhubarb\Crown\String\StringTools;
use Rhubarb\Leaf\Presenters\Controls\Buttons\Button;
use Rhubarb\Leaf\Presenters\Forms\MvpRestBoundForm;
use Rhubarb\Leaf\Presenters\Presenter;
use Rhubarb\Stem\Schema\SolutionSchema;

class ModelFormPresenter extends MvpRestBoundForm
{
    protected function getTitle()
    {
        if ($this->restModel !== null) {
            if ($this->restModel->isNewRecord()) {
                return "Adding a new " . strtolower(
                    StringTools::wordifyStringByUpperCase(
                        SolutionSchema::getModelNameFromClass(get_class($this->restModel))
                    )
                ) . " entry";
            } else {
                return ucfirst(
                    strtolower(
                        StringTools::wordifyStringByUpperCase(
                            SolutionSchema::getModelNameFromClass(get_class($this->restModel))
                        )
                    ) . " '" . $this->restModel->GetLabel() . "'"
                );
            }
        } else {
            if ($this->restCollection !== null) {
                return StringTools::wordifyStringByUpperCase(
                    StringTools::makePlural(
                        SolutionSchema::getModelNameFromClass($this->restCollection->getModelClassName())
                    )
                );
            } else {
                return "Untitled";
            }
        }
    }

    protected function applyModelToView()
    {
        parent::applyModelToView();

        $this->view->title = $this->getTitle();

        if ($this->restModel) {
            $this->view->newModel = $this->restModel->isNewRecord();
        }
    }

    protected function redirectAfterSave()
    {
        $this->redirectAfterCancel();
    }

    protected function redirectAfterCancel()
    {
        throw new ForceResponseException(new RedirectResponse("../"));
    }

    protected function saveRestModel()
    {
        $this->restModel->save();
        return $this->restModel;
    }

    protected final function save()
    {
        $validator = $this->createDefaultValidator();

        if ($validator && $validator instanceof Validator) {
            if (!$this->validate($validator)) {
                return;
            }
        }

        $this->saveRestModel();
        $this->redirectAfterSave();
    }

    protected function cancel()
    {
        $this->redirectAfterCancel();
    }

    protected function onPresenterAdded(Presenter $presenter)
    {
        if ($presenter->getName() == "Save") {
            if ($presenter instanceof Button) {
                $presenter->validator = $this->createDefaultClientSideValidator();
            }
        }

        parent::onPresenterAdded($presenter);
    }

    protected function configureView()
    {
        parent::configureView();

        $this->view->attachEventHandler(
            "GetRestCollection",
            function () {
                return $this->restCollection;
            }
        );

        $this->view->attachEventHandler(
            "GetRestModel",
            function () {
                return $this->getRestModel();
            }
        );

        $this->view->attachEventHandler(
            "SavePressed",
            function () {
                $this->save();
            }
        );

        $this->view->attachEventHandler(
            "CancelPressed",
            function () {
                $this->cancel();
            }
        );
    }
}


text:
package org.ovirt.engine.core.common.action;

import java.io.Serializable;
import org.ovirt.engine.core.common.businessentities.DiskImage;
import org.ovirt.engine.core.common.businessentities.ImageDbOperationScope;
import org.ovirt.engine.core.compat.Guid;

public class RemoveImageParameters extends ImagesContainterParametersBase implements Serializable {
    private static final long serialVersionUID = -225670698019388215L;

    private DiskImage diskImage;
    private ImageDbOperationScope dbOperationScope;
    private boolean removeFromSnapshots;
    private boolean shouldLockImage;

    public RemoveImageParameters(Guid imageId) {
        super(imageId, null);
        setForceDelete(false);
        dbOperationScope = ImageDbOperationScope.IMAGE;
        shouldLockImage= true;
    }

    public RemoveImageParameters() {
    }

    public DiskImage getDiskImage() {
        return diskImage;
    }

    public void setDiskImage(DiskImage value) {
        diskImage = value;
    }

    public ImageDbOperationScope getDbOperationScope() {
        return dbOperationScope;
    }

    public boolean isShouldLockImage() {
        return shouldLockImage;
    }

    public void setShouldLockImage(boolean shouldLockImage) {
        this.shouldLockImage = shouldLockImage;
    }

    public void setDbOperationScope(ImageDbOperationScope dbOperationScope) {
        this.dbOperationScope = dbOperationScope;
    }

    public boolean isRemoveFromSnapshots() {
        return removeFromSnapshots;
    }

    public void setRemoveFromSnapshots(boolean removeFromSnapshots) {
        this.removeFromSnapshots = removeFromSnapshots;
    }
}


text:
<?php

namespace Symfony\Bridge\Monolog\Processor;

use Monolog\Processor\WebProcessor as BaseWebProcessor;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

/**
 * WebProcessor override to read from the HttpFoundation's Request
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class WebProcessor extends BaseWebProcessor
{
    public function __construct()
    {
        // Pass an empty array as the default null value would access $_SERVER
        parent::__construct(array());
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        if ($event->isMasterRequest()) {
            $this->serverData = $event->getRequest()->server->all();
        }
    }
}


text:
#region MIT License

#endregion

#if UseDouble
using Scalar = System.Double;
#else
using Scalar = System.Single;
#endif
using System;
using Tao.OpenGl;

namespace Graphics2DDotNet
{
    public sealed class LineWidthProperty : IDrawProperty
    {
        Scalar lineWidth;
        public LineWidthProperty(Scalar lineWidth)
        {
            this.lineWidth = lineWidth;
        }
        public void Apply()
        {
            Gl.glLineWidth((System.Single)lineWidth);
        }
        public void Remove()
        {
        }
    }

}


text:
require 'formula'

class Rdate < Formula
  homepage 'http://www.aelius.com/njh/rdate/'
  url 'http://www.aelius.com/njh/rdate/rdate-1.5.tar.gz'
  sha1 'd7212503907db741ab53c4dd13ed141702d1809b'

  def install
    system "./configure", "--disable-debug", "--disable-dependency-tracking",
                          "--prefix=#{prefix}",
                          "--mandir=#{man}"
    system "make install"
  end
end


text:
import CalendarAddBox from './calendarAddBox';
import CalendarList from './calendarList';
import React from 'react';

const calendarColumn = props => {
    const plantNames = props.plants.map(plant => plant.name);
    return (

{props.title}

            <CalendarAddBox
                onAdd={props.onCreateCalendarEvent}
                plantNames={plantNames} />
            <CalendarList
                calendarEvents={props.harvestEvents}
                eventType="harvest"
                onHarvestAdded={props.onHarvestAdded}
                onHarvestDelayed={props.onHarvestDelayed}
                onPlantDied={props.onPlantDied}
                plants={props.plants} />
            <CalendarList
                calendarEvents={props.plantedEvents}
                eventType="planted"
                onHarvestAdded={props.onHarvestAdded}
                onHarvestDelayed={props.onHarvestDelayed}
                onPlantDied={props.onPlantDied}
                plants={props.plants} />

    );
};

calendarColumn.propTypes = {
    harvestEvents: React.PropTypes.array.isRequired,
    onCreateCalendarEvent: React.PropTypes.func.isRequired,
    onHarvestAdded: React.PropTypes.func.isRequired,
    onHarvestDelayed: React.PropTypes.func.isRequired,
    onPlantDied: React.PropTypes.func.isRequired,
    plantedEvents: React.PropTypes.array.isRequired,
    plants: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
    title: React.PropTypes.string.isRequired,
};

export default calendarColumn;


text:
process.title = 'node-wargames';
process.addListener('uncaughtException', function (err, stack) {
	console.log('Caught exception: ' + err);
	console.log(err.stack.split('\n'));
});
var connect = require('connect');
var assetManager = require('connect-assetmanager');
var assetHandler = require('connect-assetmanager-handlers');
var express = require('express');
var assets = assetManager({
	'js': {
		'route': /\/static\/js\/[0-9]+\/.*\.js/
		, 'path': './public/js/'
		, 'dataType': 'js'
		, 'files': [
			'http://code.jquery.com/jquery-latest.js'
			, 'http://cdn.socket.io/stable/socket.io.js'
			, 'raphael.js'
			, 'map.js'
			, 'jquery.wargames.js'
		]
		, 'preManipulate': {
			'^': [
				function (file, path, index, isLast, callback) {
					if (path.match(/jquery.wargames/)) {
						callback(file.replace(/'#socketIoPort#'/, port));
					} else {
						callback(file);
					}
				}
			]
		}
	}, 'css': {
		'route': /\/static\/css\/[0-9]+\/.*\.css/
		, 'path': './public/css/'
		, 'dataType': 'css'
		, 'files': [
			'wargames.css'
		]
	}
});

var port = 5656;
var listenip = '127.0.0.1';

var app = module.exports = express.createServer();

app.configure(function() {
	app.set('view engine', 'ejs');
	app.set('views', __dirname + '/views');
});

app.configure(function() {
	app.use(connect.logger({ format: ':req[x-real-ip]\t:status\t:method\t:url\t' }));
	app.use(assets);
	app.use(connect.staticProvider(__dirname + '/public'));
});

app.dynamicHelpers({
	'cacheTimeStamps': function(req, res) {
		return assets.cacheTimestamps;
	}
});
app.get(/.*/, function(req, res) {
	res.render('layout');
});

app.listen(port, listenip);

var Wargames = require(__dirname+'/lib/wargames');
new Wargames(app, {
	ircNetwork: 'irc.freenode.net'
	, ircChannel: '#testingchannel9'
	, ircBotNick: 'yourbot'
	, ircUserName: 'yourbot'
	, ircRealName: 'yourbot'
	, cachePath: '/tmp/wargames.cache.json'
	, ldbname: 'somedbname'
	, ldbtable: 'somedbtable'
	, ldbuser: 'someuser'
	, ldbpass: 'somepass'
	, rssList: [
		{ name: 'github',
		url: 'http://feeds.feedburner.com/RecentCommitsToSick-beardmaster?format=atom',
		interval: 10*60*1000 },
		{ name: 'twitter',
		url: 'http://twitter.com/statuses/user_timeline/44425690.rss',
		interval: 20*60*1000 } ]
	, bitlyUser: 'somebitlyuser'
	, bitlyKey: 'Z_yourapykey'
});


text:
package org.springframework.web.servlet.mvc.method.annotation;

import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.util.WebUtils;

/**
 * A convenient base class for {@link ControllerAdvice @ControllerAdvice} classes
 * that wish to provide centralized exception handling across all
 * {@code @RequestMapping} methods through {@code @ExceptionHandler} methods.
 *
 *
This base class provides an {@code @ExceptionHandler} method for handling
 * internal Spring MVC exceptions. This method returns a {@code ResponseEntity}
 * for writing to the response with a {@link HttpMessageConverter message converter},
 * in contrast to
 * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
 * DefaultHandlerExceptionResolver} which returns a
 * {@link org.springframework.web.servlet.ModelAndView ModelAndView}.
 *
 *
If there is no need to write error content to the response body, or when
 * using view resolution (e.g., via {@code ContentNegotiatingViewResolver}),
 * then {@code DefaultHandlerExceptionResolver} is good enough.
 *
 *
Note that in order for an {@code @ControllerAdvice} sub-class to be
 * detected, {@link ExceptionHandlerExceptionResolver} must be configured.
 *
 * @author Rossen Stoyanchev
 * @since 3.2
 * @see #handleException(Exception, WebRequest)
 * @see org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
 */
public abstract class ResponseEntityExceptionHandler {

	/**
	 * Log category to use when no mapped handler is found for a request.
	 * @see #pageNotFoundLogger
	 */
	public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";

	/**
	 * Specific logger to use when no mapped handler is found for a request.
	 * @see #PAGE_NOT_FOUND_LOG_CATEGORY
	 */
	protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);

	/**
	 * Common logger for use in subclasses.
	 */
	protected final Log logger = LogFactory.getLog(getClass());

	/**
	 * Provides handling for standard Spring MVC exceptions.
	 * @param ex the target exception
	 * @param request the current request
	 */
	@ExceptionHandler({
			HttpRequestMethodNotSupportedException.class,
			HttpMediaTypeNotSupportedException.class,
			HttpMediaTypeNotAcceptableException.class,
			MissingPathVariableException.class,
			MissingServletRequestParameterException.class,
			ServletRequestBindingException.class,
			ConversionNotSupportedException.class,
			TypeMismatchException.class,
			HttpMessageNotReadableException.class,
			HttpMessageNotWritableException.class,
			MethodArgumentNotValidException.class,
			MissingServletRequestPartException.class,
			BindException.class,
			NoHandlerFoundException.class,
			AsyncRequestTimeoutException.class
		})
	public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
		HttpHeaders headers = new HttpHeaders();
		if (ex instanceof HttpRequestMethodNotSupportedException) {
			HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
			return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
		}
		else if (ex instanceof HttpMediaTypeNotSupportedException) {
			HttpStatus status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
			return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
		}
		else if (ex instanceof HttpMediaTypeNotAcceptableException) {
			HttpStatus status = HttpStatus.NOT_ACCEPTABLE;
			return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, headers, status, request);
		}
		else if (ex instanceof MissingPathVariableException) {
			HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
			return handleMissingPathVariable((MissingPathVariableException) ex, headers, status, request);
		}
		else if (ex instanceof MissingServletRequestParameterException) {
			HttpStatus status = HttpStatus.BAD_REQUEST;
			return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, headers, status, request);
		}
		else if (ex instanceof ServletRequestBindingException) {
			HttpStatus status = HttpStatus.BAD_REQUEST;
			return handleServletRequestBindingException((ServletRequestBindingException) ex, headers, status, request);
		}
		else if (ex instanceof ConversionNotSupportedException) {
			HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
			return handleConversionNotSupported((ConversionNotSupportedException) ex, headers, status, request);
		}
		else if (ex instanceof TypeMismatchException) {
			HttpStatus status = HttpStatus.BAD_REQUEST;
			return handleTypeMismatch((TypeMismatchException) ex, headers, status, request);
		}
		else if (ex instanceof HttpMessageNotReadableException) {
			HttpStatus status = HttpStatus.BAD_REQUEST;
			return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, headers, status, request);
		}
		else if (ex instanceof HttpMessageNotWritableException) {
			HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
			return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, headers, status, request);
		}
		else if (ex instanceof MethodArgumentNotValidException) {
			HttpStatus status = HttpStatus.BAD_REQUEST;
			return handleMethodArgumentNotValid((MethodArgumentNotValidException) ex, headers, status, request);
		}
		else if (ex instanceof MissingServletRequestPartException) {
			HttpStatus status = HttpStatus.BAD_REQUEST;
			return handleMissingServletRequestPart((MissingServletRequestPartException) ex, headers, status, request);
		}
		else if (ex instanceof BindException) {
			HttpStatus status = HttpStatus.BAD_REQUEST;
			return handleBindException((BindException) ex, headers, status, request);
		}
		else if (ex instanceof NoHandlerFoundException) {
			HttpStatus status = HttpStatus.NOT_FOUND;
			return handleNoHandlerFoundException((NoHandlerFoundException) ex, headers, status, request);
		}
		else if (ex instanceof AsyncRequestTimeoutException) {
			HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE;
			return handleAsyncRequestTimeoutException(
					(AsyncRequestTimeoutException) ex, headers, status, request);
		}
		else {
			if (logger.isWarnEnabled()) {
				logger.warn("Unknown exception type: " + ex.getClass().getName());
			}
			HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
			return handleExceptionInternal(ex, null, headers, status, request);
		}
	}

	/**
	 * A single place to customize the response body of all Exception types.
	 *
The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
	 * request attribute and creates a {@link ResponseEntity} from the given
	 * body, headers, and status.
	 * @param ex the exception
	 * @param body the body for the response
	 * @param headers the headers for the response
	 * @param status the response status
	 * @param request the current request
	 */
	protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
			request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
		}
		return new ResponseEntity<>(body, headers, status);
	}

	/**
	 * Customize the response for HttpRequestMethodNotSupportedException.
	 *
This method logs a warning, sets the "Allow" header, and delegates to
	 * {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		pageNotFoundLogger.warn(ex.getMessage());

		Set<HttpMethod> supportedMethods = ex.getSupportedHttpMethods();
		if (!supportedMethods.isEmpty()) {
			headers.setAllow(supportedMethods);
		}
		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for HttpMediaTypeNotSupportedException.
	 *
This method sets the "Accept" header and delegates to
	 * {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
		if (!CollectionUtils.isEmpty(mediaTypes)) {
			headers.setAccept(mediaTypes);
		}

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for HttpMediaTypeNotAcceptableException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for MissingPathVariableException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 * @since 4.2
	 */
	protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for MissingServletRequestParameterException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for ServletRequestBindingException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for ConversionNotSupportedException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for TypeMismatchException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers,
			HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for HttpMessageNotReadableException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for HttpMessageNotWritableException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for MethodArgumentNotValidException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for MissingServletRequestPartException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for BindException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers,
			HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for NoHandlerFoundException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 * @since 4.0
	 */
	protected ResponseEntity<Object> handleNoHandlerFoundException(
			NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, null, headers, status, request);
	}

	/**
	 * Customize the response for NoHandlerFoundException.
	 *
This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param webRequest the current request
	 * @return a {@code ResponseEntity} instance
	 * @since 4.2.8
	 */
	protected ResponseEntity<Object> handleAsyncRequestTimeoutException(
			AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) {

		if (webRequest instanceof ServletWebRequest) {
			ServletWebRequest servletRequest = (ServletWebRequest) webRequest;
			HttpServletRequest request = servletRequest.getNativeRequest(HttpServletRequest.class);
			HttpServletResponse response = servletRequest.getNativeResponse(HttpServletResponse.class);
			if (response.isCommitted()) {
				logger.error("Async timeout for " + request.getMethod() + " [" +  request.getRequestURI() + "]");
				return null;
			}
		}
		return handleExceptionInternal(ex, null, headers, status, webRequest);
	}

}


text:
[Week 3 Home](../../)

# U1.W3: Calculate a Letter Grade

## Learning Competencies
- Define local variables and methods
- Use conditional statements
- Apply basic Ruby methods to variables

## Summary
Create a method `get_grade` that accepts an average in the class and returns the letter grade as a `String`.

It should only return one of `'A'`, `'B'`, `'C'`, etc.  Don't worry about `+` and `-` grades.

For example,

```ruby
get_grade(89) # => returns "B", *not* "B+"
get_grade(70) # => returns "C"
```

## Releases

## Release 0: Pseudocode
Consider what you need to do to solve the challenge. You can write it down if it will help you, or you can move on to the next release.

## Release 1: Initial Solution
Code your solution in the [my_solution.rb](my_solution.rb) file.

## Release 2: Test
Run the tests to make sure your code passes!

Remember, type `rspec` followed by the spec file name (and make sure you are in the proper directory).

## Release 3: Refactor
Make any necessary changes based on the tests. Then run the tests again.

Make sure all the tests pass before moving on.

## Release 4: Publish
When your solution is passing all of the tests, commit and push your changes!


text:
const validate = require('./validate');

// Pass it an old middleware and return a new one 'ctx => promise'
module.exports = middle => {

  // Validate it early so no requests need to be made
  validate.middleware(middle);

  // Create and return the modern middleware function
  return ctx => new Promise((resolve, reject) => {
    validate.context(ctx);

    // It can handle both success or errors. Pass the right ctx
    const next = err => err ? reject(err) : resolve();

    // Call the old middleware
    middle(ctx.req, ctx.res, next);
  });
};


text:
using System;
using System.Linq;

namespace Microsoft.Azure.Management.Automation.Models
{
    /// <summary>
    /// The parameters supplied to the list job operation.
    /// </summary>
    public partial class DscNodeReportListParameters
    {
        private string _endTime;

        /// <summary>
        /// Optional. For the end time filter the value should be a datetime
        /// string in UTC format as defined in ISO 8601. For example,
        /// 2014-09-25T17:49:17.2252204Z
        /// </summary>
        public string EndTime
        {
            get { return this._endTime; }
            set { this._endTime = value; }
        }

        private Guid _nodeId;

        /// <summary>
        /// Optional. Gets or sets the the node id.
        /// </summary>
        public Guid NodeId
        {
            get { return this._nodeId; }
            set { this._nodeId = value; }
        }

        private string _startTime;

        /// <summary>
        /// Optional. For the start time filter the value should be a datetime
        /// string in UTC format as defined in ISO 8601. For example,
        /// 2014-09-25T17:49:17.2252204Z
        /// </summary>
        public string StartTime
        {
            get { return this._startTime; }
            set { this._startTime = value; }
        }

        private string _type;

        /// <summary>
        /// Optional. Gets or sets the type of the node report.
        /// </summary>
        public string Type
        {
            get { return this._type; }
            set { this._type = value; }
        }

        /// <summary>
        /// Initializes a new instance of the DscNodeReportListParameters class.
        /// </summary>
        public DscNodeReportListParameters()
        {
        }
    }
}


text:
package org.apache.flink.table.types.inference;

import org.apache.flink.annotation.Internal;
import org.apache.flink.table.api.TableException;
import org.apache.flink.table.api.ValidationException;
import org.apache.flink.table.catalog.DataTypeFactory;
import org.apache.flink.table.functions.FunctionDefinition;
import org.apache.flink.table.functions.FunctionKind;
import org.apache.flink.table.types.DataType;
import org.apache.flink.table.types.inference.utils.AdaptedCallContext;
import org.apache.flink.table.types.inference.utils.UnknownCallContext;
import org.apache.flink.table.types.logical.LogicalTypeRoot;

import javax.annotation.Nullable;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.apache.flink.table.types.logical.utils.LogicalTypeCasts.supportsImplicitCast;
import static org.apache.flink.table.types.logical.utils.LogicalTypeChecks.hasRoot;

/**
 * Utility for performing type inference.
 *
 *
The following steps summarize the envisioned type inference process. Not all features are implemented
 * or exposed through the API yet (*).
 *
 *

 *
1. Validate number of arguments.
 *
2. (*) Apply assignment operators on the call by permuting operands and adding default values. These
 *        are preparations for {@link CallContext}.
 *
3. For resolving unknown (NULL) operands: Access the outer wrapping call and try to get its operand
 *        type for the return type of the actual call. E.g. for {@code takes_string(this_function(NULL))}
 *        infer operands from {@code takes_string(NULL)} and use the inferred string type as the return
 *        type of {@code this_function(NULL)}.
 *
4. Try infer unknown operands, fail if not possible.
 *
5. (*) Check the usage of DEFAULT operands are correct using validator.isOptional().
 *
6. Perform input type validation.
 *
7. (Optional) Infer accumulator type.
 *
8. Infer return type.
 *
9. (*) In the planner: Call the strategies again at any point in time to enrich a DataType that
 *        has been created from a logical type with a conversion class.
 *
10. (*) In the planner: Check for an implementation evaluation method matching the operands. The
 *        matching happens class-based. Thus, for example, eval(Object) is valid for (INT). Or eval(Object...)
 *        is valid for (INT, STRING). We rely on the conversion classes specified by DataType.
 *

 */
@Internal
public final class TypeInferenceUtil {

	/**
	 * Runs the entire type inference process.
	 *
	 * @param typeInference type inference of the current call
	 * @param callContext call context of the current call
	 * @param surroundingInfo information about the outer wrapping call of a current function call for
	 *                        performing input type inference
	 */
	public static Result runTypeInference(
			TypeInference typeInference,
			CallContext callContext,
			@Nullable SurroundingInfo surroundingInfo) {
		try {
			return runTypeInferenceInternal(typeInference, callContext, surroundingInfo);
		} catch (ValidationException e) {
			throw createInvalidCallException(callContext, e);
		} catch (Throwable t) {
			throw createUnexpectedException(callContext, t);
		}
	}

	/**
	 * Adapts the call's argument if necessary.
	 *
	 *
This includes casts that need to be inserted, reordering of arguments (*), or insertion of default
	 * values (*) where (*) is future work.
	 */
	public static AdaptedCallContext adaptArguments(
			TypeInference typeInference,
			CallContext callContext,
			@Nullable DataType outputType) {
		return adaptArguments(typeInference, callContext, outputType, true);
	}

	private static AdaptedCallContext adaptArguments(
			TypeInference typeInference,
			CallContext callContext,
			@Nullable DataType outputType,
			boolean throwOnInferInputFailure) {
		final List<DataType> actualTypes = callContext.getArgumentDataTypes();

		typeInference.getTypedArguments()
			.ifPresent((dataTypes) -> {
				if (actualTypes.size() != dataTypes.size()) {
					throw new ValidationException(
						String.format(
							"Invalid number of arguments. %d arguments expected after argument expansion but %d passed.",
							dataTypes.size(),
							actualTypes.size()));
				}
			});

		final AdaptedCallContext adaptedCallContext = inferInputTypes(
			typeInference,
			callContext,
			outputType,
			throwOnInferInputFailure
		);

		// final check if the call is valid after casting
		final List<DataType> expectedTypes = adaptedCallContext.getArgumentDataTypes();
		for (int pos = 0; pos < actualTypes.size(); pos++) {
			final DataType expectedType = expectedTypes.get(pos);
			final DataType actualType = actualTypes.get(pos);
			if (!supportsImplicitCast(actualType.getLogicalType(), expectedType.getLogicalType())) {
				throw new ValidationException(
					String.format(
						"Invalid argument type at position %d. Data type %s expected but %s passed.",
						pos,
						expectedType,
						actualType));
			}
		}

		return adaptedCallContext;
	}

	/**
	 * Infers an output type using the given {@link TypeStrategy}. It assumes that input arguments have
	 * been adapted before if necessary.
	 */
	public static DataType inferOutputType(CallContext callContext, TypeStrategy outputTypeStrategy) {
		final Optional<DataType> potentialOutputType = outputTypeStrategy.inferType(callContext);
		if (!potentialOutputType.isPresent()) {
			throw new ValidationException("Could not infer an output type for the given arguments.");
		}
		final DataType outputType = potentialOutputType.get();

		if (isUnknown(outputType)) {
			throw new ValidationException(
				"Could not infer an output type for the given arguments. Untyped NULL received.");
		}
		return outputType;
	}

	/**
	 * Generates a signature of the given {@link FunctionDefinition}.
	 */
	public static String generateSignature(
			TypeInference typeInference,
			String name,
			FunctionDefinition definition) {
		if (typeInference.getNamedArguments().isPresent() || typeInference.getTypedArguments().isPresent()) {
			return formatNamedOrTypedArguments(name, typeInference);
		}
		return typeInference.getInputTypeStrategy().getExpectedSignatures(definition)
			.stream()
			.map(s -> formatSignature(name, s))
			.collect(Collectors.joining("\n"));
	}

	/**
	 * Returns an exception for invalid input arguments.
	 */
	public static ValidationException createInvalidInputException(
			TypeInference typeInference,
			CallContext callContext,
			ValidationException cause) {
		return new ValidationException(
			String.format(
				"Invalid input arguments. Expected signatures are:\n%s",
				generateSignature(
					typeInference,
					callContext.getName(),
					callContext.getFunctionDefinition())
			),
			cause);
	}

	/**
	 * Returns an exception for an invalid call to a function.
	 */
	public static ValidationException createInvalidCallException(
			CallContext callContext,
			ValidationException cause) {
		return new ValidationException(
			String.format(
				"Invalid function call:\n%s(%s)",
				callContext.getName(),
				callContext.getArgumentDataTypes().stream()
					.map(DataType::toString)
					.collect(Collectors.joining(", "))),
			cause);
	}

	/**
	 * Returns an exception for an unexpected error during type inference.
	 */
	public static TableException createUnexpectedException(
			CallContext callContext,
			Throwable cause) {
		return new TableException(
			String.format(
				"Unexpected error in type inference logic of function '%s'. This is a bug.",
				callContext.getName()),
			cause);
	}

	/**
	 * Information what the outer world (i.e. an outer wrapping call) expects from the current
	 * function call. This can be helpful for an {@link InputTypeStrategy}.
	 *
	 * @see CallContext#getOutputDataType()
	 */
	public static final class SurroundingInfo {

		private final String name;

		private final FunctionDefinition functionDefinition;

		private final TypeInference typeInference;

		private final int argumentCount;

		private final int innerCallPosition;

		public SurroundingInfo(
				String name,
				FunctionDefinition functionDefinition,
				TypeInference typeInference,
				int argumentCount,
				int innerCallPosition) {
			this.name = name;
			this.functionDefinition = functionDefinition;
			this.typeInference = typeInference;
			this.argumentCount = argumentCount;
			this.innerCallPosition = innerCallPosition;
		}

		private Optional<DataType> inferOutputType(DataTypeFactory typeFactory) {
			final boolean isValidCount = validateArgumentCount(
				typeInference.getInputTypeStrategy().getArgumentCount(),
				argumentCount,
				false);
			if (!isValidCount) {
				return Optional.empty();
			}
			// for "takes_string(this_function(NULL))" simulate "takes_string(NULL)"
			// for retrieving the output type of "this_function(NULL)"
			final CallContext callContext = new UnknownCallContext(
				typeFactory,
				name,
				functionDefinition,
				argumentCount);

			// We might not be able to infer the input types at this moment, if the surrounding function
			// does not provide an explicit input type strategy.
			final AdaptedCallContext adaptedContext = adaptArguments(typeInference, callContext, null, false);
			return typeInference.getInputTypeStrategy()
				.inferInputTypes(adaptedContext, false)
				.map(dataTypes -> dataTypes.get(innerCallPosition));
		}
	}

	/**
	 * The result of a type inference run. It contains information about how arguments need to be
	 * adapted in order to comply with the function's signature.
	 *
	 *
This includes casts that need to be inserted, reordering of arguments (*), or insertion of default
	 * values (*) where (*) is future work.
	 */
	public static final class Result {

		private final List<DataType> expectedArgumentTypes;

		private final @Nullable DataType accumulatorDataType;

		private final DataType outputDataType;

		public Result(
				List<DataType> expectedArgumentTypes,
				@Nullable DataType accumulatorDataType,
				DataType outputDataType) {
			this.expectedArgumentTypes = expectedArgumentTypes;
			this.accumulatorDataType = accumulatorDataType;
			this.outputDataType = outputDataType;
		}

		public List<DataType> getExpectedArgumentTypes() {
			return expectedArgumentTypes;
		}

		public Optional<DataType> getAccumulatorDataType() {
			return Optional.ofNullable(accumulatorDataType);
		}

		public DataType getOutputDataType() {
			return outputDataType;
		}
	}

	// --------------------------------------------------------------------------------------------

	private static Result runTypeInferenceInternal(
			TypeInference typeInference,
			CallContext callContext,
			@Nullable SurroundingInfo surroundingInfo) {
		try {
			validateArgumentCount(
				typeInference.getInputTypeStrategy().getArgumentCount(),
				callContext.getArgumentDataTypes().size(),
				true);
		} catch (ValidationException e) {
			throw createInvalidInputException(
				typeInference,
				callContext,
				e);
		}

		final AdaptedCallContext adaptedCallContext;
		try {
			// use information of surrounding call to determine output type of this call
			final DataType outputType;
			if (surroundingInfo != null) {
				outputType = surroundingInfo.inferOutputType(callContext.getDataTypeFactory())
					.orElse(null);
			} else {
				outputType = null;
			}

			adaptedCallContext = adaptArguments(
				typeInference,
				callContext,
				outputType);
		} catch (ValidationException e) {
			throw createInvalidInputException(
				typeInference,
				callContext,
				e);
		}

		// infer output type first for better error message
		// (logically an accumulator type should be inferred first)
		final DataType outputType = inferOutputType(
			adaptedCallContext,
			typeInference.getOutputTypeStrategy());

		final DataType accumulatorType = inferAccumulatorType(
			adaptedCallContext,
			outputType,
			typeInference.getAccumulatorTypeStrategy().orElse(null));

		return new Result(
			adaptedCallContext.getArgumentDataTypes(),
			accumulatorType,
			outputType);
	}

	private static String formatNamedOrTypedArguments(String name, TypeInference typeInference) {
		final Optional<List<String>> optionalNames = typeInference.getNamedArguments();
		final Optional<List<DataType>> optionalDataTypes = typeInference.getTypedArguments();
		final int count = Math.max(
			optionalNames.map(List::size).orElse(0),
			optionalDataTypes.map(List::size).orElse(0));
		final String arguments = IntStream.range(0, count)
			.mapToObj(pos -> {
				final StringBuilder builder = new StringBuilder();
				optionalNames.ifPresent(names -> builder.append(names.get(pos)).append(" => "));
				optionalDataTypes.ifPresent(dataTypes -> builder.append(dataTypes.get(pos).toString()));
				return builder.toString();
			})
			.collect(Collectors.joining(", "));
		return String.format("%s(%s)", name, arguments);
	}

	private static String formatSignature(String name, Signature s) {
		final String arguments = s.getArguments()
			.stream()
			.map(TypeInferenceUtil::formatArgument)
			.collect(Collectors.joining(", "));
		return String.format("%s(%s)", name, arguments);
	}

	private static String formatArgument(Signature.Argument arg) {
		final StringBuilder stringBuilder = new StringBuilder();
		arg.getName().ifPresent(n -> stringBuilder.append(n).append(" "));
		stringBuilder.append(arg.getType());
		return stringBuilder.toString();
	}

	private static boolean validateArgumentCount(
			ArgumentCount argumentCount,
			int actualCount,
			boolean throwOnFailure) {
		final int minCount = argumentCount.getMinCount().orElse(0);
		if (actualCount < minCount) {
			if (throwOnFailure) {
				throw new ValidationException(
					String.format(
						"Invalid number of arguments. At least %d arguments expected but %d passed.",
						minCount,
						actualCount));
			}
			return false;
		}
		final int maxCount = argumentCount.getMaxCount().orElse(Integer.MAX_VALUE);
		if (actualCount > maxCount) {
			if (throwOnFailure) {
				throw new ValidationException(
					String.format(
						"Invalid number of arguments. At most %d arguments expected but %d passed.",
						maxCount,
						actualCount));
			}
			return false;
		}
		if (!argumentCount.isValidCount(actualCount)) {
			if (throwOnFailure) {
				throw new ValidationException(
					String.format(
						"Invalid number of arguments. %d arguments passed.",
						actualCount));
			}
			return false;
		}
		return true;
	}

	private static AdaptedCallContext inferInputTypes(
			TypeInference typeInference,
			CallContext callContext,
			@Nullable DataType outputType,
			boolean throwOnFailure) {

		final AdaptedCallContext adaptedCallContext = new AdaptedCallContext(callContext, outputType);

		// typed arguments have highest priority
		typeInference.getTypedArguments().ifPresent(adaptedCallContext::setExpectedArguments);

		final List<DataType> inferredDataTypes = typeInference.getInputTypeStrategy()
			.inferInputTypes(adaptedCallContext, throwOnFailure)
			.orElse(null);

		if (inferredDataTypes != null) {
			adaptedCallContext.setExpectedArguments(inferredDataTypes);
		} else if (throwOnFailure) {
			throw new ValidationException("Invalid input arguments.");
		}

		return adaptedCallContext;
	}

	private static @Nullable DataType inferAccumulatorType(
			CallContext callContext,
			DataType outputType,
			@Nullable TypeStrategy accumulatorTypeStrategy) {
		if (callContext.getFunctionDefinition().getKind() != FunctionKind.TABLE_AGGREGATE &&
				callContext.getFunctionDefinition().getKind() != FunctionKind.AGGREGATE) {
			return null;
		}

		// an accumulator might be an internal feature of the planner, therefore it is not
		// mandatory here; we assume the output type to be the accumulator type in this case
		if (accumulatorTypeStrategy == null) {
			return outputType;
		}
		final Optional<DataType> potentialAccumulatorType = accumulatorTypeStrategy.inferType(callContext);
		if (!potentialAccumulatorType.isPresent()) {
			throw new ValidationException("Could not infer an accumulator type for the given arguments.");
		}
		final DataType accumulatorType = potentialAccumulatorType.get();

		if (isUnknown(accumulatorType)) {
			throw new ValidationException("Could not infer an accumulator type for the given arguments. Untyped NULL received.");
		}

		return accumulatorType;
	}

	private static boolean isUnknown(DataType dataType) {
		return hasRoot(dataType.getLogicalType(), LogicalTypeRoot.NULL);
	}

	private TypeInferenceUtil() {
		// no instantiation
	}
}