1 // Copyright 2013 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
15 #include "liblouis_instance.h"
19 #include <sys/mount.h>
22 #include "nacl_io/nacl_io.h"
23 #include "ppapi/c/pp_errors.h"
24 #include "ppapi/cpp/module.h"
26 #include "translation_result.h"
30 static const char kHexadecimalChars
[] = "0123456789abcdef";
32 // Converts a vector of bytes into a (lowercase) hexadecimal string.
33 static void BytesToHexString(const std::vector
<unsigned char>& bytes
,
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]);
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) {
53 std::vector
<unsigned char> bytes
;
54 bytes
.reserve(hex
.size() / 2);
55 for (size_t i
= 0; i
< hex
.size(); i
+= 2) {
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;
68 if ('0' <= ch
&& ch
<= '9') {
70 } else if ('a' <= ch
&& ch
<= 'f') {
71 byte
|= ch
- 'a' + 10;
72 } else if ('A' <= ch
&& ch
<= 'F') {
73 byte
|= ch
- 'A' + 10;
77 bytes
.push_back(byte
);
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
) {
96 namespace liblouis_nacl
{
98 // Well-known strings used for configuration.
99 static const char kTablesDirKey
[] = "tablesdir";
100 static const char kTablesDirDefault
[] = "tables";
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";
118 LibLouisInstance::LibLouisInstance(PP_Instance instance
)
119 : pp::Instance(instance
), liblouis_thread_(this), cc_factory_(this) {}
121 LibLouisInstance::~LibLouisInstance() {}
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
];
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.
139 return liblouis_thread_
.Start();
142 void LibLouisInstance::HandleMessage(const pp::Var
& var_message
) {
143 if (!var_message
.is_string()) {
144 PostError("expected message to be a JSON string");
150 bool parsed
= reader
.parse(var_message
.AsString(),
151 message
, false /* collectComments */);
153 PostError("expected message to be a JSON string");
157 Json::Value message_id
= message
[kMessageIdKey
];
158 if (!message_id
.isString()) {
159 PostError("expected message_id string");
162 std::string message_id_str
= message_id
.asString();
164 Json::Value command
= message
[kCommandKey
];
165 if (!command
.isString()) {
166 PostError("expected command string", message_id_str
);
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
);
178 PostError("unknown command", message_id_str
);
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
);
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
);
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
);
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
);
216 PostWorkToBackground(cc_factory_
.NewCallback(
217 &LibLouisInstance::CheckTableInBackground
,
218 table_names
.asString(), message_id
));
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
);
227 bool success
= liblouis_
.CheckTable(table_names
);
228 Json::Value
reply(Json::objectValue
);
229 reply
[kSuccessKey
] = success
;
230 PostReply(reply
, message_id
);
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
);
241 } else if (!text
.isString()) {
242 PostError("expected text to be a string", message_id
);
244 } else if (!cursor_position
.isNull() && !cursor_position
.isIntegral()) {
245 PostError("expected cursor_position to be null or integral", message_id
);
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
));
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
);
264 TranslationResult translation_result
;
265 bool success
= liblouis_
.Translate(params
, &translation_result
);
266 Json::Value
reply(Json::objectValue
);
267 reply
[kSuccessKey
] = 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
;
282 PostReply(reply
, message_id
);
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
);
292 } else if (!cells
.isString()) {
293 PostError("expected cells to be a string", message_id
);
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
);
301 PostWorkToBackground(cc_factory_
.NewCallback(
302 &LibLouisInstance::BackTranslateInBackground
,
303 table_names
.asString(), cells_vector
, message_id
));
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
);
314 bool success
= liblouis_
.BackTranslate(table_names
, cells
, &text
);
315 Json::Value
reply(Json::objectValue
);
316 reply
[kSuccessKey
] = success
;
318 reply
[kTextKey
] = text
;
320 PostReply(reply
, message_id
);
323 } // namespace liblouis_nacl