Implement IBusLookupTable.

IBusLookupTable is one of representations an obeject used in communication with
ibus-daemon.

According to this CL, ibus_lookup_table will be comipled and tested but not in
used production binary at thi moment.

BUG=chromium-os:26334
TEST=chromeos_unittests, unit_tests, dbus_unittests

Review URL: https://chromiumcodereview.appspot.com/10392039

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@138507 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp
index 2b05ac1..172af0cc 100644
--- a/chromeos/chromeos.gyp
+++ b/chromeos/chromeos.gyp
@@ -72,6 +72,8 @@
         'dbus/ibus/ibus_client.cc',
         'dbus/ibus/ibus_client.h',
         'dbus/ibus/ibus_constants.h',
+        'dbus/ibus/ibus_lookup_table.cc',
+        'dbus/ibus/ibus_lookup_table.h',
         'dbus/ibus/ibus_object.cc',
         'dbus/ibus/ibus_object.h',
         'dbus/ibus/ibus_text.cc',
@@ -187,6 +189,7 @@
         'dbus/flimflam_service_client_unittest.cc',
         'dbus/gsm_sms_client_unittest.cc',
         'dbus/ibus/ibus_client_unittest.cc',
+        'dbus/ibus/ibus_lookup_table_unittest.cc',
         'dbus/ibus/ibus_object_unittest.cc',
         'dbus/ibus/ibus_text_unittest.cc',
         'dbus/ibus/ibus_input_context_client_unittest.cc',
diff --git a/chromeos/dbus/ibus/ibus_lookup_table.cc b/chromeos/dbus/ibus/ibus_lookup_table.cc
new file mode 100644
index 0000000..eb804109
--- /dev/null
+++ b/chromeos/dbus/ibus/ibus_lookup_table.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/dbus/ibus/ibus_lookup_table.h"
+
+#include <string>
+#include "base/logging.h"
+#include "dbus/message.h"
+#include "chromeos/dbus/ibus/ibus_text.h"
+#include "chromeos/dbus/ibus/ibus_object.h"
+
+namespace chromeos {
+// TODO(nona): Remove ibus namespace after complete libibus removal.
+namespace ibus {
+
+void AppendIBusLookupTable(const IBusLookupTable& table,
+                           dbus::MessageWriter* writer) {
+  IBusObjectWriter ibus_lookup_table_writer("IBusLookupTable",
+                                            "uubbiavav",
+                                            writer);
+  ibus_lookup_table_writer.AppendUint32(table.page_size());
+  ibus_lookup_table_writer.AppendUint32(table.cursor_position());
+  ibus_lookup_table_writer.AppendBool(table.is_cursor_visible());
+  ibus_lookup_table_writer.AppendBool(false);  // Not used in Chrome.
+  ibus_lookup_table_writer.AppendInt32(static_cast<int32>(table.orientation()));
+
+  const std::vector<IBusLookupTable::Entry>& candidates = table.candidates();
+  dbus::MessageWriter text_writer(NULL);
+  ibus_lookup_table_writer.OpenArray("v", &text_writer);
+  bool have_labels = false;
+  for (size_t i = 0; i < candidates.size(); ++i) {
+    // Write candidate string as IBusText.
+    AppendStringAsIBusText(candidates[i].value, &text_writer);
+    if (!candidates[i].label.empty())
+      have_labels = true;
+  }
+  ibus_lookup_table_writer.CloseContainer(&text_writer);
+
+  dbus::MessageWriter label_writer(NULL);
+  ibus_lookup_table_writer.OpenArray("v", &label_writer);
+
+  // If there are not any labels, don't append labels.
+  if (have_labels) {
+    for (size_t i = 0; i < candidates.size(); ++i) {
+      // Write label string as IBusText.
+      AppendStringAsIBusText(candidates[i].label, &label_writer);
+    }
+  }
+  ibus_lookup_table_writer.CloseContainer(&label_writer);
+
+  ibus_lookup_table_writer.CloseAll();
+}
+
+bool PopIBusLookupTable(dbus::MessageReader* reader, IBusLookupTable* table) {
+  IBusObjectReader ibus_object_reader("IBusLookupTable", reader);
+  if (!ibus_object_reader.Init())
+    return false;
+
+  uint32 page_size = 0;
+  if (!ibus_object_reader.PopUint32(&page_size)) {
+    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+               << "1st argument should be uint32.";
+    return false;
+  }
+  table->set_page_size(page_size);
+
+  uint32 cursor_position = 0;
+  if (!ibus_object_reader.PopUint32(&cursor_position)) {
+    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+               << "2nd argument should be uint32.";
+    return false;
+  }
+  table->set_cursor_position(cursor_position);
+
+  bool cursor_visible = true;
+  if (!ibus_object_reader.PopBool(&cursor_visible)) {
+    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+               << "3rd arugment should be boolean.";
+    return false;
+  }
+  table->set_is_cursor_visible(cursor_visible);
+
+  bool unused_round_value = true;
+  if (!ibus_object_reader.PopBool(&unused_round_value)) {
+    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+               << "4th argument should be boolean.";
+    return false;
+  }
+
+  int32 orientation = 0;
+  if (!ibus_object_reader.PopInt32(&orientation)) {
+    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+               << "5th arguemnt should be int32.";
+    return false;
+  }
+  table->set_orientation(
+      static_cast<IBusLookupTable::Orientation>(orientation));
+
+  dbus::MessageReader text_array_reader(NULL);
+  if (!ibus_object_reader.PopArray(&text_array_reader)) {
+    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+               << "6th argument should be array.";
+    return false;
+  }
+
+  std::vector<IBusLookupTable::Entry>* candidates = table->mutable_candidates();
+  while (text_array_reader.HasMoreData()) {
+    std::string candidate_text;
+    // The attributes in IBusText are not used in Chrome.
+    if (!PopStringFromIBusText(&text_array_reader, &candidate_text)) {
+      LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+                 << "6th argument should be array of IBusText.";
+      return false;
+    }
+    IBusLookupTable::Entry entry;
+    entry.value = candidate_text;
+    candidates->push_back(entry);
+  }
+
+  dbus::MessageReader label_array_reader(NULL);
+  if (!ibus_object_reader.PopArray(&label_array_reader)) {
+    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+               << "7th argument should be array.";
+    return false;
+  }
+
+  if (!label_array_reader.HasMoreData()) {
+    return true;
+  }
+
+  for (size_t i = 0; i < candidates->size(); ++i) {
+    if (!label_array_reader.HasMoreData()) {
+      LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+                 << "The number of label entry does not match with candidate "
+                 << "text. Same length or no label entry can be accepted.";
+      return false;
+    }
+
+    std::string label_text;
+    // The attributes in IBusText are not used in Chrome.
+    if (!PopStringFromIBusText(&label_array_reader, &label_text)) {
+      LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
+                 << "7th argument should be array of IBusText.";
+      return false;
+    }
+    (*candidates)[i].label = label_text;
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// IBusLookupTable
+IBusLookupTable::IBusLookupTable()
+    : page_size_(0),
+      cursor_position_(0),
+      is_cursor_visible_(true),
+      orientation_(IBUS_LOOKUP_TABLE_ORIENTATION_HORIZONTAL) {
+}
+
+IBusLookupTable::~IBusLookupTable() {
+}
+
+}  // namespace ibus
+}  // namespace chromeos
diff --git a/chromeos/dbus/ibus/ibus_lookup_table.h b/chromeos/dbus/ibus/ibus_lookup_table.h
new file mode 100644
index 0000000..099119f7
--- /dev/null
+++ b/chromeos/dbus/ibus/ibus_lookup_table.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_DBUS_IBUS_IBUS_LOOKUP_TABLE_H_
+#define CHROMEOS_DBUS_IBUS_IBUS_LOOKUP_TABLE_H_
+
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "chromeos/chromeos_export.h"
+
+namespace dbus {
+class MessageWriter;
+class MessageReader;
+}  // namespace dbus
+
+namespace chromeos {
+// TODO(nona): Remove ibus namespace after complete libibus removal.
+namespace ibus {
+
+// The IBusLookupTable is one of IBusObjects. IBusLookupTable contains IBusTexts
+// but all of them are used as plain string. The overview of each data
+// strucutres is as follows:
+//
+// DATA STRUCTURE OVERVIEW:
+//  variant  struct {
+//   string "IBusLookupTable"
+//   array [
+//   ]
+//   uint32 9  // Page size
+//   uint32 1  // Cursor position
+//   boolean true  // Cursor visibility.
+//   boolean true  // Round lookup table or not. Not used in Chrome.
+//   int32 1  // Orientation
+//   array [  // Array of candidate text.
+//    variant struct {
+//      string "IBusText"
+//      array []
+//      string "Candidate Text"
+//      variant struct {
+//       string "IBusAttrList"
+//       array []
+//       array []
+//       }
+//     }
+//     ... more IBusTexts
+//   ]
+//   array [  // Array of label text
+//    variant struct {
+//      string "IBusText"
+//      array []
+//      string "1"
+//      variant struct {
+//       string "IBusAttrList"
+//       array []
+//       array []
+//       }
+//     }
+//     ... more IBusTexts
+//   ]
+//  }
+class IBusLookupTable;
+
+// Pops a IBusLookupTable from |reader|.
+// Returns false if an error occures.
+bool CHROMEOS_EXPORT PopIBusLookupTable(dbus::MessageReader* reader,
+                                        IBusLookupTable* table);
+// Appends a IBusLookupTable to |writer|.
+void CHROMEOS_EXPORT AppendIBusLookupTable(const IBusLookupTable& table,
+                                           dbus::MessageWriter* writer);
+
+// An representation of IBusLookupTable object which is used in dbus
+// communication with ibus-daemon.
+class CHROMEOS_EXPORT IBusLookupTable {
+ public:
+  enum Orientation {
+    IBUS_LOOKUP_TABLE_ORIENTATION_HORIZONTAL = 0,
+    IBUS_LOOKUP_TABLE_ORIENTATION_VERTICAL = 1,
+  };
+
+  // Represents a candidate entry. In dbus communication, each
+  // field is represented as IBusText, but attributes are not used in Chrome.
+  // So just simple string is sufficient in this case.
+  struct Entry {
+    std::string value;
+    std::string label;
+  };
+
+  IBusLookupTable();
+  virtual ~IBusLookupTable();
+
+  // Returns the number of candidates in one page.
+  uint32 page_size() const {
+    return page_size_;
+  }
+
+  void set_page_size(uint32 page_size) {
+    page_size_ = page_size;
+  }
+
+  // Returns the cursor index of the currently selected candidate.
+  uint32 cursor_position() const {
+    return cursor_position_;
+  }
+
+  void set_cursor_position(uint32 cursor_position) {
+    cursor_position_ = cursor_position;
+  }
+
+  // Returns true if the cusros is visible.
+  bool is_cursor_visible() const {
+    return is_cursor_visible_;
+  }
+
+  void set_is_cursor_visible(bool is_cursor_visible) {
+    is_cursor_visible_ = is_cursor_visible;
+  }
+
+  // Returns the orientation of lookup table.
+  Orientation orientation() const {
+    return orientation_;
+  }
+
+  void set_orientation(Orientation orientation) {
+    orientation_ = orientation;
+  }
+
+  const std::vector<Entry>& candidates() const {
+    return candidates_;
+  }
+
+  std::vector<Entry>* mutable_candidates() {
+    return &candidates_;
+  }
+
+ private:
+  uint32 page_size_;
+  uint32 cursor_position_;
+  bool is_cursor_visible_;
+  Orientation orientation_;
+  std::vector<Entry> candidates_;
+
+  DISALLOW_COPY_AND_ASSIGN(IBusLookupTable);
+};
+
+}  // namespace ibus
+}  // namespace chromeos
+
+#endif  // CHROMEOS_DBUS_IBUS_IBUS_LOOKUP_TABLE_H_
diff --git a/chromeos/dbus/ibus/ibus_lookup_table_unittest.cc b/chromeos/dbus/ibus/ibus_lookup_table_unittest.cc
new file mode 100644
index 0000000..c1d4c25
--- /dev/null
+++ b/chromeos/dbus/ibus/ibus_lookup_table_unittest.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// TODO(nona): Add more tests.
+
+#include "chromeos/dbus/ibus/ibus_lookup_table.h"
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "chromeos/dbus/ibus/ibus_object.h"
+#include "dbus/message.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+// TODO(nona): Remove ibus namespace after complete libibus removale.
+namespace ibus {
+
+TEST(IBusLookupTable, WriteReadTest) {
+  const char kSampleText1[] = "Sample Text 1";
+  const char kSampleText2[] = "Sample Text 2";
+  const char kSampleLabel1[] = "Sample Label 1";
+  const char kSampleLabel2[] = "Sample Label 2";
+  const uint32 kPageSize = 11;
+  const uint32 kCursorPosition = 12;
+  const bool kIsCursorVisible = true;
+  const IBusLookupTable::Orientation kOrientation =
+      IBusLookupTable::IBUS_LOOKUP_TABLE_ORIENTATION_VERTICAL;
+
+  // Create IBusLookupTable.
+  IBusLookupTable lookup_table;
+  lookup_table.set_page_size(kPageSize);
+  lookup_table.set_cursor_position(kCursorPosition);
+  lookup_table.set_is_cursor_visible(kIsCursorVisible);
+  lookup_table.set_orientation(kOrientation);
+  std::vector<IBusLookupTable::Entry>* candidates =
+      lookup_table.mutable_candidates();
+  IBusLookupTable::Entry entry1;
+  entry1.value = kSampleText1;
+  entry1.label = kSampleLabel1;
+  candidates->push_back(entry1);
+
+  IBusLookupTable::Entry entry2;
+  entry2.value = kSampleText2;
+  entry2.label = kSampleLabel2;
+  candidates->push_back(entry2);
+
+  // Write IBusLookupTable.
+  scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+  dbus::MessageWriter writer(response.get());
+  AppendIBusLookupTable(lookup_table, &writer);
+
+  // Read IBusLookupTable.
+  IBusLookupTable target_lookup_table;
+  dbus::MessageReader reader(response.get());
+  PopIBusLookupTable(&reader, &target_lookup_table);
+
+  // Check values.
+  EXPECT_EQ(kPageSize, target_lookup_table.page_size());
+  EXPECT_EQ(kCursorPosition, target_lookup_table.cursor_position());
+  EXPECT_EQ(kIsCursorVisible, target_lookup_table.is_cursor_visible());
+  EXPECT_EQ(kOrientation, target_lookup_table.orientation());
+  ASSERT_EQ(2UL, target_lookup_table.candidates().size());
+  EXPECT_EQ(kSampleText1, target_lookup_table.candidates().at(0).value);
+  EXPECT_EQ(kSampleText2, target_lookup_table.candidates().at(1).value);
+  EXPECT_EQ(kSampleLabel1, target_lookup_table.candidates().at(0).label);
+  EXPECT_EQ(kSampleLabel2, target_lookup_table.candidates().at(1).label);
+}
+
+TEST(IBusLookupTable, WriteReadWithoutLableTest) {
+  const char kSampleText1[] = "Sample Text 1";
+  const char kSampleText2[] = "Sample Text 2";
+  const uint32 kPageSize = 11;
+  const uint32 kCursorPosition = 12;
+  const bool kIsCursorVisible = true;
+  const IBusLookupTable::Orientation kOrientation =
+      IBusLookupTable::IBUS_LOOKUP_TABLE_ORIENTATION_VERTICAL;
+
+  // Create IBusLookupTable.
+  IBusLookupTable lookup_table;
+  lookup_table.set_page_size(kPageSize);
+  lookup_table.set_cursor_position(kCursorPosition);
+  lookup_table.set_is_cursor_visible(kIsCursorVisible);
+  lookup_table.set_orientation(kOrientation);
+  std::vector<IBusLookupTable::Entry>* candidates =
+      lookup_table.mutable_candidates();
+  IBusLookupTable::Entry entry1;
+  entry1.value = kSampleText1;
+  candidates->push_back(entry1);
+
+  IBusLookupTable::Entry entry2;
+  entry2.value = kSampleText2;
+  candidates->push_back(entry2);
+
+  // Write IBusLookupTable.
+  scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+  dbus::MessageWriter writer(response.get());
+  AppendIBusLookupTable(lookup_table, &writer);
+
+  // Read IBusLookupTable.
+  IBusLookupTable target_lookup_table;
+  dbus::MessageReader reader(response.get());
+  PopIBusLookupTable(&reader, &target_lookup_table);
+
+  // Check values.
+  EXPECT_EQ(kPageSize, target_lookup_table.page_size());
+  EXPECT_EQ(kCursorPosition, target_lookup_table.cursor_position());
+  EXPECT_EQ(kIsCursorVisible, target_lookup_table.is_cursor_visible());
+  EXPECT_EQ(kOrientation, target_lookup_table.orientation());
+  ASSERT_EQ(2UL, target_lookup_table.candidates().size());
+  EXPECT_EQ(kSampleText1, target_lookup_table.candidates().at(0).value);
+  EXPECT_EQ(kSampleText2, target_lookup_table.candidates().at(1).value);
+  EXPECT_TRUE(target_lookup_table.candidates().at(0).label.empty());
+  EXPECT_TRUE(target_lookup_table.candidates().at(1).label.empty());
+}
+
+}  // namespace ibus
+}  // namespace chromeos