text:
package com.flux4j;

import junit.framework.Assert;
import org.junit.Test;

import java.util.function.Function;

import static org.junit.Assert.assertTrue;

public class DispatcherTest {

    @Test
    public void test_registerUnRegister() {
        Dispatcher dispatcher = Dispatcher.getInstance();
        String id = dispatcher.register(new Function<Action, Object>() {
            @Override
            public Object apply(Action o) {
                return null;
            }
        });
        Assert.assertFalse(dispatcher.callbacks.isEmpty());
        dispatcher.unRegister(id);
        Assert.assertTrue(dispatcher.callbacks.isEmpty());
    }

    @Test
    public void test_dispatch() {
        Dispatcher dispatcher = Dispatcher.getInstance();
        Function<Action, Object> callback = new Function<Action, Object>() {
            public Integer nbCall = 0;

            @Override
            public Object apply(Action action) {
                ++nbCall;
                return nbCall;
            }
        };
        dispatcher.register(callback);
        dispatcher.dispatch(new Action("action.test"));
        assertTrue((Integer) callback.apply(new Action("action.test")) == 2);
    }
}

redpajama_set_name: RedPajamaGithub


text:
<?php

namespace Hgabka\KunstmaanExtensionBundle\User;

use Doctrine\Bundle\DoctrineBundle\Registry;
use Kunstmaan\AdminBundle\Entity\User;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;

/**
 * UserEditCommand db services.
 *
 * @author Gabe <hgabka@gmail.com>
 */
class UserEditService
{
    /**
     * @var Registry
     */
    private $registry;
    /**
     * @var UserPasswordEncoder
     */
    private $encoder;

    /**
     * @param Registry            $registry
     * @param UserPasswordEncoder $encoder
     */
    public function __construct(Registry $registry, UserPasswordEncoder $encoder)
    {
        $this->registry = $registry;
        $this->encoder = $encoder;
    }

    /**
     * @return UserPasswordEncoder
     */
    public function getEncoder()
    {
        return $this->encoder;
    }

    /**
     * @return Registry
     */
    public function getRegistry()
    {
        return $this->registry;
    }

    /**
     * Find user choices.
     *
     * @param string $username
     * @param string $email
     * @param bool   $or       combine search params with OR
     * @param int    $limit    Limit the number of results
     *
     * @return array
     */
    public function getChoices($username = null, $email = null, $or = false, $limit = null)
    {
        $qb = $this->getRepository()->createQueryBuilder('u');
        $qb->orderBy('u.username');
        $method = $or ? 'orWhere' : 'andWhere';
        if ($username) {
            $qb->$method('u.username LIKE :username');
            $qb->setParameter('username', '%'.$username.'%');
        }
        if ($email) {
            $qb->$method('u.email LIKE :email');
            $qb->setParameter('email', '%'.$email.'%');
        }
        if ($limit) {
            $qb->setMaxResults($limit);
        }

        return $qb->getQuery()->getResult();
    }

    /**
     * Get selector choices as combined username+email.
     *
     * @param User[] $choices
     *
     * @return string[]
     */
    public function getChoicesAsEmailUsername(array &$choices)
    {
        $ret = [];
        foreach ($choices as $item) {
            $ret[] = sprintf('%s (%s)', $item->getEmail(), $item->getUsername());
        }

        return $ret;
    }

    /**
     * User choices is separate usernames/email for autocomplete.
     *
     * @param User[] $choices
     *
     * @return string[]
     */
    public function getChoicesAsSeparateEmailUsername(array &$choices)
    {
        $ret = [];
        foreach ($choices as $item) {
            $ret[] = $item->getEmail();
            $ret[] = $item->getUsername();
        }

        return $ret;
    }

    /**
     * Update user details.
     *
     * @param User        $user
     * @param UserUpdater $up
     */
    public function updateUser(User $user, UserUpdater $up)
    {
        $up->updateUser($user, $this->getEncoder());
        $em = $this->getRegistry()->getManager();
        $em->persist($user);
        $em->flush();
    }

    /**
     * @return \Kunstmaan\AdminBundle\Repository\UserRepository
     */
    protected function getRepository()
    {
        return $this->getRegistry()->getRepository('KunstmaanAdminBundle:User');
    }
}


text:
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/ime/linux/input_method_auralinux.h"

#include <stddef.h>

#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/singleton.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/ime/dummy_text_input_client.h"
#include "ui/base/ime/ime_key_event_dispatcher.h"
#include "ui/base/ime/init/input_method_initializer.h"
#include "ui/base/ime/linux/linux_input_method_context_factory.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/ime/virtual_keyboard_controller_stub.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"

namespace ui {
namespace {

const char16_t kActionCommit = L'C';
const char16_t kActionCompositionStart = L'S';
const char16_t kActionCompositionUpdate = L'U';
const char16_t kActionCompositionEnd = L'E';

class TestResult {
 public:
  static TestResult* GetInstance() {
    return base::Singleton<TestResult>::get();
  }

  void RecordAction(const std::u16string& action) {
    recorded_actions_.push_back(action);
  }

  void ExpectAction(const std::string& action) {
    expected_actions_.push_back(base::ASCIIToUTF16(action));
  }

  void Verify() {
    size_t len = recorded_actions_.size();
    size_t len_exp = expected_actions_.size();
    EXPECT_EQ(len_exp, len);
    for (size_t i = 0; i < len; i++)
      EXPECT_EQ(expected_actions_[i], recorded_actions_[i]);
    recorded_actions_.clear();
    expected_actions_.clear();
  }

 private:
  std::vector<std::u16string> recorded_actions_;
  std::vector<std::u16string> expected_actions_;
};

class LinuxInputMethodContextForTesting : public LinuxInputMethodContext {
 public:
  explicit LinuxInputMethodContextForTesting(
      LinuxInputMethodContextDelegate* delegate)
      : delegate_(delegate), is_sync_mode_(false), eat_key_(false) {}

  LinuxInputMethodContextForTesting(const LinuxInputMethodContextForTesting&) =
      delete;
  LinuxInputMethodContextForTesting& operator=(
      const LinuxInputMethodContextForTesting&) = delete;

  void SetSyncMode(bool is_sync_mode) { is_sync_mode_ = is_sync_mode; }
  void SetEatKey(bool eat_key) { eat_key_ = eat_key; }

  void AddCommitAction(const std::string& text) {
    actions_.push_back(u"C:" + base::ASCIIToUTF16(text));
  }

  void AddCompositionUpdateAction(const std::string& text) {
    actions_.push_back(u"U:" + base::ASCIIToUTF16(text));
  }

  void AddCompositionStartAction() { actions_.push_back(u"S"); }

  void AddCompositionEndAction() { actions_.push_back(u"E"); }

  VirtualKeyboardController* GetVirtualKeyboardController() override {
    return &virtual_keyboard_controller_;
  }

  TextInputType input_type() const { return input_type_; }
  TextInputMode input_mode() const { return input_mode_; }
  uint32_t input_flags() const { return input_flags_; }
  bool should_do_learning() const { return should_do_learning_; }
  TextInputClient* old_client() { return old_client_; }
  TextInputClient* new_client() { return new_client_; }

 protected:
  bool DispatchKeyEvent(const ui::KeyEvent& key_event) override {
    if (!is_sync_mode_) {
      actions_.clear();
      return eat_key_;
    }

    for (const auto& action : actions_) {
      std::vector<std::u16string> parts =
          base::SplitString(action, std::u16string(1, ':'),
                            base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
      char16_t id = parts[0][0];
      std::u16string param;
      if (parts.size() > 1)
        param = parts[1];
      if (id == kActionCommit) {
        delegate_->OnCommit(param);
      } else if (id == kActionCompositionStart) {
        delegate_->OnPreeditStart();
      } else if (id == kActionCompositionUpdate) {
        CompositionText comp;
        comp.text = param;
        delegate_->OnPreeditChanged(comp);
      } else if (id == kActionCompositionEnd) {
        delegate_->OnPreeditEnd();
      }
    }

    actions_.clear();
    return eat_key_;
  }

  bool IsPeekKeyEvent(const ui::KeyEvent& key_event) override {
    const auto* properties = key_event.properties();
    // For the purposes of tests if kPropertyKeyboardImeFlag is not
    // explicitly set assume the event is not a key event.
    if (!properties)
      return true;
    auto it = properties->find(kPropertyKeyboardImeFlag);
    if (it == properties->end())
      return true;
    return !(it->second[0] & kPropertyKeyboardImeIgnoredFlag);
  }

  void Reset() override {}

  void WillUpdateFocus(TextInputClient* old_client,
                       TextInputClient* new_client) override {
    old_client_ = old_client;
    new_client_ = new_client;
  }

  void UpdateFocus(bool has_client,
                   TextInputType old_type,
                   TextInputType new_type) override {}

  void SetCursorLocation(const gfx::Rect& rect) override {
    cursor_position_ = rect;
  }

  void SetSurroundingText(const std::u16string& text,
                          const gfx::Range& selection_range) override {
    TestResult::GetInstance()->RecordAction(u"surroundingtext:" + text);

    std::stringstream rs;
    rs << "selectionrangestart:" << selection_range.start();
    std::stringstream re;
    re << "selectionrangeend:" << selection_range.end();
    TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(rs.str()));
    TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(re.str()));
  }

  void SetContentType(TextInputType type,
                      TextInputMode mode,
                      uint32_t flags,
                      bool should_do_learning) override {
    input_type_ = type;
    input_mode_ = mode;
    input_flags_ = flags;
    should_do_learning_ = should_do_learning;
  }

  void SetGrammarFragmentAtCursor(
      const ui::GrammarFragment& fragment) override {}
  void SetAutocorrectInfo(const gfx::Range& autocorrect_range,
                          const gfx::Rect& autocorrect_bounds) override {}

 private:
  raw_ptr<LinuxInputMethodContextDelegate> delegate_;
  VirtualKeyboardControllerStub virtual_keyboard_controller_;
  std::vector<std::u16string> actions_;
  bool is_sync_mode_;
  bool eat_key_;
  gfx::Rect cursor_position_;
  TextInputType input_type_;
  TextInputMode input_mode_;
  uint32_t input_flags_;
  bool should_do_learning_;
  TextInputClient* old_client_ = nullptr;
  TextInputClient* new_client_ = nullptr;
};

class InputMethodDelegateForTesting : public ImeKeyEventDispatcher {
 public:
  InputMethodDelegateForTesting() {}

  InputMethodDelegateForTesting(const InputMethodDelegateForTesting&) = delete;
  InputMethodDelegateForTesting& operator=(
      const InputMethodDelegateForTesting&) = delete;

  ~InputMethodDelegateForTesting() override {}

  ui::EventDispatchDetails DispatchKeyEventPostIME(
      ui::KeyEvent* key_event) override {
    std::string action;
    switch (key_event->type()) {
      case ET_KEY_PRESSED:
        action = "keydown:";
        break;
      case ET_KEY_RELEASED:
        action = "keyup:";
        break;
      default:
        break;
    }
    std::stringstream ss;
    ss << key_event->key_code();
    action += std::string(ss.str());
    TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(action));
    return ui::EventDispatchDetails();
  }
};

class TextInputClientForTesting : public DummyTextInputClient {
 public:
  explicit TextInputClientForTesting(TextInputType text_input_type)
      : DummyTextInputClient(text_input_type) {}

  std::u16string composition_text;
  gfx::Range text_range;
  gfx::Range selection_range;
  std::u16string surrounding_text;

  absl::optional<gfx::Rect> caret_not_in_rect;

 protected:
  void SetCompositionText(const CompositionText& composition) override {
    composition_text = composition.text;
    TestResult::GetInstance()->RecordAction(u"compositionstart");
    TestResult::GetInstance()->RecordAction(u"compositionupdate:" +
                                            composition.text);
  }

  bool HasCompositionText() const override { return !composition_text.empty(); }

  size_t ConfirmCompositionText(bool keep_selection) override {
    // TODO(b/134473433) Modify this function so that when keep_selection is
    // true, the selection is not changed when text committed
    if (keep_selection) {
      NOTIMPLEMENTED_LOG_ONCE();
    }
    TestResult::GetInstance()->RecordAction(u"compositionend");
    TestResult::GetInstance()->RecordAction(u"textinput:" + composition_text);
    const size_t composition_text_length = composition_text.length();
    composition_text.clear();
    return composition_text_length;
  }

  void ClearCompositionText() override {
    TestResult::GetInstance()->RecordAction(u"compositionend");
    composition_text.clear();
  }

  void InsertText(
      const std::u16string& text,
      TextInputClient::InsertTextCursorBehavior cursor_behavior) override {
    if (HasCompositionText()) {
      TestResult::GetInstance()->RecordAction(u"compositionend");
    }
    TestResult::GetInstance()->RecordAction(u"textinput:" + text);
    composition_text.clear();
  }

  void InsertChar(const ui::KeyEvent& event) override {
    std::stringstream ss;
    ss << static_cast<uint16_t>(event.GetCharacter());
    TestResult::GetInstance()->RecordAction(u"keypress:" +
                                            base::ASCIIToUTF16(ss.str()));
  }

  bool GetTextRange(gfx::Range* range) const override {
    *range = text_range;
    return true;
  }
  bool GetEditableSelectionRange(gfx::Range* range) const override {
    *range = selection_range;
    return true;
  }
  bool GetTextFromRange(const gfx::Range& range,
                        std::u16string* text) const override {
    if (surrounding_text.empty())
      return false;
    *text = surrounding_text.substr(range.GetMin(), range.length());
    return true;
  }

  void EnsureCaretNotInRect(const gfx::Rect& rect) override {
    caret_not_in_rect = rect;
  }
};

class InputMethodAuraLinuxTest : public testing::Test {
 public:
  InputMethodAuraLinuxTest(const InputMethodAuraLinuxTest&) = delete;
  InputMethodAuraLinuxTest& operator=(const InputMethodAuraLinuxTest&) = delete;

 protected:
  InputMethodAuraLinuxTest()
      : input_method_auralinux_(nullptr),
        delegate_(nullptr),
        context_(nullptr) {
    GetInputMethodContextFactoryForTest() =
        base::BindRepeating([](LinuxInputMethodContextDelegate* delegate)
                                -> std::unique_ptr<LinuxInputMethodContext> {
          return std::make_unique<LinuxInputMethodContextForTesting>(delegate);
        });
    test_result_ = TestResult::GetInstance();
  }
  ~InputMethodAuraLinuxTest() override {
    ShutdownInputMethodForTesting();
    test_result_ = nullptr;
  }

  void SetUp() override {
    delegate_ = new InputMethodDelegateForTesting();
    input_method_auralinux_ = new InputMethodAuraLinux(delegate_);
    input_method_auralinux_->OnFocus();
    context_ = static_cast<LinuxInputMethodContextForTesting*>(
        input_method_auralinux_->GetContextForTesting());
  }

  void TearDown() override {
    context_->SetSyncMode(false);
    context_->SetEatKey(false);
    context_ = nullptr;

    delete input_method_auralinux_;
    input_method_auralinux_ = nullptr;
    delete delegate_;
    delegate_ = nullptr;
  }

  raw_ptr<InputMethodAuraLinux> input_method_auralinux_;
  raw_ptr<InputMethodDelegateForTesting> delegate_;
  raw_ptr<LinuxInputMethodContextForTesting> context_;
  raw_ptr<TestResult> test_result_;
};

TEST_F(InputMethodAuraLinuxTest, BasicSyncModeTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCommitAction("a");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');

  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:65");
  test_result_->ExpectAction("keypress:97");
  test_result_->Verify();

  input_method_auralinux_->DetachTextInputClient(client.get());
  client =
      std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_PASSWORD);
  context_->SetEatKey(false);

  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:65");
  test_result_->ExpectAction("keypress:97");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, BasicAsyncModeTest) {
  context_->SetSyncMode(false);
  context_->SetEatKey(true);

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());
  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);
  input_method_auralinux_->OnCommit(u"a");

  test_result_->ExpectAction("keydown:65");
  test_result_->ExpectAction("keypress:97");
  test_result_->Verify();

  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);
  input_method_auralinux_->OnCommit(u"foo");

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("textinput:foo");
  test_result_->Verify();

  input_method_auralinux_->DetachTextInputClient(client.get());
  client =
      std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_PASSWORD);
  context_->SetEatKey(false);

  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:65");
  test_result_->ExpectAction("keypress:97");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, IBusUSTest) {
  context_->SetSyncMode(false);
  context_->SetEatKey(true);

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());
  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  // IBus mutes the key down.
  test_result_->Verify();

  // IBus simulates a faked key down and handle it in sync mode.
  context_->SetSyncMode(true);
  context_->AddCommitAction("a");
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:65");
  test_result_->ExpectAction("keypress:97");
  test_result_->Verify();

  // IBus does NOT handle the key up.
  context_->SetEatKey(false);
  KeyEvent key_up(ET_KEY_RELEASED, VKEY_A, 0);
  input_method_auralinux_->DispatchKeyEvent(&key_up);

  test_result_->ExpectAction("keyup:65");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, IBusPinyinTest) {
  context_->SetSyncMode(false);
  context_->SetEatKey(true);

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());
  KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
  key.set_character(L'a');
  input_method_auralinux_->DispatchKeyEvent(&key);

  // IBus issues a standalone set_composition action.
  input_method_auralinux_->OnPreeditStart();
  CompositionText comp;
  comp.text = u"a";
  input_method_auralinux_->OnPreeditChanged(comp);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  // IBus issues a commit text with composition after muting the space key down.
  KeyEvent key_up(ET_KEY_RELEASED, VKEY_SPACE, 0);
  input_method_auralinux_->DispatchKeyEvent(&key_up);

  input_method_auralinux_->OnPreeditEnd();
  input_method_auralinux_->OnCommit(u"A");

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:A");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, JapaneseCommit) {
  context_->SetSyncMode(false);
  context_->SetEatKey(true);

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());
  KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
  key.set_character(L'a');
  input_method_auralinux_->DispatchKeyEvent(&key);

  // IBus issues a standalone set_composition action.
  input_method_auralinux_->OnPreeditStart();
  CompositionText comp;
  comp.text = u"a";
  input_method_auralinux_->OnPreeditChanged(comp);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  // IBus issues a commit text with composition after muting the space key down.
  // Typing return issues a commit, followed by preedit change (to make
  // composition empty), then preedit end.
  KeyEvent key_up(ET_KEY_PRESSED, VKEY_RETURN, 0);
  input_method_auralinux_->DispatchKeyEvent(&key_up);

  input_method_auralinux_->OnCommit(u"a");
  comp.text = u"";
  input_method_auralinux_->OnPreeditChanged(comp);
  input_method_auralinux_->OnPreeditEnd();

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:a");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, EmptyCommit) {
  context_->SetSyncMode(false);
  context_->SetEatKey(true);

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());
  KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
  key.set_character(L'a');
  input_method_auralinux_->DispatchKeyEvent(&key);

  input_method_auralinux_->OnPreeditStart();
  CompositionText comp;
  comp.text = u"a";
  input_method_auralinux_->OnPreeditChanged(comp);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  input_method_auralinux_->OnCommit(u"");
  comp.text = u"";
  input_method_auralinux_->OnPreeditChanged(comp);
  input_method_auralinux_->OnPreeditEnd();

  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:");
  test_result_->Verify();
}

// crbug.com/463491
void DeadKeyTest(TextInputType text_input_type,
                 InputMethodAuraLinux* input_method_auralinux,
                 LinuxInputMethodContextForTesting* context,
                 TestResult* test_result) {
  context->SetSyncMode(true);
  context->SetEatKey(true);

  auto client = std::make_unique<TextInputClientForTesting>(text_input_type);
  input_method_auralinux->SetFocusedTextInputClient(client.get());
  input_method_auralinux->OnTextInputTypeChanged(client.get());

  constexpr int32_t kCombiningGraveAccent = 0x0300;
  {
    KeyEvent dead_key(
        ET_KEY_PRESSED, VKEY_OEM_4, ui::DomCode::BRACKET_LEFT,
        /* flags= */ 0,
        DomKey::DeadKeyFromCombiningCharacter(kCombiningGraveAccent),
        base::TimeTicks());
    input_method_auralinux->DispatchKeyEvent(&dead_key);
  }

  // Do not filter release key event.
  context->SetEatKey(false);
  {
    KeyEvent dead_key(
        ET_KEY_RELEASED, VKEY_OEM_4, ui::DomCode::BRACKET_LEFT,
        /* flags= */ 0,
        DomKey::DeadKeyFromCombiningCharacter(kCombiningGraveAccent),
        base::TimeTicks());
    input_method_auralinux->DispatchKeyEvent(&dead_key);
  }

  // The single quote key is muted.
  test_result->ExpectAction("keydown:219");
  test_result->ExpectAction("keyup:219");
  test_result->Verify();

  // Reset to filter press key again.
  context->SetEatKey(true);

  context->AddCommitAction("X");
  KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
  key.set_character(L'a');
  input_method_auralinux->DispatchKeyEvent(&key);

  // The following A key generates the accent key: á.
  test_result->ExpectAction("keydown:65");
  test_result->ExpectAction("keypress:88");
  test_result->Verify();
}

TEST_F(InputMethodAuraLinuxTest, DeadKeyTest) {
  DeadKeyTest(TEXT_INPUT_TYPE_TEXT, input_method_auralinux_, context_,
              test_result_);
}

TEST_F(InputMethodAuraLinuxTest, DeadKeyTestTypeNone) {
  DeadKeyTest(TEXT_INPUT_TYPE_NONE, input_method_auralinux_, context_,
              test_result_);
}

// Wayland may send both a peek key event and a key event for key events not
// consumed by IME. In that case, the peek key should not be dispatched.
TEST_F(InputMethodAuraLinuxTest, MockWaylandEventsTest) {
  KeyEvent peek_key(ET_KEY_PRESSED, VKEY_TAB, 0);
  input_method_auralinux_->DispatchKeyEvent(&peek_key);
  // No expected action for peek key events.
  test_result_->Verify();

  KeyEvent key(ET_KEY_PRESSED, VKEY_TAB, 0);
  ui::Event::Properties properties;
  properties[ui::kPropertyKeyboardImeFlag] =
      std::vector<uint8_t>({ui::kPropertyKeyboardImeIgnoredFlag});
  key.SetProperties(properties);
  input_method_auralinux_->DispatchKeyEvent(&key);
  test_result_->ExpectAction("keydown:9");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, MultiCommitsTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCommitAction("a");
  context_->AddCommitAction("b");
  context_->AddCommitAction("c");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
  key.set_character(L'a');
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("textinput:abc");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, MixedCompositionAndCommitTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCommitAction("a");
  context_->AddCompositionStartAction();
  context_->AddCompositionUpdateAction("b");
  context_->AddCommitAction("c");
  context_->AddCompositionUpdateAction("d");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("textinput:ac");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:d");
  test_result_->Verify();

  context_->AddCommitAction("e");
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:e");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, CompositionEndWithoutCommitTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCompositionStartAction();
  context_->AddCompositionUpdateAction("a");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  context_->AddCompositionEndAction();
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, CompositionEndWithEmptyCommitTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCompositionStartAction();
  context_->AddCompositionUpdateAction("a");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  context_->AddCompositionEndAction();
  context_->AddCommitAction("");
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, CompositionEndWithCommitTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCompositionStartAction();
  context_->AddCompositionUpdateAction("a");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  context_->AddCompositionEndAction();
  context_->AddCommitAction("b");
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  // Verifies single char commit under composition mode will call InsertText
  // intead of InsertChar.
  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:b");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, CompositionUpdateWithCommitTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCompositionStartAction();
  context_->AddCompositionUpdateAction("a");
  context_->AddCommitAction("b");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("textinput:b");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  // crbug.com/513124.
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCommitAction("c");
  context_->AddCompositionUpdateAction("");
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:c");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, MixedAsyncAndSyncTest) {
  context_->SetSyncMode(false);
  context_->SetEatKey(true);

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);
  CompositionText comp;
  comp.text = u"a";
  input_method_auralinux_->OnPreeditChanged(comp);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  context_->SetSyncMode(true);
  context_->AddCompositionEndAction();
  context_->AddCommitAction("b");

  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:b");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, MixedSyncAndAsyncTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCompositionStartAction();
  context_->AddCompositionUpdateAction("a");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'a');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  context_->SetSyncMode(false);

  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);
  input_method_auralinux_->OnCommit(u"b");

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:b");
  test_result_->Verify();

  context_->SetSyncMode(true);
  context_->AddCommitAction("c");
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:65");
  test_result_->ExpectAction("keypress:99");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, ReleaseKeyTest) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);
  context_->AddCompositionUpdateAction("a");

  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
  key_new.set_character(L'A');
  KeyEvent key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keydown:229");
  test_result_->ExpectAction("compositionstart");
  test_result_->ExpectAction("compositionupdate:a");
  test_result_->Verify();

  context_->SetEatKey(false);
  context_->AddCommitAction("b");
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("compositionend");
  test_result_->ExpectAction("textinput:b");
  test_result_->ExpectAction("keydown:65");
  test_result_->ExpectAction("keypress:65");
  test_result_->Verify();

  context_->AddCommitAction("c");
  key = key_new;
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("textinput:c");
  test_result_->ExpectAction("keydown:65");
  test_result_->ExpectAction("keypress:65");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, ReleaseKeyTest_PeekKey) {
  context_->SetSyncMode(true);
  context_->SetEatKey(true);

  KeyEvent key(ET_KEY_RELEASED, VKEY_A, 0);
  key.set_character(L'A');
  input_method_auralinux_->DispatchKeyEvent(&key);

  test_result_->ExpectAction("keyup:65");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, SurroundingText_NoSelectionTest) {
  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  client->surrounding_text = u"abcdef";
  client->text_range = gfx::Range(0, 6);
  client->selection_range = gfx::Range(3, 3);

  input_method_auralinux_->OnCaretBoundsChanged(client.get());

  test_result_->ExpectAction("surroundingtext:abcdef");
  test_result_->ExpectAction("selectionrangestart:3");
  test_result_->ExpectAction("selectionrangeend:3");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, SurroundingText_SelectionTest) {
  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  client->surrounding_text = u"abcdef";
  client->text_range = gfx::Range(0, 6);
  client->selection_range = gfx::Range(2, 5);

  input_method_auralinux_->OnCaretBoundsChanged(client.get());

  test_result_->ExpectAction("surroundingtext:abcdef");
  test_result_->ExpectAction("selectionrangestart:2");
  test_result_->ExpectAction("selectionrangeend:5");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, SurroundingText_PartialText) {
  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  client->surrounding_text = u"abcdefghij";
  client->text_range = gfx::Range(5, 10);
  client->selection_range = gfx::Range(7, 9);

  input_method_auralinux_->OnCaretBoundsChanged(client.get());

  test_result_->ExpectAction("surroundingtext:fghij");
  test_result_->ExpectAction("selectionrangestart:7");
  test_result_->ExpectAction("selectionrangeend:9");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, SetPreeditRegionSingleCharTest) {
  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  client->surrounding_text = u"a";
  client->text_range = gfx::Range(0, 1);
  client->selection_range = gfx::Range(1, 1);

  input_method_auralinux_->OnCaretBoundsChanged(client.get());
  input_method_auralinux_->OnSetPreeditRegion(client->text_range,
                                              std::vector<ImeTextSpan>());

  test_result_->ExpectAction("surroundingtext:a");
  test_result_->ExpectAction("selectionrangestart:1");
  test_result_->ExpectAction("selectionrangeend:1");

  input_method_auralinux_->OnCommit(u"a");

  // Verifies single char commit under composition mode will call InsertText
  // instead of InsertChar.
  test_result_->ExpectAction("textinput:a");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, SetPreeditRegionCompositionEndTest) {
  std::unique_ptr<TextInputClientForTesting> client(
      new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
  input_method_auralinux_->SetFocusedTextInputClient(client.get());
  input_method_auralinux_->OnTextInputTypeChanged(client.get());

  input_method_auralinux_->OnCommit(u"a");

  test_result_->ExpectAction("keypress:97");

  client->surrounding_text = u"a";
  client->text_range = gfx::Range(0, 1);
  client->selection_range = gfx::Range(1, 1);

  input_method_auralinux_->OnCaretBoundsChanged(client.get());
  input_method_auralinux_->OnSetPreeditRegion(client->text_range,
                                              std::vector<ImeTextSpan>());

  test_result_->ExpectAction("surroundingtext:a");
  test_result_->ExpectAction("selectionrangestart:1");
  test_result_->ExpectAction("selectionrangeend:1");

  CompositionText comp;
  comp.text = u"";
  input_method_auralinux_->OnPreeditChanged(comp);

  test_result_->ExpectAction("compositionend");
  test_result_->Verify();
}

TEST_F(InputMethodAuraLinuxTest, OnSetVirtualKeyboardOccludedBounds) {
  auto client =
      std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_TEXT);
  input_method_auralinux_->SetFocusedTextInputClient(client.get());

  constexpr gfx::Rect kBounds(10, 20, 300, 400);
  input_method_auralinux_->OnSetVirtualKeyboardOccludedBounds(kBounds);

  EXPECT_EQ(client->caret_not_in_rect, kBounds);
}

TEST_F(InputMethodAuraLinuxTest, GetVirtualKeyboardController) {
  EXPECT_EQ(input_method_auralinux_->GetVirtualKeyboardController(),
            context_->GetVirtualKeyboardController());
}

TEST_F(InputMethodAuraLinuxTest, SetContentTypeWithUpdateFocus) {
  auto client1 =
      std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_TEXT);
  auto client2 =
      std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_URL);

  EXPECT_EQ(context_->old_client(), nullptr);
  EXPECT_EQ(context_->new_client(), nullptr);

  input_method_auralinux_->SetFocusedTextInputClient(client1.get());

  EXPECT_EQ(context_->input_type(), TEXT_INPUT_TYPE_TEXT);
  EXPECT_EQ(context_->old_client(), nullptr);
  EXPECT_EQ(context_->new_client(), client1.get());

  input_method_auralinux_->SetFocusedTextInputClient(client2.get());

  EXPECT_EQ(context_->input_type(), TEXT_INPUT_TYPE_URL);
  EXPECT_EQ(context_->old_client(), client1.get());
  EXPECT_EQ(context_->new_client(), client2.get());

  input_method_auralinux_->SetFocusedTextInputClient(client1.get());

  EXPECT_EQ(context_->input_type(), TEXT_INPUT_TYPE_TEXT);
  EXPECT_EQ(context_->old_client(), client2.get());
  EXPECT_EQ(context_->new_client(), client1.get());

  input_method_auralinux_->SetFocusedTextInputClient(nullptr);

  EXPECT_EQ(context_->input_type(), TEXT_INPUT_TYPE_NONE);
  EXPECT_EQ(context_->old_client(), client1.get());
  EXPECT_EQ(context_->new_client(), nullptr);
}

}  // namespace
}  // namespace ui


text:
  $29
  Per Month

<form action=''>
  <fieldset class='js-quantityInput-field'>
    Decrease
    <input class='js-quantityInput' type='text' value='1'>
    Increase
  </fieldset>
</form>


text:
describe Fastlane do
  describe Fastlane::ErbTemplateHelper do

    describe ".load_template" do
      it "raises an error if file does not exist" do
        expect {
          Fastlane::ErbTemplateHelper.load('invalid_name')
        }.to raise_exception "Could not find Template at path './/lib/assets/invalid_name.erb'".red
      end

      it "should load file if exists" do
        f = Fastlane::ErbTemplateHelper.load('s3_html_template')
        expect(f).not_to be_empty
      end
    end

    describe "#render_template" do
      before do
        @template = File.read("./spec/fixtures/templates/dummy_html_template.erb")
      end

      it "return true if it's a platform" do
        rendered_template = Fastlane::ErbTemplateHelper.render(@template,{
          template_name: "name"
        }).delete!("\n")
        expect(rendered_template).to eq("
name
")
      end
    end

  end
end


text:
package org.apache.sis.internal.netcdf;

import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.referencing.operation.transform.LinearTransform;
import org.apache.sis.referencing.operation.transform.MathTransforms;

/**
 * Contains information computed together with {@link Variable#findGrid(GridAdjustment)} but which are still specific
 * to the variable. Those information are kept in a class separated from {@link Grid} because the same {@code Grid}
 * instance may apply to many variables while {@code GridAdjustment} may contain amendments that are specific to a
 * particular {@link Variable} instance.
 *
 *
Instance is created by {@link Variable#getGridGeometry()} and updated by {@link Variable#findGrid(GridAdjustment)}.
 * Subclasses of {@link Variable} do not need to know the details of this class; they just need to pass it verbatim
 * to their parent class.

 *
 * @author  Martin Desruisseaux (Geomatys)
 * @version 1.1
 * @since   1.1
 * @module
 */
public final class GridAdjustment {
    /**
     * Factors by which to multiply a grid index in order to get the corresponding data index, or {@code null} if none.
     * This is usually null, meaning that there is an exact match between grid indices and data indices. This array may
     * be non-null if the localization grid has shorter dimensions than the dimensions of the variable, as documented
     * in {@link Convention#nameOfDimension(Variable, int)} javadoc.
     *
     *
Created by {@link Variable#findGrid(GridAdjustment)} and is consumed by {@link Variable#getGridGeometry()}.
     * Some values may be {@link Double#NaN} if the {@code "resampling_interval"} attribute was not found.
     * This array may be longer than necessary.

     *
     * @see #dataToGridIndices()
     */
    private double[] gridToDataIndices;

    /**
     * Maps grid dimensions to variable dimensions when those dimensions are not the same. This map should always be empty,
     * except in the case described in {@link #mapLabelToGridDimensions mapLabelToGridDimensions(…)} method. If non-empty,
     * then the keys are dimensions in the {@link Grid} and values are corresponding dimensions in the {@link Variable}.
     */
    final Map<Dimension,Dimension> gridToVariable;

    /**
     * Only {@link Variable#getGridGeometry()} should instantiate this class.
     */
    GridAdjustment() {
        gridToVariable = new HashMap<>();
    }

    /**
     * Builds a map of "dimension labels" to the actual {@link Dimension} instances of the grid.
     * The dimension labels are not the dimension names, but some other convention-dependent identifiers.
     * The mechanism is documented in {@link Convention#nameOfDimension(Variable, int)}.
     * For example, given a file with the following netCDF variables:
     *
     * {@preformat text
     *     float Latitude(grid_y, grid_x)
     *       dim0 = "Line grids"
     *       dim1 = "Pixel grids"
     *       resampling_interval = 10
     *     float Longitude(grid_y, grid_x)
     *       dim0 = "Line grids"
     *       dim1 = "Pixel grids"
     *       resampling_interval = 10
     *     ushort SST(data_y, data_x)
     *       dim0 = "Line grids"
     *       dim1 = "Pixel grids"
     * }
     *
     * this method will add the following entries in the {@code toGridDimensions} map, provided that
     * the dimensions are not already keys in that map:
     *
     * {@preformat text
     *     "Line grids"   →  Dimension[grid_x]
     *     "Pixel grids"  →  Dimension[grid_y]
     * }
     *
     * @param  variable          the variable for which a "label to grid dimensions" mapping is desired.
     * @param  axes              all axes in the netCDF file (not only the variable axes).
     * @param  toGridDimensions  in input, the dimensions to accept. In output, "label → grid dimension" entries.
     * @param  convention        convention for getting dimension labels.
     * @return {@code true} if the {@code Variable.findGrid(…)} caller should abort.
     *
     * @see Convention#nameOfDimension(Variable, int)
     */
    boolean mapLabelToGridDimensions(final Variable variable, final List<Variable> axes,
            final Map<Object,Dimension> toGridDimensions, final Convention convention)
    {
        final Set<Dimension> requestedByConvention = new HashSet<>();                       // Only in case of ambiguities.
        final String[] namesOfAxisVariables = convention.namesOfAxisVariables(variable);    // Only in case of ambiguities.
        for (final Variable axis : axes) {
            final boolean isRequested = ArraysExt.containsIgnoreCase(namesOfAxisVariables, axis.getName());
            final List<Dimension> candidates = axis.getGridDimensions();
            for (int j=candidates.size(); --j >= 0;) {
                final Dimension dim = candidates.get(j);
                if (toGridDimensions.containsKey(dim)) {
                    /*
                     * Found a dimension that has not already be taken by the 'dimensions' array.
                     * If this dimension has a name defined by an attribute like "Dim0" or "Dim1",
                     * make this dimension available for consideration by 'dimensions[i] = …' later.
                     */
                    final String name = convention.nameOfDimension(axis, j);
                    if (name != null) {
                        if (gridToDataIndices == null) {
                            gridToDataIndices = new double[axes.size()];    // Conservatively use longest possible length.
                        }
                        gridToDataIndices[j] = convention.gridToDataIndices(axis);
                        final boolean overwrite = isRequested && requestedByConvention.add(dim);
                        final Dimension previous = toGridDimensions.put(name, dim);
                        if (previous != null && !previous.equals(dim)) {
                            /*
                             * The same name maps to two different dimensions. Given the ambiguity, we should give up.
                             * However, we make an exception if only one dimension is part of a variable that has been
                             * explicitly requested. We identify this disambiguation in the following ways:
                             *
                             *   isRequested = true   →  ok if overwrite = true  →  keep the newly added dimension.
                             *   isRequested = false  →  if was previously in requestedByConvention, restore previous.
                             */
                            if (!overwrite) {
                                if (!isRequested && requestedByConvention.contains(dim)) {
                                    toGridDimensions.put(name, previous);
                                } else {
                                    // Variable.getGridGeometry() is (indirectly) the caller of this method.
                                    variable.error(Variable.class, "getGridGeometry", null, Errors.Keys.DuplicatedIdentifier_1, name);
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * Returns the factors by which to multiply a data index in order to get the corresponding grid index,
     * or {@code null} if none. This array may be non-null if the localization grid has shorter dimensions
     * than the ones of the variable (see {@link #mapLabelToGridDimensions mapLabelToGridDimensions(…)}).
     * Caller needs to verify that the returned array, if non-null, is long enough.
     */
    final double[] dataToGridIndices() {
        double[] dataToGridIndices = null;
        if (gridToDataIndices != null) {
            for (int i=gridToDataIndices.length; --i >= 0;) {
                final double s = gridToDataIndices[i];
                if (s > 0 && s != Double.POSITIVE_INFINITY) {
                    if (dataToGridIndices == null) {
                        dataToGridIndices = new double[i + 1];
                    }
                    dataToGridIndices[i] = 1 / s;
                } else {
                    dataToGridIndices = null;
                    // May return a shorter array.
                }
            }
        }
        return dataToGridIndices;
    }

    /**
     * Creates a new grid geometry with a scale factor applied in grid coordinates before the "grid to CRS" conversion.
     *
     * @param  grid               the grid geometry to scale.
     * @param  extent             the extent to allocate to the new grid geometry.
     * @param  anchor             the transform to adjust: "center to CRS" or "corner to CRS".
     * @param  dataToGridIndices  value of {@link #dataToGridIndices()}.
     * @return scaled grid geometry.
     */
    static GridGeometry scale(final GridGeometry grid, final GridExtent extent, final PixelInCell anchor,
                              final double[] dataToGridIndices)
    {
        MathTransform gridToCRS = grid.getGridToCRS(anchor);
        final LinearTransform scale = MathTransforms.scale(dataToGridIndices);
        gridToCRS = MathTransforms.concatenate(scale, gridToCRS);
        return new GridGeometry(extent, anchor, gridToCRS,
                grid.isDefined(GridGeometry.CRS) ? grid.getCoordinateReferenceSystem() : null);
    }
}