Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / liblouis / nacl_wrapper / liblouis_instance.cc
1 // Copyright 2013 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "liblouis_instance.h"
16
17 #include <cstdio>
18 #include <cstring>
19 #include <sys/mount.h>
20 #include <vector>
21
22 #include "nacl_io/nacl_io.h"
23 #include "ppapi/c/pp_errors.h"
24 #include "ppapi/cpp/module.h"
25
26 #include "translation_result.h"
27
28 namespace {
29
30 static const char kHexadecimalChars[] = "0123456789abcdef";
31
32 // Converts a vector of bytes into a (lowercase) hexadecimal string.
33 static void BytesToHexString(const std::vector<unsigned char>& bytes,
34     std::string* out) {
35   std::string hex;
36   hex.reserve(bytes.size() * 2);
37   for (size_t i = 0; i < bytes.size(); ++i) {
38     unsigned char byte = bytes[i];
39     hex.push_back(kHexadecimalChars[byte >> 4]);
40     hex.push_back(kHexadecimalChars[byte & 0x0f]);
41   }
42   out->swap(hex);
43 }
44
45 // Converts a hexadecimal string to a vector of bytes.
46 // Returns false on failure.
47 static bool HexStringToBytes(const std::string& hex,
48     std::vector<unsigned char>* out) {
49   if (hex.size() % 2 != 0) {
50     return false;
51   }
52
53   std::vector<unsigned char> bytes;
54   bytes.reserve(hex.size() / 2);
55   for (size_t i = 0; i < hex.size(); i += 2) {
56     unsigned char byte;
57     char ch = hex[i];
58     if ('0' <= ch && ch <= '9') {
59       byte = (ch - '0') << 4;
60     } else if ('a' <= ch && ch <= 'f') {
61       byte = (ch - 'a' + 10) << 4;
62     } else if ('A' <= ch && ch <= 'F') {
63       byte = (ch - 'A' + 10) << 4;
64     } else {
65       return false;
66     }
67     ch = hex[i+1];
68     if ('0' <= ch && ch <= '9') {
69       byte |= ch - '0';
70     } else if ('a' <= ch && ch <= 'f') {
71       byte |= ch - 'a' + 10;
72     } else if ('A' <= ch && ch <= 'F') {
73       byte |= ch - 'A' + 10;
74     } else {
75       return false;
76     }
77     bytes.push_back(byte);
78   }
79   out->swap(bytes);
80   return true;
81 }
82
83 template <typename T>
84 static void CopyVectorToJson(const std::vector<T>& vec, Json::Value* out) {
85   Json::Value result(Json::arrayValue);
86   result.resize(vec.size());
87   for (size_t i = 0; i < vec.size(); ++i) {
88     result[i] = vec[i];
89   }
90   out->swap(result);
91 }
92
93 }  // namespace
94
95
96 namespace liblouis_nacl {
97
98 // Well-known strings used for configuration.
99 static const char kTablesDirKey[] = "tablesdir";
100 static const char kTablesDirDefault[] = "tables";
101
102 // Well-known strings used in JSON messages.
103 static const char kCommandKey[] = "command";
104 static const char kMessageIdKey[] = "message_id";
105 static const char kInReplyToKey[] = "in_reply_to";
106 static const char kErrorKey[] = "error";
107 static const char kTableNamesKey[] = "table_names";
108 static const char kSuccessKey[] = "success";
109 static const char kTextKey[] = "text";
110 static const char kCellsKey[] = "cells";
111 static const char kCursorPositionKey[] = "cursor_position";
112 static const char kTextToBrailleKey[] = "text_to_braille";
113 static const char kBrailleToTextKey[] = "braille_to_text";
114 static const char kCheckTableCommand[] = "CheckTable";
115 static const char kTranslateCommand[] = "Translate";
116 static const char kBackTranslateCommand[] = "BackTranslate";
117
118 LibLouisInstance::LibLouisInstance(PP_Instance instance)
119     : pp::Instance(instance), liblouis_thread_(this), cc_factory_(this) {}
120
121 LibLouisInstance::~LibLouisInstance() {}
122
123 bool LibLouisInstance::Init(uint32_t argc, const char* argn[],
124     const char* argv[]) {
125   const char* tables_dir = kTablesDirDefault;
126   for (size_t i = 0; i < argc; ++i) {
127     if (strcmp(argn[i], kTablesDirKey) == 0) {
128       tables_dir = argv[i];
129     }
130   }
131
132   nacl_io_init_ppapi(pp_instance(),
133       pp::Module::Get()->get_browser_interface());
134   if (mount(tables_dir, liblouis_.tables_dir(), "httpfs", 0, "") != 0) {
135     // TODO(jbroman): report this error.
136     return false;
137   }
138
139   return liblouis_thread_.Start();
140 }
141
142 void LibLouisInstance::HandleMessage(const pp::Var& var_message) {
143   if (!var_message.is_string()) {
144     PostError("expected message to be a JSON string");
145     return;
146   }
147
148   Json::Value message;
149   Json::Reader reader;
150   bool parsed = reader.parse(var_message.AsString(),
151       message, false /* collectComments */);
152   if (!parsed) {
153     PostError("expected message to be a JSON string");
154     return;
155   }
156
157   Json::Value message_id = message[kMessageIdKey];
158   if (!message_id.isString()) {
159     PostError("expected message_id string");
160     return;
161   }
162   std::string message_id_str = message_id.asString();
163
164   Json::Value command = message[kCommandKey];
165   if (!command.isString()) {
166     PostError("expected command string", message_id_str);
167     return;
168   }
169
170   std::string command_str = command.asString();
171   if (command_str == kCheckTableCommand) {
172     HandleCheckTable(message, message_id_str);
173   } else if (command_str == kTranslateCommand) {
174     HandleTranslate(message, message_id_str);
175   } else if (command_str == kBackTranslateCommand) {
176     HandleBackTranslate(message, message_id_str);
177   } else {
178     PostError("unknown command", message_id_str);
179   }
180 }
181
182 void LibLouisInstance::PostReply(Json::Value reply,
183     const std::string& in_reply_to) {
184   Json::FastWriter writer;
185   reply[kInReplyToKey] = in_reply_to;
186   pp::Var var_reply(writer.write(reply));
187   PostMessage(var_reply);
188 }
189
190 void LibLouisInstance::PostError(const std::string& error_message) {
191   Json::FastWriter writer;
192   Json::Value reply(Json::objectValue);
193   reply[kErrorKey] = error_message;
194   pp::Var var_reply(writer.write(reply));
195   PostMessage(var_reply);
196 }
197
198 void LibLouisInstance::PostError(const std::string& error_message,
199     const std::string& in_reply_to) {
200   Json::FastWriter writer;
201   Json::Value reply(Json::objectValue);
202   reply[kErrorKey] = error_message;
203   reply[kInReplyToKey] = in_reply_to;
204   reply[kSuccessKey] = false;
205   pp::Var var_reply(writer.write(reply));
206   PostMessage(var_reply);
207 }
208
209 void LibLouisInstance::HandleCheckTable(const Json::Value& message,
210     const std::string& message_id) {
211   Json::Value table_names = message[kTableNamesKey];
212   if (!table_names.isString()) {
213     PostError("expected table_names to be a string", message_id);
214     return;
215   }
216   PostWorkToBackground(cc_factory_.NewCallback(
217       &LibLouisInstance::CheckTableInBackground,
218       table_names.asString(), message_id));
219 }
220
221 void LibLouisInstance::CheckTableInBackground(int32_t result,
222     const std::string& table_names, const std::string& message_id) {
223   if (result != PP_OK) {
224     PostError("failed to transfer call to background thread", message_id);
225     return;
226   }
227   bool success = liblouis_.CheckTable(table_names);
228   Json::Value reply(Json::objectValue);
229   reply[kSuccessKey] = success;
230   PostReply(reply, message_id);
231 }
232
233 void LibLouisInstance::HandleTranslate(const Json::Value& message,
234     const std::string& message_id) {
235   Json::Value table_names = message[kTableNamesKey];
236   Json::Value text = message[kTextKey];
237   Json::Value cursor_position = message[kCursorPositionKey];
238   if (!table_names.isString()) {
239     PostError("expected table_names to be a string", message_id);
240     return;
241   } else if (!text.isString()) {
242     PostError("expected text to be a string", message_id);
243     return;
244   } else if (!cursor_position.isNull() && !cursor_position.isIntegral()) {
245     PostError("expected cursor_position to be null or integral", message_id);
246     return;
247   }
248   TranslationParams params;
249   params.table_names = table_names.asString();
250   params.text = text.asString();
251   params.cursor_position = cursor_position.isIntegral() ?
252       cursor_position.asInt() : -1;
253   PostWorkToBackground(cc_factory_.NewCallback(
254       &LibLouisInstance::TranslateInBackground,
255       params, message_id));
256 }
257
258 void LibLouisInstance::TranslateInBackground(int32_t result,
259     const TranslationParams& params, const std::string& message_id) {
260   if (result != PP_OK) {
261     PostError("failed to transfer call to background thread", message_id);
262     return;
263   }
264   TranslationResult translation_result;
265   bool success = liblouis_.Translate(params, &translation_result);
266   Json::Value reply(Json::objectValue);
267   reply[kSuccessKey] = success;
268   if (success) {
269     std::string hex_cells;
270     Json::Value text_to_braille;
271     Json::Value braille_to_text;
272     BytesToHexString(translation_result.cells, &hex_cells);
273     CopyVectorToJson(translation_result.text_to_braille, &text_to_braille);
274     CopyVectorToJson(translation_result.braille_to_text, &braille_to_text);
275     reply[kCellsKey] = hex_cells;
276     reply[kTextToBrailleKey] = text_to_braille;
277     reply[kBrailleToTextKey] = braille_to_text;
278     if (translation_result.cursor_position >= 0) {
279       reply[kCursorPositionKey] = translation_result.cursor_position;
280     }
281   }
282   PostReply(reply, message_id);
283 }
284
285 void LibLouisInstance::HandleBackTranslate(const Json::Value& message,
286     const std::string& message_id) {
287   Json::Value table_names = message[kTableNamesKey];
288   Json::Value cells = message[kCellsKey];
289   if (!table_names.isString()) {
290     PostError("expected table_names to be a string", message_id);
291     return;
292   } else if (!cells.isString()) {
293     PostError("expected cells to be a string", message_id);
294     return;
295   }
296   std::vector<unsigned char> cells_vector;
297   if (!HexStringToBytes(cells.asString(), &cells_vector)) {
298     PostError("expected cells to be a valid hexadecimal string", message_id);
299     return;
300   }
301   PostWorkToBackground(cc_factory_.NewCallback(
302       &LibLouisInstance::BackTranslateInBackground,
303       table_names.asString(), cells_vector, message_id));
304 }
305
306 void LibLouisInstance::BackTranslateInBackground(int32_t result,
307     const std::string& table_names, const std::vector<unsigned char>& cells,
308     const std::string& message_id) {
309   if (result != PP_OK) {
310     PostError("failed to transfer call to background thread", message_id);
311     return;
312   }
313   std::string text;
314   bool success = liblouis_.BackTranslate(table_names, cells, &text);
315   Json::Value reply(Json::objectValue);
316   reply[kSuccessKey] = success;
317   if (success) {
318     reply[kTextKey] = text;
319   }
320   PostReply(reply, message_id);
321 }
322
323 }  // namespace liblouis_nacl