text:
<?php

namespace Chamilo\Core\Repository\ContentObject\Assignment\Display\Component;

use Chamilo\Application\Weblcms\Rights\WeblcmsRights;
use Chamilo\Core\Repository\Common\Rendition\ContentObjectRendition;
use Chamilo\Core\Repository\Common\Rendition\ContentObjectRenditionImplementation;
use Chamilo\Core\Repository\ContentObject\Assignment\Display\Form\ScoreFormType;
use Chamilo\Core\Repository\ContentObject\Assignment\Display\FormHandler\SetScoreFormHandler;
use Chamilo\Core\Repository\ContentObject\Assignment\Display\Manager;
use Chamilo\Core\Repository\ContentObject\Assignment\Display\Service\EntryNavigator;
use Chamilo\Core\Repository\ContentObject\Assignment\Display\Service\ScoreService;
use Chamilo\Core\Repository\ContentObject\Assignment\Display\Bridge\Storage\DataClass\Entry;
use Chamilo\Core\Repository\ContentObject\Assignment\Display\Bridge\Storage\DataClass\Score;
use Chamilo\Core\Repository\ContentObject\Assignment\Display\Table\Entry\EntryTableParameters;
use Chamilo\Core\Repository\ContentObject\Presence\Display\Service\QRService;
use Chamilo\Core\Repository\Storage\DataClass\ContentObject;
use Chamilo\Libraries\Architecture\Application\ApplicationConfiguration;
use Chamilo\Libraries\Architecture\Application\ApplicationConfigurationInterface;
use Chamilo\Libraries\Architecture\Exceptions\NotAllowedException;
use Chamilo\Libraries\Format\Structure\ActionBar\Button;
use Chamilo\Libraries\Format\Structure\ActionBar\ButtonGroup;
use Chamilo\Libraries\Format\Structure\ActionBar\ButtonToolBar;
use Chamilo\Libraries\Format\Structure\ActionBar\DropdownButton;
use Chamilo\Libraries\Format\Structure\ActionBar\Renderer\ButtonToolBarRenderer;
use Chamilo\Libraries\Format\Structure\ActionBar\SplitDropdownButton;
use Chamilo\Libraries\Format\Structure\ActionBar\SubButton;
use Chamilo\Libraries\Format\Structure\ActionBar\SubButtonHeader;
use Chamilo\Libraries\Format\Structure\BreadcrumbTrail;
use Chamilo\Libraries\Format\Structure\Glyph\FontAwesomeGlyph;
use Chamilo\Libraries\Format\Structure\ToolbarItem;
use Chamilo\Libraries\Format\Table\Interfaces\TableSupport;
use Chamilo\Libraries\Storage\DataClass\DataClass;
use Chamilo\Libraries\Translation\Translation;
use Chamilo\Libraries\Utilities\DatetimeUtilities;
use Chamilo\Libraries\Utilities\Utilities;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;

/**
 *
 * @package Chamilo\Core\Repository\ContentObject\Assignment\Display\Component
 * @author Hans De Bisschop <hans.de.bisschop@ehb.be>
 * @author Magali Gillard <magali.gillard@ehb.be>
 * @author Eduard Vossen <eduard.vossen@ehb.be>
 */
class EntryComponent extends Manager implements \Chamilo\Core\Repository\Feedback\FeedbackSupport, TableSupport
{
    const PARAM_RUBRIC_ENTRY = 'RubricEntry';
    const PARAM_RUBRIC_RESULTS = 'RubricResult';

    /**
     * @var ScoreService
     */
    protected $scoreService;

    /**
     * @var Score
     */
    protected $score;

    /**
     *
     * @var ButtonToolBarRenderer
     */
    private $buttonToolbarRenderer;

    /**
     * @var EntryNavigator
     */
    protected $entryNavigator;

    public function __construct(ApplicationConfigurationInterface $applicationConfiguration)
    {
        parent::__construct($applicationConfiguration);

        $this->scoreService = new ScoreService($this->getAssignmentServiceBridge());
    }

    /**
     * @return string
     * @throws NotAllowedException
     * @throws \Exception
     * @throws \Twig_Error_Loader
     * @throws \Twig_Error_Runtime
     * @throws \Twig_Error_Syntax
     */
    public function run()
    {
        $this->initializeEntry();
        $this->checkAccessRights();

        $scoreForm = $this->getScoreForm();
        $this->processSubmittedData($scoreForm);
        $this->getRubricBridge()->setEntryURL($this->get_url([self::PARAM_RUBRIC_ENTRY => 1], [self::PARAM_RUBRIC_RESULTS]));
        $this->getRubricBridge()->setAllowCreateFromExistingRubric($this->supportsRubrics() && $this->canUseRubricEvaluation());

        return $this->getTwig()->render(
            Manager::context() . ':EntryViewer.html.twig',
            $this->getTemplateProperties($scoreForm)
        );
    }

    /**
     *
     */
    protected function initializeEntry()
    {
        try
        {
            parent::initializeEntry();
        }
        catch (\Exception $ex)
        {
            $this->entry =
                $this->getAssignmentServiceBridge()->findLastEntryForEntity(
                    $this->getEntityType(), $this->getEntityIdentifier()
                );
        }

        if (!$this->entry instanceof Entry)
        {
            if ($this->getAssignmentServiceBridge()->canEditAssignment())
            {
                $this->redirect(null, false, [self::PARAM_ACTION => self::ACTION_VIEW]);
            }

            $breadcrumbTrail = BreadcrumbTrail::getInstance();
            $breadcrumbTrail->get_last()->set_name(
                Translation::getInstance()->getTranslation('ViewerComponent', null, Manager::context())
            );
        }
    }

    /**
     * @throws \Chamilo\Libraries\Architecture\Exceptions\NotAllowedException
     */
    protected function checkAccessRights()
    {
        if ($this->getEntry() &&
            $this->getRightsService()->canUserViewEntry($this->getUser(), $this->getAssignment(), $this->getEntry()))
        {
            return;
        }

        if($this->getEntry() &&
            ($this->getEntry()->getEntityId() != $this->getEntityIdentifier() || $this->getEntry()->getEntityType() != $this->getEntityType())
        )
        {
            throw new NotAllowedException();
        }

        if ($this->getRightsService()->canUserViewEntity(
            $this->getUser(), $this->getAssignment(), $this->getEntityType(), $this->getEntityIdentifier()
        ))
        {
            return;
        }

        throw new NotAllowedException();
    }

    /**
     * @param FormInterface $scoreForm
     *
     * @return array|string[]
     * @throws NotAllowedException
     * @throws \Chamilo\Libraries\Architecture\Exceptions\ClassNotExistException
     * @throws \Chamilo\Libraries\Architecture\Exceptions\UserException
     */
    protected function getTemplateProperties(FormInterface $scoreForm = null)
    {
        /** @var \Chamilo\Core\Repository\ContentObject\Assignment\Storage\DataClass\Assignment $assignment */
        $assignment = $this->get_root_content_object();

        $baseParameters = [
            'HAS_ENTRY' => false,
            'IS_USER_PART_OF_ENTITY' => $this->getAssignmentServiceBridge()->isUserPartOfEntity(
                $this->getUser(), $this->getEntityType(), $this->getEntityIdentifier()
            ),
            'CHANGE_ENTITY_URL' => $this->get_url([self::PARAM_ENTITY_ID => '__ENTITY_ID__']),
            'HEADER' => $this->render_header(),
            'FOOTER' => $this->render_footer(),
            'BUTTON_TOOLBAR' => $this->getButtonToolbarRenderer()->render(),
            'NAVIGATOR_BUTTON_TOOLBAR' => $this->getNavigatorButtonToolbarRenderer()->render(),
            'ASSIGNMENT_TITLE' => $this->get_root_content_object()->get_title(),
            'ASSIGNMENT_RENDITION' => $this->renderAssignment(),
            'ATTACHMENT_VIEWER_URL' => $this->get_url(
                [
                    self::PARAM_ACTION => self::ACTION_VIEW_ATTACHMENT,
                    self::PARAM_ATTACHMENT_ID => '__ATTACHMENT_ID__'
                ]
            )
        ];

        $baseParameters = $this->getAvailableEntitiesParameters($baseParameters);

        if (!$this->getEntry() instanceof Entry)
        {
            return $baseParameters;
        }

        $dateFormat = Translation::get('DateTimeFormatLong', null, Utilities::COMMON_LIBRARIES);
        $submittedDate = DatetimeUtilities::format_locale_date($dateFormat, $this->getEntry()->getSubmitted());

        $configuration = new ApplicationConfiguration($this->getRequest(), $this->getUser(), $this);
        $configuration->set(\Chamilo\Core\Repository\Feedback\Manager::CONFIGURATION_SHOW_FEEDBACK_HEADER, false);

        $feedbackManager = $this->getApplicationFactory()->getApplication(
            "Chamilo\Core\Repository\Feedback", $configuration,
            \Chamilo\Core\Repository\Feedback\Manager::ACTION_BROWSE
        );

        $feedbackManagerHtml = $feedbackManager->run();

        $contentObjects = $assignment->getAutomaticFeedbackObjects();

        $extensionManager = $this->getExtensionManager();
        $titleExtension =
            $extensionManager->extendEntryViewerTitle(
                $this, $this->getAssignment(), $this->getEntry(), $this->getUser()
            );

        $partsExtension =
            $extensionManager->extendEntryViewerParts(
                $this, $this->getAssignment(), $this->getEntry(), $this->getUser()
            );

        $rubricView = null;
        $hasRubric = $canUseRubricEvaluation = false;

        if ($this->supportsRubrics())
        {
            $hasRubric = $this->getAssignmentRubricService()->assignmentHasRubric($this->getAssignment());

            $canUseRubricEvaluation = $this->canUseRubricEvaluation();

            if ($this->getRequest()->getFromUrl(self::PARAM_RUBRIC_ENTRY) && $canUseRubricEvaluation)
            {
                $rubricView = $this->runRubricComponent('Entry');
            }

            if ($this->getRequest()->getFromUrl(self::PARAM_RUBRIC_RESULTS))
            {
                $rubricView = $this->runRubricComponent('Result');
            }
        }

        $extendParameters = [
            'HAS_ENTRY' => true,
            'CONTENT_OBJECT_TITLE' => $this->getEntry()->getContentObject() ?
                $this->getEntry()->getContentObject()->get_title() :
                Translation::getInstance()->getTranslation('SubmissionRemoved', null, Manager::context()),
            'CONTENT_OBJECT_RENDITION' => $this->getEntry()->getContentObject() ? $this->renderContentObject() : null,
            'FEEDBACK_MANAGER' => $feedbackManagerHtml,
            'FEEDBACK_COUNT' => $this->count_feedbacks(),
            'SUBMITTED_DATE' => $submittedDate,
            'SUBMITTED_BY' => $this->getUserService()->getUserFullNameById($this->getEntry()->getUserId()),
            'SCORE_FORM' => $scoreForm->createView(),
            'SCORE' => $this->getScore(),
            'CAN_EDIT_ASSIGNMENT' => $this->getAssignmentServiceBridge()->canEditAssignment(),
            'ENTRY_TABLE' => $this->renderEntryTable(),
            'ENTRY_COUNT' => $this->getAssignmentServiceBridge()->countEntriesForEntityTypeAndId(
                $this->getEntityType(), $this->getEntityIdentifier()
            ),
            'SHOW_AUTOMATIC_FEEDBACK' => $assignment->isAutomaticFeedbackVisible(),
            'AUTOMATIC_FEEDBACK_TEXT' => $assignment->get_automatic_feedback_text(),
            'AUTOMATIC_FEEDBACK_CONTENT_OBJECTS' => $contentObjects,
            'UPLOAD_ENTRY_ATTACHMENT_URL' => $this->get_url(
                [
                    self::PARAM_ACTION => self::ACTION_AJAX,
                    \Chamilo\Core\Repository\ContentObject\Assignment\Display\Ajax\Manager::PARAM_ACTION => \Chamilo\Core\Repository\ContentObject\Assignment\Display\Ajax\Manager::ACTION_UPLOAD_ENTRY_ATTACHMENT,
                    self::PARAM_ENTRY_ID => $this->getEntry()->getId()
                ]
            ),
            'DELETE_ENTRY_ATTACHMENT_URL' => $this->get_url(
                [
                    self::PARAM_ACTION => self::ACTION_AJAX,
                    \Chamilo\Core\Repository\ContentObject\Assignment\Display\Ajax\Manager::PARAM_ACTION => \Chamilo\Core\Repository\ContentObject\Assignment\Display\Ajax\Manager::ACTION_DELETE_ENTRY_ATTACHMENT
                ]
            ),
            'ATTACHMENT_VIEWER_URL' => $this->get_url(
                [
                    self::PARAM_ACTION => self::ACTION_VIEW_ATTACHMENT,
                    self::PARAM_ATTACHMENT_ID => '__ATTACHMENT_ID__',
                    self::PARAM_ENTRY_ID => $this->getEntry()->getId()
                ]
            ),
            'ATTACHED_CONTENT_OBJECTS' => $this->getAttachedContentObjects(),
            'SHOW_COMPACT_FEEDBACK' => $this->getConfigurationConsulter()->getSetting(
                ['Chamilo\Core\Repository\ContentObject\Assignment', 'show_compact_feedback']
            ),
            'TITLE_EXTENSION' => $titleExtension,
            'PARTS_EXTENSION' => $partsExtension,
            'HAS_RUBRIC' => $hasRubric,
            'RUBRIC_VIEW' => $rubricView,
            'RUBRIC_ENTRY_URL' => $this->get_url([self::PARAM_RUBRIC_ENTRY => 1], [self::PARAM_RUBRIC_RESULTS]),
            'RUBRIC_RESULTS_URL' => $this->get_url([self::PARAM_RUBRIC_RESULTS => 1], [self::PARAM_RUBRIC_ENTRY]),
            'CAN_USE_RUBRIC_EVALUATION' => $canUseRubricEvaluation,
            'IS_LATE_ASSIGNMENT' => $this->getAssignmentServiceBridge()->isDateAfterAssignmentEndTime(
                $this->getEntry()->getSubmitted()
            ),
            'QR_CODE' => $this->getQrCodeForEntry()
        ];

        return array_merge($baseParameters, $extendParameters);
    }

    /**
     * @param FormInterface $scoreForm
     *
     * @return bool
     * @throws \Exception
     */
    protected function processSubmittedData(FormInterface $scoreForm)
    {
        if (!$this->getAssignmentServiceBridge()->canEditAssignment() || !$this->getEntry() instanceof Entry)
        {
            return false;
        }

        $scoreFormHandler = new SetScoreFormHandler($this->scoreService);
        $scoreFormHandler->setScoringUser($this->getUser());

        $success = $scoreFormHandler->handle($scoreForm, $this->getRequest());
        if ($success)
        {
            $score = $scoreForm->getData();
            $this->getNotificationServiceBridge()->createNotificationForNewScore(
                $this->getUser(), $this->getEntry(), $score
            );
        }

        return $success;
    }

    /**
     * @return int|null
     */
    protected function getScore()
    {
        if (!is_null($this->score))
        {
            return $this->score->getScore();
        }
        else
        {
            return $this->getScoreDataClass() instanceof Score ? $this->getScoreDataClass()->getScore() : null;
        }
    }

    /**
     * @return Score
     */
    protected function getScoreDataClass()
    {
        if (!$this->getEntry() instanceof Entry)
        {
            return null;
        }

        return $this->getAssignmentServiceBridge()->findScoreByEntry($this->getEntry());
    }

    /**
     *
     * @return string
     */
    protected function renderContentObject()
    {
        $contentObject = $this->getEntry()->getContentObject();

        $display = ContentObjectRenditionImplementation::factory(
            $contentObject,
            ContentObjectRendition::FORMAT_HTML,
            ContentObjectRendition::VIEW_DESCRIPTION,
            $this
        );

        return $display->render();
    }

    /**
     *
     * @return string
     */
    protected function renderAssignment()
    {
        $contentObject = $this->get_root_content_object();

        $display = ContentObjectRenditionImplementation::factory(
            $contentObject,
            ContentObjectRendition::FORMAT_HTML,
            ContentObjectRendition::VIEW_DESCRIPTION,
            $this
        );

        return $display->render();
    }

    /**
     * @return Form|\Symfony\Component\Form\FormInterface
     */
    protected function getScoreForm()
    {
        $this->score = $this->getScoreDataClass();

        if (empty($this->score))
        {
            $this->score = $this->getAssignmentServiceBridge()->initializeScore();

            if ($this->getEntry() instanceof Entry)
            {
                $this->score->setEntryId($this->getEntry()->getId());
            }
        }

        $formFactory = $this->getForm();

        return $formFactory->create(ScoreFormType::class, $this->score);
    }

    /**
     * @return string
     * @throws \Chamilo\Libraries\Architecture\Exceptions\UserException
     */
    protected function renderEntryTable()
    {
        $entryTableParameters = new EntryTableParameters();
        $entryTableParameters->setAssignmentServiceBridge($this->getAssignmentServiceBridge());
        $entryTableParameters->setFeedbackServiceBridge($this->getFeedbackServiceBridge());
        $entryTableParameters->setAssignment($this->getAssignment());
        $entryTableParameters->setEntityType($this->getEntityType());
        $entryTableParameters->setEntityId($this->getEntityIdentifier());
        $entryTableParameters->setUser($this->getUser());
        $entryTableParameters->setCurrentEntry($this->getEntry());

        $table = $this->getAssignmentServiceBridge()->getEntryTableForEntityTypeAndId($this, $entryTableParameters);

        if (!empty($table))
        {
            return $table->render();
        }

        return '';
    }

    /**
     *
     * @see \Chamilo\Core\Repository\Feedback\FeedbackSupport::retrieve_feedbacks()
     */
    public function retrieve_feedbacks($count, $offset)
    {
        return $this->getFeedbackServiceBridge()->getFeedbackByEntry($this->getEntry());
    }

    /**
     *
     * @see \Chamilo\Core\Repository\Feedback\FeedbackSupport::count_feedbacks()
     */
    public function count_feedbacks()
    {
        return $this->getFeedbackServiceBridge()->countFeedbackByEntry($this->getEntry());
    }

    /**
     *
     * @see \Chamilo\Core\Repository\Feedback\FeedbackSupport::retrieve_feedback()
     */
    public function retrieve_feedback($feedbackIdentifier)
    {
        return $this->getFeedbackServiceBridge()->getFeedbackByIdentifier($feedbackIdentifier);
    }

    /**
     *
     * @see \Chamilo\Core\Repository\Feedback\FeedbackSupport::get_feedback()
     */
    public function get_feedback()
    {
        return null;
    }

    /**
     *
     * @see \Chamilo\Core\Repository\Feedback\FeedbackSupport::is_allowed_to_view_feedback()
     */
    public function is_allowed_to_view_feedback()
    {
        return true;
    }

    /**
     *
     * @see \Chamilo\Core\Repository\Feedback\FeedbackSupport::is_allowed_to_create_feedback()
     */
    public function is_allowed_to_create_feedback()
    {
        return true;
    }

    /**
     * @param \Chamilo\Core\Repository\Feedback\Storage\DataClass\Feedback $feedback
     *
     * @return bool
     */
    public function is_allowed_to_update_feedback($feedback)
    {
        return $feedback->get_user_id() == $this->getUser()->getId();
    }

    /**
     *
     * @see \Chamilo\Core\Repository\Feedback\FeedbackSupport::is_allowed_to_delete_feedback()
     */
    public function is_allowed_to_delete_feedback($feedback)
    {
        return $feedback->get_user_id() == $this->getUser()->getId();
    }

    protected function getButtonToolbarRenderer()
    {
        $buttonToolBar = new ButtonToolBar();

        if (!isset($this->actionBar))
        {

            $buttonGroup = new ButtonGroup();

            if (
                $this->getRightsService()->canUserCreateEntry(
                    $this->getUser(), $this->getAssignment(), $this->getEntityType(), $this->getEntityIdentifier()
                ) && $this->getAssignment()->canSubmit() && $this->getAssignmentServiceBridge()->areSubmissionsAllowed()
            )
            {
                $buttonGroup->addButton(
                    new Button(
                        Translation::get('AddNewEntry'),
                        new FontAwesomeGlyph('plus'),
                        $this->get_url(
                            [
                                self::PARAM_ACTION => self::ACTION_CREATE,
                                self::PARAM_ENTITY_TYPE => $this->getEntityType(),
                                self::PARAM_ENTITY_ID => $this->getEntityIdentifier()
                            ]
                        ),
                        Button::DISPLAY_ICON_AND_LABEL,
                        false,
                        'btn-success'
                    )
                );
            }

            $buttonToolBar->addButtonGroup($buttonGroup);

            /*            if ($this->getAssignmentServiceBridge()->canEditAssignment())
                        {
                            $buttonToolBar->addButtonGroup(
                                new ButtonGroup(
                                    array(
                                        new Button(
                                            Translation::get(
                                                'CorrectCurrentEntry'
                                            ),
                                            new FontAwesomeGlyph('pencil-square-o'),
                                            $this->get_url(
                                                [
                                                    self::PARAM_ACTION => self::ACTION_ENTRY_CODE_PAGE_CORRECTOR,
                                                    self::PARAM_ENTITY_TYPE => $this->getEntityType(),
                                                    self::PARAM_ENTITY_ID => $this->getEntityIdentifier(),
                                                    self::PARAM_ENTRY_ID => $this->getEntry()->getId()
                                                ]
                                            ),
                                            Button::DISPLAY_ICON_AND_LABEL,
                                            false,
                                            'btn-success'
                                        )
                                    )
                                )
                            );
                        }*/

            $buttonGroup = new ButtonGroup();

            if ($this->getEntry() instanceof Entry)
            {
                $splitDropdownButton = new SplitDropdownButton(
                    Translation::get('DownloadAll'),
                    new FontAwesomeGlyph('download'),
                    $this->get_url(
                        [
                            self::PARAM_ACTION => self::ACTION_DOWNLOAD,
                            self::PARAM_ENTITY_TYPE => $this->getEntityType(),
                            self::PARAM_ENTITY_ID => $this->getEntityIdentifier()
                        ],
                        [self::PARAM_ENTRY_ID]
                    )
                );

                $splitDropdownButton->addSubButton(
                    new SubButton(
                        Translation::get('DownloadCurrent'),
                        new FontAwesomeGlyph('download'),
                        $this->get_url(
                            [
                                self::PARAM_ACTION => self::ACTION_DOWNLOAD,
                                self::PARAM_ENTITY_TYPE => $this->getEntityType(),
                                self::PARAM_ENTITY_ID => $this->getEntityIdentifier(),
                                self::PARAM_ENTRY_ID => $this->getEntry()->getId()
                            ]

                        )
                    )
                );

                $buttonGroup->addButton($splitDropdownButton);
            }

            $buttonToolBar->addButtonGroup($buttonGroup);

            if ($this->getAssignmentServiceBridge()->canEditAssignment() ||
                $this->getAssignment()->get_visibility_submissions())
            {
                $buttonToolBar->addButtonGroup(
                    new ButtonGroup(
                        array(
                            new Button(
                                Translation::get(
                                    'BrowseEntities',
                                    [
                                        'NAME' => strtolower(
                                            $this->getAssignmentServiceBridge()->getPluralEntityNameByType(
                                                $this->getEntityType()
                                            )
                                        )
                                    ]
                                ),
                                new FontAwesomeGlyph('user'),
                                $this->get_url(
                                    [
                                        self::PARAM_ACTION => self::ACTION_VIEW
                                    ],
                                    [self::PARAM_ENTRY_ID]
                                )
                            )
                        )
                    )
                );
            }
        }

        $this->actionBar = new ButtonToolBarRenderer($buttonToolBar);

        return $this->actionBar;
    }

    /**
     * @return \Chamilo\Libraries\Format\Structure\ActionBar\Renderer\ButtonToolBarRenderer
     */
    protected function getNavigatorButtonToolbarRenderer()
    {
        $buttonToolBar = new ButtonToolBar();

        if (!$this->getAssignmentServiceBridge()->canEditAssignment() || empty($this->getEntry()))
        {
            return new ButtonToolBarRenderer($buttonToolBar);
        }

        $currentEntityPosition = $this->getEntryNavigator()->getCurrentEntityPosition(
            $this->getAssignmentServiceBridge(), $this->getEntry(), $this->getEntityType(), $this->getEntityIdentifier()
        );

        $currentEntryPosition = $this->getEntryNavigator()->getCurrentEntryPosition(
            $this->getAssignmentServiceBridge(), $this->getEntry(), $this->getEntityType(), $this->getEntityIdentifier()
        );

        $translator = Translation::getInstance();
        $entityName = $this->getAssignmentServiceBridge()->getEntityNameByType($this->getEntityType());

        $entitiesCount =
            $this->getAssignmentServiceBridge()->countEntitiesWithEntriesByEntityType($this->getEntityType());
        $entriesCount = $this->getAssignmentServiceBridge()->countEntriesForEntityTypeAndId(
            $this->getEntityType(), $this->getEntityIdentifier()
        );

        if ($entitiesCount > 1)
        {
            $entityNavigatorActions = new ButtonGroup();

            $entityNavigatorActions->addButton(
                new Button(
                    $translator->getTranslation('PreviousEntity', ['ENTITY_NAME' => strtolower($entityName)]),
                    new FontAwesomeGlyph('backward'),
                    $this->getPreviousEntityUrl(),
                    ToolbarItem::DISPLAY_ICON_AND_LABEL
                )
            );

            $entityNavigatorActions->addButton(
                new Button(
                    '<span class="badge" style="color: white; background-color: #5bc0de; height: 19px; top: 0;">'
                    . $currentEntityPosition . ' / ' . $entitiesCount . '</span>',
                    null,
                    '#',
                    ToolbarItem::DISPLAY_LABEL
                )
            );

            $entityNavigatorActions->addButton(
                new Button(
                    $translator->getTranslation('NextEntity', ['ENTITY_NAME' => strtolower($entityName)]),
                    new FontAwesomeGlyph('forward'),
                    $this->getNextEntityUrl(),
                    ToolbarItem::DISPLAY_ICON_AND_LABEL
                )
            );

            $buttonToolBar->addButtonGroup($entityNavigatorActions);

            $selectEntityButton = new DropdownButton(
                $this->getAssignmentServiceBridge()->renderEntityNameByEntityTypeAndEntityId(
                    $this->getEntityType(), $this->getEntityIdentifier()
                ),
                new FontAwesomeGlyph('user')
            );

            $selectEntityButton->setDropdownClasses('dropdown-entities');

            $selectEntityButton->addSubButton(
                new SubButtonHeader(
                    $translator->getTranslation('SelectOtherEntity', ['ENTITY_TYPE' => strtolower($entityName)])
                )
            );

            $buttonToolBar->addItem($selectEntityButton);

            $entities = $this->getEntryNavigator()->getEntities();
            foreach ($entities as $entity)
            {
                if ($entity->getId() == $this->getEntityIdentifier())
                {
                    $classes = 'selected';
                    $url = '';
                }
                else
                {
                    $url = $this->get_url(
                        array(self::PARAM_ENTITY_ID => $entity->getId()), array(self::PARAM_ENTRY_ID)
                    );

                    $classes = 'not-selected';
                }

                $selectEntityButton->addSubButton(
                    new SubButton(
                        $this->getAssignmentServiceBridge()->renderEntityNameByEntityTypeAndEntity(
                            $this->getEntityType(), $entity
                        ), null, $url, SubButton::DISPLAY_LABEL, false, $classes
                    )
                );
            }
        }

        if ($entriesCount > 1)
        {
            $entriesNavigatorActions = new ButtonGroup();

            $entriesNavigatorActions->addButton(
                new Button(
                    $translator->getTranslation('EarlierEntry'),
                    new FontAwesomeGlyph('backward'),
                    $this->getPreviousEntryUrl(),
                    ToolbarItem::DISPLAY_ICON_AND_LABEL
                )
            );

            $entriesNavigatorActions->addButton(
                new Button(
                    '<span class="badge" style="color: white; background-color: #28a745; height: 19px; top: 0;">'
                    . $currentEntryPosition . ' / ' . $entriesCount . '</span>',
                    null,
                    '#',
                    ToolbarItem::DISPLAY_LABEL
                )
            );

            $entriesNavigatorActions->addButton(
                new Button(
                    $translator->getTranslation('LaterEntry'),
                    new FontAwesomeGlyph('forward'),
                    $this->getNextEntryUrl(),
                    ToolbarItem::DISPLAY_ICON_AND_LABEL
                )
            );

            $buttonToolBar->addButtonGroup($entriesNavigatorActions);
        }

        return new ButtonToolBarRenderer($buttonToolBar);
    }

    /**
     * @return string
     */
    protected function getPreviousEntryUrl()
    {
        $previousEntry = $this->getEntryNavigator()->getPreviousEntry(
            $this->getAssignmentServiceBridge(), $this->getEntry(), $this->getEntityType(), $this->getEntityIdentifier()
        );

        if (!$previousEntry instanceof Entry)
        {
            return null;
        }

        return $this->get_url(array(self::PARAM_ENTRY_ID => $previousEntry->getId()));
    }

    /**
     * @return string
     */
    protected function getNextEntryUrl()
    {
        $nextEntry = $this->getEntryNavigator()->getNextEntry(
            $this->getAssignmentServiceBridge(), $this->getEntry(), $this->getEntityType(), $this->getEntityIdentifier()
        );

        if (!$nextEntry instanceof Entry)
        {
            return null;
        }

        return $this->get_url(array(self::PARAM_ENTRY_ID => $nextEntry->getId()));
    }

    /**
     * @return string
     */
    protected function getPreviousEntityUrl()
    {
        $previousEntity = $this->getEntryNavigator()->getPreviousEntity(
            $this->getAssignmentServiceBridge(), $this->getEntry(), $this->getEntityType(), $this->getEntityIdentifier()
        );

        if (!$previousEntity instanceof DataClass)
        {
            return null;
        }

        return $this->get_url(
            array(self::PARAM_ENTITY_ID => $previousEntity->getId()), array(self::PARAM_ENTRY_ID)
        );
    }

    /**
     * @return string
     */
    protected function getNextEntityUrl()
    {
        $nextEntity = $this->getEntryNavigator()->getNextEntity(
            $this->getAssignmentServiceBridge(), $this->getEntry(), $this->getEntityType(), $this->getEntityIdentifier()
        );

        if (!$nextEntity instanceof DataClass)
        {
            return null;
        }

        return $this->get_url(
            array(self::PARAM_ENTITY_ID => $nextEntity->getId()), array(self::PARAM_ENTRY_ID)
        );
    }

    /**
     * Returns the condition
     *
     * @param string $tableClassname
     *
     * @return \Chamilo\Libraries\Storage\Query\Condition\Condition
     */
    public function get_table_condition($tableClassname)
    {
        // TODO: Implement get_table_condition() method.
    }

    public function get_additional_parameters()
    {
        return array(self::PARAM_ENTRY_ID, self::PARAM_ENTITY_ID, self::PARAM_ENTITY_TYPE, self::PARAM_RUBRIC_ENTRY);
    }

    /**
     * @return \Chamilo\Core\Repository\ContentObject\Assignment\Display\Service\EntryNavigator
     */
    protected function getEntryNavigator()
    {
        if (!isset($this->entryNavigator))
        {
            $this->entryNavigator = new EntryNavigator();
        }

        return $this->entryNavigator;
    }

    /**
     * @return array
     */
    protected function getAttachedContentObjects()
    {
        if (!$this->getEntry() instanceof Entry)
        {
            return [];
        }

        $entryAttachments = $this->getAssignmentServiceBridge()->findAttachmentsByEntry($this->getEntry());

        if (empty($entryAttachments))
        {
            return [];
        }

        $contentObjectAttachments = [];

        foreach ($entryAttachments as $entryAttachment)
        {
            /** @var ContentObject $contentObject */
            $contentObject = \Chamilo\Core\Repository\Storage\DataManager::retrieve_by_id(
                ContentObject::class, $entryAttachment->getAttachmentId()
            );

            $owner = $this->getUserService()->getUserFullNameById($contentObject->get_owner_id());

            $contentObjectAttachments[] =
                [
                    'attachment_id' => $entryAttachment->getId(),
                    'content_object' => [
                        'id' => $contentObject->getId(),
                        'title' => $contentObject->get_title(),
                        'user' => $owner,
                        'date' => DatetimeUtilities::format_locale_date(null, $contentObject->get_creation_date())
                    ]
                ];
        }

        return $contentObjectAttachments;
    }

    /**
     * @return \Chamilo\Core\User\Service\UserService
     */
    protected function getUserService()
    {
        return $this->getService('chamilo.core.user.service.user_service');
    }

    /**
     * @return bool|null
     */
    protected function canUseRubricEvaluation()
    {
        if ($this->getAssignmentServiceBridge()->canEditAssignment())
        {
            return true;
        }

        return $this->getAssignmentRubricService()->isSelfEvaluationAllowed($this->getAssignment());
    }

    /**
     * @throws \Exception
     */
    protected function getQrCodeForEntry()
    {
        $url = $this->get_url(
            [
                self::PARAM_ENTITY_TYPE => $this->getEntityType(),
                self::PARAM_ENTITY_ID => $this->getEntityIdentifier()
            ]
        );

        return $this->getQRService()->getQRForURL($url, 250);
    }

    /**
     * @return QRService
     */
    protected function getQRService()
    {
        return $this->getService(QRService::class);
    }
}


text:
package steerclear.wm.ui.fragment;

import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.AppCompatImageButton;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.Toast;

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.PlaceBuffer;
import com.google.android.gms.location.places.Places;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import java.io.IOException;
import java.util.Locale;

import javax.inject.Inject;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import de.greenrobot.event.EventBus;
import icepick.State;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import steerclear.wm.MainApp;
import steerclear.wm.R;
import steerclear.wm.ui.activity.HomeActivity;
import steerclear.wm.data.AdapterAutoComplete;
import steerclear.wm.data.event.EventAnimateToMarker;
import steerclear.wm.data.event.EventPlacesChosen;
import steerclear.wm.util.Hue;
import steerclear.wm.ui.view.ViewAutoComplete;
import steerclear.wm.ui.view.ViewFooter;
import steerclear.wm.ui.view.ViewHeader;
import steerclear.wm.ui.view.ViewMarkerSelectLayout;

public class MapFragment extends BaseFragment
	implements View.OnClickListener, ViewAutoComplete.AutoCompleteListener,
        OnMapReadyCallback, GoogleMap.OnMarkerDragListener, GoogleMap.OnMarkerClickListener,
        GoogleMap.OnMapClickListener {

    @Bind(R.id.fragment_map_header) ViewHeader header;
	@Bind(R.id.fragment_map_pickup) ViewAutoComplete editPickup;
    @Bind(R.id.fragment_map_dropoff) ViewAutoComplete editDropoff;
	@Bind(R.id.fragment_map_view) MapView mapView;
    @Bind(R.id.fragment_map_post) ViewFooter confirmView;
    @Bind(R.id.fragment_map_current_location) AppCompatImageButton myLocationButton;
    @Bind(R.id.fragment_map_marker_select_layout) ViewMarkerSelectLayout viewMarkerSelectLayout;

    @Inject EventBus bus;

    private final static String USER_LOCATION = "user";
    private final static String PICKUP_LATLNG = "pickup";
    private final static String DROPOFF_LATLNG = "dropoff";
    private final static String PICKUP_MARKER_TITLE = "Pick Up Location";
    private final static String DROPOFF_MARKER_TITLE = "Drop Off Location";
    private LatLng pickupLatLng, dropoffLatLng;
    @State CharSequence pickupName, dropoffName;

    private Geocoder geocoder;
    private Marker pickupMarker, dropoffMarker;
    private AdapterAutoComplete mAdapter;
    private static final LatLngBounds BOUNDS_WILLIAMSBURG = new LatLngBounds(
			new LatLng(37.244926, -76.747861), new LatLng(37.295667, -76.686084));

	public MapFragment() {}

	public static MapFragment newInstance(LatLng userLocation) {
		MapFragment frag = new MapFragment();
		Bundle args = new Bundle();
        args.putParcelable(USER_LOCATION, userLocation);
		frag.setArguments(args);
		return frag;
	}

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        ((MainApp) context.getApplicationContext()).getApplicationComponent().inject(this);
        geocoder = new Geocoder(context, Locale.US);
    }

	@Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
        bus.register(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
        bus.unregister(this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
        outState.putParcelable(PICKUP_LATLNG, pickupLatLng);
        outState.putParcelable(DROPOFF_LATLNG, dropoffLatLng);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View rootView = inflater.inflate(R.layout.fragment_map, container, false);
		ButterKnife.bind(this, rootView);

        mAdapter = new AdapterAutoComplete(getActivity(), R.layout.adapter_view,
                getGoogleApiClient(), BOUNDS_WILLIAMSBURG);

		editPickup.setAdapter(mAdapter);
        editPickup.setAutoCompleteListener(this);
        editPickup.setOnItemClickListener(pickupAdapterViewClick);

        editDropoff.setAdapter(mAdapter);
        editDropoff.setAutoCompleteListener(this);
        editDropoff.setOnItemClickListener(dropoffAdapterViewClick);

		mapView.onCreate(savedInstanceState);

		return rootView;
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

	    if (savedInstanceState != null) {
			editPickup.setText(pickupName);
            editDropoff.setText(dropoffName);

            pickupLatLng = savedInstanceState.getParcelable(PICKUP_LATLNG);
            dropoffLatLng = savedInstanceState.getParcelable(DROPOFF_LATLNG);
	    }

        mapView.getMapAsync(this);
    }

    @Override
	public void onMapReady(GoogleMap map) {
        UiSettings settings = map.getUiSettings();
        settings.setCompassEnabled(false);
        settings.setMyLocationButtonEnabled(false);
        settings.setIndoorLevelPickerEnabled(false);
        map.setOnMapClickListener(this);
        map.setOnMarkerDragListener(this);
        map.setOnMarkerClickListener(this);

        LatLng userLocation = getArguments().getParcelable(USER_LOCATION);
        if (pickupLatLng != null) {
            dropMarkerOnMap(pickupLatLng, PICKUP_MARKER_TITLE);
            animateCameraToLocation(pickupLatLng);
        } else {
            dropMarkerOnMap(userLocation, PICKUP_MARKER_TITLE);
            animateCameraToLocation(userLocation);
        }

        if (dropoffLatLng != null) {
            dropMarkerOnMap(dropoffLatLng, DROPOFF_MARKER_TITLE);
        }

        mapView.setVisibility(View.VISIBLE);
	}

    @Override
    public void onMapClick(LatLng latLng) {
        if (!BOUNDS_WILLIAMSBURG.contains(latLng)) {
            Toast.makeText(getActivity(), getResources().getString(R.string.fragment_map_no_service),
                    Toast.LENGTH_SHORT).show();
            return;
        }

        int idOfSelectedButton = viewMarkerSelectLayout.getIdOfSelectedButton();
        if (idOfSelectedButton == -1) {
            Toast.makeText(getActivity(),
                    getResources().getString(R.string.fragment_map_marker_drop_hint),
                    Toast.LENGTH_SHORT).show();
            return;
        }

        switch (idOfSelectedButton) {
            case R.id.fragment_map_show_pickup_location:
                pickupLatLng = latLng;

                dropMarkerOnMap(pickupLatLng, PICKUP_MARKER_TITLE);

                reverseGeocodeLocation(pickupLatLng, PICKUP_MARKER_TITLE);

                animateCameraToLocation(pickupLatLng);
                break;
            case R.id.fragment_map_show_dropoff_location:
                dropoffLatLng = latLng;

                dropMarkerOnMap(dropoffLatLng, DROPOFF_MARKER_TITLE);

                reverseGeocodeLocation(dropoffLatLng, DROPOFF_MARKER_TITLE);

                animateCameraToLocation(dropoffLatLng);
                break;
        }
    }

    @Override
    public boolean onMarkerClick(Marker marker) {
        Toast.makeText(getActivity(), getResources().getString(R.string.fragment_map_marker_hint),
                Toast.LENGTH_SHORT).show();
        return false;
    }

    @Override
    public void onMarkerDragStart(Marker marker) {
    }

    @Override
    public void onMarkerDrag(Marker marker) {

    }

    @Override
    public void onMarkerDragEnd(Marker marker) {
        reverseGeocodeLocation(marker.getPosition(), marker.getTitle());
    }

    @Override
    @OnClick({R.id.fragment_map_post, R.id.fragment_map_current_location})
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.fragment_map_post:
                if (pickupLatLng == null) {
                    editPickup.setError(getResources().getString(R.string.fragment_map_pickup_snackbar_text));
                } else if (dropoffLatLng == null) {
                    editDropoff.setError(getResources().getString(R.string.fragment_map_dropoff_snackbar_text));
                } else if (pickupLatLng.equals(dropoffLatLng)) {
                    Snackbar.make(getView(), getResources().getString(R.string.fragment_map_same_location), Snackbar.LENGTH_SHORT).show();
                } else {
                    bus.post(new EventPlacesChosen(pickupLatLng, pickupName, dropoffLatLng, dropoffName));
                }
                break;
            case R.id.fragment_map_current_location:
                animateCameraToLocation(getArguments().getParcelable(USER_LOCATION));
                break;
        }
    }

    @Override
    public void clearClicked(View v) {
        final InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
        if (v.getWindowToken() != null) {
            imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
        }

        switch (v.getId()) {
            case R.id.fragment_map_pickup:
                pickupLatLng = null;
                pickupName = null;
                if (pickupMarker != null) {
                    pickupMarker.remove();
                }
                break;
            case R.id.fragment_map_dropoff:
                dropoffLatLng = null;
                dropoffName = null;
                if (dropoffMarker != null) {
                    dropoffMarker.remove();
                }
                break;
        }
    }

    @SuppressWarnings("unused")
    public void onEvent(EventAnimateToMarker eventAnimateToMarker) {
        switch (eventAnimateToMarker.buttonId) {
            case R.id.fragment_map_show_pickup_location:
                if (pickupMarker != null) {
                    animateCameraToLocation(pickupMarker.getPosition());
                }
                break;
            case  R.id.fragment_map_show_dropoff_location:
                if (dropoffMarker != null) {
                    animateCameraToLocation(dropoffMarker.getPosition());
                }
                break;
        }
    }

    private GoogleApiClient getGoogleApiClient() {
        return ((HomeActivity) getActivity()).getGoogleApiClient();
    }

    private final AdapterView.OnItemClickListener pickupAdapterViewClick
            = (parent, view, position, id) -> {
        editPickup.closeKeyboard();

        final AdapterAutoComplete.AdapterAutoCompleteItem item = mAdapter.getItem(position);
        final String placeId = String.valueOf(item.placeId);

        PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi.getPlaceById(getGoogleApiClient(), placeId);
        placeResult.setResultCallback(places -> {
            if (!places.getStatus().isSuccess()) {
                places.release();
                return;
            }

            final Place place = places.get(0);

            if (!BOUNDS_WILLIAMSBURG.contains(place.getLatLng())) {
                places.release();
                Toast.makeText(getActivity(), getResources().getString(R.string.toast_too_far_for_steer_clear),
                        Toast.LENGTH_SHORT).show();
                pickupLatLng = null;
                pickupName = null;
                editPickup.setText("");
                return;
            }

            pickupLatLng = place.getLatLng();
            pickupName = place.getAddress();

            dropMarkerOnMap(pickupLatLng, PICKUP_MARKER_TITLE);

            animateCameraToLocation(pickupLatLng);

            places.release();
        });
    };

    private final AdapterView.OnItemClickListener dropoffAdapterViewClick
            = (parent, view, position, id) -> {
        editDropoff.closeKeyboard();

        final AdapterAutoComplete.AdapterAutoCompleteItem item = mAdapter.getItem(position);
        final String placeId = String.valueOf(item.placeId);

        PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi.getPlaceById(getGoogleApiClient(), placeId);
        placeResult.setResultCallback(places -> {
            if (!places.getStatus().isSuccess()) {
                places.release();
                return;
            }

            final Place place = places.get(0);

            if (!BOUNDS_WILLIAMSBURG.contains(place.getLatLng())) {
                places.release();
                Toast.makeText(getActivity(), getResources().getString(R.string.fragment_map_no_service),
                        Toast.LENGTH_SHORT).show();
                dropoffLatLng = null;
                dropoffName = null;
                editDropoff.setText("");
                return;
            }

            dropoffLatLng = place.getLatLng();
            dropoffName = place.getAddress();

            dropMarkerOnMap(dropoffLatLng, DROPOFF_MARKER_TITLE);

            animateCameraToLocation(dropoffLatLng);

            places.release();
        });
    };

    private void dropMarkerOnMap(LatLng latLng, String whichMarker) {
        switch (whichMarker) {
            case PICKUP_MARKER_TITLE:

                MarkerOptions options = new MarkerOptions()
                        .position(latLng)
                        .title(PICKUP_MARKER_TITLE)
                        .icon(BitmapDescriptorFactory.defaultMarker(
                                Hue.getHue(getResources().getColor(R.color.accent))))
                        .draggable(true);

                if (pickupMarker != null) {
                    pickupMarker.remove();
                }

                pickupMarker = mapView.getMap().addMarker(options);
                break;
            case DROPOFF_MARKER_TITLE:

                MarkerOptions options1 = new MarkerOptions()
                        .position(latLng)
                        .title(DROPOFF_MARKER_TITLE)
                        .icon(BitmapDescriptorFactory.defaultMarker(
                                Hue.getHue(getResources().getColor(R.color.primary_dark))))
                        .draggable(true);

                if (dropoffMarker != null) {
                    dropoffMarker.remove();
                }
                dropoffMarker = mapView.getMap().addMarker(options1);
                break;
        }
    }

    private void reverseGeocodeLocation(LatLng latLng, String whichMarker) {
        if (!BOUNDS_WILLIAMSBURG.contains(latLng)) {
            Toast.makeText(getActivity(), getResources().getString(R.string.fragment_map_no_service),
                    Toast.LENGTH_SHORT).show();

            switch (whichMarker) {
                case PICKUP_MARKER_TITLE:
                    if (pickupLatLng != null) {
                        pickupMarker.setPosition(pickupLatLng);
                    }
                    break;
                case DROPOFF_MARKER_TITLE:
                    if (dropoffLatLng != null) {
                        dropoffMarker.setPosition(dropoffLatLng);
                    }
                    break;
            }
            return;
        }

        try {
            Observable.just(geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1))
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(addresses -> {
                        Address address = addresses.get(0);
                        switch (whichMarker) {
                            case PICKUP_MARKER_TITLE:
                                pickupLatLng = new LatLng(address.getLatitude(), address.getLongitude());
                                pickupName = address.getAddressLine(0) + ", " +
                                        address.getAddressLine(1);
                                editPickup.setText(address.getAddressLine(0) + ", " +
                                        address.getAddressLine(1), false);
                                break;
                            case DROPOFF_MARKER_TITLE:
                                dropoffLatLng = new LatLng(address.getLatitude(), address.getLongitude());
                                dropoffName = address.getAddressLine(0) + ", " +
                                        address.getAddressLine(1);
                                editDropoff.setText(address.getAddressLine(0) + ", " +
                                        address.getAddressLine(1), false);
                                break;
                        }
                    });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void animateCameraToLocation(LatLng latLng) {
        CameraPosition cameraPosition = new CameraPosition.Builder()
                .target(latLng)
                .zoom(17)
                .bearing(45)
                .tilt(45)
                .build();
        mapView.getMap().animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
    }
}

redpajama_set_name: RedPajamaGithub


text:
package org.softlang.xom;

import nu.xom.Element;
import static org.softlang.xom.XOMUtilities.*;

public class Total {

	public static double total(Element elem) {
		double total = 0;
		for (Element s : descendants(elem, "salary"))
			total += Double.valueOf(s.getValue());
		return total;
	}
}


text:
>
> * 原文出自《RxJava Essentials》
* 原文作者 : [Ivan Morgillo](https://www.packtpub.com/books/info/authors/ivan-morgillo)
* 译文出自 : [开发技术前线 www.devtf.cn](http://www.devtf.cn)
* 转载声明: 本译文已授权[开发者头条](http://toutiao.io/download)享有独家转载权,未经允许,不得转载!
* 译者 : [yuxingxin](https://github.com/yuxingxin)
* 项目地址 : [RxJava-Essentials-CN](https://github.com/yuxingxin/RxJava-Essentials-CN)

上一章中,我们学到如何转换可观测序列。我们也看到了`map()`,`scan()`,`groupBY()`,以及更多有用的函数的实际例子,它们帮助我们操作Observable来创建我们想要的Observable。

本章中,我们将研究组合函数并学习如何同时处理多个Observables来创建我们想要的Observable。

## Merge

在异步的世界经常会创建这样的场景,我们有多个来源但是只想有一个结果:多输入,单输出。RxJava的`merge()`方法将帮助你把两个甚至更多的Observables合并到他们发射的数据里。下图给出了把两个序列合并在一个最终发射的Observable。

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_1.png)

正如你看到的那样,发射的数据被交叉合并到一个Observable里面。注意如果你同步的合并Observable,它们将连接在一起并且不会交叉。

像通常一样,我们用我们的App和已安装的App列表来创建了一个"真实世界"的例子。我们还需要第二个Observable。我们可以创建一个单独的应用列表然后逆序。当然没有实际的意义,只是为了这个例子。第二个列表,我们的`loadList()`函数像下面这样:
```java
private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    List reversedApps = Lists.reverse(apps);
    Observable<AppInfo> observableApps =Observable.from(apps);
    Observable<AppInfo> observableReversedApps =Observable.from(reversedApps);
    Observable<AppInfo> mergedObserbable = Observable.merge(observableApps,observableReversedApps);

    mergedObserbable.subscribe(new Observer<AppInfo>(){
        @Override
        public void onCompleted() {
            mSwipeRefreshLayout.setRefreshing(false);
            Toast.makeText(getActivity(), "Here is the list!", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onError(Throwable e) {
            Toast.makeText(getActivity(), "One of the two Observable threw an error!", Toast.LENGTH_SHORT).show();
            mSwipeRefreshLayout.setRefreshing(false);
        }

        @Override
        public void onNext(AppInfoappInfo) {
            mAddedApps.add(appInfo);
            mAdapter.addApplication(mAddedApps.size() - 1, appInfo);
        }
    });
}
```

我们创建了Observable和observableApps数据以及新的observableReversedApps逆序列表。使用`Observable.merge()`,我们可以创建新的`ObservableMergedObservable`在单个可观测序列中发射源Observables发出的所有数据。

正如你能看到的,每个方法签名都是一样的,因此我们的观察者无需在意任何不同就可以复用代码。结果如下:

注意错误时的toast消息,你可以认为每个Observable抛出的错误将会打断合并。如果你需要避免这种情况,RxJava提供了`mergeDelayError()`,它能从一个Observable中继续发射数据即便是其中有一个抛出了错误。当所有的Observables都完成时,`mergeDelayError()`将会发射`onError()`,如下图所示:

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_3.png)

## ZIP

我们在处理多源时可能会带来这样一种场景:多从个Observables接收数据,处理它们,然后将它们合并成一个新的可观测序列来使用。RxJava有一个特殊的方法可以完成:`zip()`合并两个或者多个Observables发射出的数据项,根据指定的函数`Func*`变换它们,并发射一个新值。下图展示了`zip()`方法如何处理发射的"numbers"和"letters"然后将它们合并一个新的数据项:

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_4.png)

对于"真实世界"的例子来说,我们将使用已安装的应用列表和一个新的动态的Observable来让例子变得有点有趣味。

```java
Observable<Long> tictoc = Observable.interval(1, TimeUnit.SECONDS);
```
`tictoc`Observable变量使用`interval()`函数每秒生成一个Long类型的数据:简单且高效,正如之前所说的,我们需要一个`Func`对象。因为它需要传两个参数,所以是`Func2`:

```java
private AppInfo updateTitle(AppInfoappInfo, Long time) {
    appInfo.setName(time + " " + appInfo.getName());
    return appInfo;
}
```
现在我们的`loadList()`函数变成这样:
```java
private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    Observable<AppInfo> observableApp = Observable.from(apps);

    Observable<Long> tictoc = Observable.interval(1, TimeUnit.SECONDS);

    Observable.zip(observableApp, tictoc,
    (AppInfo appInfo, Long time) -> updateTitle(appInfo, time))
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<AppInfo>() {
        @Override
        public void onCompleted() {
            Toast.makeText(getActivity(), "Here is the list!", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onError(Throwable e) {
            mSwipeRefreshLayout.setRefreshing(false);
            Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onNext(AppInfoappInfo) {
            if (mSwipeRefreshLayout.isRefreshing()) {
                mSwipeRefreshLayout.setRefreshing(false);
            }
            mAddedApps.add(appInfo);
            int position = mAddedApps.size() - 1;
            mAdapter.addApplication(position, appInfo);
            mRecyclerView.smoothScrollToPosition(position);
        }
    });
}
```
正如你看到的那样,`zip()`函数有三个参数:两个Observables和一个`Func2`。

仔细一看会发现`observeOn()`函数。它将在下一章中讲解:现在我们可以小试一下。

结果如下:

## Join

前面两个方法,`zip()`和`merge()`方法作用在发射数据的范畴内,在决定如何操作值之前有些场景我们需要考虑时间的。RxJava的`join()`函数基于时间窗口将两个Observables发射的数据结合在一起。

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_6.png)

为了正确的理解上一张图,我们解释下`join()`需要的参数:

* 第二个Observable和源Observable结合。
* `Func1`参数:在指定的由时间窗口定义时间间隔内,源Observable发射的数据和从第二个Observable发射的数据相互配合返回的Observable。
* `Func1`参数:在指定的由时间窗口定义时间间隔内,第二个Observable发射的数据和从源Observable发射的数据相互配合返回的Observable。
* `Func2`参数:定义已发射的数据如何与新发射的数据项相结合。
*
如下练习的例子,我们可以修改`loadList()`函数像下面这样:
```java
private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);

    Observable<AppInfo> appsSequence =
    Observable.interval(1000, TimeUnit.MILLISECONDS)
                .map(position -> {
                    return apps.get(position.intValue());
                });

    Observable<Long> tictoc = Observable.interval(1000,TimeUnit.MILLISECONDS);

    appsSequence.join(
        tictoc,
        appInfo -> Observable.timer(2,TimeUnit.SECONDS),
        time -> Observable.timer(0, TimeUnit.SECONDS),
        this::updateTitle)
        .observeOn(AndroidSchedulers.mainThread())
        .take(10)
        .subscribe(new Observer<AppInfo>() {
            @Override
            public void onCompleted() {
                Toast.makeText(getActivity(), "Here is the list!", Toast.LENGTH_LONG).show();
            }

            @Override
            public void onError(Throwable e) {
                mSwipeRefreshLayout.setRefreshing(false);
                Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onNext(AppInfoappInfo) {
                if (mSwipeRefreshLayout.isRefreshing()) {
                    mSwipeRefreshLayout.setRefreshing(false);
                }
                mAddedApps.add(appInfo);
                int position = mAddedApps.size() - 1;
                mAdapter.addApplication(position, appInfo);
                mRecyclerView.smoothScrollToPosition(position);
            }
        });
}
```

我们有一个新的对象`appsSequence`,它是一个每秒从我们已安装的app列表发射app数据的可观测序列。`tictoc`这个Observable数据每秒只发射一个新的`Long`型整数。为了合并它们,我们需要指定两个`Func1`变量:

```java
appInfo -> Observable.timer(2, TimeUnit.SECONDS)

time -> Observable.timer(0, TimeUnit.SECONDS)
```
上面描述了两个时间窗口。下面一行描述我们如何使用`Func2`将两个发射的数据结合在一起。
```java
this::updateTitle
```

结果如下:

它看起来有点乱,但是注意app的名字和我们指定的时间窗口,我们可以看到:一旦第二个数据发射了我们就会将它与源数据结合,但我们用同一个源数据有2秒钟。这就是为什么标题重复数字累加的原因。

值得一提的是,为了简单起见,也有一个`join()`操作符作用于字符串然后简单的和发射的字符串连接成最终的字符串。

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_8.png)

## combineLatest

RxJava的`combineLatest()`函数有点像`zip()`函数的特殊形式。正如我们已经学习的,`zip()`作用于最近未打包的两个Observables。相反,`combineLatest()`作用于最近发射的数据项:如果`Observable1`发射了A并且`Observable2`发射了B和C,`combineLatest()`将会分组处理AB和AC,如下图所示:

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_9.png)

`combineLatest()`函数接受二到九个Observable作为参数,如果有需要的话或者单个Observables列表作为参数。

从之前的例子中把`loadList()`函数借用过来,我们可以修改一下来用于`combineLatest()`实现"真实世界"这个例子:
```java
private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    Observable<AppInfo> appsSequence = Observable.interval(1000, TimeUnit.MILLISECONDS)
              .map(position ->apps.get(position.intValue()));
    Observable<Long> tictoc = Observable.interval(1500, TimeUnit.MILLISECONDS);
    Observable.combineLatest(appsSequence, tictoc,
               this::updateTitle)
       .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Observer<AppInfo>() {

        @Override
        public void onCompleted() {
            Toast.makeText(getActivity(), "Here is the list!", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onError(Throwable e) {
            mSwipeRefreshLayout.setRefreshing(false);
            Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onNext(AppInfoappInfo) {
            if (mSwipeRefreshLayout.isRefreshing()) {
                mSwipeRefreshLayout.setRefreshing(false);
            }
            mAddedApps.add(appInfo);
            int position = mAddedApps.size() - 1;
            mAdapter.addApplication(position, appInfo);
            mRecyclerView.smoothScrollToPosition(position);
        }
    });
}
```
这我们使用了两个Observables:一个是每秒钟从我们已安装的应用列表发射一个App数据,第二个是每隔1.5秒发射一个`Long`型整数。我们将他们结合起来并执行`updateTitle()`函数,结果如下:

正如你看到的,由于不同的时间间隔,`AppInfo`对象如我们所预料的那样有时候会重复。

## And,Then和When

在将来还有一些`zip()`满足不了的场景。如复杂的架构,或者是仅仅为了个人爱好,你可以使用And/Then/When解决方案。它们在RxJava的joins包下,使用Pattern和Plan作为中介,将发射的数据集合并到一起。

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_11.png)

我们的`loadList()`函数将会被修改从这样:
```java
private void loadList(List<AppInfo> apps) {

    mRecyclerView.setVisibility(View.VISIBLE);

    Observable<AppInfo> observableApp = Observable.from(apps);

    Observable<Long> tictoc = Observable.interval(1, TimeUnit.SECONDS);

    Pattern2<AppInfo, Long> pattern = JoinObservable.from(observableApp).and(tictoc);

    Plan0<AppInfo> plan = pattern.then(this::updateTitle);

    JoinObservable
        .when(plan)
        .toObservable()
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Observer<AppInfo>() {

            @Override
            public void onCompleted() {
                Toast.makeText(getActivity(), "Here is the list!", Toast.LENGTH_LONG).show();
            }

            @Override
            public void onError(Throwable e) {
                mSwipeRefreshLayout.setRefreshing(false);
                Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onNext(AppInfoappInfo) {
                if (mSwipeRefreshLayout.isRefreshing()) {
                mSwipeRefreshLayout.setRefreshing(false);
                }
                mAddedApps.add(appInfo);
                int position = mAddedApps.size() - 1;
                mAdapter.addApplication(position, appInfo); mRecyclerView.smoothScrollToPosition(position);
            }
        });
}
```
和通常一样,我们有两个发射的序列,`observableApp`,发射我们安装的应用列表数据,`tictoc`每秒发射一个`Long`型整数。现在我们用`and()`连接源Observable和第二个Observable。

```java
JoinObservable.from(observableApp).and(tictoc);
```
这里创建一个`pattern`对象,使用这个对象我们可以创建一个`Plan`对象:"我们有两个发射数据的Observables,`then()`是做什么的?"
```java
pattern.then(this::updateTitle);
```
现在我们有了一个`Plan`对象并且当plan发生时我们可以决定接下来发生的事情。
```java
.when(plan).toObservable()
```
这时候,我们可以订阅新的Observable,正如我们总是做的那样。

## Switch

有这样一个复杂的场景就是在一个`subscribe-unsubscribe`的序列里我们能够从一个Observable自动取消订阅来订阅一个新的Observable。

RxJava的`switch()`,正如定义的,将一个发射多个Observables的Observable转换成另一个单独的Observable,后者发射那些Observables最近发射的数据项。

给出一个发射多个Observables序列的源Observable,`switch()`订阅到源Observable然后开始发射由第一个发射的Observable发射的一样的数据。当源Observable发射一个新的Observable时,`switch()`立即取消订阅前一个发射数据的Observable(因此打断了从它那里发射的数据流)然后订阅一个新的Observable,并开始发射它的数据。

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_12.png)

## StartWith

我们已经学到如何连接多个Observables并追加指定的值到一个发射序列里。RxJava的`startWith()`是`concat()`的对应部分。正如`concat()`向发射数据的Observable追加数据那样,在Observable开始发射他们的数据之前, `startWith()`通过传递一个参数来先发射一个数据序列。

![](https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master/images/chapter6_13.png)

## 总结

这章中,我们学习了如何将两个或者更多个Observable结合来创建一个新的可观测序列。我们将能够`merge` Observable,`join` Observables ,`zip` Observables 并在几种情况下把他们结合在一起。

下一章,我们将介绍调度器,它将很容易的帮助我们创建主线程以及提高我们应用程序的性能。我们也将学习如何正确的执行长任务或者I/O任务来获得更好的性能。


text:
using DISampleLib;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Windows;

namespace WPFClient
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            RegisterServices();
        }

        public void RegisterServices()
        {
            var services = new ServiceCollection();
            services.AddSingleton<IMessageService, WPFMessageService>();
            services.AddTransient<ShowMessageViewModel>();
            Container = services.BuildServiceProvider();
        }

        public IServiceProvider Container { get; private set; }
    }
}


text:
namespace base {
class DictionaryValue;
class ListValue;
}

namespace content {
class WebUIDataSource;
}

class Profile;

namespace extensions {

class Extension;

// The handler page for the Extension Commands UI overlay.
class ExtensionErrorHandler : public content::WebUIMessageHandler {
 public:
  explicit ExtensionErrorHandler(Profile* profile);
  ~ExtensionErrorHandler() override;

  // Fetches the localized values for the page and deposits them into |source|.
  void GetLocalizedValues(content::WebUIDataSource* source);

  // WebUIMessageHandler implementation.
  void RegisterMessages() override;

 private:
  friend class ManifestHighlightUnitTest;

  // Handle the "requestFileSource" call.
  void HandleRequestFileSource(const base::ListValue* args);

  // Called when |error_ui_util::HandleRequestFileSource| finishes.
  void OnFileSourceHandled(const base::DictionaryValue& source);

  // Handle the "openDevTools" call.
  void HandleOpenDevTools(const base::ListValue* args);

  // Populate the results for a manifest file's content in response to the
  // "requestFileSource" call. Highlight the part of the manifest which
  // corresponds to the given |key| and |specific| locations. Caller owns
  // |dict|.
  void GetManifestFileCallback(base::DictionaryValue* dict,
                               const std::string& key,
                               const std::string& specific,
                               const std::string& contents);

  // Populate the results for a source file's content in response to the
  // "requestFileSource" call. Highlight the part of the source which
  // corresponds to the given |line_number|.
  void GetSourceFileCallback(base::DictionaryValue* results,
                             int line_number,
                             const std::string& contents);

  // The profile with which this Handler is associated.
  Profile* profile_;

  // Weak pointer factory for posting background tasks.
  base::WeakPtrFactory<ExtensionErrorHandler> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(ExtensionErrorHandler);
};

}  // namespace extensions

#endif  // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_ERROR_HANDLER_H_


text:
package org.apache.flink.table.planner.plan.nodes.physical.batch

import org.apache.flink.api.dag.Transformation
import org.apache.flink.configuration.Configuration
import org.apache.flink.core.memory.ManagedMemoryUseCase
import org.apache.flink.streaming.api.operators.OneInputStreamOperator
import org.apache.flink.streaming.api.transformations.OneInputTransformation
import org.apache.flink.table.api.config.ExecutionConfigOptions
import org.apache.flink.table.data.RowData
import org.apache.flink.table.functions.UserDefinedFunction
import org.apache.flink.table.functions.python.PythonFunctionInfo
import org.apache.flink.table.planner.calcite.FlinkRelBuilder.PlannerNamedWindowProperty
import org.apache.flink.table.planner.calcite.FlinkTypeFactory
import org.apache.flink.table.planner.codegen.agg.batch.WindowCodeGenerator
import org.apache.flink.table.planner.delegation.BatchPlanner
import org.apache.flink.table.planner.expressions.{PlannerRowtimeAttribute, PlannerWindowEnd, PlannerWindowStart}
import org.apache.flink.table.planner.plan.cost.{FlinkCost, FlinkCostFactory}
import org.apache.flink.table.planner.plan.logical.LogicalWindow
import org.apache.flink.table.planner.plan.nodes.common.CommonPythonAggregate
import org.apache.flink.table.planner.plan.nodes.exec.{BatchExecNode, ExecEdge, ExecNode}
import org.apache.flink.table.planner.plan.nodes.physical.batch.BatchExecPythonGroupWindowAggregate.ARROW_PYTHON_GROUP_WINDOW_AGGREGATE_FUNCTION_OPERATOR_NAME
import org.apache.flink.table.runtime.typeutils.InternalTypeInfo
import org.apache.flink.table.types.logical.RowType

import org.apache.calcite.plan.{RelOptCluster, RelOptCost, RelOptPlanner, RelTraitSet}
import org.apache.calcite.rel.RelNode
import org.apache.calcite.rel.`type`.RelDataType
import org.apache.calcite.rel.core.AggregateCall
import org.apache.calcite.rel.metadata.RelMetadataQuery
import org.apache.calcite.tools.RelBuilder

import java.util

import scala.collection.JavaConversions._

/**
  * Batch physical RelNode for group widow aggregate (Python user defined aggregate function).
  */
class BatchExecPythonGroupWindowAggregate(
    cluster: RelOptCluster,
    relBuilder: RelBuilder,
    traitSet: RelTraitSet,
    inputRel: RelNode,
    outputRowType: RelDataType,
    inputRowType: RelDataType,
    grouping: Array[Int],
    auxGrouping: Array[Int],
    aggCalls: Seq[AggregateCall],
    aggFunctions: Array[UserDefinedFunction],
    window: LogicalWindow,
    inputTimeFieldIndex: Int,
    inputTimeIsDate: Boolean,
    namedProperties: Seq[PlannerNamedWindowProperty])
  extends BatchExecWindowAggregateBase(
    cluster,
    traitSet,
    inputRel,
    outputRowType,
    inputRowType,
    grouping,
    auxGrouping,
    aggCalls.zip(aggFunctions),
    window,
    namedProperties,
    false,
    false,
    true)
  with BatchExecNode[RowData]
  with CommonPythonAggregate {

  override def copy(traitSet: RelTraitSet, inputs: util.List[RelNode]): RelNode = {
    new BatchExecPythonGroupWindowAggregate(
      cluster,
      relBuilder,
      traitSet,
      inputs.get(0),
      outputRowType,
      inputRowType,
      grouping,
      auxGrouping,
      aggCalls,
      aggFunctions,
      window,
      inputTimeFieldIndex,
      inputTimeIsDate,
      namedProperties)
  }

  override def computeSelfCost(planner: RelOptPlanner, mq: RelMetadataQuery): RelOptCost = {
    val inputRowCnt = mq.getRowCount(getInput)
    if (inputRowCnt == null) {
      return null
    }
    // does not take pane optimization into consideration here
    // sort is not done here
    val aggCallToAggFunction = aggCalls.zip(aggFunctions)
    val cpu = FlinkCost.FUNC_CPU_COST * inputRowCnt * aggCallToAggFunction.size
    val averageRowSize: Double = mq.getAverageRowSize(this)
    val memCost = averageRowSize
    val costFactory = planner.getCostFactory.asInstanceOf[FlinkCostFactory]
    costFactory.makeCost(mq.getRowCount(this), cpu, 0, 0, memCost)
  }

  //~ ExecNode methods -----------------------------------------------------------

  override def getInputNodes: util.List[ExecNode[BatchPlanner, _]] =
    List(getInput.asInstanceOf[ExecNode[BatchPlanner, _]])

  override def getInputEdges: util.List[ExecEdge] = List(ExecEdge.DEFAULT)

  override def replaceInputNode(
      ordinalInParent: Int,
      newInputNode: ExecNode[BatchPlanner, _]): Unit = {
    replaceInput(ordinalInParent, newInputNode.asInstanceOf[RelNode])
  }

  override protected def translateToPlanInternal(
      planner: BatchPlanner): Transformation[RowData] = {
    val input = getInputNodes.get(0).translateToPlan(planner)
      .asInstanceOf[Transformation[RowData]]
    val outputType = FlinkTypeFactory.toLogicalRowType(getRowType)
    val inputType = FlinkTypeFactory.toLogicalRowType(inputRowType)

    val (windowSize: Long, slideSize: Long) = WindowCodeGenerator.getWindowDef(window)

    val groupBufferLimitSize = planner.getTableConfig.getConfiguration.getInteger(
      ExecutionConfigOptions.TABLE_EXEC_WINDOW_AGG_BUFFER_SIZE_LIMIT)

    val ret = createPythonOneInputTransformation(
      input,
      inputType,
      outputType,
      inputTimeFieldIndex,
      groupBufferLimitSize,
      windowSize,
      slideSize,
      getConfig(planner.getExecEnv, planner.getTableConfig))

    if (isPythonWorkerUsingManagedMemory(planner.getTableConfig.getConfiguration)) {
      ret.declareManagedMemoryUseCaseAtSlotScope(ManagedMemoryUseCase.PYTHON)
    }
    ret
  }

  private[this] def createPythonOneInputTransformation(
      inputTransform: Transformation[RowData],
      inputRowType: RowType,
      outputRowType: RowType,
      inputTimeFieldIndex: Int,
      maxLimitSize: Int,
      windowSize: Long,
      slideSize: Long,
      config: Configuration): OneInputTransformation[RowData, RowData] = {
    val namePropertyTypeArray = namedProperties.map {
      case PlannerNamedWindowProperty(_, p) => p match {
        case PlannerWindowStart(_) => 0
        case PlannerWindowEnd(_) => 1
        case PlannerRowtimeAttribute(_) => 2
      }
    }.toArray

    val (pythonUdafInputOffsets, pythonFunctionInfos) =
      extractPythonAggregateFunctionInfosFromAggregateCall(aggCalls)

    val pythonOperator = getPythonGroupWindowAggregateFunctionOperator(
      config,
      inputRowType,
      outputRowType,
      inputTimeFieldIndex,
      maxLimitSize,
      windowSize,
      slideSize,
      namePropertyTypeArray,
      pythonUdafInputOffsets,
      pythonFunctionInfos)

    new OneInputTransformation(
      inputTransform,
      "BatchExecPythonGroupWindowAggregate",
      pythonOperator,
      InternalTypeInfo.of(outputRowType),
      inputTransform.getParallelism)
  }

  private[this] def getPythonGroupWindowAggregateFunctionOperator(
      config: Configuration,
      inputRowType: RowType,
      outputRowType: RowType,
      inputTimeFieldIndex: Int,
      maxLimitSize: Int,
      windowSize: Long,
      slideSize: Long,
      namedProperties: Array[Int],
      udafInputOffsets: Array[Int],
      pythonFunctionInfos: Array[PythonFunctionInfo]): OneInputStreamOperator[RowData, RowData] = {
    val clazz = loadClass(ARROW_PYTHON_GROUP_WINDOW_AGGREGATE_FUNCTION_OPERATOR_NAME)

    val ctor = clazz.getConstructor(
      classOf[Configuration],
      classOf[Array[PythonFunctionInfo]],
      classOf[RowType],
      classOf[RowType],
      classOf[Int],
      classOf[Int],
      classOf[Long],
      classOf[Long],
      classOf[Array[Int]],
      classOf[Array[Int]],
      classOf[Array[Int]],
      classOf[Array[Int]])

    ctor.newInstance(
      config,
      pythonFunctionInfos,
      inputRowType,
      outputRowType,
      Integer.valueOf(inputTimeFieldIndex),
      Integer.valueOf(maxLimitSize),
      java.lang.Long.valueOf(windowSize),
      java.lang.Long.valueOf(slideSize),
      namedProperties,
      grouping,
      grouping ++ auxGrouping,
      udafInputOffsets)
      .asInstanceOf[OneInputStreamOperator[RowData, RowData]]
  }
}

object BatchExecPythonGroupWindowAggregate {
  val ARROW_PYTHON_GROUP_WINDOW_AGGREGATE_FUNCTION_OPERATOR_NAME: String =
    "org.apache.flink.table.runtime.operators.python.aggregate.arrow.batch." +
      "BatchArrowPythonGroupWindowAggregateFunctionOperator"
}


text:
public class Rating implements Comparable<Rating> {
    private String item;
    private double value;

    public Rating (String anItem, double aValue) {
        item = anItem;
        value = aValue;
    }

    // Returns item being rated
    public String getItem () {
        return item;
    }

    // Returns the value of this rating (as a number so it can be used in calculations)
    public double getValue () {
        return value;
    }

    // Returns a string of all the rating information
    public String toString () {
        return "[" + getItem() + ", " + getValue() + "]";
    }

    public int compareTo(Rating other) {
        if (value < other.value) return -1;
        if (value > other.value) return 1;

        return 0;
    }
}


text:
import org.jetbrains.annotations.Nullable;

class Test {
    void test(){
        @Nullable String s = null;
        if (s == null) return;
        <selection>System.out.println(s);</selection>
    }
}


text:
package eu.mivrenik.particles.io;

import eu.mivrenik.particles.model.ExperimentSettings;
import eu.mivrenik.particles.model.ExperimentState;
import eu.mivrenik.particles.model.Simulator;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class ExperimentIOTest {
    @Rule
    public TemporaryFolder tmpFolder = new TemporaryFolder();

    public static String FILE_NAME = "simulation_test.bin";

    private File file;
    private ExperimentSettings settings;
    private ExperimentLoader loader;

    @Before
    public void setUp() throws Exception {
        file = tmpFolder.newFile(FILE_NAME);

        settings = ExperimentSettings.newBuilder()
                .boxSize(100.0f, 100.0f)
                .barrier(45.0f, 10.0f)
                .hole(45.0f, 10.0f)
                .initialSpeed(1.0f)
                .speedDelta(0.05f, 0.05f, 0.05f)
                .speedLoss(0.005f)
                .particleRadius(0.05f)
                .particleCount(50, 50)
                .fps(30)
                .g(9.8f)
                .duration(1)
                .seed(5553535)
                .build();

        Simulator simulator = new Simulator(settings);
        SimulationWriter writer = new SimulationWriter(simulator, file);

        writer.saveSimulation();

        loader = new ExperimentLoader(file);
    }

    @Test
    public void testSizeCalculations() throws Exception {
        // refer to readme for explanation on those "magic numbers"

        assertEquals(3608, loader.getDataChunkSize());
        assertEquals(30 * 1 * 60, loader.getStateCount()); // fps * duration
    }

    @Test
    public void testInitialData() throws Exception {
        assertEquals(settings, loader.getExperimentSettings());

        ExperimentState state = loader.getState(0);

        assertEquals(0, state.getTime());
        assertEquals(settings, state.getSettings());
    }

    @Test
    public void testRandomAccess() throws Exception {
        ExperimentState state_base = loader.getState(53);
        ExperimentState state_forward = loader.getState(56);
        ExperimentState state_before = loader.getState(50);

        assertEquals(settings, state_base.getSettings());
        assertEquals(settings, state_forward.getSettings());
        assertEquals(settings, state_before.getSettings());

        assertTrue(state_base.getTime() < state_forward.getTime());
        assertTrue(state_before.getTime() < state_base.getTime());
    }

}


text:
require 'spec_helper'
require 'json'

describe BeakerAnswers::Version20173 do
  let(:ver)         { '2017.3.0' }
  let(:options)     { StringifyHash.new }
  let(:mono_hosts) do
    basic_hosts = make_hosts({'pe_ver' => ver }, 1)
    basic_hosts[0]['roles'] = ['master', 'agent', 'dashboard', 'database']
    basic_hosts[0]['platform'] = 'el-7-x86_64'
    basic_hosts
  end
  let(:answers)     { BeakerAnswers::Answers.create(ver, hosts, options) }
  let(:answer_hash) { answers.answers }

  context 'when generating a default 1.0 config' do
    context 'for a monolithic install' do
      let(:hosts) { mono_hosts }
      let(:gold_role_answers) do
        {
          "console_admin_password" => default_password,
          "puppet_enterprise::puppet_master_host" => hosts[0].hostname,
        }
      end

      include_examples 'pe.conf'
      include_examples 'valid MEEP 1.0 pe.conf'
    end
  end

  context 'when generating a meep 2.0 config' do
    before(:each) do
      options[:meep_schema_version] = '2.0'
    end

    context 'for a monolithic install' do
      let(:hosts) { mono_hosts }
      let(:gold_role_answers) do
        {
          "console_admin_password" => default_password,
          "node_roles" => {
            "pe_role::monolithic::primary_master" => [hosts[0].hostname],
          },
          "agent_platforms" => match_array(['el_7_x86_64']),
          "meep_schema_version" => "2.0",
        }
      end

      include_examples 'pe.conf'
      include_examples 'valid MEEP 2.0 pe.conf'
    end
  end
end


text:
/**
 * message.router.dev.js
 *
 * @package   MessageRouter
 * @category  Router
 * @version   1.0
 * @author    Ricky Hurtado <ricky@aelogica.com>
 */

define([
  'core_module'
],
function()
{
  /**
   * Init MessageRouter class
   */
  var MessageRouter = Backbone.SubRoute.extend(
  {
    /**
     * Initialize router
     */
    initialize : function()
    {
      this.checkPage = I.Core.Router.checkPage;
      I.Message.View.renderMessagePage();

      console.log('Backbone.SubRoute.MessageRouter has been initialized.');
    },

    /**
     * Register routes
     */
    routes :
    {
      ''        : 'index',
      'message' : 'index'
    },

    /**
     * Index route function
     */
    index : function()
    {
      console.log('[Route] router-and-view');

      clearTimeout(this.checkPage);
      $(I.Message.View.target.message).html('');
      I.Core.Controller.showContent( { el : '#message-content', rel : '.sub-content[id]' } );
    }
  });

  return MessageRouter;
});


text:
import tempfile

from stomp.__main__ import StompCLI
from .testutils import *

username = get_default_user()
password = get_default_password()

(host, port) = get_default_host()[0]

test_text = '''subscribe /queue/testfile
send /queue/testfile this is a test
unsubscribe /queue/testfile'''

def create_test_file():
    with tempfile.NamedTemporaryFile('w', delete=False) as f:
        f.write('''subscribe /queue/testfile
send /queue/testfile this is a test
unsubscribe /queue/testfile''')
    return f

class TestCLI(object):
    def test_invalid_version(self):
        with pytest.raises(RuntimeError):
            StompCLI(host, port, username, password, "invalid")

    def test_help(self):
        teststdin = StubStdin()

        cli = StompCLI(host, port, username, password, "1.0", stdin=teststdin)

        cli.help_help()
        cli.help_quit()
        cli.help_exit()
        cli.help_EOF()
        cli.help_subscribe()
        cli.help_unsubscribe()
        cli.help_send()
        cli.help_sendrec()
        cli.help_sendreply()
        cli.help_sendfile()
        cli.help_version()
        cli.help_ack()
        cli.help_nack()
        cli.help_abort()
        cli.help_commit()
        cli.help_stats()
        cli.help_run()
        cli.help_begin()

    def testsubscribe(self):
        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.0", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        teststdout.expect("Subscribing to '/queue/testsubscribe' with acknowledge set to 'auto', id set to '1'")
        cli.onecmd("subscribe /queue/testsubscribe")
        teststdout.expect("MESSAGE")
        teststdout.expect("this is a test")
        cli.onecmd("send /queue/testsubscribe this is a test")

        time.sleep(3)

        teststdout.expect("Unsubscribing from '/queue/testsubscribe'")
        cli.onecmd("unsubscribe /queue/testsubscribe")
        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def testsendrec(self):
        teststdin = StubStdin()
        teststdout = StubStdout(self)

        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.1", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        teststdout.expect("RECEIPT")
        cli.onecmd("sendrec /queue/testsendrec this is a test")

        time.sleep(3)

        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def testsendfile(self):
        f = create_test_file()

        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.2", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        teststdout.expect("Subscribing to '/queue/testsendfile' with acknowledge set to 'auto', id set to '1'")
        cli.onecmd("subscribe /queue/testsendfile")

        time.sleep(3)

        teststdout.expect("MESSAGE")
        cli.onecmd("sendfile /queue/testsendfile %s" % f.name)

        time.sleep(3)

        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def testsendfileheaders(self):
        f = create_test_file()

        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.2", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        cli.onecmd("sendfile /queue/testsendfile %s { \"custom\" : \"header\" }" % f.name)

        time.sleep(3)

        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def testabort(self):
        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.2", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        teststdout.expect("Subscribing to '/queue/testabort' with acknowledge set to 'auto', id set to '1'")
        cli.onecmd("subscribe /queue/testabort")

        cli.onecmd("begin")
        cli.onecmd("send /queue/testabort this is a test")
        cli.onecmd("abort")

        time.sleep(3)

        teststdout.expect("Unsubscribing from '/queue/testabort'")
        cli.onecmd("unsubscribe /queue/testabort")
        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def testcommit(self):
        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.0", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        teststdout.expect("Subscribing to '/queue/testcommit' with acknowledge set to 'auto', id set to '1'")
        cli.onecmd("subscribe /queue/testcommit")

        cli.onecmd("begin")
        cli.onecmd("send /queue/testcommit this is a test")

        teststdout.expect("Committing.*")
        teststdout.expect("MESSAGE")
        teststdout.expect("this is a test")
        cli.onecmd("commit")

        time.sleep(3)

        teststdout.expect("Unsubscribing from '/queue/testcommit'")
        cli.onecmd("unsubscribe /queue/testcommit")
        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def teststats(self):
        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.0", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        teststdout.expect("Subscribing to '/queue/teststats' with acknowledge set to 'auto', id set to '1'")
        cli.onecmd("subscribe /queue/teststats")

        teststdout.expect(".*No stats available.*")
        cli.onecmd("stats")

        time.sleep(1)

        cli.onecmd("stats on")
        cli.onecmd("send /queue/teststats this is a test")

        teststdout.expect("MESSAGE")
        teststdout.expect("this is a test")

        time.sleep(3)

        cli.onecmd("stats")

        teststdout.expect("Unsubscribing from '/queue/teststats'")
        cli.onecmd("unsubscribe /queue/teststats")
        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def testrun(self):
        f = create_test_file()

        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.0", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        teststdout.expect("Subscribing to '/queue/testfile' with acknowledge set to 'auto', id set to '1'")
        teststdout.expect("this is a test")
        teststdout.expect("MESSAGE")
        teststdout.expect("Unsubscribing from '/queue/testfile'")
        cli.onecmd("run %s" % f.name)
        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def testrunarg(self):
        f = create_test_file()

        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.0", stdin=teststdin, stdout=teststdout)

        time.sleep(3)

        teststdout.expect("Subscribing to '/queue/testfile' with acknowledge set to 'auto', id set to '1'")
        teststdout.expect("this is a test")
        teststdout.expect("MESSAGE")
        teststdout.expect("Unsubscribing from '/queue/testfile'")

        cli.do_run(f.name)

        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    def test_multicast(self):
        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(None, None, None, None, "multicast", stdin=teststdin, stdout=teststdout)

        teststdout.expect("Subscribing to '/queue/testmulticastclisubscribe' with acknowledge set to 'auto', id set to '1'")
        cli.onecmd("subscribe /queue/testmulticastclisubscribe")
        teststdout.expect("MESSAGE")
        teststdout.expect("this is a test")
        cli.onecmd("send /queue/testsubscribe this is a test")

        teststdout.expect("Shutting down, please wait")
        cli.onecmd("quit")

    # just here for coverage
    def test_on_disconnected_edgecase(self):
        teststdin = StubStdin()
        teststdout = StubStdout(self)
        teststdout.expect("CONNECTED")

        cli = StompCLI(host, port, username, password, "1.0", stdin=teststdin, stdout=teststdout)

        teststdout.expect(".*lost connection.*")

        cli.on_disconnected()


text:
"""Utility functions for creating TFRecord data sets."""

import tensorflow as tf

def int64_feature(value):
  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

def int64_list_feature(value):
  return tf.train.Feature(int64_list=tf.train.Int64List(value=value))

def bytes_feature(value):
  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

def bytes_list_feature(value):
  return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))

def float_list_feature(value):
  return tf.train.Feature(float_list=tf.train.FloatList(value=value))

def read_examples_list(path):
  """Read list of training or validation examples.

  The file is assumed to contain a single example per line where the first
  token in the line is an identifier that allows us to find the image and
  annotation xml for that example.

  For example, the line:
  xyz 3
  would allow us to find files xyz.jpg and xyz.xml (the 3 would be ignored).

  Args:
    path: absolute path to examples list file.

  Returns:
    list of example identifiers (strings).
  """
  with tf.gfile.GFile(path) as fid:
    lines = fid.readlines()
  return [line.strip().split(' ')[0] for line in lines]

def recursive_parse_xml_to_dict(xml):
  """Recursively parses XML contents to python dict.

  We assume that `object` tags are the only ones that can appear
  multiple times at the same level of a tree.

  Args:
    xml: xml tree obtained by parsing XML file contents using lxml.etree

  Returns:
    Python dictionary holding XML contents.
  """
  if not xml:
    return {xml.tag: xml.text}
  result = {}
  for child in xml:
    child_result = recursive_parse_xml_to_dict(child)
    if child.tag != 'object':
      result[child.tag] = child_result[child.tag]
    else:
      if child.tag not in result:
        result[child.tag] = []
      result[child.tag].append(child_result[child.tag])
  return {xml.tag: result}

def make_initializable_iterator(dataset):
  """Creates an iterator, and initializes tables.

  This is useful in cases where make_one_shot_iterator wouldn't work because
  the graph contains a hash table that needs to be initialized.

  Args:
    dataset: A `tf.data.Dataset` object.

  Returns:
    A `tf.data.Iterator`.
  """
  iterator = dataset.make_initializable_iterator()
  tf.add_to_collection(tf.GraphKeys.TABLE_INITIALIZERS, iterator.initializer)
  return iterator

def read_dataset(file_read_func, decode_func, input_files, config):
  """Reads a dataset, and handles repetition and shuffling.

  Args:
    file_read_func: Function to use in tf.data.Dataset.interleave, to read
      every individual file into a tf.data.Dataset.
    decode_func: Function to apply to all records.
    input_files: A list of file paths to read.
    config: A input_reader_builder.InputReader object.

  Returns:
    A tf.data.Dataset based on config.
  """
  # Shard, shuffle, and read files.
  filenames = tf.concat([tf.matching_files(pattern) for pattern in input_files],
                        0)
  filename_dataset = tf.data.Dataset.from_tensor_slices(filenames)
  if config.shuffle:
    filename_dataset = filename_dataset.shuffle(
        config.filenames_shuffle_buffer_size)
  filename_dataset = filename_dataset.repeat(config.num_epochs or None)

  records_dataset = filename_dataset.apply(
      tf.contrib.data.parallel_interleave(
          file_read_func, cycle_length=config.num_readers, sloppy=True))
  if config.shuffle:
    records_dataset.shuffle(config.shuffle_buffer_size)
  tensor_dataset = records_dataset.map(
      decode_func, num_parallel_calls=config.num_parallel_map_calls)
  return tensor_dataset.prefetch(config.prefetch_size)


text:
var Board  = require('../models/Board'),
    Column = require('../models/Column'),
    User   = require('../models/User'),
    conf   = sails.config.app;

var stringifyRegex = function(regex) {
  return String(regex).replace(/^\/|\/$/g, '');
};

conf.regex = {
  username:    stringifyRegex(User.usernameRegex),
  columnTitle: stringifyRegex(Column.titleRegex),
  boardTitle:  stringifyRegex(Board.titleRegex)
};

module.exports = {
  all:         function() { return conf; },
  appname:     function() { return conf.appname; },
  uiversion:   function() { return conf.uiversion; },
  signin:      function() { return conf.signin; },
  colsets:     function() { return conf.colsets; },
  regex:       function() { return conf.regex; },
  colsetsById: function() {
    var ret = {};
    conf.colsets.forEach(function(s) { ret[s.id] = s; });
    return ret;
  }
};


text:
package io.tessilab.oss.openutils.hocr;

import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/* Class: Paragraph in Hocr documents */
public class HocrParagraph extends HocrNode {

    private static final Logger LOGGER = LoggerFactory.getLogger(HocrParagraph.class);

    private static final long serialVersionUID = 8562855616568852356L;

    private ReadingDirection dir;

    public HocrParagraph(HocrSection parent, Element element) throws HocrSyntaxException {
        super(parent, element);
        if ("ltr".equals(element.attr("dir"))) {
            dir = ReadingDirection.LEFT_TO_RIGHT;
        } else if ("rtl".equals(element.attr("dir"))) {
            dir = ReadingDirection.RIGHT_TO_LEFT;
        } else {
            dir = ReadingDirection.UNDEFINED_DIRECTION;
        }
    }

    public HocrParagraph(HocrSection parent) {
        super(parent);
        dir = ReadingDirection.UNDEFINED_DIRECTION;
    }

    @Override
    protected HocrSection getChildFromElement(Element element) {
        try {
            return new HocrLine(this, element);
        } catch (HocrSyntaxException hse) {
            LOGGER.debug("Exception while creating an HocrLine from an html element.", hse);
            return null;
        }
    }

    public ReadingDirection getReadingDirection() {
        return this.dir;
    }

    @Override
    protected String getElementTag() {
        return HTMLConstants.TAG_P;
    }

    @Override
    protected String getElementClassAttribute() {
        return HTMLConstants.PAR_CLASS;
    }

    @Override
    protected Element toHtmlElement() {
        Element elem = super.toHtmlElement();
        elem.attr("dir", ReadingDirection.toHocrString(this.getReadingDirection()));
        return elem;
    }

}